about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock27
-rw-r--r--library/alloc/src/collections/btree/map.rs205
-rw-r--r--library/alloc/src/collections/btree/navigate.rs104
-rw-r--r--library/alloc/src/collections/btree/node.rs4
-rw-r--r--library/alloc/src/raw_vec.rs10
-rw-r--r--library/alloc/tests/btree/map.rs24
-rw-r--r--library/alloc/tests/lib.rs1
-rw-r--r--library/core/src/array/iter.rs144
-rw-r--r--library/core/src/hint.rs4
-rw-r--r--library/core/src/mem/maybe_uninit.rs12
-rw-r--r--library/core/src/num/mod.rs48
-rw-r--r--library/core/src/ptr/const_ptr.rs32
-rw-r--r--library/core/src/ptr/mut_ptr.rs32
-rw-r--r--library/core/src/ptr/non_null.rs54
-rw-r--r--library/core/src/str/mod.rs15
-rw-r--r--library/core/src/time.rs18
-rw-r--r--library/panic_unwind/src/lib.rs2
-rw-r--r--library/std/Cargo.toml2
-rw-r--r--library/std/src/collections/hash/map.rs160
-rw-r--r--library/std/src/keyword_docs.rs20
-rw-r--r--library/std/src/net/ip.rs25
-rw-r--r--library/std/src/panicking.rs59
-rw-r--r--library/std/src/path.rs232
-rw-r--r--library/std/src/rt.rs10
-rw-r--r--library/std/src/sys/hermit/mod.rs2
-rw-r--r--library/std/src/sys/hermit/thread.rs2
-rw-r--r--library/std/src/sys/unix/fd.rs4
-rw-r--r--library/std/src/sys/windows/mod.rs41
-rw-r--r--library/std/src/sys_common/backtrace.rs39
-rw-r--r--library/std/src/thread/local.rs6
-rw-r--r--library/test/src/helpers/concurrency.rs4
-rw-r--r--library/test/src/lib.rs5
-rw-r--r--library/unwind/src/lib.rs2
-rw-r--r--src/etc/gdb_providers.py11
-rw-r--r--src/etc/lldb_providers.py11
-rw-r--r--src/etc/natvis/libstd.natvis35
-rw-r--r--src/librustc_ast/Cargo.toml1
-rw-r--r--src/librustc_ast/ast.rs30
-rw-r--r--src/librustc_ast/attr/mod.rs122
-rw-r--r--src/librustc_ast/entry.rs28
-rw-r--r--src/librustc_ast/expand/allocator.rs24
-rw-r--r--src/librustc_ast/expand/mod.rs9
-rw-r--r--src/librustc_ast/lib.rs1
-rw-r--r--src/librustc_ast/mut_visit.rs2
-rw-r--r--src/librustc_ast/token.rs13
-rw-r--r--src/librustc_ast/util/comments.rs120
-rw-r--r--src/librustc_ast/util/comments/tests.rs48
-rw-r--r--src/librustc_ast/util/lev_distance/tests.rs2
-rw-r--r--src/librustc_ast/visit.rs2
-rw-r--r--src/librustc_ast_lowering/expr.rs2
-rw-r--r--src/librustc_ast_lowering/item.rs3
-rw-r--r--src/librustc_ast_lowering/lib.rs11
-rw-r--r--src/librustc_ast_passes/ast_validation.rs16
-rw-r--r--src/librustc_ast_passes/feature_gate.rs79
-rw-r--r--src/librustc_ast_pretty/pprust.rs52
-rw-r--r--src/librustc_ast_pretty/pprust/tests.rs2
-rw-r--r--src/librustc_attr/builtin.rs164
-rw-r--r--src/librustc_builtin_macros/cfg.rs2
-rw-r--r--src/librustc_builtin_macros/cfg_accessible.rs7
-rw-r--r--src/librustc_builtin_macros/deriving/default.rs2
-rw-r--r--src/librustc_builtin_macros/deriving/generic/mod.rs4
-rw-r--r--src/librustc_builtin_macros/env.rs4
-rw-r--r--src/librustc_builtin_macros/global_allocator.rs2
-rw-r--r--src/librustc_builtin_macros/llvm_asm.rs10
-rw-r--r--src/librustc_builtin_macros/proc_macro_harness.rs17
-rw-r--r--src/librustc_builtin_macros/standard_library_imports.rs14
-rw-r--r--src/librustc_builtin_macros/test.rs31
-rw-r--r--src/librustc_builtin_macros/test_harness.rs101
-rw-r--r--src/librustc_builtin_macros/util.rs2
-rw-r--r--src/librustc_codegen_llvm/consts.rs2
-rw-r--r--src/librustc_codegen_llvm/coverageinfo/mapgen.rs19
-rw-r--r--src/librustc_codegen_llvm/coverageinfo/mod.rs7
-rw-r--r--src/librustc_codegen_llvm/debuginfo/gdb.rs7
-rw-r--r--src/librustc_codegen_llvm/debuginfo/metadata.rs4
-rw-r--r--src/librustc_codegen_ssa/back/link.rs4
-rw-r--r--src/librustc_codegen_ssa/back/write.rs12
-rw-r--r--src/librustc_codegen_ssa/mir/place.rs15
-rw-r--r--src/librustc_data_structures/Cargo.toml2
-rw-r--r--src/librustc_data_structures/transitive_relation.rs39
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustc_error_codes/error_codes/E0271.md43
-rw-r--r--src/librustc_error_codes/error_codes/E0502.md4
-rw-r--r--src/librustc_error_codes/error_codes/E0746.md10
-rw-r--r--src/librustc_error_codes/error_codes/E0747.md4
-rw-r--r--src/librustc_error_codes/error_codes/E0749.md23
-rw-r--r--src/librustc_error_codes/error_codes/E0750.md22
-rw-r--r--src/librustc_expand/base.rs40
-rw-r--r--src/librustc_expand/config.rs57
-rw-r--r--src/librustc_expand/expand.rs47
-rw-r--r--src/librustc_expand/mbe/macro_rules.rs52
-rw-r--r--src/librustc_expand/module.rs33
-rw-r--r--src/librustc_expand/mut_visit/tests.rs2
-rw-r--r--src/librustc_expand/parse/lexer/tests.rs18
-rw-r--r--src/librustc_expand/parse/tests.rs8
-rw-r--r--src/librustc_expand/proc_macro.rs8
-rw-r--r--src/librustc_expand/proc_macro_server.rs11
-rw-r--r--src/librustc_expand/tests.rs2
-rw-r--r--src/librustc_expand/tokenstream/tests.rs2
-rw-r--r--src/librustc_feature/active.rs5
-rw-r--r--src/librustc_hir/def.rs12
-rw-r--r--src/librustc_hir/lang_items.rs16
-rw-r--r--src/librustc_hir/weak_lang_items.rs9
-rw-r--r--src/librustc_incremental/assert_dep_graph.rs4
-rw-r--r--src/librustc_incremental/assert_module_sources.rs50
-rw-r--r--src/librustc_incremental/persist/dirty_clean.rs6
-rw-r--r--src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs6
-rw-r--r--src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs3
-rw-r--r--src/librustc_infer/infer/glb.rs4
-rw-r--r--src/librustc_infer/infer/nll_relate/mod.rs2
-rw-r--r--src/librustc_infer/infer/region_constraints/leak_check.rs4
-rw-r--r--src/librustc_infer/infer/sub.rs2
-rw-r--r--src/librustc_interface/interface.rs2
-rw-r--r--src/librustc_interface/passes.rs35
-rw-r--r--src/librustc_interface/proc_macro_decls.rs10
-rw-r--r--src/librustc_interface/queries.rs4
-rw-r--r--src/librustc_interface/tests.rs10
-rw-r--r--src/librustc_interface/util.rs48
-rw-r--r--src/librustc_lint/builtin.rs33
-rw-r--r--src/librustc_lint/levels.rs2
-rw-r--r--src/librustc_lint/nonstandard_style.rs9
-rw-r--r--src/librustc_lint/types.rs5
-rw-r--r--src/librustc_lint/unused.rs5
-rw-r--r--src/librustc_metadata/creader.rs40
-rw-r--r--src/librustc_metadata/link_args.rs12
-rw-r--r--src/librustc_metadata/native_libs.rs25
-rw-r--r--src/librustc_metadata/rmeta/decoder.rs4
-rw-r--r--src/librustc_metadata/rmeta/decoder/cstore_impl.rs5
-rw-r--r--src/librustc_metadata/rmeta/encoder.rs43
-rw-r--r--src/librustc_middle/middle/limits.rs2
-rw-r--r--src/librustc_middle/traits/query.rs2
-rw-r--r--src/librustc_middle/ty/context.rs10
-rw-r--r--src/librustc_middle/ty/flags.rs6
-rw-r--r--src/librustc_middle/ty/fold.rs6
-rw-r--r--src/librustc_middle/ty/instance.rs97
-rw-r--r--src/librustc_middle/ty/mod.rs10
-rw-r--r--src/librustc_middle/ty/query/job.rs12
-rw-r--r--src/librustc_middle/ty/query/on_disk_cache.rs22
-rw-r--r--src/librustc_mir/borrow_check/borrow_set.rs56
-rw-r--r--src/librustc_mir/borrow_check/constraint_generation.rs2
-rw-r--r--src/librustc_mir/borrow_check/diagnostics/mod.rs2
-rw-r--r--src/librustc_mir/borrow_check/invalidation.rs10
-rw-r--r--src/librustc_mir/borrow_check/mod.rs7
-rw-r--r--src/librustc_mir/borrow_check/nll.rs2
-rw-r--r--src/librustc_mir/borrow_check/region_infer/values.rs15
-rw-r--r--src/librustc_mir/borrow_check/type_check/mod.rs4
-rw-r--r--src/librustc_mir/dataflow/framework/engine.rs2
-rw-r--r--src/librustc_mir/dataflow/impls/borrows.rs14
-rw-r--r--src/librustc_mir/dataflow/mod.rs9
-rw-r--r--src/librustc_mir/monomorphize/polymorphize.rs51
-rw-r--r--src/librustc_mir/transform/const_prop.rs130
-rw-r--r--src/librustc_mir/transform/promote_consts.rs2
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs4
-rw-r--r--src/librustc_mir/transform/rustc_peek.rs13
-rw-r--r--src/librustc_mir/transform/simplify_try.rs2
-rw-r--r--src/librustc_mir/transform/validate.rs2
-rw-r--r--src/librustc_mir_build/build/matches/mod.rs9
-rw-r--r--src/librustc_mir_build/build/matches/test.rs31
-rw-r--r--src/librustc_mir_build/build/mod.rs2
-rw-r--r--src/librustc_mir_build/thir/cx/mod.rs3
-rw-r--r--src/librustc_parse/lexer/mod.rs24
-rw-r--r--src/librustc_parse/lib.rs6
-rw-r--r--src/librustc_parse/parser/attr.rs15
-rw-r--r--src/librustc_parse/parser/diagnostics.rs2
-rw-r--r--src/librustc_parse/parser/expr.rs19
-rw-r--r--src/librustc_parse/parser/generics.rs2
-rw-r--r--src/librustc_parse/parser/item.rs4
-rw-r--r--src/librustc_parse/parser/mod.rs20
-rw-r--r--src/librustc_parse_format/lib.rs2
-rw-r--r--src/librustc_passes/check_attr.rs27
-rw-r--r--src/librustc_passes/check_const.rs2
-rw-r--r--src/librustc_passes/dead.rs8
-rw-r--r--src/librustc_passes/diagnostic_items.rs7
-rw-r--r--src/librustc_passes/entry.rs15
-rw-r--r--src/librustc_passes/lang_items.rs3
-rw-r--r--src/librustc_passes/layout_test.rs2
-rw-r--r--src/librustc_passes/lib_features.rs4
-rw-r--r--src/librustc_passes/liveness.rs2
-rw-r--r--src/librustc_passes/stability.rs13
-rw-r--r--src/librustc_passes/weak_lang_items.rs3
-rw-r--r--src/librustc_plugin_impl/build.rs10
-rw-r--r--src/librustc_plugin_impl/lib.rs1
-rw-r--r--src/librustc_plugin_impl/load.rs2
-rw-r--r--src/librustc_privacy/lib.rs17
-rw-r--r--src/librustc_resolve/build_reduced_graph.rs27
-rw-r--r--src/librustc_resolve/diagnostics.rs24
-rw-r--r--src/librustc_resolve/late.rs42
-rw-r--r--src/librustc_resolve/late/lifetimes.rs13
-rw-r--r--src/librustc_resolve/lib.rs39
-rw-r--r--src/librustc_resolve/macros.rs6
-rw-r--r--src/librustc_save_analysis/lib.rs13
-rw-r--r--src/librustc_session/filesearch.rs2
-rw-r--r--src/librustc_session/output.rs45
-rw-r--r--src/librustc_session/session.rs79
-rw-r--r--src/librustc_span/hygiene.rs37
-rw-r--r--src/librustc_span/lib.rs73
-rw-r--r--src/librustc_span/span_encoding.rs17
-rw-r--r--src/librustc_span/symbol.rs6
-rw-r--r--src/librustc_symbol_mangling/test.rs6
-rw-r--r--src/librustc_trait_selection/autoderef.rs2
-rw-r--r--src/librustc_trait_selection/traits/error_reporting/suggestions.rs2
-rw-r--r--src/librustc_trait_selection/traits/on_unimplemented.rs2
-rw-r--r--src/librustc_trait_selection/traits/select/mod.rs5
-rw-r--r--src/librustc_traits/chalk/db.rs14
-rw-r--r--src/librustc_ty/ty.rs2
-rw-r--r--src/librustc_typeck/check/demand.rs2
-rw-r--r--src/librustc_typeck/check/dropck.rs2
-rw-r--r--src/librustc_typeck/check/generator_interior.rs33
-rw-r--r--src/librustc_typeck/check/mod.rs4
-rw-r--r--src/librustc_typeck/check/pat.rs36
-rw-r--r--src/librustc_typeck/check/place_op.rs12
-rw-r--r--src/librustc_typeck/collect.rs70
-rw-r--r--src/librustc_typeck/collect/type_of.rs44
-rw-r--r--src/librustc_typeck/lib.rs4
-rw-r--r--src/librustc_typeck/mem_categorization.rs4
-rw-r--r--src/librustdoc/clean/cfg/tests.rs2
-rw-r--r--src/librustdoc/clean/mod.rs27
-rw-r--r--src/librustdoc/clean/types.rs13
-rw-r--r--src/librustdoc/clean/utils.rs14
-rw-r--r--src/librustdoc/docfs.rs10
-rw-r--r--src/librustdoc/html/highlight/tests.rs2
-rw-r--r--src/librustdoc/html/render/mod.rs4
-rw-r--r--src/librustdoc/html/static/main.js4
-rw-r--r--src/librustdoc/html/static/rustdoc.css28
-rw-r--r--src/librustdoc/html/static/themes/ayu.css2
-rw-r--r--src/librustdoc/html/static/themes/dark.css2
-rw-r--r--src/librustdoc/html/static/themes/light.css2
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs6
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs194
-rw-r--r--src/librustdoc/test.rs9
-rw-r--r--src/test/codegen-units/polymorphization/pr-75255.rs52
-rw-r--r--src/test/codegen/some-global-nonnull.rs25
-rw-r--r--src/test/debuginfo/function-arguments-naked.rs10
-rw-r--r--src/test/debuginfo/pretty-std-collections-hash.rs30
-rw-r--r--src/test/mir-opt/const-promotion-extern-static.rs2
-rw-r--r--src/test/mir-opt/const_allocation.rs2
-rw-r--r--src/test/mir-opt/const_allocation2.rs2
-rw-r--r--src/test/mir-opt/const_allocation3.rs2
-rw-r--r--src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit85
-rw-r--r--src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit85
-rw-r--r--src/test/mir-opt/const_prop/large_array_index.rs7
-rw-r--r--src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff6
-rw-r--r--src/test/mir-opt/inline/inline-into-box-place.rs2
-rw-r--r--src/test/rustdoc-ui/auxiliary/extern_macros.rs7
-rw-r--r--src/test/rustdoc-ui/doctest-output.rs12
-rw-r--r--src/test/rustdoc-ui/doctest-output.stdout9
-rw-r--r--src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs68
-rw-r--r--src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr95
-rw-r--r--src/test/rustdoc/auxiliary/elided-lifetime.rs11
-rw-r--r--src/test/rustdoc/elided-lifetime.rs43
-rw-r--r--src/test/rustdoc/intra-link-prim-precedence.rs7
-rw-r--r--src/test/rustdoc/intra-link-trait-item.rs12
-rw-r--r--src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs2
-rw-r--r--src/test/ui-fulldeps/auxiliary/lint-for-crate.rs2
-rw-r--r--src/test/ui-fulldeps/mod_dir_path_canonicalized.rs2
-rw-r--r--src/test/ui-fulldeps/pprust-expr-roundtrip.rs2
-rw-r--r--src/test/ui/asm/bad-arch.rs19
-rw-r--r--src/test/ui/asm/bad-arch.stderr8
-rw-r--r--src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs23
-rw-r--r--src/test/ui/const-generics/const-param-in-trait-ungated.stderr4
-rw-r--r--src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr4
-rw-r--r--src/test/ui/const-generics/defaults/wrong-order.full.stderr (renamed from src/test/ui/const-generics/defaults/wrong-order.stderr)8
-rw-r--r--src/test/ui/const-generics/defaults/wrong-order.min.stderr10
-rw-r--r--src/test/ui/const-generics/defaults/wrong-order.rs4
-rw-r--r--src/test/ui/const-generics/issues/issue-56445.full.stderr (renamed from src/test/ui/const-generics/issues/issue-56445.stderr)6
-rw-r--r--src/test/ui/const-generics/issues/issue-56445.min.stderr20
-rw-r--r--src/test/ui/const-generics/issues/issue-56445.rs7
-rw-r--r--src/test/ui/const-generics/issues/issue-60263.stderr4
-rw-r--r--src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr (renamed from src/test/ui/const-generics/issues/issue-61336-1.stderr)6
-rw-r--r--src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs6
-rw-r--r--src/test/ui/const-generics/issues/issue-61336-1.full.stderr (renamed from src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr)6
-rw-r--r--src/test/ui/const-generics/issues/issue-61336-1.rs6
-rw-r--r--src/test/ui/const-generics/issues/issue-61336-2.full.stderr (renamed from src/test/ui/const-generics/issues/issue-61336-2.stderr)8
-rw-r--r--src/test/ui/const-generics/issues/issue-61336-2.min.stderr15
-rw-r--r--src/test/ui/const-generics/issues/issue-61336-2.rs5
-rw-r--r--src/test/ui/const-generics/issues/issue-61336.full.stderr24
-rw-r--r--src/test/ui/const-generics/issues/issue-61336.min.stderr15
-rw-r--r--src/test/ui/const-generics/issues/issue-61336.rs5
-rw-r--r--src/test/ui/const-generics/issues/issue-61422.full.stderr (renamed from src/test/ui/const-generics/issues/issue-61432.stderr)6
-rw-r--r--src/test/ui/const-generics/issues/issue-61422.rs6
-rw-r--r--src/test/ui/const-generics/issues/issue-61432.full.stderr (renamed from src/test/ui/const-generics/issues/issue-61422.stderr)6
-rw-r--r--src/test/ui/const-generics/issues/issue-61432.rs6
-rw-r--r--src/test/ui/const-generics/issues/issue-61747.full.stderr (renamed from src/test/ui/const-generics/issues/issue-61747.stderr)8
-rw-r--r--src/test/ui/const-generics/issues/issue-61747.min.stderr10
-rw-r--r--src/test/ui/const-generics/issues/issue-61747.rs8
-rw-r--r--src/test/ui/const-generics/min_const_generics/assoc_const.rs18
-rw-r--r--src/test/ui/const-generics/min_const_generics/complex-expression.rs29
-rw-r--r--src/test/ui/const-generics/min_const_generics/complex-expression.stderr34
-rw-r--r--src/test/ui/const-generics/min_const_generics/complex-types.rs18
-rw-r--r--src/test/ui/const-generics/min_const_generics/complex-types.stderr38
-rw-r--r--src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs4
-rw-r--r--src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr12
-rw-r--r--src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs5
-rw-r--r--src/test/ui/feature-gates/feature-gate-asm.rs2
-rw-r--r--src/test/ui/feature-gates/feature-gate-asm2.rs2
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr8
-rw-r--r--src/test/ui/feature-gates/feature-gate-const_generics.stderr8
-rw-r--r--src/test/ui/higher-rank-trait-bounds/issue-59311.rs20
-rw-r--r--src/test/ui/higher-rank-trait-bounds/issue-59311.stderr8
-rw-r--r--src/test/ui/issues/issue-40782.fixed2
-rw-r--r--src/test/ui/issues/issue-40782.rs2
-rw-r--r--src/test/ui/issues/issue-40782.stderr8
-rw-r--r--src/test/ui/issues/issue-75283.rs6
-rw-r--r--src/test/ui/issues/issue-75283.stderr18
-rw-r--r--src/test/ui/panics/issue-47429-short-backtraces.rs18
-rw-r--r--src/test/ui/panics/issue-47429-short-backtraces.run.stderr5
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/fn.rs29
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/fnmut.rs34
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/fnonce.rs34
-rw-r--r--src/test/ui/polymorphization/closure_in_upvar/other.rs38
-rw-r--r--src/test/ui/polymorphization/promoted-function-1.rs12
-rw-r--r--src/test/ui/polymorphization/promoted-function-1.stderr8
-rw-r--r--src/test/ui/polymorphization/promoted-function-2.rs16
-rw-r--r--src/test/ui/polymorphization/promoted-function-2.stderr17
-rw-r--r--src/test/ui/polymorphization/promoted-function.rs2
-rw-r--r--src/test/ui/polymorphization/unsized_cast.rs1
-rw-r--r--src/test/ui/polymorphization/unsized_cast.stderr18
-rw-r--r--src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs10
-rw-r--r--src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr15
-rw-r--r--src/test/ui/proc-macro/doc-comment-preserved.rs24
-rw-r--r--src/test/ui/proc-macro/doc-comment-preserved.stdout54
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-57089.rs7
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-57089.stdout32
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-62325.rs8
-rw-r--r--src/test/ui/proc-macro/dollar-crate-issue-62325.stdout44
-rw-r--r--src/test/ui/proc-macro/dollar-crate.rs7
-rw-r--r--src/test/ui/proc-macro/dollar-crate.stdout96
-rw-r--r--src/test/ui/proc-macro/input-interpolated.rs5
-rw-r--r--src/test/ui/proc-macro/input-interpolated.stdout24
-rw-r--r--src/test/ui/proc-macro/meta-macro-hygiene.rs7
-rw-r--r--src/test/ui/proc-macro/meta-macro-hygiene.stdout66
-rw-r--r--src/test/ui/proc-macro/meta-macro.rs4
-rw-r--r--src/test/ui/proc-macro/meta-macro.stdout2
-rw-r--r--src/test/ui/proc-macro/nested-macro-rules.rs4
-rw-r--r--src/test/ui/proc-macro/nested-macro-rules.stdout8
-rw-r--r--src/test/ui/proc-macro/nodelim-groups.rs4
-rw-r--r--src/test/ui/proc-macro/nodelim-groups.stdout54
-rw-r--r--src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr6
-rw-r--r--src/test/ui/simd/simd-intrinsic-generic-bitmask.rs1
-rw-r--r--src/test/ui/simd/simd-intrinsic-generic-select.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/doc.rs81
-rw-r--r--src/tools/clippy/clippy_lints/src/functions.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs7
-rwxr-xr-xsrc/tools/clippy/clippy_lints/src/utils/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/attrs.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs2
-rw-r--r--src/tools/clippy/tests/ui/indexing_slicing_index.stderr22
-rw-r--r--src/tools/compiletest/src/common.rs2
-rw-r--r--src/tools/compiletest/src/header.rs1
-rw-r--r--src/tools/compiletest/src/runtest.rs19
-rw-r--r--src/tools/compiletest/src/util.rs20
-rw-r--r--src/tools/error_index_generator/main.rs3
m---------src/tools/miri18
-rwxr-xr-xsrc/tools/publish_toolstate.py2
-rw-r--r--src/tools/tidy/src/error_codes_check.rs3
-rw-r--r--src/tools/unicode-table-generator/src/main.rs2
357 files changed, 4485 insertions, 2505 deletions
diff --git a/Cargo.lock b/Cargo.lock
index d4f4ec7f6f0..2685de2244e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -139,12 +139,6 @@ dependencies = [
 
 [[package]]
 name = "autocfg"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
-
-[[package]]
-name = "autocfg"
 version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
@@ -766,7 +760,7 @@ version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
 dependencies = [
- "autocfg 1.0.0",
+ "autocfg",
  "cfg-if",
  "lazy_static",
 ]
@@ -1245,11 +1239,11 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.6.2"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cd9867f119b19fecb08cd5c326ad4488d7a1da4bf75b4d95d71db742525aaab"
+checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25"
 dependencies = [
- "autocfg 0.1.7",
+ "autocfg",
  "compiler_builtins",
  "rustc-std-workspace-alloc",
  "rustc-std-workspace-core",
@@ -1388,9 +1382,13 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "1.0.2"
+version = "1.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e81a7c05f79578dbc15793d8b619db9ba32b4577003ef3af1a91c416798c58d"
+checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
 
 [[package]]
 name = "installer"
@@ -2079,7 +2077,7 @@ version = "0.9.54"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1024c0a59774200a555087a6da3f253a9095a5f344e353b212ac4c8b8e450986"
 dependencies = [
- "autocfg 1.0.0",
+ "autocfg",
  "cc",
  "libc",
  "openssl-src",
@@ -3201,7 +3199,6 @@ dependencies = [
  "rustc_macros",
  "rustc_serialize",
  "rustc_span",
- "scoped-tls",
  "smallvec 1.4.0",
  "tracing",
 ]
@@ -4925,7 +4922,7 @@ dependencies = [
  "ansi_term 0.12.1",
  "lazy_static",
  "matchers",
- "parking_lot 0.9.0",
+ "parking_lot 0.10.2",
  "regex",
  "sharded-slab",
  "smallvec 1.4.0",
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 1db629c3bdf..7e27aeb8539 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -1,3 +1,5 @@
+// ignore-tidy-filelength
+
 use core::borrow::Borrow;
 use core::cmp::Ordering;
 use core::fmt::Debug;
@@ -355,6 +357,30 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> {
     inner: IterMut<'a, K, V>,
 }
 
+/// An owning iterator over the keys of a `BTreeMap`.
+///
+/// This `struct` is created by the [`into_keys`] method on [`BTreeMap`].
+/// See its documentation for more.
+///
+/// [`into_keys`]: BTreeMap::into_keys
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[derive(Debug)]
+pub struct IntoKeys<K, V> {
+    inner: IntoIter<K, V>,
+}
+
+/// An owning iterator over the values of a `BTreeMap`.
+///
+/// This `struct` is created by the [`into_values`] method on [`BTreeMap`].
+/// See its documentation for more.
+///
+/// [`into_values`]: BTreeMap::into_values
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+#[derive(Debug)]
+pub struct IntoValues<K, V> {
+    inner: IntoIter<K, V>,
+}
+
 /// An iterator over a sub-range of entries in a `BTreeMap`.
 ///
 /// This `struct` is created by the [`range`] method on [`BTreeMap`]. See its
@@ -1291,10 +1317,56 @@ impl<K: Ord, V> BTreeMap<K, V> {
 
         self.length = dfs(self.root.as_ref().unwrap().as_ref());
     }
+
+    /// Creates a consuming iterator visiting all the keys, in sorted order.
+    /// The map cannot be used after calling this.
+    /// The iterator element type is `K`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(map_into_keys_values)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut a = BTreeMap::new();
+    /// a.insert(2, "b");
+    /// a.insert(1, "a");
+    ///
+    /// let keys: Vec<i32> = a.into_keys().collect();
+    /// assert_eq!(keys, [1, 2]);
+    /// ```
+    #[inline]
+    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    pub fn into_keys(self) -> IntoKeys<K, V> {
+        IntoKeys { inner: self.into_iter() }
+    }
+
+    /// Creates a consuming iterator visiting all the values, in order by key.
+    /// The map cannot be used after calling this.
+    /// The iterator element type is `V`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(map_into_keys_values)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut a = BTreeMap::new();
+    /// a.insert(1, "hello");
+    /// a.insert(2, "goodbye");
+    ///
+    /// let values: Vec<&str> = a.into_values().collect();
+    /// assert_eq!(values, ["hello", "goodbye"]);
+    /// ```
+    #[inline]
+    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    pub fn into_values(self) -> IntoValues<K, V> {
+        IntoValues { inner: self.into_iter() }
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap<K, V> {
+impl<'a, K, V> IntoIterator for &'a BTreeMap<K, V> {
     type Item = (&'a K, &'a V);
     type IntoIter = Iter<'a, K, V>;
 
@@ -1363,7 +1435,7 @@ impl<K, V> Clone for Iter<'_, K, V> {
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
-impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap<K, V> {
+impl<'a, K, V> IntoIterator for &'a mut BTreeMap<K, V> {
     type Item = (&'a K, &'a mut V);
     type IntoIter = IterMut<'a, K, V>;
 
@@ -1697,10 +1769,9 @@ impl<'a, K: 'a, V: 'a> DrainFilterInner<'a, K, V> {
             let (k, v) = kv.kv_mut();
             if pred(k, v) {
                 *self.length -= 1;
-                let RemoveResult { old_kv, pos, emptied_internal_root } = kv.remove_kv_tracking();
+                let (kv, pos) = kv.remove_kv_tracking(|_| self.emptied_internal_root = true);
                 self.cur_leaf_edge = Some(pos);
-                self.emptied_internal_root |= emptied_internal_root;
-                return Some(old_kv);
+                return Some(kv);
             }
             self.cur_leaf_edge = Some(kv.next_leaf_edge());
         }
@@ -1781,6 +1852,82 @@ impl<'a, K, V> Range<'a, K, V> {
     }
 }
 
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoKeys<K, V> {
+    type Item = K;
+
+    fn next(&mut self) -> Option<K> {
+        self.inner.next().map(|(k, _)| k)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+
+    fn last(mut self) -> Option<K> {
+        self.next_back()
+    }
+
+    fn min(mut self) -> Option<K> {
+        self.next()
+    }
+
+    fn max(mut self) -> Option<K> {
+        self.next_back()
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> DoubleEndedIterator for IntoKeys<K, V> {
+    fn next_back(&mut self) -> Option<K> {
+        self.inner.next_back().map(|(k, _)| k)
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoKeys<K, V> {
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoKeys<K, V> {}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoValues<K, V> {
+    type Item = V;
+
+    fn next(&mut self) -> Option<V> {
+        self.inner.next().map(|(_, v)| v)
+    }
+
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+
+    fn last(mut self) -> Option<V> {
+        self.next_back()
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> DoubleEndedIterator for IntoValues<K, V> {
+    fn next_back(&mut self) -> Option<V> {
+        self.inner.next_back().map(|(_, v)| v)
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoValues<K, V> {
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoValues<K, V> {}
+
 #[stable(feature = "btree_range", since = "1.17.0")]
 impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> {
     fn next_back(&mut self) -> Option<(&'a K, &'a V)> {
@@ -2645,35 +2792,28 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> {
     fn remove_kv(self) -> (K, V) {
         *self.length -= 1;
 
-        let RemoveResult { old_kv, pos, emptied_internal_root } = self.handle.remove_kv_tracking();
-        let root = pos.into_node().into_root_mut();
-        if emptied_internal_root {
-            root.pop_internal_level();
-        }
+        let (old_kv, _) =
+            self.handle.remove_kv_tracking(|root| root.into_root_mut().pop_internal_level());
         old_kv
     }
 }
 
-struct RemoveResult<'a, K, V> {
-    // Key and value removed.
-    old_kv: (K, V),
-    // Unique location at the leaf level that the removed KV lopgically collapsed into.
-    pos: Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>,
-    // Whether the remove left behind and empty internal root node, that should be removed
-    // using `pop_internal_level`.
-    emptied_internal_root: bool,
-}
-
 impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV> {
     /// Removes a key/value-pair from the tree, and returns that pair, as well as
     /// the leaf edge corresponding to that former pair. It's possible this leaves
     /// an empty internal root node, which the caller should subsequently pop from
     /// the map holding the tree. The caller should also decrement the map's length.
-    fn remove_kv_tracking(self) -> RemoveResult<'a, K, V> {
-        let (mut pos, old_key, old_val, was_internal) = match self.force() {
+    fn remove_kv_tracking<F>(
+        self,
+        handle_emptied_internal_root: F,
+    ) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>)
+    where
+        F: FnOnce(NodeRef<marker::Mut<'a>, K, V, marker::Internal>),
+    {
+        let (old_kv, mut pos, was_internal) = match self.force() {
             Leaf(leaf) => {
-                let (hole, old_key, old_val) = leaf.remove();
-                (hole, old_key, old_val, false)
+                let (old_kv, pos) = leaf.remove();
+                (old_kv, pos, false)
             }
             Internal(mut internal) => {
                 // Replace the location freed in the internal node with the next KV,
@@ -2688,17 +2828,16 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInter
                 let to_remove = internal.left_edge().descend().last_leaf_edge().left_kv().ok();
                 let to_remove = unsafe { unwrap_unchecked(to_remove) };
 
-                let (hole, key, val) = to_remove.remove();
+                let (kv, pos) = to_remove.remove();
 
-                let old_key = unsafe { mem::replace(&mut *key_loc, key) };
-                let old_val = unsafe { mem::replace(&mut *val_loc, val) };
+                let old_key = unsafe { mem::replace(&mut *key_loc, kv.0) };
+                let old_val = unsafe { mem::replace(&mut *val_loc, kv.1) };
 
-                (hole, old_key, old_val, true)
+                ((old_key, old_val), pos, true)
             }
         };
 
         // Handle underflow
-        let mut emptied_internal_root = false;
         let mut cur_node = unsafe { ptr::read(&pos).into_node().forget_type() };
         let mut at_leaf = true;
         while cur_node.len() < node::MIN_LEN {
@@ -2719,8 +2858,10 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInter
 
                     let parent = edge.into_node();
                     if parent.len() == 0 {
-                        // This empty parent must be the root, and should be popped off the tree.
-                        emptied_internal_root = true;
+                        // The parent that was just emptied must be the root,
+                        // because nodes on a lower level would not have been
+                        // left underfull. It has to be popped off the tree soon.
+                        handle_emptied_internal_root(parent);
                         break;
                     } else {
                         cur_node = parent.forget_type();
@@ -2747,7 +2888,7 @@ impl<'a, K: 'a, V: 'a> Handle<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInter
             pos = unsafe { unwrap_unchecked(pos.next_kv().ok()).next_leaf_edge() };
         }
 
-        RemoveResult { old_kv: (old_key, old_val), pos, emptied_internal_root }
+        (old_kv, pos)
     }
 }
 
diff --git a/library/alloc/src/collections/btree/navigate.rs b/library/alloc/src/collections/btree/navigate.rs
index 0dcb5930964..33b1ee003ed 100644
--- a/library/alloc/src/collections/btree/navigate.rs
+++ b/library/alloc/src/collections/btree/navigate.rs
@@ -1,3 +1,5 @@
+use core::intrinsics;
+use core::mem;
 use core::ptr;
 
 use super::node::{marker, ForceResult::*, Handle, NodeRef};
@@ -79,16 +81,24 @@ def_next_kv_uncheched_dealloc! {unsafe fn next_kv_unchecked_dealloc: right_kv}
 def_next_kv_uncheched_dealloc! {unsafe fn next_back_kv_unchecked_dealloc: left_kv}
 
 /// This replaces the value behind the `v` unique reference by calling the
-/// relevant function.
+/// relevant function, and returns a result obtained along the way.
 ///
-/// Safety: The change closure must not panic.
+/// If a panic occurs in the `change` closure, the entire process will be aborted.
 #[inline]
-unsafe fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
+fn replace<T, R>(v: &mut T, change: impl FnOnce(T) -> (T, R)) -> R {
+    struct PanicGuard;
+    impl Drop for PanicGuard {
+        fn drop(&mut self) {
+            intrinsics::abort()
+        }
+    }
+    let guard = PanicGuard;
     let value = unsafe { ptr::read(v) };
     let (new_value, ret) = change(value);
     unsafe {
         ptr::write(v, new_value);
     }
+    mem::forget(guard);
     ret
 }
 
@@ -97,26 +107,22 @@ impl<'a, K, V> Handle<NodeRef<marker::Immut<'a>, K, V, marker::Leaf>, marker::Ed
     /// key and value in between.
     /// Unsafe because the caller must ensure that the leaf edge is not the last one in the tree.
     pub unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (kv.next_leaf_edge(), kv.into_kv())
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (kv.next_leaf_edge(), kv.into_kv())
+        })
     }
 
     /// Moves the leaf edge handle to the previous leaf edge and returns references to the
     /// key and value in between.
     /// Unsafe because the caller must ensure that the leaf edge is not the first one in the tree.
     pub unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_back_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (kv.next_back_leaf_edge(), kv.into_kv())
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_back_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (kv.next_back_leaf_edge(), kv.into_kv())
+        })
     }
 }
 
@@ -127,16 +133,14 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge
     /// - The caller must ensure that the leaf edge is not the last one in the tree.
     /// - Using the updated handle may well invalidate the returned references.
     pub unsafe fn next_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
-        unsafe {
-            let kv = replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (ptr::read(&kv).next_leaf_edge(), kv)
-            });
-            // Doing the descend (and perhaps another move) invalidates the references
-            // returned by `into_kv_mut`, so we have to do this last.
-            kv.into_kv_mut()
-        }
+        let kv = replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (unsafe { ptr::read(&kv) }.next_leaf_edge(), kv)
+        });
+        // Doing the descend (and perhaps another move) invalidates the references
+        // returned by `into_kv_mut`, so we have to do this last.
+        kv.into_kv_mut()
     }
 
     /// Moves the leaf edge handle to the previous leaf and returns references to the
@@ -145,16 +149,14 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge
     /// - The caller must ensure that the leaf edge is not the first one in the tree.
     /// - Using the updated handle may well invalidate the returned references.
     pub unsafe fn next_back_unchecked(&mut self) -> (&'a mut K, &'a mut V) {
-        unsafe {
-            let kv = replace(self, |leaf_edge| {
-                let kv = leaf_edge.next_back_kv();
-                let kv = unwrap_unchecked(kv.ok());
-                (ptr::read(&kv).next_back_leaf_edge(), kv)
-            });
-            // Doing the descend (and perhaps another move) invalidates the references
-            // returned by `into_kv_mut`, so we have to do this last.
-            kv.into_kv_mut()
-        }
+        let kv = replace(self, |leaf_edge| {
+            let kv = leaf_edge.next_back_kv();
+            let kv = unsafe { unwrap_unchecked(kv.ok()) };
+            (unsafe { ptr::read(&kv) }.next_back_leaf_edge(), kv)
+        });
+        // Doing the descend (and perhaps another move) invalidates the references
+        // returned by `into_kv_mut`, so we have to do this last.
+        kv.into_kv_mut()
     }
 }
 
@@ -172,14 +174,12 @@ impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
     ///   call this method again subject to both preconditions listed in the first point,
     ///   or call counterpart `next_back_unchecked` subject to its preconditions.
     pub unsafe fn next_unchecked(&mut self) -> (K, V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = next_kv_unchecked_dealloc(leaf_edge);
-                let k = ptr::read(kv.reborrow().into_kv().0);
-                let v = ptr::read(kv.reborrow().into_kv().1);
-                (kv.next_leaf_edge(), (k, v))
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = unsafe { next_kv_unchecked_dealloc(leaf_edge) };
+            let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+            let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
+            (kv.next_leaf_edge(), (k, v))
+        })
     }
 
     /// Moves the leaf edge handle to the previous leaf edge and returns the key
@@ -195,14 +195,12 @@ impl<K, V> Handle<NodeRef<marker::Owned, K, V, marker::Leaf>, marker::Edge> {
     ///   call this method again subject to both preconditions listed in the first point,
     ///   or call counterpart `next_unchecked` subject to its preconditions.
     pub unsafe fn next_back_unchecked(&mut self) -> (K, V) {
-        unsafe {
-            replace(self, |leaf_edge| {
-                let kv = next_back_kv_unchecked_dealloc(leaf_edge);
-                let k = ptr::read(kv.reborrow().into_kv().0);
-                let v = ptr::read(kv.reborrow().into_kv().1);
-                (kv.next_back_leaf_edge(), (k, v))
-            })
-        }
+        replace(self, |leaf_edge| {
+            let kv = unsafe { next_back_kv_unchecked_dealloc(leaf_edge) };
+            let k = unsafe { ptr::read(kv.reborrow().into_kv().0) };
+            let v = unsafe { ptr::read(kv.reborrow().into_kv().1) };
+            (kv.next_back_leaf_edge(), (k, v))
+        })
     }
 }
 
diff --git a/library/alloc/src/collections/btree/node.rs b/library/alloc/src/collections/btree/node.rs
index 6a4c495ea14..4e52c16d20d 100644
--- a/library/alloc/src/collections/btree/node.rs
+++ b/library/alloc/src/collections/btree/node.rs
@@ -1083,12 +1083,12 @@ impl<'a, K, V> Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::KV>
     /// between the now adjacent key/value pairs (if any) to the left and right of this handle.
     pub fn remove(
         mut self,
-    ) -> (Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>, K, V) {
+    ) -> ((K, V), Handle<NodeRef<marker::Mut<'a>, K, V, marker::Leaf>, marker::Edge>) {
         unsafe {
             let k = slice_remove(self.node.keys_mut(), self.idx);
             let v = slice_remove(self.node.vals_mut(), self.idx);
             (*self.node.as_leaf_mut()).len -= 1;
-            (self.left_edge(), k, v)
+            ((k, v), self.left_edge())
         }
     }
 }
diff --git a/library/alloc/src/raw_vec.rs b/library/alloc/src/raw_vec.rs
index 2abd7231711..247b636c808 100644
--- a/library/alloc/src/raw_vec.rs
+++ b/library/alloc/src/raw_vec.rs
@@ -203,13 +203,15 @@ impl<T, A: AllocRef> RawVec<T, A> {
     ///
     /// # Safety
     ///
-    /// The `ptr` must be allocated (via the given allocator `a`), and with the given `capacity`.
+    /// The `ptr` must be allocated (via the given allocator `alloc`), and with the given
+    /// `capacity`.
     /// The `capacity` cannot exceed `isize::MAX` for sized types. (only a concern on 32-bit
     /// systems). ZST vectors may have a capacity up to `usize::MAX`.
-    /// If the `ptr` and `capacity` come from a `RawVec` created via `a`, then this is guaranteed.
+    /// If the `ptr` and `capacity` come from a `RawVec` created via `alloc`, then this is
+    /// guaranteed.
     #[inline]
-    pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, a: A) -> Self {
-        Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc: a }
+    pub unsafe fn from_raw_parts_in(ptr: *mut T, capacity: usize, alloc: A) -> Self {
+        Self { ptr: unsafe { Unique::new_unchecked(ptr) }, cap: capacity, alloc }
     }
 
     /// Gets a raw pointer to the start of the allocation. Note that this is
diff --git a/library/alloc/tests/btree/map.rs b/library/alloc/tests/btree/map.rs
index f9f81716e35..5777bd60907 100644
--- a/library/alloc/tests/btree/map.rs
+++ b/library/alloc/tests/btree/map.rs
@@ -1461,3 +1461,27 @@ fn test_into_iter_drop_leak_height_1() {
         assert_eq!(DROPS.load(Ordering::SeqCst), size);
     }
 }
+
+#[test]
+fn test_into_keys() {
+    let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+    let map: BTreeMap<_, _> = vec.into_iter().collect();
+    let keys: Vec<_> = map.into_keys().collect();
+
+    assert_eq!(keys.len(), 3);
+    assert!(keys.contains(&1));
+    assert!(keys.contains(&2));
+    assert!(keys.contains(&3));
+}
+
+#[test]
+fn test_into_values() {
+    let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+    let map: BTreeMap<_, _> = vec.into_iter().collect();
+    let values: Vec<_> = map.into_values().collect();
+
+    assert_eq!(values.len(), 3);
+    assert!(values.contains(&'a'));
+    assert!(values.contains(&'b'));
+    assert!(values.contains(&'c'));
+}
diff --git a/library/alloc/tests/lib.rs b/library/alloc/tests/lib.rs
index c680a3fc25b..3aacd4a687e 100644
--- a/library/alloc/tests/lib.rs
+++ b/library/alloc/tests/lib.rs
@@ -4,6 +4,7 @@
 #![feature(drain_filter)]
 #![feature(exact_size_is_empty)]
 #![feature(map_first_last)]
+#![feature(map_into_keys_values)]
 #![feature(new_uninit)]
 #![feature(pattern)]
 #![feature(str_split_once)]
diff --git a/library/core/src/array/iter.rs b/library/core/src/array/iter.rs
index 174f7e26efb..919070aadf9 100644
--- a/library/core/src/array/iter.rs
+++ b/library/core/src/array/iter.rs
@@ -56,38 +56,34 @@ impl<T, const N: usize> IntoIter<T, N> {
 
         // FIXME(LukasKalbertodt): actually use `mem::transmute` here, once it
         // works with const generics:
-        //     `mem::transmute::<[T; {N}], [MaybeUninit<T>; {N}]>(array)`
+        //     `mem::transmute::<[T; N], [MaybeUninit<T>; N]>(array)`
         //
-        // Until then, we do it manually here. We first create a bitwise copy
-        // but cast the pointer so that it is treated as a different type. Then
-        // we forget `array` so that it is not dropped.
-        let data = unsafe {
-            let data = ptr::read(&array as *const [T; N] as *const [MaybeUninit<T>; N]);
+        // Until then, we can use `mem::transmute_copy` to create a bitwise copy
+        // as a different type, then forget `array` so that it is not dropped.
+        unsafe {
+            let iter = Self { data: mem::transmute_copy(&array), alive: 0..N };
             mem::forget(array);
-            data
-        };
-
-        Self { data, alive: 0..N }
+            iter
+        }
     }
 
     /// Returns an immutable slice of all elements that have not been yielded
     /// yet.
     fn as_slice(&self) -> &[T] {
-        let slice = &self.data[self.alive.clone()];
-        // SAFETY: This transmute is safe. As mentioned in `new`, `MaybeUninit` retains
-        // the size and alignment of `T`. Furthermore, we know that all
-        // elements within `alive` are properly initialized.
-        unsafe { mem::transmute::<&[MaybeUninit<T>], &[T]>(slice) }
+        // SAFETY: We know that all elements within `alive` are properly initialized.
+        unsafe {
+            let slice = self.data.get_unchecked(self.alive.clone());
+            MaybeUninit::slice_get_ref(slice)
+        }
     }
 
     /// Returns a mutable slice of all elements that have not been yielded yet.
     fn as_mut_slice(&mut self) -> &mut [T] {
-        // This transmute is safe, same as in `as_slice` above.
-        let slice = &mut self.data[self.alive.clone()];
-        // SAFETY: This transmute is safe. As mentioned in `new`, `MaybeUninit` retains
-        // the size and alignment of `T`. Furthermore, we know that all
-        // elements within `alive` are properly initialized.
-        unsafe { mem::transmute::<&mut [MaybeUninit<T>], &mut [T]>(slice) }
+        // SAFETY: We know that all elements within `alive` are properly initialized.
+        unsafe {
+            let slice = self.data.get_unchecked_mut(self.alive.clone());
+            MaybeUninit::slice_get_mut(slice)
+        }
     }
 }
 
@@ -95,30 +91,20 @@ impl<T, const N: usize> IntoIter<T, N> {
 impl<T, const N: usize> Iterator for IntoIter<T, N> {
     type Item = T;
     fn next(&mut self) -> Option<Self::Item> {
-        if self.alive.start == self.alive.end {
-            return None;
-        }
-
-        // Bump start index.
+        // Get the next index from the front.
         //
-        // From the check above we know that `alive.start != alive.end`.
-        // Combine this with the invariant `alive.start <= alive.end`, we know
-        // that `alive.start < alive.end`. Increasing `alive.start` by 1
-        // maintains the invariant regarding `alive`. However, due to this
-        // change, for a short time, the alive zone is not `data[alive]`
-        // anymore, but `data[idx..alive.end]`.
-        let idx = self.alive.start;
-        self.alive.start += 1;
-
-        // Read the element from the array.
-        // SAFETY: This is safe: `idx` is an index
-        // into the "alive" region of the array. Reading this element means
-        // that `data[idx]` is regarded as dead now (i.e. do not touch). As
-        // `idx` was the start of the alive-zone, the alive zone is now
-        // `data[alive]` again, restoring all invariants.
-        let out = unsafe { self.data.get_unchecked(idx).read() };
-
-        Some(out)
+        // Increasing `alive.start` by 1 maintains the invariant regarding
+        // `alive`. However, due to this change, for a short time, the alive
+        // zone is not `data[alive]` anymore, but `data[idx..alive.end]`.
+        self.alive.next().map(|idx| {
+            // Read the element from the array.
+            // SAFETY: `idx` is an index into the former "alive" region of the
+            // array. Reading this element means that `data[idx]` is regarded as
+            // dead now (i.e. do not touch). As `idx` was the start of the
+            // alive-zone, the alive zone is now `data[alive]` again, restoring
+            // all invariants.
+            unsafe { self.data.get_unchecked(idx).read() }
+        })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>) {
@@ -138,33 +124,20 @@ impl<T, const N: usize> Iterator for IntoIter<T, N> {
 #[stable(feature = "array_value_iter_impls", since = "1.40.0")]
 impl<T, const N: usize> DoubleEndedIterator for IntoIter<T, N> {
     fn next_back(&mut self) -> Option<Self::Item> {
-        if self.alive.start == self.alive.end {
-            return None;
-        }
-
-        // Decrease end index.
+        // Get the next index from the back.
         //
-        // From the check above we know that `alive.start != alive.end`.
-        // Combine this with the invariant `alive.start <= alive.end`, we know
-        // that `alive.start < alive.end`. As `alive.start` cannot be negative,
-        // `alive.end` is at least 1, meaning that we can safely decrement it
-        // by one. This also maintains the invariant `alive.start <=
-        // alive.end`. However, due to this change, for a short time, the alive
-        // zone is not `data[alive]` anymore, but `data[alive.start..alive.end
-        // + 1]`.
-        self.alive.end -= 1;
-
-        // Read the element from the array.
-        // SAFETY: This is safe: `alive.end` is an
-        // index into the "alive" region of the array. Compare the previous
-        // comment that states that the alive region is
-        // `data[alive.start..alive.end + 1]`. Reading this element means that
-        // `data[alive.end]` is regarded as dead now (i.e. do not touch). As
-        // `alive.end` was the end of the alive-zone, the alive zone is now
-        // `data[alive]` again, restoring all invariants.
-        let out = unsafe { self.data.get_unchecked(self.alive.end).read() };
-
-        Some(out)
+        // Decreasing `alive.end` by 1 maintains the invariant regarding
+        // `alive`. However, due to this change, for a short time, the alive
+        // zone is not `data[alive]` anymore, but `data[alive.start..=idx]`.
+        self.alive.next_back().map(|idx| {
+            // Read the element from the array.
+            // SAFETY: `idx` is an index into the former "alive" region of the
+            // array. Reading this element means that `data[idx]` is regarded as
+            // dead now (i.e. do not touch). As `idx` was the end of the
+            // alive-zone, the alive zone is now `data[alive]` again, restoring
+            // all invariants.
+            unsafe { self.data.get_unchecked(idx).read() }
+        })
     }
 }
 
@@ -203,26 +176,19 @@ unsafe impl<T, const N: usize> TrustedLen for IntoIter<T, N> {}
 #[stable(feature = "array_value_iter_impls", since = "1.40.0")]
 impl<T: Clone, const N: usize> Clone for IntoIter<T, N> {
     fn clone(&self) -> Self {
-        // SAFETY: each point of unsafety is documented inside the unsafe block
-        unsafe {
-            // This creates a new uninitialized array. Note that the `assume_init`
-            // refers to the array, not the individual elements. And it is Ok if
-            // the array is in an uninitialized state as all elements may be
-            // uninitialized (all bit patterns are valid). Compare the
-            // `MaybeUninit` docs for more information.
-            let mut new_data: [MaybeUninit<T>; N] = MaybeUninit::uninit().assume_init();
-
-            // Clone all alive elements.
-            for idx in self.alive.clone() {
-                // The element at `idx` in the old array is alive, so we can
-                // safely call `get_ref()`. We then clone it, and write the
-                // clone into the new array.
-                let clone = self.data.get_unchecked(idx).get_ref().clone();
-                new_data.get_unchecked_mut(idx).write(clone);
-            }
-
-            Self { data: new_data, alive: self.alive.clone() }
+        // Note, we don't really need to match the exact same alive range, so
+        // we can just clone into offset 0 regardless of where `self` is.
+        let mut new = Self { data: MaybeUninit::uninit_array(), alive: 0..0 };
+
+        // Clone all alive elements.
+        for (src, dst) in self.as_slice().iter().zip(&mut new.data) {
+            // Write a clone into the new array, then update its alive range.
+            // If cloning panics, we'll correctly drop the previous items.
+            dst.write(src.clone());
+            new.alive.end += 1;
         }
+
+        new
     }
 }
 
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index 3116815f5d6..3dc0ee2b555 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -119,9 +119,11 @@ pub fn black_box<T>(dummy: T) -> T {
     // box. This isn't the greatest implementation since it probably deoptimizes
     // more than we want, but it's so far good enough.
 
+    #[cfg(not(miri))] // This is just a hint, so it is fine to skip in Miri.
     // SAFETY: the inline assembly is a no-op.
     unsafe {
         llvm_asm!("" : : "r"(&dummy));
-        dummy
     }
+
+    dummy
 }
diff --git a/library/core/src/mem/maybe_uninit.rs b/library/core/src/mem/maybe_uninit.rs
index cf721b01ce3..027498d3911 100644
--- a/library/core/src/mem/maybe_uninit.rs
+++ b/library/core/src/mem/maybe_uninit.rs
@@ -405,9 +405,11 @@ impl<T> MaybeUninit<T> {
     /// (Notice that the rules around references to uninitialized data are not finalized yet, but
     /// until they are, it is advisable to avoid them.)
     #[stable(feature = "maybe_uninit", since = "1.36.0")]
+    #[rustc_const_unstable(feature = "const_maybe_uninit_as_ptr", issue = "75251")]
     #[inline(always)]
-    pub fn as_ptr(&self) -> *const T {
-        unsafe { &*self.value as *const T }
+    pub const fn as_ptr(&self) -> *const T {
+        // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer.
+        self as *const _ as *const T
     }
 
     /// Gets a mutable pointer to the contained value. Reading from this pointer or turning it
@@ -442,9 +444,11 @@ impl<T> MaybeUninit<T> {
     /// (Notice that the rules around references to uninitialized data are not finalized yet, but
     /// until they are, it is advisable to avoid them.)
     #[stable(feature = "maybe_uninit", since = "1.36.0")]
+    #[rustc_const_unstable(feature = "const_maybe_uninit_as_ptr", issue = "75251")]
     #[inline(always)]
-    pub fn as_mut_ptr(&mut self) -> *mut T {
-        unsafe { &mut *self.value as *mut T }
+    pub const fn as_mut_ptr(&mut self) -> *mut T {
+        // `MaybeUninit` and `ManuallyDrop` are both `repr(transparent)` so we can cast the pointer.
+        self as *mut _ as *mut T
     }
 
     /// Extracts the value from the `MaybeUninit<T>` container. This is a great way
diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs
index eb50dc28b9f..68937176270 100644
--- a/library/core/src/num/mod.rs
+++ b/library/core/src/num/mod.rs
@@ -2346,17 +2346,12 @@ assert_eq!(
             #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
             // SAFETY: const sound because integers are plain old datatypes so we can always
             // transmute them to arrays of bytes
-            #[allow_internal_unstable(const_fn_union)]
+            #[allow_internal_unstable(const_fn_transmute)]
             #[inline]
             pub const fn to_ne_bytes(self) -> [u8; mem::size_of::<Self>()] {
-                #[repr(C)]
-                union Bytes {
-                    val: $SelfT,
-                    bytes: [u8; mem::size_of::<$SelfT>()],
-                }
                 // SAFETY: integers are plain old datatypes so we can always transmute them to
                 // arrays of bytes
-                unsafe { Bytes { val: self }.bytes }
+                unsafe { mem::transmute(self) }
             }
         }
 
@@ -2464,16 +2459,11 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT),
             #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
             // SAFETY: const sound because integers are plain old datatypes so we can always
             // transmute to them
-            #[allow_internal_unstable(const_fn_union)]
+            #[allow_internal_unstable(const_fn_transmute)]
             #[inline]
             pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
-                #[repr(C)]
-                union Bytes {
-                    val: $SelfT,
-                    bytes: [u8; mem::size_of::<$SelfT>()],
-                }
                 // SAFETY: integers are plain old datatypes so we can always transmute to them
-                unsafe { Bytes { bytes }.val }
+                unsafe { mem::transmute(bytes) }
             }
         }
 
@@ -4368,23 +4358,18 @@ assert_eq!(
             #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
             // SAFETY: const sound because integers are plain old datatypes so we can always
             // transmute them to arrays of bytes
-            #[allow_internal_unstable(const_fn_union)]
+            #[allow_internal_unstable(const_fn_transmute)]
             #[inline]
             pub const fn to_ne_bytes(self) -> [u8; mem::size_of::<Self>()] {
-                #[repr(C)]
-                union Bytes {
-                    val: $SelfT,
-                    bytes: [u8; mem::size_of::<$SelfT>()],
-                }
                 // SAFETY: integers are plain old datatypes so we can always transmute them to
                 // arrays of bytes
-                unsafe { Bytes { val: self }.bytes }
+                unsafe { mem::transmute(self) }
             }
         }
 
         doc_comment! {
-            concat!("Create an integer value from its representation as a byte array in
-big endian.
+            concat!("Create a native endian integer value from its representation
+as a byte array in big endian.
 ",
 $from_xe_bytes_doc,
 "
@@ -4416,8 +4401,8 @@ fn read_be_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT),
 
         doc_comment! {
             concat!("
-Create an integer value from its representation as a byte array in
-little endian.
+Create a native endian integer value from its representation
+as a byte array in little endian.
 ",
 $from_xe_bytes_doc,
 "
@@ -4448,8 +4433,8 @@ fn read_le_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT),
         }
 
         doc_comment! {
-            concat!("Create an integer value from its memory representation as a byte
-array in native endianness.
+            concat!("Create a native endian integer value from its memory representation
+as a byte array in native endianness.
 
 As the target platform's native endianness is used, portable code
 likely wants to use [`from_be_bytes`] or [`from_le_bytes`], as
@@ -4486,16 +4471,11 @@ fn read_ne_", stringify!($SelfT), "(input: &mut &[u8]) -> ", stringify!($SelfT),
             #[rustc_const_stable(feature = "const_int_conversion", since = "1.44.0")]
             // SAFETY: const sound because integers are plain old datatypes so we can always
             // transmute to them
-            #[allow_internal_unstable(const_fn_union)]
+            #[allow_internal_unstable(const_fn_transmute)]
             #[inline]
             pub const fn from_ne_bytes(bytes: [u8; mem::size_of::<Self>()]) -> Self {
-                #[repr(C)]
-                union Bytes {
-                    val: $SelfT,
-                    bytes: [u8; mem::size_of::<$SelfT>()],
-                }
                 // SAFETY: integers are plain old datatypes so we can always transmute to them
-                unsafe { Bytes { bytes }.val }
+                unsafe { mem::transmute(bytes) }
             }
         }
 
diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs
index a2acc239bd3..a16970e9fd1 100644
--- a/library/core/src/ptr/const_ptr.rs
+++ b/library/core/src/ptr/const_ptr.rs
@@ -656,6 +656,38 @@ impl<T: ?Sized> *const T {
         self.wrapping_offset((count as isize).wrapping_neg())
     }
 
+    /// Sets the pointer value to `ptr`.
+    ///
+    /// In case `self` is a (fat) pointer to an unsized type, this operation
+    /// will only affect the pointer part, whereas for (thin) pointers to
+    /// sized types, this has the same effect as a simple assignment.
+    ///
+    /// # Examples
+    ///
+    /// This function is primarily useful for allowing byte-wise pointer
+    /// arithmetic on potentially fat pointers:
+    ///
+    /// ```
+    /// #![feature(set_ptr_value)]
+    /// # use core::fmt::Debug;
+    /// let arr: [i32; 3] = [1, 2, 3];
+    /// let mut ptr = &arr[0] as *const dyn Debug;
+    /// let thin = ptr as *const u8;
+    /// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
+    /// assert_eq!(unsafe { *(ptr as *const i32) }, 3);
+    /// ```
+    #[unstable(feature = "set_ptr_value", issue = "75091")]
+    #[inline]
+    pub fn set_ptr_value(mut self, val: *const ()) -> Self {
+        let thin = &mut self as *mut *const T as *mut *const ();
+        // SAFETY: In case of a thin pointer, this operations is identical
+        // to a simple assignment. In case of a fat pointer, with the current
+        // fat pointer layout implementation, the first field of such a
+        // pointer is always the data pointer, which is likewise assigned.
+        unsafe { *thin = val };
+        self
+    }
+
     /// Reads the value from `self` without moving it. This leaves the
     /// memory in `self` unchanged.
     ///
diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs
index 17fa90ecc08..b47f90c5996 100644
--- a/library/core/src/ptr/mut_ptr.rs
+++ b/library/core/src/ptr/mut_ptr.rs
@@ -712,6 +712,38 @@ impl<T: ?Sized> *mut T {
         self.wrapping_offset((count as isize).wrapping_neg())
     }
 
+    /// Sets the pointer value to `ptr`.
+    ///
+    /// In case `self` is a (fat) pointer to an unsized type, this operation
+    /// will only affect the pointer part, whereas for (thin) pointers to
+    /// sized types, this has the same effect as a simple assignment.
+    ///
+    /// # Examples
+    ///
+    /// This function is primarily useful for allowing byte-wise pointer
+    /// arithmetic on potentially fat pointers:
+    ///
+    /// ```
+    /// #![feature(set_ptr_value)]
+    /// # use core::fmt::Debug;
+    /// let mut arr: [i32; 3] = [1, 2, 3];
+    /// let mut ptr = &mut arr[0] as *mut dyn Debug;
+    /// let thin = ptr as *mut u8;
+    /// ptr = ptr.set_ptr_value(unsafe { thin.add(8).cast() });
+    /// assert_eq!(unsafe { *(ptr as *mut i32) }, 3);
+    /// ```
+    #[unstable(feature = "set_ptr_value", issue = "75091")]
+    #[inline]
+    pub fn set_ptr_value(mut self, val: *mut ()) -> Self {
+        let thin = &mut self as *mut *mut T as *mut *mut ();
+        // SAFETY: In case of a thin pointer, this operations is identical
+        // to a simple assignment. In case of a fat pointer, with the current
+        // fat pointer layout implementation, the first field of such a
+        // pointer is always the data pointer, which is likewise assigned.
+        unsafe { *thin = val };
+        self
+    }
+
     /// Reads the value from `self` without moving it. This leaves the
     /// memory in `self` unchanged.
     ///
diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs
index 9f843a57099..d876ab23653 100644
--- a/library/core/src/ptr/non_null.rs
+++ b/library/core/src/ptr/non_null.rs
@@ -117,6 +117,24 @@ impl<T: ?Sized> NonNull<T> {
     /// The resulting lifetime is bound to self so this behaves "as if"
     /// it were actually an instance of T that is getting borrowed. If a longer
     /// (unbound) lifetime is needed, use `&*my_ptr.as_ptr()`.
+    ///
+    /// # Safety
+    ///
+    /// When calling this method, you have to ensure that all of the following is true:
+    /// - `self` is properly aligned
+    /// - `self` must point to an initialized instance of T; in particular, the pointer must be
+    ///   "dereferencable" in the sense defined [here].
+    ///
+    /// This applies even if the result of this method is unused!
+    /// (The part about being initialized is not yet fully decided, but until
+    /// it is, the only safe approach is to ensure that they are indeed initialized.)
+    ///
+    /// Additionally, the lifetime of `self` does not necessarily reflect the actual
+    /// lifetime of the data. *You* must enforce Rust's aliasing rules. In particular,
+    /// for the duration of this lifetime, the memory the pointer points to must not
+    /// get mutated (except inside `UnsafeCell`).
+    ///
+    /// [here]: crate::ptr#safety
     #[stable(feature = "nonnull", since = "1.25.0")]
     #[inline]
     pub unsafe fn as_ref(&self) -> &T {
@@ -130,6 +148,24 @@ impl<T: ?Sized> NonNull<T> {
     /// The resulting lifetime is bound to self so this behaves "as if"
     /// it were actually an instance of T that is getting borrowed. If a longer
     /// (unbound) lifetime is needed, use `&mut *my_ptr.as_ptr()`.
+    ///
+    /// # Safety
+    ///
+    /// When calling this method, you have to ensure that all of the following is true:
+    /// - `self` is properly aligned
+    /// - `self` must point to an initialized instance of T; in particular, the pointer must be
+    ///   "dereferenceable" in the sense defined [here].
+    ///
+    /// This applies even if the result of this method is unused!
+    /// (The part about being initialized is not yet fully decided, but until
+    /// it is the only safe approach is to ensure that they are indeed initialized.)
+    ///
+    /// Additionally, the lifetime of `self` does not necessarily reflect the actual
+    /// lifetime of the data. *You* must enforce Rust's aliasing rules. In particular,
+    /// for the duration of this lifetime, the memory this pointer points to must not
+    /// get accessed (read or written) through any other pointer.
+    ///
+    /// [here]: crate::ptr#safety
     #[stable(feature = "nonnull", since = "1.25.0")]
     #[inline]
     pub unsafe fn as_mut(&mut self) -> &mut T {
@@ -224,6 +260,24 @@ impl<T> NonNull<[T]> {
         unsafe { NonNull::new_unchecked(self.as_ptr().as_mut_ptr()) }
     }
 
+    /// Returns a raw pointer to the slice's buffer.
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// #![feature(slice_ptr_get, nonnull_slice_from_raw_parts)]
+    /// use std::ptr::NonNull;
+    ///
+    /// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
+    /// assert_eq!(slice.as_mut_ptr(), 1 as *mut i8);
+    /// ```
+    #[inline]
+    #[unstable(feature = "slice_ptr_get", issue = "74265")]
+    #[rustc_const_unstable(feature = "slice_ptr_get", issue = "74265")]
+    pub const fn as_mut_ptr(self) -> *mut T {
+        self.as_non_null_ptr().as_ptr()
+    }
+
     /// Returns a raw pointer to an element or subslice, without doing bounds
     /// checking.
     ///
diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs
index 9d7e38d0e18..eac4741cd26 100644
--- a/library/core/src/str/mod.rs
+++ b/library/core/src/str/mod.rs
@@ -1923,7 +1923,10 @@ mod traits {
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
             let (start, end) = (self.start, self.end);
-            self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end))
+            match self.get(slice) {
+                Some(s) => s,
+                None => super::slice_error_fail(slice, start, end),
+            }
         }
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
@@ -1995,7 +1998,10 @@ mod traits {
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
             let end = self.end;
-            self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, 0, end))
+            match self.get(slice) {
+                Some(s) => s,
+                None => super::slice_error_fail(slice, 0, end),
+            }
         }
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
@@ -2068,7 +2074,10 @@ mod traits {
         #[inline]
         fn index(self, slice: &str) -> &Self::Output {
             let (start, end) = (self.start, slice.len());
-            self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, start, end))
+            match self.get(slice) {
+                Some(s) => s,
+                None => super::slice_error_fail(slice, start, end),
+            }
         }
         #[inline]
         fn index_mut(self, slice: &mut str) -> &mut Self::Output {
diff --git a/library/core/src/time.rs b/library/core/src/time.rs
index acaedbd135e..5741f8a53b5 100644
--- a/library/core/src/time.rs
+++ b/library/core/src/time.rs
@@ -30,12 +30,10 @@ const MICROS_PER_SEC: u64 = 1_000_000;
 /// nanosecond-level precision, APIs binding a system timeout will typically round up
 /// the number of nanoseconds.
 ///
-/// `Duration`s implement many common traits, including [`Add`], [`Sub`], and other
-/// [`ops`] traits. It implements `Default` by returning a zero-length `Duration`.
+/// [`Duration`]s implement many common traits, including [`Add`], [`Sub`], and other
+/// [`ops`] traits. It implements [`Default`] by returning a zero-length `Duration`.
 ///
-/// [`Add`]: ../../std/ops/trait.Add.html
-/// [`Sub`]: ../../std/ops/trait.Sub.html
-/// [`ops`]: ../../std/ops/index.html
+/// [`ops`]: crate::ops
 ///
 /// # Examples
 ///
@@ -293,7 +291,7 @@ impl Duration {
     ///            + duration.subsec_nanos() as f64 * 1e-9);
     /// ```
     ///
-    /// [`subsec_nanos`]: #method.subsec_nanos
+    /// [`subsec_nanos`]: Duration::subsec_nanos
     #[stable(feature = "duration", since = "1.3.0")]
     #[rustc_const_stable(feature = "duration", since = "1.32.0")]
     #[inline]
@@ -421,8 +419,6 @@ impl Duration {
     /// Checked `Duration` addition. Computes `self + other`, returning [`None`]
     /// if overflow occurred.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// Basic usage:
@@ -457,8 +453,6 @@ impl Duration {
     /// Checked `Duration` subtraction. Computes `self - other`, returning [`None`]
     /// if the result would be negative or if overflow occurred.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// Basic usage:
@@ -494,8 +488,6 @@ impl Duration {
     /// Checked `Duration` multiplication. Computes `self * other`, returning
     /// [`None`] if overflow occurred.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// Basic usage:
@@ -526,8 +518,6 @@ impl Duration {
     /// Checked `Duration` division. Computes `self / other`, returning [`None`]
     /// if `other == 0`.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// Basic usage:
diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs
index 72eab0763d8..7d14893c4cc 100644
--- a/library/panic_unwind/src/lib.rs
+++ b/library/panic_unwind/src/lib.rs
@@ -50,6 +50,7 @@ cfg_if::cfg_if! {
     } else if #[cfg(any(
         all(target_family = "windows", target_env = "gnu"),
         target_os = "cloudabi",
+        target_os = "psp",
         target_family = "unix",
         all(target_vendor = "fortanix", target_env = "sgx"),
     ))] {
@@ -65,7 +66,6 @@ cfg_if::cfg_if! {
         // - os=uefi
         // - nvptx64-nvidia-cuda
         // - avr-unknown-unknown
-        // - mipsel-sony-psp
         #[path = "dummy.rs"]
         mod real_imp;
     }
diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml
index 474765d8638..fc07fa77b85 100644
--- a/library/std/Cargo.toml
+++ b/library/std/Cargo.toml
@@ -20,7 +20,7 @@ libc = { version = "0.2.51", default-features = false, features = ['rustc-dep-of
 compiler_builtins = { version = "0.1.32" }
 profiler_builtins = { path = "../profiler_builtins", optional = true }
 unwind = { path = "../unwind" }
-hashbrown = { version = "0.6.2", default-features = false, features = ['rustc-dep-of-std'] }
+hashbrown = { version = "0.8.1", default-features = false, features = ['rustc-dep-of-std'] }
 
 # Dependencies of the `backtrace` crate
 addr2line = { version = "0.13.0", optional = true, default-features = false }
diff --git a/library/std/src/collections/hash/map.rs b/library/std/src/collections/hash/map.rs
index 7b48deee1ab..70f7214e2f1 100644
--- a/library/std/src/collections/hash/map.rs
+++ b/library/std/src/collections/hash/map.rs
@@ -580,7 +580,7 @@ where
     #[inline]
     #[unstable(feature = "try_reserve", reason = "new API", issue = "48043")]
     pub fn try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> {
-        self.base.try_reserve(additional).map_err(map_collection_alloc_err)
+        self.base.try_reserve(additional).map_err(map_try_reserve_error)
     }
 
     /// Shrinks the capacity of the map as much as possible. It will drop
@@ -872,6 +872,52 @@ where
     {
         self.base.retain(f)
     }
+
+    /// Creates a consuming iterator visiting all the keys in arbitrary order.
+    /// The map cannot be used after calling this.
+    /// The iterator element type is `K`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(map_into_keys_values)]
+    /// use std::collections::HashMap;
+    ///
+    /// let mut map = HashMap::new();
+    /// map.insert("a", 1);
+    /// map.insert("b", 2);
+    /// map.insert("c", 3);
+    ///
+    /// let vec: Vec<&str> = map.into_keys().collect();
+    /// ```
+    #[inline]
+    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    pub fn into_keys(self) -> IntoKeys<K, V> {
+        IntoKeys { inner: self.into_iter() }
+    }
+
+    /// Creates a consuming iterator visiting all the values in arbitrary order.
+    /// The map cannot be used after calling this.
+    /// The iterator element type is `V`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(map_into_keys_values)]
+    /// use std::collections::HashMap;
+    ///
+    /// let mut map = HashMap::new();
+    /// map.insert("a", 1);
+    /// map.insert("b", 2);
+    /// map.insert("c", 3);
+    ///
+    /// let vec: Vec<i32> = map.into_values().collect();
+    /// ```
+    #[inline]
+    #[unstable(feature = "map_into_keys_values", issue = "75294")]
+    pub fn into_values(self) -> IntoValues<K, V> {
+        IntoValues { inner: self.into_iter() }
+    }
 }
 
 impl<K, V, S> HashMap<K, V, S>
@@ -1154,6 +1200,28 @@ pub struct ValuesMut<'a, K: 'a, V: 'a> {
     inner: IterMut<'a, K, V>,
 }
 
+/// An owning iterator over the keys of a `HashMap`.
+///
+/// This `struct` is created by the [`into_keys`] method on [`HashMap`].
+/// See its documentation for more.
+///
+/// [`into_keys`]: HashMap::into_keys
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+pub struct IntoKeys<K, V> {
+    inner: IntoIter<K, V>,
+}
+
+/// An owning iterator over the values of a `HashMap`.
+///
+/// This `struct` is created by the [`into_values`] method on [`HashMap`].
+/// See its documentation for more.
+///
+/// [`into_values`]: HashMap::into_values
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+pub struct IntoValues<K, V> {
+    inner: IntoIter<K, V>,
+}
+
 /// A builder for computing where in a HashMap a key-value pair would be stored.
 ///
 /// See the [`HashMap::raw_entry_mut`] docs for usage examples.
@@ -1827,6 +1895,66 @@ where
     }
 }
 
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoKeys<K, V> {
+    type Item = K;
+
+    #[inline]
+    fn next(&mut self) -> Option<K> {
+        self.inner.next().map(|(k, _)| k)
+    }
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoKeys<K, V> {
+    #[inline]
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoKeys<K, V> {}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K: Debug, V: Debug> fmt::Debug for IntoKeys<K, V> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.inner.iter().map(|(k, _)| k)).finish()
+    }
+}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> Iterator for IntoValues<K, V> {
+    type Item = V;
+
+    #[inline]
+    fn next(&mut self) -> Option<V> {
+        self.inner.next().map(|(_, v)| v)
+    }
+    #[inline]
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> ExactSizeIterator for IntoValues<K, V> {
+    #[inline]
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+}
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K, V> FusedIterator for IntoValues<K, V> {}
+
+#[unstable(feature = "map_into_keys_values", issue = "75294")]
+impl<K: Debug, V: Debug> fmt::Debug for IntoValues<K, V> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.inner.iter().map(|(_, v)| v)).finish()
+    }
+}
+
 #[stable(feature = "drain", since = "1.6.0")]
 impl<'a, K, V> Iterator for Drain<'a, K, V> {
     type Item = (K, V);
@@ -2569,10 +2697,10 @@ fn map_entry<'a, K: 'a, V: 'a>(raw: base::RustcEntry<'a, K, V>) -> Entry<'a, K,
 }
 
 #[inline]
-fn map_collection_alloc_err(err: hashbrown::CollectionAllocErr) -> TryReserveError {
+fn map_try_reserve_error(err: hashbrown::TryReserveError) -> TryReserveError {
     match err {
-        hashbrown::CollectionAllocErr::CapacityOverflow => TryReserveError::CapacityOverflow,
-        hashbrown::CollectionAllocErr::AllocErr { layout } => {
+        hashbrown::TryReserveError::CapacityOverflow => TryReserveError::CapacityOverflow,
+        hashbrown::TryReserveError::AllocError { layout } => {
             TryReserveError::AllocError { layout, non_exhaustive: () }
         }
     }
@@ -3085,6 +3213,30 @@ mod test_map {
     }
 
     #[test]
+    fn test_into_keys() {
+        let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+        let map: HashMap<_, _> = vec.into_iter().collect();
+        let keys: Vec<_> = map.into_keys().collect();
+
+        assert_eq!(keys.len(), 3);
+        assert!(keys.contains(&1));
+        assert!(keys.contains(&2));
+        assert!(keys.contains(&3));
+    }
+
+    #[test]
+    fn test_into_values() {
+        let vec = vec![(1, 'a'), (2, 'b'), (3, 'c')];
+        let map: HashMap<_, _> = vec.into_iter().collect();
+        let values: Vec<_> = map.into_values().collect();
+
+        assert_eq!(values.len(), 3);
+        assert!(values.contains(&'a'));
+        assert!(values.contains(&'b'));
+        assert!(values.contains(&'c'));
+    }
+
+    #[test]
     fn test_find() {
         let mut m = HashMap::new();
         assert!(m.get(&1).is_none());
diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs
index c98008688ab..ff343625a19 100644
--- a/library/std/src/keyword_docs.rs
+++ b/library/std/src/keyword_docs.rs
@@ -943,8 +943,7 @@ mod mod_keyword {}
 /// Capture a [closure]'s environment by value.
 ///
 /// `move` converts any variables captured by reference or mutable reference
-/// to owned by value variables. The three [`Fn` trait]'s mirror the ways to capture
-/// variables, when `move` is used, the closures is represented by the `FnOnce` trait.
+/// to owned by value variables.
 ///
 /// ```rust
 /// let capture = "hello";
@@ -953,6 +952,23 @@ mod mod_keyword {}
 /// };
 /// ```
 ///
+/// Note: `move` closures may still implement [`Fn`] or [`FnMut`], even though
+/// they capture variables by `move`. This is because the traits implemented by
+/// a closure type are determined by *what* the closure does with captured
+/// values, not *how* it captures them:
+///
+/// ```rust
+/// fn create_fn() -> impl Fn() {
+///     let text = "Fn".to_owned();
+///
+///     move || println!("This is a: {}", text)
+/// }
+///
+///     let fn_plain = create_fn();
+///
+///     fn_plain();
+/// ```
+///
 /// `move` is often used when [threads] are involved.
 ///
 /// ```rust
diff --git a/library/std/src/net/ip.rs b/library/std/src/net/ip.rs
index a64b43ca3ad..159ab981b23 100644
--- a/library/std/src/net/ip.rs
+++ b/library/std/src/net/ip.rs
@@ -319,15 +319,9 @@ impl Ipv4Addr {
     #[stable(feature = "rust1", since = "1.0.0")]
     #[rustc_const_stable(feature = "const_ipv4", since = "1.32.0")]
     pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
-        // FIXME: should just be u32::from_be_bytes([a, b, c, d]),
-        // once that method is no longer rustc_const_unstable
-        Ipv4Addr {
-            inner: c::in_addr {
-                s_addr: u32::to_be(
-                    ((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32),
-                ),
-            },
-        }
+        // `s_addr` is stored as BE on all machine and the array is in BE order.
+        // So the native endian conversion method is used so that it's never swapped.
+        Ipv4Addr { inner: c::in_addr { s_addr: u32::from_ne_bytes([a, b, c, d]) } }
     }
 
     /// An IPv4 address with the address pointing to localhost: 127.0.0.1.
@@ -967,11 +961,6 @@ impl AsInner<c::in_addr> for Ipv4Addr {
         &self.inner
     }
 }
-impl FromInner<c::in_addr> for Ipv4Addr {
-    fn from_inner(addr: c::in_addr) -> Ipv4Addr {
-        Ipv4Addr { inner: addr }
-    }
-}
 
 #[stable(feature = "ip_u32", since = "1.1.0")]
 impl From<Ipv4Addr> for u32 {
@@ -982,8 +971,8 @@ impl From<Ipv4Addr> for u32 {
     /// ```
     /// use std::net::Ipv4Addr;
     ///
-    /// let addr = Ipv4Addr::new(13, 12, 11, 10);
-    /// assert_eq!(0x0d0c0b0au32, u32::from(addr));
+    /// let addr = Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe);
+    /// assert_eq!(0xcafebabe, u32::from(addr));
     /// ```
     fn from(ip: Ipv4Addr) -> u32 {
         let ip = ip.octets();
@@ -1000,8 +989,8 @@ impl From<u32> for Ipv4Addr {
     /// ```
     /// use std::net::Ipv4Addr;
     ///
-    /// let addr = Ipv4Addr::from(0x0d0c0b0au32);
-    /// assert_eq!(Ipv4Addr::new(13, 12, 11, 10), addr);
+    /// let addr = Ipv4Addr::from(0xcafebabe);
+    /// assert_eq!(Ipv4Addr::new(0xca, 0xfe, 0xba, 0xbe), addr);
     /// ```
     fn from(ip: u32) -> Ipv4Addr {
         Ipv4Addr::from(ip.to_be_bytes())
diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs
index ab2a6010306..08d363a9a29 100644
--- a/library/std/src/panicking.rs
+++ b/library/std/src/panicking.rs
@@ -7,6 +7,8 @@
 //! * Executing a panic up to doing the actual implementation
 //! * Shims around "try"
 
+#![deny(unsafe_op_in_unsafe_fn)]
+
 use core::panic::{BoxMeUp, Location, PanicInfo};
 
 use crate::any::Any;
@@ -322,11 +324,22 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     let mut data = Data { f: ManuallyDrop::new(f) };
 
     let data_ptr = &mut data as *mut _ as *mut u8;
-    return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
-        Ok(ManuallyDrop::into_inner(data.r))
-    } else {
-        Err(ManuallyDrop::into_inner(data.p))
-    };
+    // SAFETY:
+    //
+    // Access to the union's fields: this is `std` and we know that the `r#try`
+    // intrinsic fills in the `r` or `p` union field based on its return value.
+    //
+    // The call to `intrinsics::r#try` is made safe by:
+    // - `do_call`, the first argument, can be called with the initial `data_ptr`.
+    // - `do_catch`, the second argument, can be called with the `data_ptr` as well.
+    // See their safety preconditions for more informations
+    unsafe {
+        return if intrinsics::r#try(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
+            Ok(ManuallyDrop::into_inner(data.r))
+        } else {
+            Err(ManuallyDrop::into_inner(data.p))
+        };
+    }
 
     // We consider unwinding to be rare, so mark this function as cold. However,
     // do not mark it no-inline -- that decision is best to leave to the
@@ -334,13 +347,25 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     // non-cold function, though, as of the writing of this comment).
     #[cold]
     unsafe fn cleanup(payload: *mut u8) -> Box<dyn Any + Send + 'static> {
-        let obj = Box::from_raw(__rust_panic_cleanup(payload));
+        // SAFETY: The whole unsafe block hinges on a correct implementation of
+        // the panic handler `__rust_panic_cleanup`. As such we can only
+        // assume it returns the correct thing for `Box::from_raw` to work
+        // without undefined behavior.
+        let obj = unsafe { Box::from_raw(__rust_panic_cleanup(payload)) };
         panic_count::decrease();
         obj
     }
 
+    // SAFETY:
+    // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
+    // Its must contains a valid `f` (type: F) value that can be use to fill
+    // `data.r`.
+    //
+    // This function cannot be marked as `unsafe` because `intrinsics::r#try`
+    // expects normal function pointers.
     #[inline]
     fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
+        // SAFETY: this is the responsibilty of the caller, see above.
         unsafe {
             let data = data as *mut Data<F, R>;
             let data = &mut (*data);
@@ -352,8 +377,21 @@ pub unsafe fn r#try<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>>
     // We *do* want this part of the catch to be inlined: this allows the
     // compiler to properly track accesses to the Data union and optimize it
     // away most of the time.
+    //
+    // SAFETY:
+    // data must be non-NUL, correctly aligned, and a pointer to a `Data<F, R>`
+    // Since this uses `cleanup` it also hinges on a correct implementation of
+    // `__rustc_panic_cleanup`.
+    //
+    // This function cannot be marked as `unsafe` because `intrinsics::r#try`
+    // expects normal function pointers.
     #[inline]
     fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
+        // SAFETY: this is the responsibilty of the caller, see above.
+        //
+        // When `__rustc_panic_cleaner` is correctly implemented we can rely
+        // on `obj` being the correct thing to pass to `data.p` (after wrapping
+        // in `ManuallyDrop`).
         unsafe {
             let data = data as *mut Data<F, R>;
             let data = &mut (*data);
@@ -434,7 +472,9 @@ pub fn begin_panic_handler(info: &PanicInfo<'_>) -> ! {
 
     let loc = info.location().unwrap(); // The current implementation always returns Some
     let msg = info.message().unwrap(); // The current implementation always returns Some
-    rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
+    crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
+        rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
+    })
 }
 
 /// This is the entry point of panicking for the non-format-string variants of
@@ -453,7 +493,10 @@ pub fn begin_panic<M: Any + Send>(msg: M) -> ! {
         intrinsics::abort()
     }
 
-    rust_panic_with_hook(&mut PanicPayload::new(msg), None, Location::caller());
+    let loc = Location::caller();
+    return crate::sys_common::backtrace::__rust_end_short_backtrace(move || {
+        rust_panic_with_hook(&mut PanicPayload::new(msg), None, loc)
+    });
 
     struct PanicPayload<A> {
         inner: Option<A>,
diff --git a/library/std/src/path.rs b/library/std/src/path.rs
index 392c815ef28..e3d529df7de 100644
--- a/library/std/src/path.rs
+++ b/library/std/src/path.rs
@@ -56,16 +56,8 @@
 //! let path: PathBuf = ["c:\\", "windows", "system32.dll"].iter().collect();
 //! ```
 //!
-//! [`Component`]: ../../std/path/enum.Component.html
-//! [`components`]: ../../std/path/struct.Path.html#method.components
-//! [`PathBuf`]: ../../std/path/struct.PathBuf.html
-//! [`Path`]: ../../std/path/struct.Path.html
-//! [`push`]: ../../std/path/struct.PathBuf.html#method.push
-//! [`String`]: ../../std/string/struct.String.html
-//!
-//! [`str`]: ../../std/primitive.str.html
-//! [`OsString`]: ../../std/ffi/struct.OsString.html
-//! [`OsStr`]: ../../std/ffi/struct.OsStr.html
+//! [`components`]: Path::components
+//! [`push`]: PathBuf::push
 
 #![stable(feature = "rust1", since = "1.0.0")]
 
@@ -390,12 +382,9 @@ enum State {
 /// # }
 /// ```
 ///
-/// [`as_os_str`]: #method.as_os_str
-/// [`Component`]: enum.Component.html
-/// [`kind`]: #method.kind
-/// [`OsStr`]: ../../std/ffi/struct.OsStr.html
-/// [`Prefix` variant]: enum.Component.html#variant.Prefix
-/// [`Prefix`]: enum.Prefix.html
+/// [`as_os_str`]: PrefixComponent::as_os_str
+/// [`kind`]: PrefixComponent::kind
+/// [`Prefix` variant]: Component::Prefix
 #[stable(feature = "rust1", since = "1.0.0")]
 #[derive(Copy, Clone, Eq, Debug)]
 pub struct PrefixComponent<'a> {
@@ -411,16 +400,12 @@ impl<'a> PrefixComponent<'a> {
     ///
     /// See [`Prefix`]'s documentation for more information on the different
     /// kinds of prefixes.
-    ///
-    /// [`Prefix`]: enum.Prefix.html
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn kind(&self) -> Prefix<'a> {
         self.parsed
     }
 
     /// Returns the raw [`OsStr`] slice for this prefix.
-    ///
-    /// [`OsStr`]: ../../std/ffi/struct.OsStr.html
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn as_os_str(&self) -> &'a OsStr {
         self.raw
@@ -477,10 +462,6 @@ impl Hash for PrefixComponent<'_> {
 ///     Component::Normal("bar.txt".as_ref()),
 /// ]);
 /// ```
-///
-/// [`Components`]: struct.Components.html
-/// [`Path`]: struct.Path.html
-/// [`Path::components`]: struct.Path.html#method.components
 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub enum Component<'a> {
@@ -490,8 +471,6 @@ pub enum Component<'a> {
     /// for more.
     ///
     /// Does not occur on Unix.
-    ///
-    /// [`Prefix`]: enum.Prefix.html
     #[stable(feature = "rust1", since = "1.0.0")]
     Prefix(#[stable(feature = "rust1", since = "1.0.0")] PrefixComponent<'a>),
 
@@ -529,8 +508,6 @@ impl<'a> Component<'a> {
     /// let components: Vec<_> = path.components().map(|comp| comp.as_os_str()).collect();
     /// assert_eq!(&components, &[".", "tmp", "foo", "bar.txt"]);
     /// ```
-    ///
-    /// [`OsStr`]: ../../std/ffi/struct.OsStr.html
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn as_os_str(self) -> &'a OsStr {
         match self {
@@ -574,9 +551,7 @@ impl AsRef<Path> for Component<'_> {
 /// }
 /// ```
 ///
-/// [`Component`]: enum.Component.html
-/// [`components`]: struct.Path.html#method.components
-/// [`Path`]: struct.Path.html
+/// [`components`]: Path::components
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Components<'a> {
@@ -602,10 +577,7 @@ pub struct Components<'a> {
 /// This `struct` is created by the [`iter`] method on [`Path`].
 /// See its documentation for more.
 ///
-/// [`Component`]: enum.Component.html
-/// [`iter`]: struct.Path.html#method.iter
-/// [`OsStr`]: ../../std/ffi/struct.OsStr.html
-/// [`Path`]: struct.Path.html
+/// [`iter`]: Path::iter
 #[derive(Clone)]
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Iter<'a> {
@@ -1002,8 +974,7 @@ impl cmp::Ord for Components<'_> {
 /// }
 /// ```
 ///
-/// [`ancestors`]: struct.Path.html#method.ancestors
-/// [`Path`]: struct.Path.html
+/// [`ancestors`]: Path::ancestors
 #[derive(Copy, Clone, Debug)]
 #[stable(feature = "path_ancestors", since = "1.28.0")]
 pub struct Ancestors<'a> {
@@ -1034,11 +1005,8 @@ impl FusedIterator for Ancestors<'_> {}
 /// the path in place. It also implements [`Deref`] to [`Path`], meaning that
 /// all methods on [`Path`] slices are available on `PathBuf` values as well.
 ///
-/// [`String`]: ../string/struct.String.html
-/// [`Path`]: struct.Path.html
-/// [`push`]: struct.PathBuf.html#method.push
-/// [`set_extension`]: struct.PathBuf.html#method.set_extension
-/// [`Deref`]: ../ops/trait.Deref.html
+/// [`push`]: PathBuf::push
+/// [`set_extension`]: PathBuf::set_extension
 ///
 /// More details about the overall approach can be found in
 /// the [module documentation](index.html).
@@ -1127,8 +1095,7 @@ impl PathBuf {
     /// assert_eq!(capacity, path.capacity());
     /// ```
     ///
-    /// [`with_capacity`]: ../ffi/struct.OsString.html#method.with_capacity
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`with_capacity`]: OsString::with_capacity
     #[stable(feature = "path_buf_capacity", since = "1.44.0")]
     pub fn with_capacity(capacity: usize) -> PathBuf {
         PathBuf { inner: OsString::with_capacity(capacity) }
@@ -1136,8 +1103,6 @@ impl PathBuf {
 
     /// Coerces to a [`Path`] slice.
     ///
-    /// [`Path`]: struct.Path.html
-    ///
     /// # Examples
     ///
     /// ```
@@ -1224,18 +1189,17 @@ impl PathBuf {
     /// Returns `false` and does nothing if [`self.parent`] is [`None`].
     /// Otherwise, returns `true`.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    /// [`self.parent`]: struct.PathBuf.html#method.parent
+    /// [`self.parent`]: Path::parent
     ///
     /// # Examples
     ///
     /// ```
     /// use std::path::{Path, PathBuf};
     ///
-    /// let mut p = PathBuf::from("/test/test.rs");
+    /// let mut p = PathBuf::from("/spirited/away.rs");
     ///
     /// p.pop();
-    /// assert_eq!(Path::new("/test"), p);
+    /// assert_eq!(Path::new("/spirited"), p);
     /// p.pop();
     /// assert_eq!(Path::new("/"), p);
     /// ```
@@ -1259,9 +1223,8 @@ impl PathBuf {
     /// `file_name`. The new path will be a sibling of the original path.
     /// (That is, it will have the same parent.)
     ///
-    /// [`self.file_name`]: struct.PathBuf.html#method.file_name
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    /// [`pop`]: struct.PathBuf.html#method.pop
+    /// [`self.file_name`]: Path::file_name
+    /// [`pop`]: PathBuf::pop
     ///
     /// # Examples
     ///
@@ -1297,9 +1260,8 @@ impl PathBuf {
     /// If [`self.extension`] is [`None`], the extension is added; otherwise
     /// it is replaced.
     ///
-    /// [`self.file_name`]: struct.PathBuf.html#method.file_name
-    /// [`self.extension`]: struct.PathBuf.html#method.extension
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
+    /// [`self.file_name`]: Path::file_name
+    /// [`self.extension`]: Path::extension
     ///
     /// # Examples
     ///
@@ -1344,8 +1306,6 @@ impl PathBuf {
 
     /// Consumes the `PathBuf`, yielding its internal [`OsString`] storage.
     ///
-    /// [`OsString`]: ../ffi/struct.OsString.html
-    ///
     /// # Examples
     ///
     /// ```
@@ -1360,9 +1320,6 @@ impl PathBuf {
     }
 
     /// Converts this `PathBuf` into a [boxed][`Box`] [`Path`].
-    ///
-    /// [`Box`]: ../../std/boxed/struct.Box.html
-    /// [`Path`]: struct.Path.html
     #[stable(feature = "into_boxed_path", since = "1.20.0")]
     pub fn into_boxed_path(self) -> Box<Path> {
         let rw = Box::into_raw(self.inner.into_boxed_os_str()) as *mut Path;
@@ -1371,8 +1328,7 @@ impl PathBuf {
 
     /// Invokes [`capacity`] on the underlying instance of [`OsString`].
     ///
-    /// [`capacity`]: ../ffi/struct.OsString.html#method.capacity
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`capacity`]: OsString::capacity
     #[stable(feature = "path_buf_capacity", since = "1.44.0")]
     pub fn capacity(&self) -> usize {
         self.inner.capacity()
@@ -1380,8 +1336,7 @@ impl PathBuf {
 
     /// Invokes [`clear`] on the underlying instance of [`OsString`].
     ///
-    /// [`clear`]: ../ffi/struct.OsString.html#method.clear
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`clear`]: OsString::clear
     #[stable(feature = "path_buf_capacity", since = "1.44.0")]
     pub fn clear(&mut self) {
         self.inner.clear()
@@ -1389,8 +1344,7 @@ impl PathBuf {
 
     /// Invokes [`reserve`] on the underlying instance of [`OsString`].
     ///
-    /// [`reserve`]: ../ffi/struct.OsString.html#method.reserve
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`reserve`]: OsString::reserve
     #[stable(feature = "path_buf_capacity", since = "1.44.0")]
     pub fn reserve(&mut self, additional: usize) {
         self.inner.reserve(additional)
@@ -1398,8 +1352,7 @@ impl PathBuf {
 
     /// Invokes [`reserve_exact`] on the underlying instance of [`OsString`].
     ///
-    /// [`reserve_exact`]: ../ffi/struct.OsString.html#method.reserve_exact
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`reserve_exact`]: OsString::reserve_exact
     #[stable(feature = "path_buf_capacity", since = "1.44.0")]
     pub fn reserve_exact(&mut self, additional: usize) {
         self.inner.reserve_exact(additional)
@@ -1407,8 +1360,7 @@ impl PathBuf {
 
     /// Invokes [`shrink_to_fit`] on the underlying instance of [`OsString`].
     ///
-    /// [`shrink_to_fit`]: ../ffi/struct.OsString.html#method.shrink_to_fit
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`shrink_to_fit`]: OsString::shrink_to_fit
     #[stable(feature = "path_buf_capacity", since = "1.44.0")]
     pub fn shrink_to_fit(&mut self) {
         self.inner.shrink_to_fit()
@@ -1416,8 +1368,7 @@ impl PathBuf {
 
     /// Invokes [`shrink_to`] on the underlying instance of [`OsString`].
     ///
-    /// [`shrink_to`]: ../ffi/struct.OsString.html#method.shrink_to
-    /// [`OsString`]: ../ffi/struct.OsString.html
+    /// [`shrink_to`]: OsString::shrink_to
     #[unstable(feature = "shrink_to", issue = "56431")]
     pub fn shrink_to(&mut self, min_capacity: usize) {
         self.inner.shrink_to(min_capacity)
@@ -1703,10 +1654,6 @@ impl AsRef<OsStr> for PathBuf {
 /// pointer like `&` or [`Box`]. For an owned version of this type,
 /// see [`PathBuf`].
 ///
-/// [`str`]: ../primitive.str.html
-/// [`Box`]: ../boxed/struct.Box.html
-/// [`PathBuf`]: struct.PathBuf.html
-///
 /// More details about the overall approach can be found in
 /// the [module documentation](index.html).
 ///
@@ -1745,8 +1692,7 @@ pub struct Path {
 /// This `struct` is created by the [`strip_prefix`] method on [`Path`].
 /// See its documentation for more.
 ///
-/// [`strip_prefix`]: struct.Path.html#method.strip_prefix
-/// [`Path`]: struct.Path.html
+/// [`strip_prefix`]: Path::strip_prefix
 #[derive(Debug, Clone, PartialEq, Eq)]
 #[stable(since = "1.7.0", feature = "strip_prefix")]
 pub struct StripPrefixError(());
@@ -1791,8 +1737,6 @@ impl Path {
 
     /// Yields the underlying [`OsStr`] slice.
     ///
-    /// [`OsStr`]: ../ffi/struct.OsStr.html
-    ///
     /// # Examples
     ///
     /// ```
@@ -1812,7 +1756,7 @@ impl Path {
     /// Note that validation is performed because non-UTF-8 strings are
     /// perfectly valid for some OS.
     ///
-    /// [`&str`]: ../primitive.str.html
+    /// [`&str`]: str
     ///
     /// # Examples
     ///
@@ -1832,8 +1776,8 @@ impl Path {
     /// Any non-Unicode sequences are replaced with
     /// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
     ///
-    /// [`Cow<str>`]: ../borrow/enum.Cow.html
-    /// [U+FFFD]: ../char/constant.REPLACEMENT_CHARACTER.html
+    /// [`Cow<str>`]: Cow
+    /// [U+FFFD]: super::char::REPLACEMENT_CHARACTER
     ///
     /// # Examples
     ///
@@ -1855,8 +1799,6 @@ impl Path {
 
     /// Converts a `Path` to an owned [`PathBuf`].
     ///
-    /// [`PathBuf`]: struct.PathBuf.html
-    ///
     /// # Examples
     ///
     /// ```
@@ -1888,7 +1830,7 @@ impl Path {
     /// assert!(!Path::new("foo.txt").is_absolute());
     /// ```
     ///
-    /// [`has_root`]: #method.has_root
+    /// [`has_root`]: Path::has_root
     #[stable(feature = "rust1", since = "1.0.0")]
     #[allow(deprecated)]
     pub fn is_absolute(&self) -> bool {
@@ -1912,7 +1854,7 @@ impl Path {
     /// assert!(Path::new("foo.txt").is_relative());
     /// ```
     ///
-    /// [`is_absolute`]: #method.is_absolute
+    /// [`is_absolute`]: Path::is_absolute
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn is_relative(&self) -> bool {
         !self.is_absolute()
@@ -1947,8 +1889,6 @@ impl Path {
     ///
     /// Returns [`None`] if the path terminates in a root or prefix.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// ```
@@ -1992,10 +1932,16 @@ impl Path {
     /// assert_eq!(ancestors.next(), Some(Path::new("/foo")));
     /// assert_eq!(ancestors.next(), Some(Path::new("/")));
     /// assert_eq!(ancestors.next(), None);
+    ///
+    /// let mut ancestors = Path::new("../foo/bar").ancestors();
+    /// assert_eq!(ancestors.next(), Some(Path::new("../foo/bar")));
+    /// assert_eq!(ancestors.next(), Some(Path::new("../foo")));
+    /// assert_eq!(ancestors.next(), Some(Path::new("..")));
+    /// assert_eq!(ancestors.next(), Some(Path::new("")));
+    /// assert_eq!(ancestors.next(), None);
     /// ```
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    /// [`parent`]: struct.Path.html#method.parent
+    /// [`parent`]: Path::parent
     #[stable(feature = "path_ancestors", since = "1.28.0")]
     pub fn ancestors(&self) -> Ancestors<'_> {
         Ancestors { next: Some(&self) }
@@ -2008,8 +1954,6 @@ impl Path {
     ///
     /// Returns [`None`] if the path terminates in `..`.
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// ```
@@ -2038,8 +1982,7 @@ impl Path {
     /// If `base` is not a prefix of `self` (i.e., [`starts_with`]
     /// returns `false`), returns [`Err`].
     ///
-    /// [`starts_with`]: #method.starts_with
-    /// [`Err`]: ../../std/result/enum.Result.html#variant.Err
+    /// [`starts_with`]: Path::starts_with
     ///
     /// # Examples
     ///
@@ -2053,8 +1996,9 @@ impl Path {
     /// assert_eq!(path.strip_prefix("/test/"), Ok(Path::new("haha/foo.txt")));
     /// assert_eq!(path.strip_prefix("/test/haha/foo.txt"), Ok(Path::new("")));
     /// assert_eq!(path.strip_prefix("/test/haha/foo.txt/"), Ok(Path::new("")));
-    /// assert_eq!(path.strip_prefix("test").is_ok(), false);
-    /// assert_eq!(path.strip_prefix("/haha").is_ok(), false);
+    ///
+    /// assert!(path.strip_prefix("test").is_err());
+    /// assert!(path.strip_prefix("/haha").is_err());
     ///
     /// let prefix = PathBuf::from("/test/");
     /// assert_eq!(path.strip_prefix(prefix), Ok(Path::new("haha/foo.txt")));
@@ -2087,9 +2031,13 @@ impl Path {
     /// assert!(path.starts_with("/etc"));
     /// assert!(path.starts_with("/etc/"));
     /// assert!(path.starts_with("/etc/passwd"));
-    /// assert!(path.starts_with("/etc/passwd/"));
+    /// assert!(path.starts_with("/etc/passwd/")); // extra slash is okay
+    /// assert!(path.starts_with("/etc/passwd///")); // multiple extra slashes are okay
     ///
     /// assert!(!path.starts_with("/e"));
+    /// assert!(!path.starts_with("/etc/passwd.txt"));
+    ///
+    /// assert!(!Path::new("/etc/foo.rs").starts_with("/etc/foo"));
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn starts_with<P: AsRef<Path>>(&self, base: P) -> bool {
@@ -2124,7 +2072,7 @@ impl Path {
 
     /// Extracts the stem (non-extension) portion of [`self.file_name`].
     ///
-    /// [`self.file_name`]: struct.Path.html#method.file_name
+    /// [`self.file_name`]: Path::file_name
     ///
     /// The stem is:
     ///
@@ -2133,16 +2081,13 @@ impl Path {
     /// * The entire file name if the file name begins with `.` and has no other `.`s within;
     /// * Otherwise, the portion of the file name before the final `.`
     ///
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
-    ///
     /// # Examples
     ///
     /// ```
     /// use std::path::Path;
     ///
-    /// let path = Path::new("foo.rs");
-    ///
-    /// assert_eq!("foo", path.file_stem().unwrap());
+    /// assert_eq!("foo", Path::new("foo.rs").file_stem().unwrap());
+    /// assert_eq!("foo.tar", Path::new("foo.tar.gz").file_stem().unwrap());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn file_stem(&self) -> Option<&OsStr> {
@@ -2158,17 +2103,15 @@ impl Path {
     /// * [`None`], if the file name begins with `.` and has no other `.`s within;
     /// * Otherwise, the portion of the file name after the final `.`
     ///
-    /// [`self.file_name`]: struct.Path.html#method.file_name
-    /// [`None`]: ../../std/option/enum.Option.html#variant.None
+    /// [`self.file_name`]: Path::file_name
     ///
     /// # Examples
     ///
     /// ```
     /// use std::path::Path;
     ///
-    /// let path = Path::new("foo.rs");
-    ///
-    /// assert_eq!("rs", path.extension().unwrap());
+    /// assert_eq!("rs", Path::new("foo.rs").extension().unwrap());
+    /// assert_eq!("gz", Path::new("foo.tar.gz").extension().unwrap());
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn extension(&self) -> Option<&OsStr> {
@@ -2179,9 +2122,6 @@ impl Path {
     ///
     /// See [`PathBuf::push`] for more details on what it means to adjoin a path.
     ///
-    /// [`PathBuf`]: struct.PathBuf.html
-    /// [`PathBuf::push`]: struct.PathBuf.html#method.push
-    ///
     /// # Examples
     ///
     /// ```
@@ -2205,9 +2145,6 @@ impl Path {
     ///
     /// See [`PathBuf::set_file_name`] for more details.
     ///
-    /// [`PathBuf`]: struct.PathBuf.html
-    /// [`PathBuf::set_file_name`]: struct.PathBuf.html#method.set_file_name
-    ///
     /// # Examples
     ///
     /// ```
@@ -2234,9 +2171,6 @@ impl Path {
     ///
     /// See [`PathBuf::set_extension`] for more details.
     ///
-    /// [`PathBuf`]: struct.PathBuf.html
-    /// [`PathBuf::set_extension`]: struct.PathBuf.html#method.set_extension
-    ///
     /// # Examples
     ///
     /// ```
@@ -2247,6 +2181,8 @@ impl Path {
     ///
     /// let path = Path::new("foo.tar.gz");
     /// assert_eq!(path.with_extension(""), PathBuf::from("foo.tar"));
+    /// assert_eq!(path.with_extension("xz"), PathBuf::from("foo.tar.xz"));
+    /// assert_eq!(path.with_extension("").with_extension("txt"), PathBuf::from("foo.txt"));
     /// ```
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn with_extension<S: AsRef<OsStr>>(&self, extension: S) -> PathBuf {
@@ -2291,8 +2227,7 @@ impl Path {
     /// assert_eq!(components.next(), None)
     /// ```
     ///
-    /// [`Component`]: enum.Component.html
-    /// [`CurDir`]: enum.Component.html#variant.CurDir
+    /// [`CurDir`]: Component::CurDir
     #[stable(feature = "rust1", since = "1.0.0")]
     pub fn components(&self) -> Components<'_> {
         let prefix = parse_prefix(self.as_os_str());
@@ -2312,8 +2247,7 @@ impl Path {
     /// For more information about the particulars of how the path is separated
     /// into components, see [`components`].
     ///
-    /// [`components`]: #method.components
-    /// [`OsStr`]: ../ffi/struct.OsStr.html
+    /// [`components`]: Path::components
     ///
     /// # Examples
     ///
@@ -2335,7 +2269,7 @@ impl Path {
     /// Returns an object that implements [`Display`] for safely printing paths
     /// that may contain non-Unicode data.
     ///
-    /// [`Display`]: ../fmt/trait.Display.html
+    /// [`Display`]: fmt::Display
     ///
     /// # Examples
     ///
@@ -2358,8 +2292,6 @@ impl Path {
     ///
     /// This is an alias to [`fs::metadata`].
     ///
-    /// [`fs::metadata`]: ../fs/fn.metadata.html
-    ///
     /// # Examples
     ///
     /// ```no_run
@@ -2378,8 +2310,6 @@ impl Path {
     ///
     /// This is an alias to [`fs::symlink_metadata`].
     ///
-    /// [`fs::symlink_metadata`]: ../fs/fn.symlink_metadata.html
-    ///
     /// # Examples
     ///
     /// ```no_run
@@ -2399,8 +2329,6 @@ impl Path {
     ///
     /// This is an alias to [`fs::canonicalize`].
     ///
-    /// [`fs::canonicalize`]: ../fs/fn.canonicalize.html
-    ///
     /// # Examples
     ///
     /// ```no_run
@@ -2418,8 +2346,6 @@ impl Path {
     ///
     /// This is an alias to [`fs::read_link`].
     ///
-    /// [`fs::read_link`]: ../fs/fn.read_link.html
-    ///
     /// # Examples
     ///
     /// ```no_run
@@ -2435,15 +2361,11 @@ impl Path {
 
     /// Returns an iterator over the entries within a directory.
     ///
-    /// The iterator will yield instances of [`io::Result`]`<`[`DirEntry`]`>`. New
+    /// The iterator will yield instances of [`io::Result`]`<`[`fs::DirEntry`]`>`. New
     /// errors may be encountered after an iterator is initially constructed.
     ///
     /// This is an alias to [`fs::read_dir`].
     ///
-    /// [`io::Result`]: ../io/type.Result.html
-    /// [`DirEntry`]: ../fs/struct.DirEntry.html
-    /// [`fs::read_dir`]: ../fs/fn.read_dir.html
-    ///
     /// # Examples
     ///
     /// ```no_run
@@ -2473,15 +2395,13 @@ impl Path {
     ///
     /// ```no_run
     /// use std::path::Path;
-    /// assert_eq!(Path::new("does_not_exist.txt").exists(), false);
+    /// assert!(!Path::new("does_not_exist.txt").exists());
     /// ```
     ///
     /// # See Also
     ///
     /// This is a convenience function that coerces errors to false. If you want to
-    /// check errors, call [fs::metadata].
-    ///
-    /// [fs::metadata]: ../../std/fs/fn.metadata.html
+    /// check errors, call [`fs::metadata`].
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn exists(&self) -> bool {
         fs::metadata(self).is_ok()
@@ -2506,20 +2426,14 @@ impl Path {
     /// # See Also
     ///
     /// This is a convenience function that coerces errors to false. If you want to
-    /// check errors, call [`fs::metadata`] and handle its Result. Then call
-    /// [`fs::Metadata::is_file`] if it was Ok.
+    /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
+    /// [`fs::Metadata::is_file`] if it was [`Ok`].
     ///
     /// When the goal is simply to read from (or write to) the source, the most
     /// reliable way to test the source can be read (or written to) is to open
     /// it. Only using `is_file` can break workflows like `diff <( prog_a )` on
-    /// a Unix-like system for example. See [`File::open`] or
-    /// [`OpenOptions::open`] for more information.
-    ///
-    /// [`fs::metadata`]: ../../std/fs/fn.metadata.html
-    /// [`fs::Metadata`]: ../../std/fs/struct.Metadata.html
-    /// [`fs::Metadata::is_file`]: ../../std/fs/struct.Metadata.html#method.is_file
-    /// [`File::open`]: ../../std/fs/struct.File.html#method.open
-    /// [`OpenOptions::open`]: ../../std/fs/struct.OpenOptions.html#method.open
+    /// a Unix-like system for example. See [`fs::File::open`] or
+    /// [`fs::OpenOptions::open`] for more information.
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn is_file(&self) -> bool {
         fs::metadata(self).map(|m| m.is_file()).unwrap_or(false)
@@ -2544,11 +2458,8 @@ impl Path {
     /// # See Also
     ///
     /// This is a convenience function that coerces errors to false. If you want to
-    /// check errors, call [fs::metadata] and handle its Result. Then call
-    /// [fs::Metadata::is_dir] if it was Ok.
-    ///
-    /// [fs::metadata]: ../../std/fs/fn.metadata.html
-    /// [fs::Metadata::is_dir]: ../../std/fs/struct.Metadata.html#method.is_dir
+    /// check errors, call [`fs::metadata`] and handle its [`Result`]. Then call
+    /// [`fs::Metadata::is_dir`] if it was [`Ok`].
     #[stable(feature = "path_ext", since = "1.5.0")]
     pub fn is_dir(&self) -> bool {
         fs::metadata(self).map(|m| m.is_dir()).unwrap_or(false)
@@ -2556,9 +2467,6 @@ impl Path {
 
     /// Converts a [`Box<Path>`][`Box`] into a [`PathBuf`] without copying or
     /// allocating.
-    ///
-    /// [`Box`]: ../../std/boxed/struct.Box.html
-    /// [`PathBuf`]: struct.PathBuf.html
     #[stable(feature = "into_boxed_path", since = "1.20.0")]
     pub fn into_path_buf(self: Box<Path>) -> PathBuf {
         let rw = Box::into_raw(self) as *mut OsStr;
@@ -2597,10 +2505,8 @@ impl fmt::Debug for Path {
 /// println!("{}", path.display());
 /// ```
 ///
-/// [`Display`]: ../../std/fmt/trait.Display.html
-/// [`format!`]: ../../std/macro.format.html
-/// [`Path`]: struct.Path.html
-/// [`Path::display`]: struct.Path.html#method.display
+/// [`Display`]: fmt::Display
+/// [`format!`]: crate::format
 #[stable(feature = "rust1", since = "1.0.0")]
 pub struct Display<'a> {
     path: &'a Path,
diff --git a/library/std/src/rt.rs b/library/std/src/rt.rs
index fb825ab16eb..45af9f68a0f 100644
--- a/library/std/src/rt.rs
+++ b/library/std/src/rt.rs
@@ -48,9 +48,7 @@ fn lang_start_internal(
         sys::args::init(argc, argv);
 
         // Let's run some code!
-        let exit_code = panic::catch_unwind(|| {
-            sys_common::backtrace::__rust_begin_short_backtrace(move || main())
-        });
+        let exit_code = panic::catch_unwind(main);
 
         sys_common::cleanup();
         exit_code.unwrap_or(101) as isize
@@ -64,5 +62,9 @@ fn lang_start<T: crate::process::Termination + 'static>(
     argc: isize,
     argv: *const *const u8,
 ) -> isize {
-    lang_start_internal(&move || main().report(), argc, argv)
+    lang_start_internal(
+        &move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report(),
+        argc,
+        argv,
+    )
 }
diff --git a/library/std/src/sys/hermit/mod.rs b/library/std/src/sys/hermit/mod.rs
index 675b82ceb77..8eaf07e52d6 100644
--- a/library/std/src/sys/hermit/mod.rs
+++ b/library/std/src/sys/hermit/mod.rs
@@ -106,7 +106,7 @@ pub unsafe extern "C" fn runtime_entry(
     argv: *const *const c_char,
     env: *const *const c_char,
 ) -> ! {
-    use crate::sys::hermit::fast_thread_local::run_dtors;
+    use crate::sys::hermit::thread_local_dtor::run_dtors;
     extern "C" {
         fn main(argc: isize, argv: *const *const c_char) -> i32;
     }
diff --git a/library/std/src/sys/hermit/thread.rs b/library/std/src/sys/hermit/thread.rs
index e11afed6687..7bd71e120de 100644
--- a/library/std/src/sys/hermit/thread.rs
+++ b/library/std/src/sys/hermit/thread.rs
@@ -4,7 +4,7 @@ use crate::ffi::CStr;
 use crate::io;
 use crate::mem;
 use crate::sys::hermit::abi;
-use crate::sys::hermit::fast_thread_local::run_dtors;
+use crate::sys::hermit::thread_local_dtor::run_dtors;
 use crate::time::Duration;
 
 pub type Tid = abi::Tid;
diff --git a/library/std/src/sys/unix/fd.rs b/library/std/src/sys/unix/fd.rs
index e36a53084ba..ba169b251b0 100644
--- a/library/std/src/sys/unix/fd.rs
+++ b/library/std/src/sys/unix/fd.rs
@@ -283,12 +283,12 @@ impl Drop for FileDesc {
 #[cfg(test)]
 mod tests {
     use super::{FileDesc, IoSlice};
+    use core::mem::ManuallyDrop;
 
     #[test]
     fn limit_vector_count() {
-        let stdout = FileDesc { fd: 1 };
+        let stdout = ManuallyDrop::new(FileDesc { fd: 1 });
         let bufs = (0..1500).map(|_| IoSlice::new(&[])).collect::<Vec<_>>();
-
         assert!(stdout.write_vectored(&bufs).is_ok());
     }
 }
diff --git a/library/std/src/sys/windows/mod.rs b/library/std/src/sys/windows/mod.rs
index 9a52371280e..982ec912c44 100644
--- a/library/std/src/sys/windows/mod.rs
+++ b/library/std/src/sys/windows/mod.rs
@@ -99,43 +99,28 @@ pub fn decode_error_kind(errno: i32) -> ErrorKind {
 
 pub fn unrolled_find_u16s(needle: u16, haystack: &[u16]) -> Option<usize> {
     let ptr = haystack.as_ptr();
-    let mut len = haystack.len();
     let mut start = &haystack[..];
 
     // For performance reasons unfold the loop eight times.
-    while len >= 8 {
-        if start[0] == needle {
-            return Some((start.as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[1] == needle {
-            return Some((start[1..].as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[2] == needle {
-            return Some((start[2..].as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[3] == needle {
-            return Some((start[3..].as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[4] == needle {
-            return Some((start[4..].as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[5] == needle {
-            return Some((start[5..].as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[6] == needle {
-            return Some((start[6..].as_ptr() as usize - ptr as usize) / 2);
-        }
-        if start[7] == needle {
-            return Some((start[7..].as_ptr() as usize - ptr as usize) / 2);
+    while start.len() >= 8 {
+        macro_rules! if_return {
+            ($($n:literal,)+) => {
+                $(
+                    if start[$n] == needle {
+                        return Some((&start[$n] as *const u16 as usize - ptr as usize) / 2);
+                    }
+                )+
+            }
         }
 
+        if_return!(0, 1, 2, 3, 4, 5, 6, 7,);
+
         start = &start[8..];
-        len -= 8;
     }
 
-    for (i, c) in start.iter().enumerate() {
+    for c in start {
         if *c == needle {
-            return Some((start.as_ptr() as usize - ptr as usize) / 2 + i);
+            return Some((c as *const u16 as usize - ptr as usize) / 2);
         }
     }
     None
diff --git a/library/std/src/sys_common/backtrace.rs b/library/std/src/sys_common/backtrace.rs
index d386a656e4f..1c5fbf7d701 100644
--- a/library/std/src/sys_common/backtrace.rs
+++ b/library/std/src/sys_common/backtrace.rs
@@ -74,6 +74,8 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
     bt_fmt.add_context()?;
     let mut idx = 0;
     let mut res = Ok(());
+    // Start immediately if we're not using a short backtrace.
+    let mut start = print_fmt != PrintFmt::Short;
     backtrace_rs::trace_unsynchronized(|frame| {
         if print_fmt == PrintFmt::Short && idx > MAX_NB_FRAMES {
             return false;
@@ -89,16 +91,24 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
                         stop = true;
                         return;
                     }
+                    if sym.contains("__rust_end_short_backtrace") {
+                        start = true;
+                        return;
+                    }
                 }
             }
 
-            res = bt_fmt.frame().symbol(frame, symbol);
+            if start {
+                res = bt_fmt.frame().symbol(frame, symbol);
+            }
         });
         if stop {
             return false;
         }
         if !hit {
-            res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+            if start {
+                res = bt_fmt.frame().print_raw(frame.ip(), None, None, None);
+            }
         }
 
         idx += 1;
@@ -123,10 +133,29 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt::
 pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
 where
     F: FnOnce() -> T,
-    F: Send,
-    T: Send,
 {
-    f()
+    let result = f();
+
+    // prevent this frame from being tail-call optimised away
+    crate::hint::black_box(());
+
+    result
+}
+
+/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that
+/// this is only inline(never) when backtraces in libstd are enabled, otherwise
+/// it's fine to optimize away.
+#[cfg_attr(feature = "backtrace", inline(never))]
+pub fn __rust_end_short_backtrace<F, T>(f: F) -> T
+where
+    F: FnOnce() -> T,
+{
+    let result = f();
+
+    // prevent this frame from being tail-call optimised away
+    crate::hint::black_box(());
+
+    result
 }
 
 pub enum RustBacktrace {
diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs
index ecd6fbc6b93..66508f06b28 100644
--- a/library/std/src/thread/local.rs
+++ b/library/std/src/thread/local.rs
@@ -172,7 +172,11 @@ macro_rules! __thread_local_inner {
                 static __KEY: $crate::thread::__OsLocalKeyInner<$t> =
                     $crate::thread::__OsLocalKeyInner::new();
 
-                __KEY.get(__init)
+                // FIXME: remove the #[allow(...)] marker when macros don't
+                // raise warning for missing/extraneous unsafe blocks anymore.
+                // See https://github.com/rust-lang/rust/issues/74838.
+                #[allow(unused_unsafe)]
+                unsafe { __KEY.get(__init) }
             }
 
             unsafe {
diff --git a/library/test/src/helpers/concurrency.rs b/library/test/src/helpers/concurrency.rs
index 2fe87247e3a..7ca27bf0dc1 100644
--- a/library/test/src/helpers/concurrency.rs
+++ b/library/test/src/helpers/concurrency.rs
@@ -4,7 +4,7 @@ use std::env;
 
 #[allow(deprecated)]
 pub fn get_concurrency() -> usize {
-    return match env::var("RUST_TEST_THREADS") {
+    match env::var("RUST_TEST_THREADS") {
         Ok(s) => {
             let opt_n: Option<usize> = s.parse().ok();
             match opt_n {
@@ -13,7 +13,7 @@ pub fn get_concurrency() -> usize {
             }
         }
         Err(..) => num_cpus(),
-    };
+    }
 }
 
 cfg_if::cfg_if! {
diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs
index 933b647071f..6bd708ef487 100644
--- a/library/test/src/lib.rs
+++ b/library/test/src/lib.rs
@@ -514,7 +514,10 @@ pub fn run_test(
 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
 #[inline(never)]
 fn __rust_begin_short_backtrace<F: FnOnce()>(f: F) {
-    f()
+    f();
+
+    // prevent this frame from being tail-call optimised away
+    black_box(());
 }
 
 fn run_test_in_process(
diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs
index c4d10ab177b..20a2ca98405 100644
--- a/library/unwind/src/lib.rs
+++ b/library/unwind/src/lib.rs
@@ -19,6 +19,7 @@ cfg_if::cfg_if! {
     } else if #[cfg(any(
         unix,
         windows,
+        target_os = "psp",
         target_os = "cloudabi",
         all(target_vendor = "fortanix", target_env = "sgx"),
     ))] {
@@ -32,7 +33,6 @@ cfg_if::cfg_if! {
         // - os=uefi
         // - os=cuda
         // - nvptx64-nvidia-cuda
-        // - mipsel-sony-psp
         // - Any new targets not listed above.
     }
 }
diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py
index cec9c56a235..67f99ec4e40 100644
--- a/src/etc/gdb_providers.py
+++ b/src/etc/gdb_providers.py
@@ -352,8 +352,13 @@ class StdHashMapProvider:
         ctrl = table["ctrl"]["pointer"]
 
         self.size = int(table["items"])
-        self.data_ptr = table["data"]["pointer"]
-        self.pair_type = self.data_ptr.dereference().type
+        self.pair_type = table.type.template_argument(0)
+
+        self.new_layout = not table.type.has_key("data")
+        if self.new_layout:
+            self.data_ptr = ctrl.cast(self.pair_type.pointer())
+        else:
+            self.data_ptr = table["data"]["pointer"]
 
         self.valid_indices = []
         for idx in range(capacity):
@@ -374,6 +379,8 @@ class StdHashMapProvider:
 
         for index in range(self.size):
             idx = self.valid_indices[index]
+            if self.new_layout:
+                idx = -(idx + 1)
             element = (pairs_start + idx).dereference()
             if self.show_values:
                 yield "key{}".format(index), element[ZERO_FIELD]
diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py
index 3c7817b3a61..19da75c35b4 100644
--- a/src/etc/lldb_providers.py
+++ b/src/etc/lldb_providers.py
@@ -514,6 +514,8 @@ class StdHashMapSyntheticProvider:
         # type: (int) -> SBValue
         pairs_start = self.data_ptr.GetValueAsUnsigned()
         idx = self.valid_indices[index]
+        if self.new_layout:
+            idx = -(idx + 1)
         address = pairs_start + idx * self.pair_type_size
         element = self.data_ptr.CreateValueFromAddress("[%s]" % index, address, self.pair_type)
         if self.show_values:
@@ -529,10 +531,15 @@ class StdHashMapSyntheticProvider:
         ctrl = table.GetChildMemberWithName("ctrl").GetChildAtIndex(0)
 
         self.size = table.GetChildMemberWithName("items").GetValueAsUnsigned()
-        self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0)
-        self.pair_type = self.data_ptr.Dereference().GetType()
+        self.pair_type = table.type.template_args[0]
         self.pair_type_size = self.pair_type.GetByteSize()
 
+        self.new_layout = not table.GetChildMemberWithName("data").IsValid()
+        if self.new_layout:
+            self.data_ptr = ctrl.Cast(self.pair_type.GetPointerType())
+        else:
+            self.data_ptr = table.GetChildMemberWithName("data").GetChildAtIndex(0)
+
         u8_type = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar)
         u8_type_size = self.valobj.GetTarget().GetBasicType(eBasicTypeUnsignedChar).GetByteSize()
 
diff --git a/src/etc/natvis/libstd.natvis b/src/etc/natvis/libstd.natvis
index b3fc3d17af7..4e81173d3d0 100644
--- a/src/etc/natvis/libstd.natvis
+++ b/src/etc/natvis/libstd.natvis
@@ -30,6 +30,7 @@
     <Expand>
       <Item Name="[size]">base.table.items</Item>
       <Item Name="[capacity]">base.table.items + base.table.growth_left</Item>
+      <Item Name="[state]">base.hash_builder</Item>
 
       <CustomListItems>
         <Variable Name="i" InitialValue="0" />
@@ -40,7 +41,7 @@
           <If Condition="(base.table.ctrl.pointer[i] &amp; 0x80) == 0">
             <!-- Bucket is populated -->
             <Exec>n--</Exec>
-            <Item Name="{base.table.data.pointer[i].__0}">base.table.data.pointer[i].__1</Item>
+            <Item Name="{static_cast&lt;tuple&lt;$T1, $T2&gt;*&gt;(base.table.ctrl.pointer)[-(i + 1)].__0}">static_cast&lt;tuple&lt;$T1, $T2&gt;*&gt;(base.table.ctrl.pointer)[-(i + 1)].__1</Item>
           </If>
           <Exec>i++</Exec>
         </Loop>
@@ -53,6 +54,7 @@
     <Expand>
       <Item Name="[size]">map.base.table.items</Item>
       <Item Name="[capacity]">map.base.table.items + map.base.table.growth_left</Item>
+      <Item Name="[state]">map.base.hash_builder</Item>
 
       <CustomListItems>
         <Variable Name="i" InitialValue="0" />
@@ -63,36 +65,7 @@
           <If Condition="(map.base.table.ctrl.pointer[i] &amp; 0x80) == 0">
             <!-- Bucket is populated -->
             <Exec>n--</Exec>
-            <Item>map.base.table.data.pointer[i].__0</Item>
-          </If>
-          <Exec>i++</Exec>
-        </Loop>
-      </CustomListItems>
-    </Expand>
-  </Type>
-
-  <Type Name="hashbrown::raw::RawTable&lt;*&gt;">
-    <!-- RawTable has a nice and simple layout.
-      items                     Number of *populated* values in the RawTable (less than the size of ctrl.pointer / data.pointer)
-      growth_left               Remaining capacity before growth
-      ctrl.pointer[i] & 0x80    Indicates the bucket is empty / should be skipped / doesn't count towards items.
-      data.pointer[i]           The (K,V) tuple, if not empty.
-    -->
-    <DisplayString>{{ size={items} }}</DisplayString>
-    <Expand>
-      <Item Name="[size]">items</Item>
-      <Item Name="[capacity]">items + growth_left</Item>
-
-      <CustomListItems>
-        <Variable Name="i" InitialValue="0" />
-        <Variable Name="n" InitialValue="items" />
-        <Size>items</Size>
-        <Loop>
-          <Break Condition="n == 0" />
-          <If Condition="(ctrl.pointer[i] &amp; 0x80) == 0">
-            <!-- Bucket is populated -->
-            <Exec>n--</Exec>
-            <Item>data.pointer[i]</Item>
+            <Item>static_cast&lt;$T1*&gt;(map.base.table.ctrl.pointer)[-(i + 1)]</Item>
           </If>
           <Exec>i++</Exec>
         </Loop>
diff --git a/src/librustc_ast/Cargo.toml b/src/librustc_ast/Cargo.toml
index a36f49bd414..ab0ab7244db 100644
--- a/src/librustc_ast/Cargo.toml
+++ b/src/librustc_ast/Cargo.toml
@@ -12,7 +12,6 @@ doctest = false
 [dependencies]
 rustc_serialize = { path = "../librustc_serialize" }
 log = { package = "tracing", version = "0.1" }
-scoped-tls = "1.0"
 rustc_span = { path = "../librustc_span" }
 rustc_data_structures = { path = "../librustc_data_structures" }
 rustc_index = { path = "../librustc_index" }
diff --git a/src/librustc_ast/ast.rs b/src/librustc_ast/ast.rs
index 6543117774a..83a9de84ed8 100644
--- a/src/librustc_ast/ast.rs
+++ b/src/librustc_ast/ast.rs
@@ -23,7 +23,7 @@ pub use GenericArgs::*;
 pub use UnsafeSource::*;
 
 use crate::ptr::P;
-use crate::token::{self, DelimToken};
+use crate::token::{self, CommentKind, DelimToken};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree};
 
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
@@ -378,7 +378,7 @@ impl Default for Generics {
 #[derive(Clone, RustcEncodable, RustcDecodable, Debug)]
 pub struct WhereClause {
     /// `true` if we ate a `where` token: this can happen
-    /// if we parsed no predicates (e.g. `struct Foo where {}
+    /// if we parsed no predicates (e.g. `struct Foo where {}`).
     /// This allows us to accurately pretty-print
     /// in `nt_to_tokenstream`
     pub has_where_token: bool,
@@ -1052,6 +1052,30 @@ impl Expr {
         }
     }
 
+    /// Is this expr either `N`, or `{ N }`.
+    ///
+    /// If this is not the case, name resolution does not resolve `N` when using
+    /// `feature(min_const_generics)` as more complex expressions are not supported.
+    pub fn is_potential_trivial_const_param(&self) -> bool {
+        let this = if let ExprKind::Block(ref block, None) = self.kind {
+            if block.stmts.len() == 1 {
+                if let StmtKind::Expr(ref expr) = block.stmts[0].kind { expr } else { self }
+            } else {
+                self
+            }
+        } else {
+            self
+        };
+
+        if let ExprKind::Path(None, ref path) = this.kind {
+            if path.segments.len() == 1 && path.segments[0].args.is_none() {
+                return true;
+            }
+        }
+
+        false
+    }
+
     pub fn to_bound(&self) -> Option<GenericBound> {
         match &self.kind {
             ExprKind::Path(None, path) => Some(GenericBound::Trait(
@@ -2365,7 +2389,7 @@ pub enum AttrKind {
     /// A doc comment (e.g. `/// ...`, `//! ...`, `/** ... */`, `/*! ... */`).
     /// Doc attributes (e.g. `#[doc="..."]`) are represented with the `Normal`
     /// variant (which is much less compact and thus more expensive).
-    DocComment(Symbol),
+    DocComment(CommentKind, Symbol),
 }
 
 /// `TraitRef`s appear in impls.
diff --git a/src/librustc_ast/attr/mod.rs b/src/librustc_ast/attr/mod.rs
index 809fda86542..edcbce3e2cf 100644
--- a/src/librustc_ast/attr/mod.rs
+++ b/src/librustc_ast/attr/mod.rs
@@ -7,75 +7,33 @@ use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
 use crate::ast::{Path, PathSegment};
 use crate::mut_visit::visit_clobber;
 use crate::ptr::P;
-use crate::token::{self, Token};
+use crate::token::{self, CommentKind, Token};
 use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};
 
-use rustc_data_structures::sync::Lock;
 use rustc_index::bit_set::GrowableBitSet;
-use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::source_map::{BytePos, Spanned};
 use rustc_span::symbol::{sym, Ident, Symbol};
 use rustc_span::Span;
 
-use log::debug;
 use std::iter;
 use std::ops::DerefMut;
 
-// Per-session global variables: this struct is stored in thread-local storage
-// in such a way that it is accessible without any kind of handle to all
-// threads within the compilation session, but is not accessible outside the
-// session.
-pub struct SessionGlobals {
-    used_attrs: Lock<GrowableBitSet<AttrId>>,
-    known_attrs: Lock<GrowableBitSet<AttrId>>,
-    span_session_globals: rustc_span::SessionGlobals,
-}
+pub struct MarkedAttrs(GrowableBitSet<AttrId>);
 
-impl SessionGlobals {
-    fn new(edition: Edition) -> SessionGlobals {
-        SessionGlobals {
-            // We have no idea how many attributes there will be, so just
-            // initiate the vectors with 0 bits. We'll grow them as necessary.
-            used_attrs: Lock::new(GrowableBitSet::new_empty()),
-            known_attrs: Lock::new(GrowableBitSet::new_empty()),
-            span_session_globals: rustc_span::SessionGlobals::new(edition),
-        }
+impl MarkedAttrs {
+    // We have no idea how many attributes there will be, so just
+    // initiate the vectors with 0 bits. We'll grow them as necessary.
+    pub fn new() -> Self {
+        MarkedAttrs(GrowableBitSet::new_empty())
     }
-}
-
-pub fn with_session_globals<R>(edition: Edition, f: impl FnOnce() -> R) -> R {
-    let ast_session_globals = SessionGlobals::new(edition);
-    SESSION_GLOBALS.set(&ast_session_globals, || {
-        rustc_span::SESSION_GLOBALS.set(&ast_session_globals.span_session_globals, f)
-    })
-}
-
-pub fn with_default_session_globals<R>(f: impl FnOnce() -> R) -> R {
-    with_session_globals(DEFAULT_EDITION, f)
-}
 
-scoped_tls::scoped_thread_local!(pub static SESSION_GLOBALS: SessionGlobals);
-
-pub fn mark_used(attr: &Attribute) {
-    debug!("marking {:?} as used", attr);
-    SESSION_GLOBALS.with(|session_globals| {
-        session_globals.used_attrs.lock().insert(attr.id);
-    });
-}
-
-pub fn is_used(attr: &Attribute) -> bool {
-    SESSION_GLOBALS.with(|session_globals| session_globals.used_attrs.lock().contains(attr.id))
-}
-
-pub fn mark_known(attr: &Attribute) {
-    debug!("marking {:?} as known", attr);
-    SESSION_GLOBALS.with(|session_globals| {
-        session_globals.known_attrs.lock().insert(attr.id);
-    });
-}
+    pub fn mark(&mut self, attr: &Attribute) {
+        self.0.insert(attr.id);
+    }
 
-pub fn is_known(attr: &Attribute) -> bool {
-    SESSION_GLOBALS.with(|session_globals| session_globals.known_attrs.lock().contains(attr.id))
+    pub fn is_marked(&self, attr: &Attribute) -> bool {
+        self.0.contains(attr.id)
+    }
 }
 
 pub fn is_known_lint_tool(m_item: Ident) -> bool {
@@ -169,23 +127,8 @@ impl Attribute {
     pub fn has_name(&self, name: Symbol) -> bool {
         match self.kind {
             AttrKind::Normal(ref item) => item.path == name,
-            AttrKind::DocComment(_) => false,
-        }
-    }
-
-    /// Returns `true` if the attribute's path matches the argument.
-    /// If it matches, then the attribute is marked as used.
-    /// Should only be used by rustc, other tools can use `has_name` instead,
-    /// because only rustc is supposed to report the `unused_attributes` lint.
-    /// `MetaItem` and `NestedMetaItem` are produced by "lowering" an `Attribute`
-    /// and don't have identity, so they only has the `has_name` method,
-    /// and you need to mark the original `Attribute` as used when necessary.
-    pub fn check_name(&self, name: Symbol) -> bool {
-        let matches = self.has_name(name);
-        if matches {
-            mark_used(self);
+            AttrKind::DocComment(..) => false,
         }
-        matches
     }
 
     /// For a single-segment attribute, returns its name; otherwise, returns `None`.
@@ -198,7 +141,7 @@ impl Attribute {
                     None
                 }
             }
-            AttrKind::DocComment(_) => None,
+            AttrKind::DocComment(..) => None,
         }
     }
     pub fn name_or_empty(&self) -> Symbol {
@@ -218,7 +161,7 @@ impl Attribute {
                 Some(MetaItem { kind: MetaItemKind::List(list), .. }) => Some(list),
                 _ => None,
             },
-            AttrKind::DocComment(_) => None,
+            AttrKind::DocComment(..) => None,
         }
     }
 
@@ -314,13 +257,13 @@ impl Attribute {
     pub fn is_doc_comment(&self) -> bool {
         match self.kind {
             AttrKind::Normal(_) => false,
-            AttrKind::DocComment(_) => true,
+            AttrKind::DocComment(..) => true,
         }
     }
 
     pub fn doc_str(&self) -> Option<Symbol> {
         match self.kind {
-            AttrKind::DocComment(symbol) => Some(symbol),
+            AttrKind::DocComment(.., data) => Some(data),
             AttrKind::Normal(ref item) if item.path == sym::doc => {
                 item.meta(self.span).and_then(|meta| meta.value_str())
             }
@@ -331,14 +274,14 @@ impl Attribute {
     pub fn get_normal_item(&self) -> &AttrItem {
         match self.kind {
             AttrKind::Normal(ref item) => item,
-            AttrKind::DocComment(_) => panic!("unexpected doc comment"),
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
 
     pub fn unwrap_normal_item(self) -> AttrItem {
         match self.kind {
             AttrKind::Normal(item) => item,
-            AttrKind::DocComment(_) => panic!("unexpected doc comment"),
+            AttrKind::DocComment(..) => panic!("unexpected doc comment"),
         }
     }
 
@@ -405,30 +348,19 @@ pub fn mk_attr_outer(item: MetaItem) -> Attribute {
     mk_attr(AttrStyle::Outer, item.path, item.kind.mac_args(item.span), item.span)
 }
 
-pub fn mk_doc_comment(style: AttrStyle, comment: Symbol, span: Span) -> Attribute {
-    Attribute { kind: AttrKind::DocComment(comment), id: mk_attr_id(), style, span }
+pub fn mk_doc_comment(
+    comment_kind: CommentKind,
+    style: AttrStyle,
+    data: Symbol,
+    span: Span,
+) -> Attribute {
+    Attribute { kind: AttrKind::DocComment(comment_kind, data), id: mk_attr_id(), style, span }
 }
 
 pub fn list_contains_name(items: &[NestedMetaItem], name: Symbol) -> bool {
     items.iter().any(|item| item.has_name(name))
 }
 
-pub fn contains_name(attrs: &[Attribute], name: Symbol) -> bool {
-    attrs.iter().any(|item| item.check_name(name))
-}
-
-pub fn find_by_name(attrs: &[Attribute], name: Symbol) -> Option<&Attribute> {
-    attrs.iter().find(|attr| attr.check_name(name))
-}
-
-pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator<Item = &Attribute> {
-    attrs.iter().filter(move |attr| attr.check_name(name))
-}
-
-pub fn first_attr_value_str_by_name(attrs: &[Attribute], name: Symbol) -> Option<Symbol> {
-    attrs.iter().find(|at| at.check_name(name)).and_then(|at| at.value_str())
-}
-
 impl MetaItem {
     fn token_trees_and_joints(&self) -> Vec<TreeAndJoint> {
         let mut idents = vec![];
diff --git a/src/librustc_ast/entry.rs b/src/librustc_ast/entry.rs
index 90d417a45fd..290f6006de0 100644
--- a/src/librustc_ast/entry.rs
+++ b/src/librustc_ast/entry.rs
@@ -1,7 +1,3 @@
-use crate::ast::{Item, ItemKind};
-use crate::attr;
-use rustc_span::symbol::sym;
-
 pub enum EntryPointType {
     None,
     MainNamed,
@@ -9,27 +5,3 @@ pub enum EntryPointType {
     Start,
     OtherMain, // Not an entry point, but some other function named main
 }
-
-// Beware, this is duplicated in librustc_middle/middle/entry.rs, make sure to keep
-// them in sync.
-pub fn entry_point_type(item: &Item, depth: usize) -> EntryPointType {
-    match item.kind {
-        ItemKind::Fn(..) => {
-            if attr::contains_name(&item.attrs, sym::start) {
-                EntryPointType::Start
-            } else if attr::contains_name(&item.attrs, sym::main) {
-                EntryPointType::MainAttr
-            } else if item.ident.name == sym::main {
-                if depth == 1 {
-                    // This is a top-level function so can be 'main'
-                    EntryPointType::MainNamed
-                } else {
-                    EntryPointType::OtherMain
-                }
-            } else {
-                EntryPointType::None
-            }
-        }
-        _ => EntryPointType::None,
-    }
-}
diff --git a/src/librustc_ast/expand/allocator.rs b/src/librustc_ast/expand/allocator.rs
index 7c67f029f38..cd27f958e46 100644
--- a/src/librustc_ast/expand/allocator.rs
+++ b/src/librustc_ast/expand/allocator.rs
@@ -1,6 +1,4 @@
-use crate::{ast, attr, visit};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::Span;
 
 #[derive(Clone, Copy)]
 pub enum AllocatorKind {
@@ -53,25 +51,3 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[
         output: AllocatorTy::ResultPtr,
     },
 ];
-
-pub fn global_allocator_spans(krate: &ast::Crate) -> Vec<Span> {
-    struct Finder {
-        name: Symbol,
-        spans: Vec<Span>,
-    }
-    impl<'ast> visit::Visitor<'ast> for Finder {
-        fn visit_item(&mut self, item: &'ast ast::Item) {
-            if item.ident.name == self.name
-                && attr::contains_name(&item.attrs, sym::rustc_std_internal_symbol)
-            {
-                self.spans.push(item.span);
-            }
-            visit::walk_item(self, item)
-        }
-    }
-
-    let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc));
-    let mut f = Finder { name, spans: Vec::new() };
-    visit::walk_crate(&mut f, krate);
-    f.spans
-}
diff --git a/src/librustc_ast/expand/mod.rs b/src/librustc_ast/expand/mod.rs
index 3c634ff40cc..eebfc38bdf4 100644
--- a/src/librustc_ast/expand/mod.rs
+++ b/src/librustc_ast/expand/mod.rs
@@ -1,12 +1,3 @@
 //! Definitions shared by macros / syntax extensions and e.g. librustc_middle.
 
-use crate::ast::Attribute;
-use rustc_span::symbol::sym;
-
 pub mod allocator;
-
-pub fn is_proc_macro_attr(attr: &Attribute) -> bool {
-    [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
-        .iter()
-        .any(|kind| attr.check_name(*kind))
-}
diff --git a/src/librustc_ast/lib.rs b/src/librustc_ast/lib.rs
index ca68db0b9f6..3f876169d22 100644
--- a/src/librustc_ast/lib.rs
+++ b/src/librustc_ast/lib.rs
@@ -42,7 +42,6 @@ pub mod util {
 
 pub mod ast;
 pub mod attr;
-pub use attr::{with_default_session_globals, with_session_globals, SESSION_GLOBALS};
 pub mod crate_disambiguator;
 pub mod entry;
 pub mod expand;
diff --git a/src/librustc_ast/mut_visit.rs b/src/librustc_ast/mut_visit.rs
index 54f81ef106f..df6e8218f6c 100644
--- a/src/librustc_ast/mut_visit.rs
+++ b/src/librustc_ast/mut_visit.rs
@@ -582,7 +582,7 @@ pub fn noop_visit_attribute<T: MutVisitor>(attr: &mut Attribute, vis: &mut T) {
             vis.visit_path(path);
             visit_mac_args(args, vis);
         }
-        AttrKind::DocComment(_) => {}
+        AttrKind::DocComment(..) => {}
     }
     vis.visit_span(span);
 }
diff --git a/src/librustc_ast/token.rs b/src/librustc_ast/token.rs
index e1c94ddf782..bcce881ed48 100644
--- a/src/librustc_ast/token.rs
+++ b/src/librustc_ast/token.rs
@@ -17,6 +17,12 @@ use rustc_span::{self, Span, DUMMY_SP};
 use std::borrow::Cow;
 use std::{fmt, mem};
 
+#[derive(Clone, Copy, PartialEq, RustcEncodable, RustcDecodable, Debug, HashStable_Generic)]
+pub enum CommentKind {
+    Line,
+    Block,
+}
+
 #[derive(Clone, PartialEq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
 #[derive(HashStable_Generic)]
 pub enum BinOpToken {
@@ -238,9 +244,10 @@ pub enum TokenKind {
 
     Interpolated(Lrc<Nonterminal>),
 
-    // Can be expanded into several tokens.
-    /// A doc comment.
-    DocComment(Symbol),
+    /// A doc comment token.
+    /// `Symbol` is the doc comment's data excluding its "quotes" (`///`, `/**`, etc)
+    /// 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
diff --git a/src/librustc_ast/util/comments.rs b/src/librustc_ast/util/comments.rs
index 39921b20226..a73891db160 100644
--- a/src/librustc_ast/util/comments.rs
+++ b/src/librustc_ast/util/comments.rs
@@ -1,11 +1,7 @@
-pub use CommentStyle::*;
-
-use crate::ast;
+use crate::ast::AttrStyle;
 use rustc_span::source_map::SourceMap;
 use rustc_span::{BytePos, CharPos, FileName, Pos, Symbol};
 
-use log::debug;
-
 #[cfg(test)]
 mod tests;
 
@@ -28,43 +24,48 @@ pub struct Comment {
     pub pos: BytePos,
 }
 
-pub fn is_line_doc_comment(s: &str) -> bool {
-    let res = (s.starts_with("///") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'/')
-        || s.starts_with("//!");
-    debug!("is {:?} a doc comment? {}", s, res);
-    res
-}
-
-pub fn is_block_doc_comment(s: &str) -> bool {
-    // Prevent `/**/` from being parsed as a doc comment
-    let res = ((s.starts_with("/**") && *s.as_bytes().get(3).unwrap_or(&b' ') != b'*')
-        || s.starts_with("/*!"))
-        && s.len() >= 5;
-    debug!("is {:?} a doc comment? {}", s, res);
-    res
-}
-
-// FIXME(#64197): Try to privatize this again.
-pub fn is_doc_comment(s: &str) -> bool {
-    (s.starts_with("///") && is_line_doc_comment(s))
-        || s.starts_with("//!")
-        || (s.starts_with("/**") && is_block_doc_comment(s))
-        || s.starts_with("/*!")
+/// For a full line comment string returns its doc comment style if it's a doc comment
+/// and returns `None` if it's a regular comment.
+pub fn line_doc_comment_style(line_comment: &str) -> Option<AttrStyle> {
+    let line_comment = line_comment.as_bytes();
+    assert!(line_comment.starts_with(b"//"));
+    match line_comment.get(2) {
+        // `//!` is an inner line doc comment.
+        Some(b'!') => Some(AttrStyle::Inner),
+        Some(b'/') => match line_comment.get(3) {
+            // `////` (more than 3 slashes) is not considered a doc comment.
+            Some(b'/') => None,
+            // Otherwise `///` is an outer line doc comment.
+            _ => Some(AttrStyle::Outer),
+        },
+        _ => None,
+    }
 }
 
-pub fn doc_comment_style(comment: Symbol) -> ast::AttrStyle {
-    let comment = &comment.as_str();
-    assert!(is_doc_comment(comment));
-    if comment.starts_with("//!") || comment.starts_with("/*!") {
-        ast::AttrStyle::Inner
-    } else {
-        ast::AttrStyle::Outer
+/// For a full block comment string returns its doc comment style if it's a doc comment
+/// and returns `None` if it's a regular comment.
+pub fn block_doc_comment_style(block_comment: &str, terminated: bool) -> Option<AttrStyle> {
+    let block_comment = block_comment.as_bytes();
+    assert!(block_comment.starts_with(b"/*"));
+    assert!(!terminated || block_comment.ends_with(b"*/"));
+    match block_comment.get(2) {
+        // `/*!` is an inner block doc comment.
+        Some(b'!') => Some(AttrStyle::Inner),
+        Some(b'*') => match block_comment.get(3) {
+            // `/***` (more than 2 stars) is not considered a doc comment.
+            Some(b'*') => None,
+            // `/**/` is not considered a doc comment.
+            Some(b'/') if block_comment.len() == 4 => None,
+            // Otherwise `/**` is an outer block doc comment.
+            _ => Some(AttrStyle::Outer),
+        },
+        _ => None,
     }
 }
 
-pub fn strip_doc_comment_decoration(comment: Symbol) -> String {
-    let comment = &comment.as_str();
-
+/// Makes a doc string more presentable to users.
+/// Used by rustdoc and perhaps other tools, but not by rustc.
+pub fn beautify_doc_string(data: Symbol) -> String {
     /// remove whitespace-only lines from the start/end of lines
     fn vertical_trim(lines: Vec<String>) -> Vec<String> {
         let mut i = 0;
@@ -126,26 +127,15 @@ pub fn strip_doc_comment_decoration(comment: Symbol) -> String {
         }
     }
 
-    // one-line comments lose their prefix
-    const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
-
-    for prefix in ONELINERS {
-        if comment.starts_with(*prefix) {
-            return (&comment[prefix.len()..]).to_string();
-        }
-    }
-
-    if comment.starts_with("/*") {
-        let lines =
-            comment[3..comment.len() - 2].lines().map(|s| s.to_string()).collect::<Vec<String>>();
-
+    let data = data.as_str();
+    if data.contains('\n') {
+        let lines = data.lines().map(|s| s.to_string()).collect::<Vec<String>>();
         let lines = vertical_trim(lines);
         let lines = horizontal_trim(lines);
-
-        return lines.join("\n");
+        lines.join("\n")
+    } else {
+        data.to_string()
     }
-
-    panic!("not a doc-comment: {}", comment);
 }
 
 /// Returns `None` if the first `col` chars of `s` contain a non-whitespace char.
@@ -203,7 +193,7 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comme
 
     if let Some(shebang_len) = rustc_lexer::strip_shebang(text) {
         comments.push(Comment {
-            style: Isolated,
+            style: CommentStyle::Isolated,
             lines: vec![text[..shebang_len].to_string()],
             pos: start_bpos,
         });
@@ -219,23 +209,23 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comme
                     while let Some(next_newline) = &token_text[idx + 1..].find('\n') {
                         idx = idx + 1 + next_newline;
                         comments.push(Comment {
-                            style: BlankLine,
+                            style: CommentStyle::BlankLine,
                             lines: vec![],
                             pos: start_bpos + BytePos((pos + idx) as u32),
                         });
                     }
                 }
             }
-            rustc_lexer::TokenKind::BlockComment { terminated: _ } => {
-                if !is_block_doc_comment(token_text) {
+            rustc_lexer::TokenKind::BlockComment { terminated } => {
+                if block_doc_comment_style(token_text, terminated).is_none() {
                     let code_to_the_right = match text[pos + token.len..].chars().next() {
                         Some('\r' | '\n') => false,
                         _ => true,
                     };
                     let style = match (code_to_the_left, code_to_the_right) {
-                        (_, true) => Mixed,
-                        (false, false) => Isolated,
-                        (true, false) => Trailing,
+                        (_, true) => CommentStyle::Mixed,
+                        (false, false) => CommentStyle::Isolated,
+                        (true, false) => CommentStyle::Trailing,
                     };
 
                     // Count the number of chars since the start of the line by rescanning.
@@ -249,9 +239,13 @@ pub fn gather_comments(sm: &SourceMap, path: FileName, src: String) -> Vec<Comme
                 }
             }
             rustc_lexer::TokenKind::LineComment => {
-                if !is_doc_comment(token_text) {
+                if line_doc_comment_style(token_text).is_none() {
                     comments.push(Comment {
-                        style: if code_to_the_left { Trailing } else { Isolated },
+                        style: if code_to_the_left {
+                            CommentStyle::Trailing
+                        } else {
+                            CommentStyle::Isolated
+                        },
                         lines: vec![token_text.to_string()],
                         pos: start_bpos + BytePos(pos as u32),
                     })
diff --git a/src/librustc_ast/util/comments/tests.rs b/src/librustc_ast/util/comments/tests.rs
index f08011fe4f8..1919b9341aa 100644
--- a/src/librustc_ast/util/comments/tests.rs
+++ b/src/librustc_ast/util/comments/tests.rs
@@ -1,11 +1,18 @@
 use super::*;
-use crate::with_default_session_globals;
+use rustc_span::with_default_session_globals;
+
+#[test]
+fn line_doc_comments() {
+    assert!(line_doc_comment_style("///").is_some());
+    assert!(line_doc_comment_style("/// blah").is_some());
+    assert!(line_doc_comment_style("////").is_none());
+}
 
 #[test]
 fn test_block_doc_comment_1() {
     with_default_session_globals(|| {
-        let comment = "/**\n * Test \n **  Test\n *   Test\n*/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+        let comment = "\n * Test \n **  Test\n *   Test\n";
+        let stripped = beautify_doc_string(Symbol::intern(comment));
         assert_eq!(stripped, " Test \n*  Test\n   Test");
     })
 }
@@ -13,8 +20,8 @@ fn test_block_doc_comment_1() {
 #[test]
 fn test_block_doc_comment_2() {
     with_default_session_globals(|| {
-        let comment = "/**\n * Test\n *  Test\n*/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+        let comment = "\n * Test\n *  Test\n";
+        let stripped = beautify_doc_string(Symbol::intern(comment));
         assert_eq!(stripped, " Test\n  Test");
     })
 }
@@ -22,37 +29,22 @@ fn test_block_doc_comment_2() {
 #[test]
 fn test_block_doc_comment_3() {
     with_default_session_globals(|| {
-        let comment = "/**\n let a: *i32;\n *a = 5;\n*/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
+        let comment = "\n let a: *i32;\n *a = 5;\n";
+        let stripped = beautify_doc_string(Symbol::intern(comment));
         assert_eq!(stripped, " let a: *i32;\n *a = 5;");
     })
 }
 
 #[test]
-fn test_block_doc_comment_4() {
-    with_default_session_globals(|| {
-        let comment = "/*******************\n test\n *********************/";
-        let stripped = strip_doc_comment_decoration(Symbol::intern(comment));
-        assert_eq!(stripped, " test");
-    })
-}
-
-#[test]
 fn test_line_doc_comment() {
     with_default_session_globals(|| {
-        let stripped = strip_doc_comment_decoration(Symbol::intern("/// test"));
-        assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("///! test"));
-        assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("// test"));
+        let stripped = beautify_doc_string(Symbol::intern(" test"));
         assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("// test"));
-        assert_eq!(stripped, " test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("///test"));
-        assert_eq!(stripped, "test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("///!test"));
-        assert_eq!(stripped, "test");
-        let stripped = strip_doc_comment_decoration(Symbol::intern("//test"));
+        let stripped = beautify_doc_string(Symbol::intern("! test"));
+        assert_eq!(stripped, "! test");
+        let stripped = beautify_doc_string(Symbol::intern("test"));
         assert_eq!(stripped, "test");
+        let stripped = beautify_doc_string(Symbol::intern("!test"));
+        assert_eq!(stripped, "!test");
     })
 }
diff --git a/src/librustc_ast/util/lev_distance/tests.rs b/src/librustc_ast/util/lev_distance/tests.rs
index 94d56a3d7b4..7ebedbcb76a 100644
--- a/src/librustc_ast/util/lev_distance/tests.rs
+++ b/src/librustc_ast/util/lev_distance/tests.rs
@@ -21,7 +21,7 @@ fn test_lev_distance() {
 
 #[test]
 fn test_find_best_match_for_name() {
-    use crate::with_default_session_globals;
+    use rustc_span::with_default_session_globals;
     with_default_session_globals(|| {
         let input = vec![Symbol::intern("aaab"), Symbol::intern("aaabc")];
         assert_eq!(
diff --git a/src/librustc_ast/visit.rs b/src/librustc_ast/visit.rs
index ccab46703df..2c3d1e97df9 100644
--- a/src/librustc_ast/visit.rs
+++ b/src/librustc_ast/visit.rs
@@ -880,7 +880,7 @@ pub fn walk_vis<'a, V: Visitor<'a>>(visitor: &mut V, vis: &'a Visibility) {
 pub fn walk_attribute<'a, V: Visitor<'a>>(visitor: &mut V, attr: &'a Attribute) {
     match attr.kind {
         AttrKind::Normal(ref item) => walk_mac_args(visitor, &item.args),
-        AttrKind::DocComment(_) => {}
+        AttrKind::DocComment(..) => {}
     }
 }
 
diff --git a/src/librustc_ast_lowering/expr.rs b/src/librustc_ast_lowering/expr.rs
index abd5df537db..f9e54903a66 100644
--- a/src/librustc_ast_lowering/expr.rs
+++ b/src/librustc_ast_lowering/expr.rs
@@ -1067,7 +1067,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
             .collect();
 
         // Stop if there were any errors when lowering the register classes
-        if operands.len() != asm.operands.len() {
+        if operands.len() != asm.operands.len() || sess.asm_arch.is_none() {
             return hir::ExprKind::Err;
         }
 
diff --git a/src/librustc_ast_lowering/item.rs b/src/librustc_ast_lowering/item.rs
index 5414e584290..5186e62fbf9 100644
--- a/src/librustc_ast_lowering/item.rs
+++ b/src/librustc_ast_lowering/item.rs
@@ -3,7 +3,6 @@ use super::{ImplTraitContext, ImplTraitPosition};
 use crate::Arena;
 
 use rustc_ast::ast::*;
-use rustc_ast::attr;
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, AssocCtxt, Visitor};
@@ -205,7 +204,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
         let attrs = self.lower_attrs(&i.attrs);
 
         if let ItemKind::MacroDef(MacroDef { ref body, macro_rules }) = i.kind {
-            if !macro_rules || attr::contains_name(&i.attrs, sym::macro_export) {
+            if !macro_rules || self.sess.contains_name(&i.attrs, sym::macro_export) {
                 let hir_id = self.lower_node_id(i.id);
                 let body = P(self.lower_mac_args(body));
                 self.exported_macros.push(hir::MacroDef {
diff --git a/src/librustc_ast_lowering/lib.rs b/src/librustc_ast_lowering/lib.rs
index 9df7ad2a9ac..7cfde3fc6d2 100644
--- a/src/librustc_ast_lowering/lib.rs
+++ b/src/librustc_ast_lowering/lib.rs
@@ -37,7 +37,6 @@
 
 use rustc_ast::ast;
 use rustc_ast::ast::*;
-use rustc_ast::attr;
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::token::{self, DelimToken, Nonterminal, Token};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
@@ -574,7 +573,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             .resolver
             .trait_map()
             .iter()
-            .map(|(&k, v)| (self.node_id_to_hir_id[k].unwrap(), v.clone()))
+            .filter_map(|(&k, v)| {
+                self.node_id_to_hir_id.get(k).and_then(|id| id.as_ref()).map(|id| (*id, v.clone()))
+            })
             .collect();
 
         let mut def_id_to_hir_id = IndexVec::default();
@@ -981,7 +982,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 path: item.path.clone(),
                 args: self.lower_mac_args(&item.args),
             }),
-            AttrKind::DocComment(comment) => AttrKind::DocComment(comment),
+            AttrKind::DocComment(comment_kind, data) => AttrKind::DocComment(comment_kind, data),
         };
 
         Attribute { kind, id: attr.id, style: attr.style, span: attr.span }
@@ -2215,7 +2216,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                     synthetic: param
                         .attrs
                         .iter()
-                        .filter(|attr| attr.check_name(sym::rustc_synthetic))
+                        .filter(|attr| self.sess.check_name(attr, sym::rustc_synthetic))
                         .map(|_| hir::SyntheticTyParamKind::ImplTrait)
                         .next(),
                 };
@@ -2236,7 +2237,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             hir_id: self.lower_node_id(param.id),
             name,
             span: param.ident.span,
-            pure_wrt_drop: attr::contains_name(&param.attrs, sym::may_dangle),
+            pure_wrt_drop: self.sess.contains_name(&param.attrs, sym::may_dangle),
             attrs: self.lower_attrs(&param.attrs),
             bounds: self.arena.alloc_from_iter(bounds),
             kind,
diff --git a/src/librustc_ast_passes/ast_validation.rs b/src/librustc_ast_passes/ast_validation.rs
index daf3e23d6a1..45a026d4b53 100644
--- a/src/librustc_ast_passes/ast_validation.rs
+++ b/src/librustc_ast_passes/ast_validation.rs
@@ -8,8 +8,6 @@
 
 use itertools::{Either, Itertools};
 use rustc_ast::ast::*;
-use rustc_ast::attr;
-use rustc_ast::expand::is_proc_macro_attr;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
 use rustc_ast::walk_list;
@@ -776,7 +774,13 @@ fn validate_generic_param_order<'a>(
             span,
             &format!(
                 "reorder the parameters: lifetimes, then types{}",
-                if sess.features_untracked().const_generics { ", then consts" } else { "" },
+                if sess.features_untracked().const_generics
+                    || sess.features_untracked().min_const_generics
+                {
+                    ", then consts"
+                } else {
+                    ""
+                },
             ),
             ordered_params.clone(),
             Applicability::MachineApplicable,
@@ -891,11 +895,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
     }
 
     fn visit_item(&mut self, item: &'a Item) {
-        if item.attrs.iter().any(|attr| is_proc_macro_attr(attr)) {
+        if item.attrs.iter().any(|attr| self.session.is_proc_macro_attr(attr)) {
             self.has_proc_macro_decls = true;
         }
 
-        if attr::contains_name(&item.attrs, sym::no_mangle) {
+        if self.session.contains_name(&item.attrs, sym::no_mangle) {
             self.check_nomangle_item_asciionly(item.ident, item.span);
         }
 
@@ -1027,7 +1031,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
             }
             ItemKind::Mod(Mod { inline, .. }) => {
                 // Ensure that `path` attributes on modules are recorded as used (cf. issue #35584).
-                if !inline && !attr::contains_name(&item.attrs, sym::path) {
+                if !inline && !self.session.contains_name(&item.attrs, sym::path) {
                     self.check_mod_file_item_asciionly(item.ident);
                 }
             }
diff --git a/src/librustc_ast_passes/feature_gate.rs b/src/librustc_ast_passes/feature_gate.rs
index 22eaca4f071..ce39ceff8f3 100644
--- a/src/librustc_ast_passes/feature_gate.rs
+++ b/src/librustc_ast_passes/feature_gate.rs
@@ -1,11 +1,11 @@
 use rustc_ast::ast::{self, AssocTyConstraint, AssocTyConstraintKind, NodeId};
 use rustc_ast::ast::{GenericParam, GenericParamKind, PatKind, RangeEnd, VariantData};
-use rustc_ast::attr;
 use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
-use rustc_errors::{struct_span_err, Handler};
+use rustc_errors::struct_span_err;
 use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP};
-use rustc_feature::{Features, GateIssue, UnstableFeatures};
-use rustc_session::parse::{feature_err, feature_err_issue, ParseSess};
+use rustc_feature::{Features, GateIssue};
+use rustc_session::parse::{feature_err, feature_err_issue};
+use rustc_session::Session;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
@@ -13,28 +13,32 @@ use rustc_span::Span;
 use tracing::debug;
 
 macro_rules! gate_feature_fn {
-    ($cx: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
-        let (cx, has_feature, span, name, explain) = (&*$cx, $has_feature, $span, $name, $explain);
-        let has_feature: bool = has_feature(&$cx.features);
+    ($visitor: expr, $has_feature: expr, $span: expr, $name: expr, $explain: expr) => {{
+        let (visitor, has_feature, span, name, explain) =
+            (&*$visitor, $has_feature, $span, $name, $explain);
+        let has_feature: bool = has_feature(visitor.features);
         debug!("gate_feature(feature = {:?}, span = {:?}); has? {}", name, span, has_feature);
         if !has_feature && !span.allows_unstable($name) {
-            feature_err_issue(cx.parse_sess, name, span, GateIssue::Language, explain).emit();
+            feature_err_issue(&visitor.sess.parse_sess, name, span, GateIssue::Language, explain)
+                .emit();
         }
     }};
 }
 
 macro_rules! gate_feature_post {
-    ($cx: expr, $feature: ident, $span: expr, $explain: expr) => {
-        gate_feature_fn!($cx, |x: &Features| x.$feature, $span, sym::$feature, $explain)
+    ($visitor: expr, $feature: ident, $span: expr, $explain: expr) => {
+        gate_feature_fn!($visitor, |x: &Features| x.$feature, $span, sym::$feature, $explain)
     };
 }
 
-pub fn check_attribute(attr: &ast::Attribute, parse_sess: &ParseSess, features: &Features) {
-    PostExpansionVisitor { parse_sess, features }.visit_attribute(attr)
+pub fn check_attribute(attr: &ast::Attribute, sess: &Session, features: &Features) {
+    PostExpansionVisitor { sess, features }.visit_attribute(attr)
 }
 
 struct PostExpansionVisitor<'a> {
-    parse_sess: &'a ParseSess,
+    sess: &'a Session,
+
+    // `sess` contains a `Features`, but this might not be that one.
     features: &'a Features,
 }
 
@@ -138,6 +142,7 @@ impl<'a> PostExpansionVisitor<'a> {
                 );
             }
             abi => self
+                .sess
                 .parse_sess
                 .span_diagnostic
                 .delay_span_bug(span, &format!("unrecognized ABI not caught in lowering: {}", abi)),
@@ -167,7 +172,7 @@ impl<'a> PostExpansionVisitor<'a> {
 
         if !discriminant_spans.is_empty() && has_fields {
             let mut err = feature_err(
-                self.parse_sess,
+                &self.sess.parse_sess,
                 sym::arbitrary_enum_discriminant,
                 discriminant_spans.clone(),
                 "custom discriminant values are not allowed in enums with tuple or struct variants",
@@ -240,7 +245,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             gate_feature_fn!(self, has_feature, attr.span, name, descr);
         }
         // Check unstable flavors of the `#[doc]` attribute.
-        if attr.check_name(sym::doc) {
+        if self.sess.check_name(attr, sym::doc) {
             for nested_meta in attr.meta_item_list().unwrap_or_default() {
                 macro_rules! gate_doc { ($($name:ident => $feature:ident)*) => {
                     $(if nested_meta.has_name(sym::$name) {
@@ -266,7 +271,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             gate_feature_post!(
                 &self,
                 non_ascii_idents,
-                self.parse_sess.source_map().guess_head_span(sp),
+                self.sess.parse_sess.source_map().guess_head_span(sp),
                 "non-ascii idents are not fully supported"
             );
         }
@@ -281,7 +286,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             }
 
             ast::ItemKind::Fn(..) => {
-                if attr::contains_name(&i.attrs[..], sym::plugin_registrar) {
+                if self.sess.contains_name(&i.attrs[..], sym::plugin_registrar) {
                     gate_feature_post!(
                         &self,
                         plugin_registrar,
@@ -289,7 +294,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                         "compiler plugins are experimental and possibly buggy"
                     );
                 }
-                if attr::contains_name(&i.attrs[..], sym::start) {
+                if self.sess.contains_name(&i.attrs[..], sym::start) {
                     gate_feature_post!(
                         &self,
                         start,
@@ -299,7 +304,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                          over time"
                     );
                 }
-                if attr::contains_name(&i.attrs[..], sym::main) {
+                if self.sess.contains_name(&i.attrs[..], sym::main) {
                     gate_feature_post!(
                         &self,
                         main,
@@ -312,7 +317,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             }
 
             ast::ItemKind::Struct(..) => {
-                for attr in attr::filter_by_name(&i.attrs[..], sym::repr) {
+                for attr in self.sess.filter_by_name(&i.attrs[..], sym::repr) {
                     for item in attr.meta_item_list().unwrap_or_else(Vec::new) {
                         if item.has_name(sym::simd) {
                             gate_feature_post!(
@@ -391,7 +396,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     fn visit_foreign_item(&mut self, i: &'a ast::ForeignItem) {
         match i.kind {
             ast::ForeignItemKind::Fn(..) | ast::ForeignItemKind::Static(..) => {
-                let link_name = attr::first_attr_value_str_by_name(&i.attrs, sym::link_name);
+                let link_name = self.sess.first_attr_value_str_by_name(&i.attrs, sym::link_name);
                 let links_to_llvm = match link_name {
                     Some(val) => val.as_str().starts_with("llvm."),
                     _ => false,
@@ -450,7 +455,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
             ast::ExprKind::Type(..) => {
                 // To avoid noise about type ascription in common syntax errors, only emit if it
                 // is the *only* error.
-                if self.parse_sess.span_diagnostic.err_count() == 0 {
+                if self.sess.parse_sess.span_diagnostic.err_count() == 0 {
                     gate_feature_post!(
                         &self,
                         type_ascription,
@@ -526,12 +531,13 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
 
     fn visit_generic_param(&mut self, param: &'a GenericParam) {
         if let GenericParamKind::Const { .. } = param.kind {
-            gate_feature_post!(
+            gate_feature_fn!(
                 &self,
-                const_generics,
+                |x: &Features| x.const_generics || x.min_const_generics,
                 param.ident.span,
+                sym::min_const_generics,
                 "const generics are unstable"
-            )
+            );
         }
         visit::walk_generic_param(self, param)
     }
@@ -599,16 +605,11 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
     }
 }
 
-pub fn check_crate(
-    krate: &ast::Crate,
-    parse_sess: &ParseSess,
-    features: &Features,
-    unstable: UnstableFeatures,
-) {
-    maybe_stage_features(&parse_sess.span_diagnostic, krate, unstable);
-    let mut visitor = PostExpansionVisitor { parse_sess, features };
+pub fn check_crate(krate: &ast::Crate, sess: &Session) {
+    maybe_stage_features(sess, krate);
+    let mut visitor = PostExpansionVisitor { sess, features: &sess.features_untracked() };
 
-    let spans = parse_sess.gated_spans.spans.borrow();
+    let spans = sess.parse_sess.gated_spans.spans.borrow();
     macro_rules! gate_all {
         ($gate:ident, $msg:literal) => {
             for span in spans.get(&sym::$gate).unwrap_or(&vec![]) {
@@ -651,18 +652,18 @@ pub fn check_crate(
     gate_all!(box_syntax, "box expression syntax is experimental; you can call `Box::new` instead");
     // To avoid noise about type ascription in common syntax errors,
     // only emit if it is the *only* error. (Also check it last.)
-    if parse_sess.span_diagnostic.err_count() == 0 {
+    if sess.parse_sess.span_diagnostic.err_count() == 0 {
         gate_all!(type_ascription, "type ascription is experimental");
     }
 
     visit::walk_crate(&mut visitor, krate);
 }
 
-fn maybe_stage_features(span_handler: &Handler, krate: &ast::Crate, unstable: UnstableFeatures) {
-    if !unstable.is_nightly_build() {
-        for attr in krate.attrs.iter().filter(|attr| attr.check_name(sym::feature)) {
+fn maybe_stage_features(sess: &Session, krate: &ast::Crate) {
+    if !sess.opts.unstable_features.is_nightly_build() {
+        for attr in krate.attrs.iter().filter(|attr| sess.check_name(attr, sym::feature)) {
             struct_span_err!(
-                span_handler,
+                sess.parse_sess.span_diagnostic,
                 attr.span,
                 E0554,
                 "`#![feature]` may not be used on the {} release channel",
diff --git a/src/librustc_ast_pretty/pprust.rs b/src/librustc_ast_pretty/pprust.rs
index 4b228629ad7..9d9ca78de55 100644
--- a/src/librustc_ast_pretty/pprust.rs
+++ b/src/librustc_ast_pretty/pprust.rs
@@ -8,10 +8,11 @@ use rustc_ast::ast::{InlineAsmOperand, InlineAsmRegOrRegClass};
 use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_ast::attr;
 use rustc_ast::ptr::P;
-use rustc_ast::token::{self, BinOpToken, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
 use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_ast::util::classify;
+use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
 use rustc_ast::util::parser::{self, AssocOp, Fixity};
-use rustc_ast::util::{classify, comments};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{SourceMap, Spanned};
 use rustc_span::symbol::{kw, sym, Ident, IdentPrinter, Symbol};
@@ -50,17 +51,17 @@ impl PpAnn for NoAnn {}
 
 pub struct Comments<'a> {
     sm: &'a SourceMap,
-    comments: Vec<comments::Comment>,
+    comments: Vec<Comment>,
     current: usize,
 }
 
 impl<'a> Comments<'a> {
     pub fn new(sm: &'a SourceMap, filename: FileName, input: String) -> Comments<'a> {
-        let comments = comments::gather_comments(sm, filename, input);
+        let comments = gather_comments(sm, filename, input);
         Comments { sm, comments, current: 0 }
     }
 
-    pub fn next(&self) -> Option<comments::Comment> {
+    pub fn next(&self) -> Option<Comment> {
         self.comments.get(self.current).cloned()
     }
 
@@ -68,9 +69,9 @@ impl<'a> Comments<'a> {
         &mut self,
         span: rustc_span::Span,
         next_pos: Option<BytePos>,
-    ) -> Option<comments::Comment> {
+    ) -> Option<Comment> {
         if let Some(cmnt) = self.next() {
-            if cmnt.style != comments::Trailing {
+            if cmnt.style != CommentStyle::Trailing {
                 return None;
             }
             let span_line = self.sm.lookup_char_pos(span.hi());
@@ -152,8 +153,8 @@ pub fn to_string(f: impl FnOnce(&mut State<'_>)) -> String {
 // and also addresses some specific regressions described in #63896 and #73345.
 fn tt_prepend_space(tt: &TokenTree, prev: &TokenTree) -> bool {
     if let TokenTree::Token(token) = prev {
-        if let token::DocComment(s) = token.kind {
-            return !s.as_str().starts_with("//");
+        if let token::DocComment(comment_kind, ..) = token.kind {
+            return comment_kind != CommentKind::Line;
         }
     }
     match tt {
@@ -194,6 +195,19 @@ fn binop_to_string(op: BinOpToken) -> &'static str {
     }
 }
 
+fn doc_comment_to_string(
+    comment_kind: CommentKind,
+    attr_style: ast::AttrStyle,
+    data: Symbol,
+) -> String {
+    match (comment_kind, attr_style) {
+        (CommentKind::Line, ast::AttrStyle::Outer) => format!("///{}", data),
+        (CommentKind::Line, ast::AttrStyle::Inner) => format!("//!{}", data),
+        (CommentKind::Block, ast::AttrStyle::Outer) => format!("/**{}*/", data),
+        (CommentKind::Block, ast::AttrStyle::Inner) => format!("/*!{}*/", data),
+    }
+}
+
 pub fn literal_to_string(lit: token::Lit) -> String {
     let token::Lit { kind, symbol, suffix } = lit;
     let mut out = match kind {
@@ -271,7 +285,9 @@ fn token_kind_to_string_ext(tok: &TokenKind, convert_dollar_crate: Option<Span>)
         token::Lifetime(s) => s.to_string(),
 
         /* Other */
-        token::DocComment(s) => s.to_string(),
+        token::DocComment(comment_kind, attr_style, data) => {
+            doc_comment_to_string(comment_kind, attr_style, data)
+        }
         token::Eof => "<eof>".to_string(),
         token::Whitespace => " ".to_string(),
         token::Comment => "/* */".to_string(),
@@ -447,9 +463,9 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
-    fn print_comment(&mut self, cmnt: &comments::Comment) {
+    fn print_comment(&mut self, cmnt: &Comment) {
         match cmnt.style {
-            comments::Mixed => {
+            CommentStyle::Mixed => {
                 if !self.is_beginning_of_line() {
                     self.zerobreak();
                 }
@@ -468,7 +484,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 }
                 self.zerobreak()
             }
-            comments::Isolated => {
+            CommentStyle::Isolated => {
                 self.hardbreak_if_not_bol();
                 for line in &cmnt.lines {
                     // Don't print empty lines because they will end up as trailing
@@ -479,7 +495,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.hardbreak();
                 }
             }
-            comments::Trailing => {
+            CommentStyle::Trailing => {
                 if !self.is_beginning_of_line() {
                     self.word(" ");
                 }
@@ -497,7 +513,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                     self.end();
                 }
             }
-            comments::BlankLine => {
+            CommentStyle::BlankLine => {
                 // We need to do at least one, possibly two hardbreaks.
                 let twice = match self.last_token() {
                     pp::Token::String(s) => ";" == s,
@@ -516,7 +532,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
         }
     }
 
-    fn next_comment(&mut self) -> Option<comments::Comment> {
+    fn next_comment(&mut self) -> Option<Comment> {
         self.comments().as_mut().and_then(|c| c.next())
     }
 
@@ -599,8 +615,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
                 self.print_attr_item(&item, attr.span);
                 self.word("]");
             }
-            ast::AttrKind::DocComment(comment) => {
-                self.word(comment.to_string());
+            ast::AttrKind::DocComment(comment_kind, data) => {
+                self.word(doc_comment_to_string(comment_kind, attr.style, data));
                 self.hardbreak()
             }
         }
diff --git a/src/librustc_ast_pretty/pprust/tests.rs b/src/librustc_ast_pretty/pprust/tests.rs
index 96377a4ae02..fdbd073255e 100644
--- a/src/librustc_ast_pretty/pprust/tests.rs
+++ b/src/librustc_ast_pretty/pprust/tests.rs
@@ -1,9 +1,9 @@
 use super::*;
 
 use rustc_ast::ast;
-use rustc_ast::with_default_session_globals;
 use rustc_span::source_map::respan;
 use rustc_span::symbol::Ident;
+use rustc_span::with_default_session_globals;
 
 fn fun_to_string(
     decl: &ast::FnDecl,
diff --git a/src/librustc_attr/builtin.rs b/src/librustc_attr/builtin.rs
index 983202aafab..552584bb4d0 100644
--- a/src/librustc_attr/builtin.rs
+++ b/src/librustc_attr/builtin.rs
@@ -1,13 +1,12 @@
 //! Parsing and validation of builtin attributes
 
-use super::{find_by_name, mark_used};
-
 use rustc_ast::ast::{self, Attribute, Lit, LitKind, MetaItem, MetaItemKind, NestedMetaItem};
 use rustc_ast_pretty::pprust;
-use rustc_errors::{struct_span_err, Applicability, Handler};
+use rustc_errors::{struct_span_err, Applicability};
 use rustc_feature::{find_gated_cfg, is_builtin_attr_name, Features, GatedCfg};
 use rustc_macros::HashStable_Generic;
 use rustc_session::parse::{feature_err, ParseSess};
+use rustc_session::Session;
 use rustc_span::hygiene::Transparency;
 use rustc_span::{symbol::sym, symbol::Symbol, Span};
 use std::num::NonZeroU32;
@@ -86,9 +85,9 @@ pub enum UnwindAttr {
 }
 
 /// Determine what `#[unwind]` attribute is present in `attrs`, if any.
-pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Option<UnwindAttr> {
+pub fn find_unwind_attr(sess: &Session, attrs: &[Attribute]) -> Option<UnwindAttr> {
     attrs.iter().fold(None, |ia, attr| {
-        if attr.check_name(sym::unwind) {
+        if sess.check_name(attr, sym::unwind) {
             if let Some(meta) = attr.meta() {
                 if let MetaItemKind::List(items) = meta.kind {
                     if items.len() == 1 {
@@ -99,19 +98,22 @@ pub fn find_unwind_attr(diagnostic: Option<&Handler>, attrs: &[Attribute]) -> Op
                         }
                     }
 
-                    if let Some(d) = diagnostic {
-                        struct_span_err!(d, attr.span, E0633, "malformed `unwind` attribute input")
-                            .span_label(attr.span, "invalid argument")
-                            .span_suggestions(
-                                attr.span,
-                                "the allowed arguments are `allowed` and `aborts`",
-                                (vec!["allowed", "aborts"])
-                                    .into_iter()
-                                    .map(|s| format!("#[unwind({})]", s)),
-                                Applicability::MachineApplicable,
-                            )
-                            .emit();
-                    };
+                    struct_span_err!(
+                        sess.diagnostic(),
+                        attr.span,
+                        E0633,
+                        "malformed `unwind` attribute input"
+                    )
+                    .span_label(attr.span, "invalid argument")
+                    .span_suggestions(
+                        attr.span,
+                        "the allowed arguments are `allowed` and `aborts`",
+                        (vec!["allowed", "aborts"])
+                            .into_iter()
+                            .map(|s| format!("#[unwind({})]", s)),
+                        Applicability::MachineApplicable,
+                    )
+                    .emit();
                 }
             }
         }
@@ -161,22 +163,10 @@ impl StabilityLevel {
     }
 }
 
-/// Checks if `attrs` contains an attribute like `#![feature(feature_name)]`.
-/// This will not perform any "sanity checks" on the form of the attributes.
-pub fn contains_feature_attr(attrs: &[Attribute], feature_name: Symbol) -> bool {
-    attrs.iter().any(|item| {
-        item.check_name(sym::feature)
-            && item
-                .meta_item_list()
-                .map(|list| list.iter().any(|mi| mi.is_word() && mi.has_name(feature_name)))
-                .unwrap_or(false)
-    })
-}
-
 /// Collects stability info from all stability attributes in `attrs`.
 /// Returns `None` if no stability attributes are found.
 pub fn find_stability(
-    sess: &ParseSess,
+    sess: &Session,
     attrs: &[Attribute],
     item_sp: Span,
 ) -> (Option<Stability>, Option<ConstStability>) {
@@ -184,7 +174,7 @@ pub fn find_stability(
 }
 
 fn find_stability_generic<'a, I>(
-    sess: &ParseSess,
+    sess: &Session,
     attrs_iter: I,
     item_sp: Span,
 ) -> (Option<Stability>, Option<ConstStability>)
@@ -197,7 +187,7 @@ where
     let mut const_stab: Option<ConstStability> = None;
     let mut promotable = false;
     let mut allow_const_fn_ptr = false;
-    let diagnostic = &sess.span_diagnostic;
+    let diagnostic = &sess.parse_sess.span_diagnostic;
 
     'outer: for attr in attrs_iter {
         if ![
@@ -214,7 +204,7 @@ where
             continue; // not a stability level
         }
 
-        mark_used(attr);
+        sess.mark_attr_used(attr);
 
         let meta = attr.meta();
 
@@ -230,7 +220,7 @@ where
             let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                 if item.is_some() {
                     handle_errors(
-                        sess,
+                        &sess.parse_sess,
                         meta.span,
                         AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
                     );
@@ -249,10 +239,18 @@ where
             match meta_name {
                 sym::rustc_const_unstable | sym::unstable => {
                     if meta_name == sym::unstable && stab.is_some() {
-                        handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+                        handle_errors(
+                            &sess.parse_sess,
+                            attr.span,
+                            AttrError::MultipleStabilityLevels,
+                        );
                         break;
                     } else if meta_name == sym::rustc_const_unstable && const_stab.is_some() {
-                        handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+                        handle_errors(
+                            &sess.parse_sess,
+                            attr.span,
+                            AttrError::MultipleStabilityLevels,
+                        );
                         break;
                     }
 
@@ -318,13 +316,13 @@ where
                                 sym::soft => {
                                     if !mi.is_word() {
                                         let msg = "`soft` should not have any arguments";
-                                        sess.span_diagnostic.span_err(mi.span, msg);
+                                        sess.parse_sess.span_diagnostic.span_err(mi.span, msg);
                                     }
                                     is_soft = true;
                                 }
                                 _ => {
                                     handle_errors(
-                                        sess,
+                                        &sess.parse_sess,
                                         meta.span(),
                                         AttrError::UnknownMetaItem(
                                             pprust::path_to_string(&mi.path),
@@ -336,7 +334,7 @@ where
                             }
                         } else {
                             handle_errors(
-                                sess,
+                                &sess.parse_sess,
                                 meta.span(),
                                 AttrError::UnsupportedLiteral("unsupported literal", false),
                             );
@@ -359,7 +357,7 @@ where
                             }
                         }
                         (None, _, _) => {
-                            handle_errors(sess, attr.span, AttrError::MissingFeature);
+                            handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
                             continue;
                         }
                         _ => {
@@ -371,10 +369,18 @@ where
                 }
                 sym::rustc_const_stable | sym::stable => {
                     if meta_name == sym::stable && stab.is_some() {
-                        handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+                        handle_errors(
+                            &sess.parse_sess,
+                            attr.span,
+                            AttrError::MultipleStabilityLevels,
+                        );
                         break;
                     } else if meta_name == sym::rustc_const_stable && const_stab.is_some() {
-                        handle_errors(sess, attr.span, AttrError::MultipleStabilityLevels);
+                        handle_errors(
+                            &sess.parse_sess,
+                            attr.span,
+                            AttrError::MultipleStabilityLevels,
+                        );
                         break;
                     }
 
@@ -395,7 +401,7 @@ where
                                 }
                                 _ => {
                                     handle_errors(
-                                        sess,
+                                        &sess.parse_sess,
                                         meta.span(),
                                         AttrError::UnknownMetaItem(
                                             pprust::path_to_string(&mi.path),
@@ -407,7 +413,7 @@ where
                             },
                             NestedMetaItem::Literal(lit) => {
                                 handle_errors(
-                                    sess,
+                                    &sess.parse_sess,
                                     lit.span,
                                     AttrError::UnsupportedLiteral("unsupported literal", false),
                                 );
@@ -431,11 +437,11 @@ where
                             }
                         }
                         (None, _) => {
-                            handle_errors(sess, attr.span, AttrError::MissingFeature);
+                            handle_errors(&sess.parse_sess, attr.span, AttrError::MissingFeature);
                             continue;
                         }
                         _ => {
-                            handle_errors(sess, attr.span, AttrError::MissingSince);
+                            handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
                             continue;
                         }
                     }
@@ -466,8 +472,8 @@ where
     (stab, const_stab)
 }
 
-pub fn find_crate_name(attrs: &[Attribute]) -> Option<Symbol> {
-    super::first_attr_value_str_by_name(attrs, sym::crate_name)
+pub fn find_crate_name(sess: &Session, attrs: &[Attribute]) -> Option<Symbol> {
+    sess.first_attr_value_str_by_name(attrs, sym::crate_name)
 }
 
 /// Tests if a cfg-pattern matches the cfg set
@@ -630,16 +636,12 @@ pub struct Deprecation {
 }
 
 /// Finds the deprecation attribute. `None` if none exists.
-pub fn find_deprecation(
-    sess: &ParseSess,
-    attrs: &[Attribute],
-    item_sp: Span,
-) -> Option<Deprecation> {
+pub fn find_deprecation(sess: &Session, attrs: &[Attribute], item_sp: Span) -> Option<Deprecation> {
     find_deprecation_generic(sess, attrs.iter(), item_sp)
 }
 
 fn find_deprecation_generic<'a, I>(
-    sess: &ParseSess,
+    sess: &Session,
     attrs_iter: I,
     item_sp: Span,
 ) -> Option<Deprecation>
@@ -647,10 +649,11 @@ where
     I: Iterator<Item = &'a Attribute>,
 {
     let mut depr: Option<Deprecation> = None;
-    let diagnostic = &sess.span_diagnostic;
+    let diagnostic = &sess.parse_sess.span_diagnostic;
 
     'outer: for attr in attrs_iter {
-        if !(attr.check_name(sym::deprecated) || attr.check_name(sym::rustc_deprecated)) {
+        if !(sess.check_name(attr, sym::deprecated) || sess.check_name(attr, sym::rustc_deprecated))
+        {
             continue;
         }
 
@@ -673,7 +676,7 @@ where
                 let get = |meta: &MetaItem, item: &mut Option<Symbol>| {
                     if item.is_some() {
                         handle_errors(
-                            sess,
+                            &sess.parse_sess,
                             meta.span,
                             AttrError::MultipleItem(pprust::path_to_string(&meta.path)),
                         );
@@ -685,7 +688,7 @@ where
                     } else {
                         if let Some(lit) = meta.name_value_literal() {
                             handle_errors(
-                                sess,
+                                &sess.parse_sess,
                                 lit.span,
                                 AttrError::UnsupportedLiteral(
                                     "literal in `deprecated` \
@@ -710,28 +713,28 @@ where
                                     continue 'outer;
                                 }
                             }
-                            sym::note if attr.check_name(sym::deprecated) => {
+                            sym::note if sess.check_name(attr, sym::deprecated) => {
                                 if !get(mi, &mut note) {
                                     continue 'outer;
                                 }
                             }
-                            sym::reason if attr.check_name(sym::rustc_deprecated) => {
+                            sym::reason if sess.check_name(attr, sym::rustc_deprecated) => {
                                 if !get(mi, &mut note) {
                                     continue 'outer;
                                 }
                             }
-                            sym::suggestion if attr.check_name(sym::rustc_deprecated) => {
+                            sym::suggestion if sess.check_name(attr, sym::rustc_deprecated) => {
                                 if !get(mi, &mut suggestion) {
                                     continue 'outer;
                                 }
                             }
                             _ => {
                                 handle_errors(
-                                    sess,
+                                    &sess.parse_sess,
                                     meta.span(),
                                     AttrError::UnknownMetaItem(
                                         pprust::path_to_string(&mi.path),
-                                        if attr.check_name(sym::deprecated) {
+                                        if sess.check_name(attr, sym::deprecated) {
                                             &["since", "note"]
                                         } else {
                                             &["since", "reason", "suggestion"]
@@ -743,7 +746,7 @@ where
                         },
                         NestedMetaItem::Literal(lit) => {
                             handle_errors(
-                                sess,
+                                &sess.parse_sess,
                                 lit.span,
                                 AttrError::UnsupportedLiteral(
                                     "item in `deprecated` must be a key/value pair",
@@ -757,13 +760,13 @@ where
             }
         }
 
-        if suggestion.is_some() && attr.check_name(sym::deprecated) {
+        if suggestion.is_some() && sess.check_name(attr, sym::deprecated) {
             unreachable!("only allowed on rustc_deprecated")
         }
 
-        if attr.check_name(sym::rustc_deprecated) {
+        if sess.check_name(attr, sym::rustc_deprecated) {
             if since.is_none() {
-                handle_errors(sess, attr.span, AttrError::MissingSince);
+                handle_errors(&sess.parse_sess, attr.span, AttrError::MissingSince);
                 continue;
             }
 
@@ -773,9 +776,9 @@ where
             }
         }
 
-        mark_used(&attr);
+        sess.mark_attr_used(&attr);
 
-        let is_since_rustc_version = attr.check_name(sym::rustc_deprecated);
+        let is_since_rustc_version = sess.check_name(attr, sym::rustc_deprecated);
         depr = Some(Deprecation { since, note, suggestion, is_since_rustc_version });
     }
 
@@ -818,18 +821,18 @@ impl IntType {
 /// the same discriminant size that the corresponding C enum would or C
 /// structure layout, `packed` to remove padding, and `transparent` to elegate representation
 /// concerns to the only non-ZST field.
-pub fn find_repr_attrs(sess: &ParseSess, attr: &Attribute) -> Vec<ReprAttr> {
+pub fn find_repr_attrs(sess: &Session, attr: &Attribute) -> Vec<ReprAttr> {
     use ReprAttr::*;
 
     let mut acc = Vec::new();
-    let diagnostic = &sess.span_diagnostic;
+    let diagnostic = &sess.parse_sess.span_diagnostic;
     if attr.has_name(sym::repr) {
         if let Some(items) = attr.meta_item_list() {
-            mark_used(attr);
+            sess.mark_attr_used(attr);
             for item in items {
                 if !item.is_meta_item() {
                     handle_errors(
-                        sess,
+                        &sess.parse_sess,
                         item.span(),
                         AttrError::UnsupportedLiteral(
                             "meta item in `repr` must be an identifier",
@@ -973,13 +976,14 @@ pub enum TransparencyError {
 }
 
 pub fn find_transparency(
+    sess: &Session,
     attrs: &[Attribute],
     macro_rules: bool,
 ) -> (Transparency, Option<TransparencyError>) {
     let mut transparency = None;
     let mut error = None;
     for attr in attrs {
-        if attr.check_name(sym::rustc_macro_transparency) {
+        if sess.check_name(attr, sym::rustc_macro_transparency) {
             if let Some((_, old_span)) = transparency {
                 error = Some(TransparencyError::MultipleTransparencyAttrs(old_span, attr.span));
                 break;
@@ -1004,18 +1008,20 @@ pub fn find_transparency(
 }
 
 pub fn allow_internal_unstable<'a>(
+    sess: &'a Session,
     attrs: &[Attribute],
-    diag: &'a rustc_errors::Handler,
 ) -> Option<impl Iterator<Item = Symbol> + 'a> {
-    let attr = find_by_name(attrs, sym::allow_internal_unstable)?;
+    let attr = sess.find_by_name(attrs, sym::allow_internal_unstable)?;
     let list = attr.meta_item_list().or_else(|| {
-        diag.span_err(attr.span, "allow_internal_unstable expects list of feature names");
+        sess.diagnostic()
+            .span_err(attr.span, "allow_internal_unstable expects list of feature names");
         None
     })?;
     Some(list.into_iter().filter_map(move |it| {
         let name = it.ident().map(|ident| ident.name);
         if name.is_none() {
-            diag.span_err(it.span(), "`allow_internal_unstable` expects feature names");
+            sess.diagnostic()
+                .span_err(it.span(), "`allow_internal_unstable` expects feature names");
         }
         name
     }))
diff --git a/src/librustc_builtin_macros/cfg.rs b/src/librustc_builtin_macros/cfg.rs
index 3c09b26af42..0247ca32991 100644
--- a/src/librustc_builtin_macros/cfg.rs
+++ b/src/librustc_builtin_macros/cfg.rs
@@ -19,7 +19,7 @@ pub fn expand_cfg(
 
     match parse_cfg(cx, sp, tts) {
         Ok(cfg) => {
-            let matches_cfg = attr::cfg_matches(&cfg, cx.parse_sess, cx.ecfg.features);
+            let matches_cfg = attr::cfg_matches(&cfg, &cx.sess.parse_sess, cx.ecfg.features);
             MacEager::expr(cx.expr_bool(sp, matches_cfg))
         }
         Err(mut err) => {
diff --git a/src/librustc_builtin_macros/cfg_accessible.rs b/src/librustc_builtin_macros/cfg_accessible.rs
index 3607a4d0d15..7a91dde5a9d 100644
--- a/src/librustc_builtin_macros/cfg_accessible.rs
+++ b/src/librustc_builtin_macros/cfg_accessible.rs
@@ -37,7 +37,12 @@ impl MultiItemModifier for Expander {
     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
         let template = AttributeTemplate { list: Some("path"), ..Default::default() };
         let attr = &ecx.attribute(meta_item.clone());
-        validate_attr::check_builtin_attribute(ecx.parse_sess, attr, sym::cfg_accessible, template);
+        validate_attr::check_builtin_attribute(
+            &ecx.sess.parse_sess,
+            attr,
+            sym::cfg_accessible,
+            template,
+        );
 
         let path = match validate_input(ecx, meta_item) {
             Some(path) => path,
diff --git a/src/librustc_builtin_macros/deriving/default.rs b/src/librustc_builtin_macros/deriving/default.rs
index 8ca1be1efb6..2611855a3a1 100644
--- a/src/librustc_builtin_macros/deriving/default.rs
+++ b/src/librustc_builtin_macros/deriving/default.rs
@@ -72,7 +72,7 @@ fn default_substructure(
         },
         StaticEnum(..) => {
             struct_span_err!(
-                cx.parse_sess.span_diagnostic,
+                &cx.sess.parse_sess.span_diagnostic,
                 trait_span,
                 E0665,
                 "`Default` cannot be derived for enums, only structs"
diff --git a/src/librustc_builtin_macros/deriving/generic/mod.rs b/src/librustc_builtin_macros/deriving/generic/mod.rs
index c43d1cf1888..908aabe9024 100644
--- a/src/librustc_builtin_macros/deriving/generic/mod.rs
+++ b/src/librustc_builtin_macros/deriving/generic/mod.rs
@@ -392,7 +392,7 @@ impl<'a> TraitDef<'a> {
         match *item {
             Annotatable::Item(ref item) => {
                 let is_packed = item.attrs.iter().any(|attr| {
-                    for r in attr::find_repr_attrs(&cx.parse_sess, attr) {
+                    for r in attr::find_repr_attrs(&cx.sess, attr) {
                         if let attr::ReprPacked(_) = r {
                             return true;
                         }
@@ -677,7 +677,7 @@ impl<'a> TraitDef<'a> {
 
         let attr = cx.attribute(cx.meta_word(self.span, sym::automatically_derived));
         // Just mark it now since we know that it'll end up used downstream
-        attr::mark_used(&attr);
+        cx.sess.mark_attr_used(&attr);
         let opt_trait_ref = Some(trait_ref);
         let unused_qual = {
             let word = rustc_ast::attr::mk_nested_word_item(Ident::new(
diff --git a/src/librustc_builtin_macros/env.rs b/src/librustc_builtin_macros/env.rs
index 6c3a1ce0958..b6f733ee93d 100644
--- a/src/librustc_builtin_macros/env.rs
+++ b/src/librustc_builtin_macros/env.rs
@@ -23,7 +23,7 @@ pub fn expand_option_env<'cx>(
 
     let sp = cx.with_def_site_ctxt(sp);
     let value = env::var(&var.as_str()).ok().as_deref().map(Symbol::intern);
-    cx.parse_sess.env_depinfo.borrow_mut().insert((Symbol::intern(&var), value));
+    cx.sess.parse_sess.env_depinfo.borrow_mut().insert((Symbol::intern(&var), value));
     let e = match value {
         None => {
             let lt = cx.lifetime(sp, Ident::new(kw::StaticLifetime, sp));
@@ -81,7 +81,7 @@ pub fn expand_env<'cx>(
 
     let sp = cx.with_def_site_ctxt(sp);
     let value = env::var(&*var.as_str()).ok().as_deref().map(Symbol::intern);
-    cx.parse_sess.env_depinfo.borrow_mut().insert((var, value));
+    cx.sess.parse_sess.env_depinfo.borrow_mut().insert((var, value));
     let e = match value {
         None => {
             cx.span_err(sp, &msg.as_str());
diff --git a/src/librustc_builtin_macros/global_allocator.rs b/src/librustc_builtin_macros/global_allocator.rs
index 89446a1aa96..ccff8aa90a8 100644
--- a/src/librustc_builtin_macros/global_allocator.rs
+++ b/src/librustc_builtin_macros/global_allocator.rs
@@ -19,7 +19,7 @@ pub fn expand(
     check_builtin_macro_attribute(ecx, meta_item, sym::global_allocator);
 
     let not_static = |item: Annotatable| {
-        ecx.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
+        ecx.sess.parse_sess.span_diagnostic.span_err(item.span(), "allocators must be statics");
         vec![item]
     };
     let item = match item {
diff --git a/src/librustc_builtin_macros/llvm_asm.rs b/src/librustc_builtin_macros/llvm_asm.rs
index 0f4efc153b9..77fd71d58ef 100644
--- a/src/librustc_builtin_macros/llvm_asm.rs
+++ b/src/librustc_builtin_macros/llvm_asm.rs
@@ -110,7 +110,7 @@ fn parse_inline_asm<'a>(
                     // If we already have a string with instructions,
                     // ending up in Asm state again is an error.
                     return Err(struct_span_err!(
-                        cx.parse_sess.span_diagnostic,
+                        cx.sess.parse_sess.span_diagnostic,
                         sp,
                         E0660,
                         "malformed inline assembly"
@@ -171,7 +171,7 @@ fn parse_inline_asm<'a>(
                         Some('+') => Some(Symbol::intern(&format!("={}", ch.as_str()))),
                         _ => {
                             struct_span_err!(
-                                cx.parse_sess.span_diagnostic,
+                                cx.sess.parse_sess.span_diagnostic,
                                 span,
                                 E0661,
                                 "output operand constraint lacks '=' or '+'"
@@ -201,7 +201,7 @@ fn parse_inline_asm<'a>(
 
                     if constraint.as_str().starts_with('=') {
                         struct_span_err!(
-                            cx.parse_sess.span_diagnostic,
+                            cx.sess.parse_sess.span_diagnostic,
                             p.prev_token.span,
                             E0662,
                             "input operand constraint contains '='"
@@ -209,7 +209,7 @@ fn parse_inline_asm<'a>(
                         .emit();
                     } else if constraint.as_str().starts_with('+') {
                         struct_span_err!(
-                            cx.parse_sess.span_diagnostic,
+                            cx.sess.parse_sess.span_diagnostic,
                             p.prev_token.span,
                             E0663,
                             "input operand constraint contains '+'"
@@ -236,7 +236,7 @@ fn parse_inline_asm<'a>(
                         cx.span_warn(p.prev_token.span, "expected a clobber, found an option");
                     } else if s.as_str().starts_with('{') || s.as_str().ends_with('}') {
                         struct_span_err!(
-                            cx.parse_sess.span_diagnostic,
+                            cx.sess.parse_sess.span_diagnostic,
                             p.prev_token.span,
                             E0664,
                             "clobber should not be surrounded by braces"
diff --git a/src/librustc_builtin_macros/proc_macro_harness.rs b/src/librustc_builtin_macros/proc_macro_harness.rs
index 763bdca35eb..4f2f066e652 100644
--- a/src/librustc_builtin_macros/proc_macro_harness.rs
+++ b/src/librustc_builtin_macros/proc_macro_harness.rs
@@ -2,13 +2,12 @@ use std::mem;
 
 use rustc_ast::ast::{self, NodeId};
 use rustc_ast::attr;
-use rustc_ast::expand::is_proc_macro_attr;
 use rustc_ast::ptr::P;
 use rustc_ast::visit::{self, Visitor};
 use rustc_ast_pretty::pprust;
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
-use rustc_session::parse::ParseSess;
+use rustc_session::Session;
 use rustc_span::hygiene::AstPass;
 use rustc_span::source_map::SourceMap;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -42,6 +41,7 @@ enum ProcMacro {
 }
 
 struct CollectProcMacros<'a> {
+    sess: &'a Session,
     macros: Vec<ProcMacro>,
     in_root: bool,
     handler: &'a rustc_errors::Handler,
@@ -51,7 +51,7 @@ struct CollectProcMacros<'a> {
 }
 
 pub fn inject(
-    sess: &ParseSess,
+    sess: &Session,
     resolver: &mut dyn ResolverExpand,
     mut krate: ast::Crate,
     is_proc_macro_crate: bool,
@@ -64,6 +64,7 @@ pub fn inject(
     let mut cx = ExtCtxt::new(sess, ecfg, resolver, None);
 
     let mut collect = CollectProcMacros {
+        sess,
         macros: Vec::new(),
         in_root: true,
         handler,
@@ -244,7 +245,7 @@ impl<'a> CollectProcMacros<'a> {
 impl<'a> Visitor<'a> for CollectProcMacros<'a> {
     fn visit_item(&mut self, item: &'a ast::Item) {
         if let ast::ItemKind::MacroDef(..) = item.kind {
-            if self.is_proc_macro_crate && attr::contains_name(&item.attrs, sym::macro_export) {
+            if self.is_proc_macro_crate && self.sess.contains_name(&item.attrs, sym::macro_export) {
                 let msg =
                     "cannot export macro_rules! macros from a `proc-macro` crate type currently";
                 self.handler.span_err(self.source_map.guess_head_span(item.span), msg);
@@ -263,7 +264,7 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
         let mut found_attr: Option<&'a ast::Attribute> = None;
 
         for attr in &item.attrs {
-            if is_proc_macro_attr(&attr) {
+            if self.sess.is_proc_macro_attr(&attr) {
                 if let Some(prev_attr) = found_attr {
                     let prev_item = prev_attr.get_normal_item();
                     let item = attr.get_normal_item();
@@ -331,11 +332,11 @@ impl<'a> Visitor<'a> for CollectProcMacros<'a> {
             return;
         }
 
-        if attr.check_name(sym::proc_macro_derive) {
+        if self.sess.check_name(attr, sym::proc_macro_derive) {
             self.collect_custom_derive(item, attr);
-        } else if attr.check_name(sym::proc_macro_attribute) {
+        } else if self.sess.check_name(attr, sym::proc_macro_attribute) {
             self.collect_attr_proc_macro(item);
-        } else if attr.check_name(sym::proc_macro) {
+        } else if self.sess.check_name(attr, sym::proc_macro) {
             self.collect_bang_proc_macro(item);
         };
 
diff --git a/src/librustc_builtin_macros/standard_library_imports.rs b/src/librustc_builtin_macros/standard_library_imports.rs
index 671ff8ce54f..52759fede75 100644
--- a/src/librustc_builtin_macros/standard_library_imports.rs
+++ b/src/librustc_builtin_macros/standard_library_imports.rs
@@ -1,8 +1,8 @@
+use rustc_ast::ast;
 use rustc_ast::ptr::P;
-use rustc_ast::{ast, attr};
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::ExpansionConfig;
-use rustc_session::parse::ParseSess;
+use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::AstPass;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -11,16 +11,16 @@ use rustc_span::DUMMY_SP;
 pub fn inject(
     mut krate: ast::Crate,
     resolver: &mut dyn ResolverExpand,
-    sess: &ParseSess,
+    sess: &Session,
     alt_std_name: Option<Symbol>,
 ) -> (ast::Crate, Option<Symbol>) {
-    let rust_2018 = sess.edition >= Edition::Edition2018;
+    let rust_2018 = sess.parse_sess.edition >= Edition::Edition2018;
 
     // the first name in this list is the crate name of the crate with the prelude
-    let names: &[Symbol] = if attr::contains_name(&krate.attrs, sym::no_core) {
+    let names: &[Symbol] = if sess.contains_name(&krate.attrs, sym::no_core) {
         return (krate, None);
-    } else if attr::contains_name(&krate.attrs, sym::no_std) {
-        if attr::contains_name(&krate.attrs, sym::compiler_builtins) {
+    } else if sess.contains_name(&krate.attrs, sym::no_std) {
+        if sess.contains_name(&krate.attrs, sym::compiler_builtins) {
             &[sym::core]
         } else {
             &[sym::core, sym::compiler_builtins]
diff --git a/src/librustc_builtin_macros/test.rs b/src/librustc_builtin_macros/test.rs
index de8f81bdadf..00593c68cfc 100644
--- a/src/librustc_builtin_macros/test.rs
+++ b/src/librustc_builtin_macros/test.rs
@@ -6,6 +6,7 @@ use rustc_ast::ast;
 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;
@@ -87,7 +88,7 @@ pub fn expand_test_or_bench(
     };
 
     if let ast::ItemKind::MacCall(_) = item.kind {
-        cx.parse_sess.span_diagnostic.span_warn(
+        cx.sess.parse_sess.span_diagnostic.span_warn(
             item.span,
             "`#[test]` attribute should not be used on macros. Use `#[cfg(test)]` instead.",
         );
@@ -232,9 +233,15 @@ pub fn expand_test_or_bench(
                                         ),
                                     ),
                                     // ignore: true | false
-                                    field("ignore", cx.expr_bool(sp, should_ignore(&item))),
+                                    field(
+                                        "ignore",
+                                        cx.expr_bool(sp, should_ignore(&cx.sess, &item)),
+                                    ),
                                     // allow_fail: true | false
-                                    field("allow_fail", cx.expr_bool(sp, should_fail(&item))),
+                                    field(
+                                        "allow_fail",
+                                        cx.expr_bool(sp, should_fail(&cx.sess, &item)),
+                                    ),
                                     // should_panic: ...
                                     field(
                                         "should_panic",
@@ -318,18 +325,18 @@ enum ShouldPanic {
     Yes(Option<Symbol>),
 }
 
-fn should_ignore(i: &ast::Item) -> bool {
-    attr::contains_name(&i.attrs, sym::ignore)
+fn should_ignore(sess: &Session, i: &ast::Item) -> bool {
+    sess.contains_name(&i.attrs, sym::ignore)
 }
 
-fn should_fail(i: &ast::Item) -> bool {
-    attr::contains_name(&i.attrs, sym::allow_fail)
+fn should_fail(sess: &Session, i: &ast::Item) -> bool {
+    sess.contains_name(&i.attrs, sym::allow_fail)
 }
 
 fn should_panic(cx: &ExtCtxt<'_>, i: &ast::Item) -> ShouldPanic {
-    match attr::find_by_name(&i.attrs, sym::should_panic) {
+    match cx.sess.find_by_name(&i.attrs, sym::should_panic) {
         Some(attr) => {
-            let sd = &cx.parse_sess.span_diagnostic;
+            let sd = &cx.sess.parse_sess.span_diagnostic;
 
             match attr.meta_item_list() {
                 // Handle #[should_panic(expected = "foo")]
@@ -393,8 +400,8 @@ fn test_type(cx: &ExtCtxt<'_>) -> TestType {
 }
 
 fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
-    let has_should_panic_attr = attr::contains_name(&i.attrs, sym::should_panic);
-    let sd = &cx.parse_sess.span_diagnostic;
+    let has_should_panic_attr = cx.sess.contains_name(&i.attrs, sym::should_panic);
+    let sd = &cx.sess.parse_sess.span_diagnostic;
     if let ast::ItemKind::Fn(_, ref sig, ref generics, _) = i.kind {
         if let ast::Unsafe::Yes(span) = sig.header.unsafety {
             sd.struct_span_err(i.span, "unsafe functions cannot be used for tests")
@@ -453,7 +460,7 @@ fn has_bench_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
     };
 
     if !has_sig {
-        cx.parse_sess.span_diagnostic.span_err(
+        cx.sess.parse_sess.span_diagnostic.span_err(
             i.span,
             "functions used as benches must have \
             signature `fn(&mut Bencher) -> impl Termination`",
diff --git a/src/librustc_builtin_macros/test_harness.rs b/src/librustc_builtin_macros/test_harness.rs
index 3f7128d605d..a80763d6ec8 100644
--- a/src/librustc_builtin_macros/test_harness.rs
+++ b/src/librustc_builtin_macros/test_harness.rs
@@ -2,13 +2,13 @@
 
 use rustc_ast::ast;
 use rustc_ast::attr;
-use rustc_ast::entry::{self, EntryPointType};
+use rustc_ast::entry::EntryPointType;
 use rustc_ast::mut_visit::{ExpectOne, *};
 use rustc_ast::ptr::P;
 use rustc_expand::base::{ExtCtxt, ResolverExpand};
 use rustc_expand::expand::{AstFragment, ExpansionConfig};
 use rustc_feature::Features;
-use rustc_session::parse::ParseSess;
+use rustc_session::Session;
 use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency};
 use rustc_span::source_map::respan;
 use rustc_span::symbol::{sym, Ident, Symbol};
@@ -35,41 +35,35 @@ struct TestCtxt<'a> {
 
 // Traverse the crate, collecting all the test functions, eliding any
 // existing main functions, and synthesizing a main test harness
-pub fn inject(
-    sess: &ParseSess,
-    resolver: &mut dyn ResolverExpand,
-    should_test: bool,
-    krate: &mut ast::Crate,
-    span_diagnostic: &rustc_errors::Handler,
-    features: &Features,
-    panic_strategy: PanicStrategy,
-    platform_panic_strategy: PanicStrategy,
-    enable_panic_abort_tests: bool,
-) {
+pub fn inject(sess: &Session, resolver: &mut dyn ResolverExpand, krate: &mut ast::Crate) {
+    let span_diagnostic = sess.diagnostic();
+    let panic_strategy = sess.panic_strategy();
+    let platform_panic_strategy = sess.target.target.options.panic_strategy;
+
     // Check for #![reexport_test_harness_main = "some_name"] which gives the
     // main test function the name `some_name` without hygiene. This needs to be
     // unconditional, so that the attribute is still marked as used in
     // non-test builds.
     let reexport_test_harness_main =
-        attr::first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
+        sess.first_attr_value_str_by_name(&krate.attrs, sym::reexport_test_harness_main);
 
     // Do this here so that the test_runner crate attribute gets marked as used
     // even in non-test builds
-    let test_runner = get_test_runner(span_diagnostic, &krate);
+    let test_runner = get_test_runner(sess, span_diagnostic, &krate);
 
-    if should_test {
-        let panic_strategy = match (panic_strategy, enable_panic_abort_tests) {
+    if sess.opts.test {
+        let panic_strategy = match (panic_strategy, sess.opts.debugging_opts.panic_abort_tests) {
             (PanicStrategy::Abort, true) => PanicStrategy::Abort,
-            (PanicStrategy::Abort, false) if panic_strategy == platform_panic_strategy => {
-                // Silently allow compiling with panic=abort on these platforms,
-                // but with old behavior (abort if a test fails).
-                PanicStrategy::Unwind
-            }
             (PanicStrategy::Abort, false) => {
-                span_diagnostic.err(
-                    "building tests with panic=abort is not supported \
-                                     without `-Zpanic_abort_tests`",
-                );
+                if panic_strategy == platform_panic_strategy {
+                    // Silently allow compiling with panic=abort on these platforms,
+                    // but with old behavior (abort if a test fails).
+                } else {
+                    span_diagnostic.err(
+                        "building tests with panic=abort is not supported \
+                                         without `-Zpanic_abort_tests`",
+                    );
+                }
                 PanicStrategy::Unwind
             }
             (PanicStrategy::Unwind, _) => PanicStrategy::Unwind,
@@ -79,7 +73,7 @@ pub fn inject(
             resolver,
             reexport_test_harness_main,
             krate,
-            features,
+            &sess.features_untracked(),
             panic_strategy,
             test_runner,
         )
@@ -101,7 +95,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
 
     fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
         let mut item = i.into_inner();
-        if is_test_case(&item) {
+        if is_test_case(&self.cx.ext_cx.sess, &item) {
             debug!("this is a test item");
 
             let test = Test { span: item.span, ident: item.ident };
@@ -143,15 +137,39 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
     }
 }
 
+// Beware, this is duplicated in librustc_passes/entry.rs (with
+// `rustc_hir::Item`), so make sure to keep them in sync.
+fn entry_point_type(sess: &Session, item: &ast::Item, depth: usize) -> EntryPointType {
+    match item.kind {
+        ast::ItemKind::Fn(..) => {
+            if sess.contains_name(&item.attrs, sym::start) {
+                EntryPointType::Start
+            } else if sess.contains_name(&item.attrs, sym::main) {
+                EntryPointType::MainAttr
+            } else if item.ident.name == sym::main {
+                if depth == 1 {
+                    // This is a top-level function so can be 'main'
+                    EntryPointType::MainNamed
+                } else {
+                    EntryPointType::OtherMain
+                }
+            } else {
+                EntryPointType::None
+            }
+        }
+        _ => EntryPointType::None,
+    }
+}
 /// A folder used to remove any entry points (like fn main) because the harness
 /// generator will provide its own
-struct EntryPointCleaner {
+struct EntryPointCleaner<'a> {
     // Current depth in the ast
+    sess: &'a Session,
     depth: usize,
     def_site: Span,
 }
 
-impl MutVisitor for EntryPointCleaner {
+impl<'a> MutVisitor for EntryPointCleaner<'a> {
     fn flat_map_item(&mut self, i: P<ast::Item>) -> SmallVec<[P<ast::Item>; 1]> {
         self.depth += 1;
         let item = noop_flat_map_item(i, self).expect_one("noop did something");
@@ -160,7 +178,7 @@ impl MutVisitor for EntryPointCleaner {
         // Remove any #[main] or #[start] from the AST so it doesn't
         // clash with the one we're going to add, but mark it as
         // #[allow(dead_code)] to avoid printing warnings.
-        let item = match entry::entry_point_type(&item, self.depth) {
+        let item = match entry_point_type(self.sess, &item, self.depth) {
             EntryPointType::MainNamed | EntryPointType::MainAttr | EntryPointType::Start => item
                 .map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
                     let allow_ident = Ident::new(sym::allow, self.def_site);
@@ -170,7 +188,10 @@ impl MutVisitor for EntryPointCleaner {
                     let allow_dead_code = attr::mk_attr_outer(allow_dead_code_item);
                     let attrs = attrs
                         .into_iter()
-                        .filter(|attr| !attr.check_name(sym::main) && !attr.check_name(sym::start))
+                        .filter(|attr| {
+                            !self.sess.check_name(attr, sym::main)
+                                && !self.sess.check_name(attr, sym::start)
+                        })
                         .chain(iter::once(allow_dead_code))
                         .collect();
 
@@ -189,7 +210,7 @@ impl MutVisitor for EntryPointCleaner {
 
 /// Crawl over the crate, inserting test reexports and the test main function
 fn generate_test_harness(
-    sess: &ParseSess,
+    sess: &Session,
     resolver: &mut dyn ResolverExpand,
     reexport_test_harness_main: Option<Symbol>,
     krate: &mut ast::Crate,
@@ -211,7 +232,7 @@ fn generate_test_harness(
     let def_site = DUMMY_SP.with_def_site_ctxt(expn_id);
 
     // Remove the entry points
-    let mut cleaner = EntryPointCleaner { depth: 0, def_site };
+    let mut cleaner = EntryPointCleaner { sess, depth: 0, def_site };
     cleaner.visit_crate(krate);
 
     let cx = TestCtxt {
@@ -339,12 +360,16 @@ fn mk_tests_slice(cx: &TestCtxt<'_>, sp: Span) -> P<ast::Expr> {
     )
 }
 
-fn is_test_case(i: &ast::Item) -> bool {
-    attr::contains_name(&i.attrs, sym::rustc_test_marker)
+fn is_test_case(sess: &Session, i: &ast::Item) -> bool {
+    sess.contains_name(&i.attrs, sym::rustc_test_marker)
 }
 
-fn get_test_runner(sd: &rustc_errors::Handler, krate: &ast::Crate) -> Option<ast::Path> {
-    let test_attr = attr::find_by_name(&krate.attrs, sym::test_runner)?;
+fn get_test_runner(
+    sess: &Session,
+    sd: &rustc_errors::Handler,
+    krate: &ast::Crate,
+) -> Option<ast::Path> {
+    let test_attr = sess.find_by_name(&krate.attrs, sym::test_runner)?;
     let meta_list = test_attr.meta_item_list()?;
     let span = test_attr.span;
     match &*meta_list {
diff --git a/src/librustc_builtin_macros/util.rs b/src/librustc_builtin_macros/util.rs
index b486eadd1a8..3ee6cd73964 100644
--- a/src/librustc_builtin_macros/util.rs
+++ b/src/librustc_builtin_macros/util.rs
@@ -8,5 +8,5 @@ pub fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, na
     // All the built-in macro attributes are "words" at the moment.
     let template = AttributeTemplate { word: true, ..Default::default() };
     let attr = ecx.attribute(meta_item.clone());
-    validate_attr::check_builtin_attribute(ecx.parse_sess, &attr, name, template);
+    validate_attr::check_builtin_attribute(&ecx.sess.parse_sess, &attr, name, template);
 }
diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs
index 566fe1e15d4..b977da733aa 100644
--- a/src/librustc_codegen_llvm/consts.rs
+++ b/src/librustc_codegen_llvm/consts.rs
@@ -253,7 +253,7 @@ impl CodegenCx<'ll, 'tcx> {
             debug!("get_static: sym={} attrs={:?}", sym, attrs);
 
             for attr in attrs {
-                if attr.check_name(sym::thread_local) {
+                if self.tcx.sess.check_name(attr, sym::thread_local) {
                     llvm::set_thread_local_mode(g, self.tls_model);
                 }
             }
diff --git a/src/librustc_codegen_llvm/coverageinfo/mapgen.rs b/src/librustc_codegen_llvm/coverageinfo/mapgen.rs
index 5d1bfd810b2..a39234c0b7f 100644
--- a/src/librustc_codegen_llvm/coverageinfo/mapgen.rs
+++ b/src/librustc_codegen_llvm/coverageinfo/mapgen.rs
@@ -5,7 +5,7 @@ use crate::llvm;
 use llvm::coverageinfo::CounterMappingRegion;
 use rustc_codegen_ssa::coverageinfo::map::{Counter, CounterExpression, Region};
 use rustc_codegen_ssa::traits::{BaseTypeMethods, ConstMethods};
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_llvm::RustString;
 use tracing::debug;
 
@@ -76,13 +76,12 @@ pub fn finalize<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>) {
 }
 
 struct CoverageMapGenerator {
-    filenames: Vec<CString>,
-    filename_to_index: FxHashMap<CString, u32>,
+    filenames: FxIndexSet<CString>,
 }
 
 impl CoverageMapGenerator {
     fn new() -> Self {
-        Self { filenames: Vec::new(), filename_to_index: FxHashMap::default() }
+        Self { filenames: FxIndexSet::default() }
     }
 
     /// Using the `expressions` and `counter_regions` collected for the current function, generate
@@ -122,16 +121,8 @@ impl CoverageMapGenerator {
                 let c_filename =
                     CString::new(file_name).expect("null error converting filename to C string");
                 debug!("  file_id: {} = '{:?}'", current_file_id, c_filename);
-                let filenames_index = match self.filename_to_index.get(&c_filename) {
-                    Some(index) => *index,
-                    None => {
-                        let index = self.filenames.len() as u32;
-                        self.filenames.push(c_filename.clone());
-                        self.filename_to_index.insert(c_filename.clone(), index);
-                        index
-                    }
-                };
-                virtual_file_mapping.push(filenames_index);
+                let (filenames_index, _) = self.filenames.insert_full(c_filename);
+                virtual_file_mapping.push(filenames_index as u32);
             }
             mapping_regions.push(CounterMappingRegion::code_region(
                 counter,
diff --git a/src/librustc_codegen_llvm/coverageinfo/mod.rs b/src/librustc_codegen_llvm/coverageinfo/mod.rs
index 4d51f8b0591..d7c98e9a624 100644
--- a/src/librustc_codegen_llvm/coverageinfo/mod.rs
+++ b/src/librustc_codegen_llvm/coverageinfo/mod.rs
@@ -97,8 +97,11 @@ impl CoverageInfoBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
     }
 }
 
-pub(crate) fn write_filenames_section_to_buffer(filenames: &Vec<CString>, buffer: &RustString) {
-    let c_str_vec = filenames.iter().map(|cstring| cstring.as_ptr()).collect::<Vec<_>>();
+pub(crate) fn write_filenames_section_to_buffer<'a>(
+    filenames: impl IntoIterator<Item = &'a CString>,
+    buffer: &RustString,
+) {
+    let c_str_vec = filenames.into_iter().map(|cstring| cstring.as_ptr()).collect::<Vec<_>>();
     unsafe {
         llvm::LLVMRustCoverageWriteFilenamesSectionToBuffer(
             c_str_vec.as_ptr(),
diff --git a/src/librustc_codegen_llvm/debuginfo/gdb.rs b/src/librustc_codegen_llvm/debuginfo/gdb.rs
index 64d4076cbf0..29edd66049c 100644
--- a/src/librustc_codegen_llvm/debuginfo/gdb.rs
+++ b/src/librustc_codegen_llvm/debuginfo/gdb.rs
@@ -9,7 +9,6 @@ use rustc_codegen_ssa::traits::*;
 use rustc_middle::bug;
 use rustc_session::config::DebugInfo;
 
-use rustc_ast::attr;
 use rustc_span::symbol::sym;
 
 /// Inserts a side-effect free instruction sequence that makes sure that the
@@ -61,8 +60,10 @@ pub fn get_or_insert_gdb_debug_scripts_section_global(cx: &CodegenCx<'ll, '_>) -
 }
 
 pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool {
-    let omit_gdb_pretty_printer_section =
-        attr::contains_name(&cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section);
+    let omit_gdb_pretty_printer_section = cx
+        .tcx
+        .sess
+        .contains_name(&cx.tcx.hir().krate_attrs(), sym::omit_gdb_pretty_printer_section);
 
     !omit_gdb_pretty_printer_section
         && cx.sess().opts.debuginfo != DebugInfo::None
diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs
index 836e490c1d6..f2881355962 100644
--- a/src/librustc_codegen_llvm/debuginfo/metadata.rs
+++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs
@@ -960,7 +960,7 @@ fn pointer_type_metadata(
 fn param_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
     debug!("param_type_metadata: {:?}", t);
     let name = format!("{:?}", t);
-    return unsafe {
+    unsafe {
         llvm::LLVMRustDIBuilderCreateBasicType(
             DIB(cx),
             name.as_ptr().cast(),
@@ -968,7 +968,7 @@ fn param_type_metadata(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
             Size::ZERO.bits(),
             DW_ATE_unsigned,
         )
-    };
+    }
 }
 
 pub fn compile_unit_metadata(
diff --git a/src/librustc_codegen_ssa/back/link.rs b/src/librustc_codegen_ssa/back/link.rs
index 9191c68d453..6912667c391 100644
--- a/src/librustc_codegen_ssa/back/link.rs
+++ b/src/librustc_codegen_ssa/back/link.rs
@@ -1295,7 +1295,9 @@ fn crt_objects_fallback(sess: &Session, crate_type: CrateType) -> bool {
         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.target.target.target_vendor != "uwp",
+        Some(CrtObjectsFallback::Mingw) => {
+            sess.host == sess.target.target && sess.target.target.target_vendor != "uwp"
+        }
         // FIXME: Figure out cases in which WASM needs to link with a native toolchain.
         Some(CrtObjectsFallback::Wasm) => true,
         None => false,
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs
index b0fae566a5a..7d69bb983dd 100644
--- a/src/librustc_codegen_ssa/back/write.rs
+++ b/src/librustc_codegen_ssa/back/write.rs
@@ -9,7 +9,6 @@ use crate::{
 
 use crate::traits::*;
 use jobserver::{Acquired, Client};
-use rustc_ast::attr;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::profiling::SelfProfilerRef;
 use rustc_data_structures::profiling::TimingGuard;
@@ -416,11 +415,12 @@ pub fn start_async_codegen<B: ExtraBackendMethods>(
 
     let crate_name = tcx.crate_name(LOCAL_CRATE);
     let crate_hash = tcx.crate_hash(LOCAL_CRATE);
-    let no_builtins = attr::contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
+    let no_builtins = tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_builtins);
     let is_compiler_builtins =
-        attr::contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins);
-    let subsystem =
-        attr::first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem);
+        tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::compiler_builtins);
+    let subsystem = tcx
+        .sess
+        .first_attr_value_str_by_name(&tcx.hir().krate().item.attrs, sym::windows_subsystem);
     let windows_subsystem = subsystem.map(|subsystem| {
         if subsystem != sym::windows && subsystem != sym::console {
             tcx.sess.fatal(&format!(
@@ -490,7 +490,7 @@ fn copy_all_cgu_workproducts_to_incr_comp_cache_dir(
     let _timer = sess.timer("copy_all_cgu_workproducts_to_incr_comp_cache_dir");
 
     for module in compiled_modules.modules.iter().filter(|m| m.kind == ModuleKind::Regular) {
-        let path = module.object.as_ref().map(|path| path.clone());
+        let path = module.object.as_ref().cloned();
 
         if let Some((id, product)) =
             copy_cgu_workproduct_to_incr_comp_cache_dir(sess, &module.name, &path)
diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs
index 0c8638b673d..05656774f0e 100644
--- a/src/librustc_codegen_ssa/mir/place.rs
+++ b/src/librustc_codegen_ssa/mir/place.rs
@@ -253,14 +253,13 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
                     bx.sub(tag, bx.cx().const_uint_big(niche_llty, niche_start))
                 };
                 let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
-                let is_niche = {
-                    let relative_max = if relative_max == 0 {
-                        // Avoid calling `const_uint`, which wouldn't work for pointers.
-                        // FIXME(eddyb) check the actual primitive type here.
-                        bx.cx().const_null(niche_llty)
-                    } else {
-                        bx.cx().const_uint(niche_llty, relative_max as u64)
-                    };
+                let is_niche = if relative_max == 0 {
+                    // Avoid calling `const_uint`, which wouldn't work for pointers.
+                    // Also use canonical == 0 instead of non-canonical u<= 0.
+                    // FIXME(eddyb) check the actual primitive type here.
+                    bx.icmp(IntPredicate::IntEQ, relative_discr, bx.cx().const_null(niche_llty))
+                } else {
+                    let relative_max = bx.cx().const_uint(niche_llty, relative_max as u64);
                     bx.icmp(IntPredicate::IntULE, relative_discr, relative_max)
                 };
 
diff --git a/src/librustc_data_structures/Cargo.toml b/src/librustc_data_structures/Cargo.toml
index 386096b416f..fda0fc5973b 100644
--- a/src/librustc_data_structures/Cargo.toml
+++ b/src/librustc_data_structures/Cargo.toml
@@ -11,7 +11,7 @@ doctest = false
 
 [dependencies]
 ena = "0.14"
-indexmap = "1"
+indexmap = "1.5.1"
 tracing = "0.1"
 jobserver_crate = { version = "0.1.13", package = "jobserver" }
 lazy_static = "1"
diff --git a/src/librustc_data_structures/transitive_relation.rs b/src/librustc_data_structures/transitive_relation.rs
index 189da3395ad..7d137a55033 100644
--- a/src/librustc_data_structures/transitive_relation.rs
+++ b/src/librustc_data_structures/transitive_relation.rs
@@ -1,4 +1,4 @@
-use crate::fx::FxHashMap;
+use crate::fx::FxIndexSet;
 use crate::stable_hasher::{HashStable, StableHasher};
 use crate::sync::Lock;
 use rustc_index::bit_set::BitMatrix;
@@ -13,10 +13,7 @@ mod tests;
 #[derive(Clone, Debug)]
 pub struct TransitiveRelation<T: Eq + Hash> {
     // List of elements. This is used to map from a T to a usize.
-    elements: Vec<T>,
-
-    // Maps each element to an index.
-    map: FxHashMap<T, Index>,
+    elements: FxIndexSet<T>,
 
     // List of base edges in the graph. Require to compute transitive
     // closure.
@@ -39,7 +36,6 @@ impl<T: Eq + Hash> Default for TransitiveRelation<T> {
     fn default() -> Self {
         TransitiveRelation {
             elements: Default::default(),
-            map: Default::default(),
             edges: Default::default(),
             closure: Default::default(),
         }
@@ -65,20 +61,16 @@ impl<T: Clone + Debug + Eq + Hash> TransitiveRelation<T> {
     }
 
     fn index(&self, a: &T) -> Option<Index> {
-        self.map.get(a).cloned()
+        self.elements.get_index_of(a).map(Index)
     }
 
     fn add_index(&mut self, a: T) -> Index {
-        let &mut TransitiveRelation { ref mut elements, ref mut closure, ref mut map, .. } = self;
-
-        *map.entry(a.clone()).or_insert_with(|| {
-            elements.push(a);
-
+        let (index, added) = self.elements.insert_full(a);
+        if added {
             // if we changed the dimensions, clear the cache
-            *closure.get_mut() = None;
-
-            Index(elements.len() - 1)
-        })
+            *self.closure.get_mut() = None;
+        }
+        Index(index)
     }
 
     /// Applies the (partial) function to each edge and returns a new
@@ -430,14 +422,11 @@ where
 {
     fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
         d.read_struct("TransitiveRelation", 2, |d| {
-            let elements: Vec<T> = d.read_struct_field("elements", 0, |d| Decodable::decode(d))?;
-            let edges = d.read_struct_field("edges", 1, |d| Decodable::decode(d))?;
-            let map = elements
-                .iter()
-                .enumerate()
-                .map(|(index, elem)| (elem.clone(), Index(index)))
-                .collect();
-            Ok(TransitiveRelation { elements, edges, map, closure: Lock::new(None) })
+            Ok(TransitiveRelation {
+                elements: d.read_struct_field("elements", 0, |d| Decodable::decode(d))?,
+                edges: d.read_struct_field("edges", 1, |d| Decodable::decode(d))?,
+                closure: Lock::new(None),
+            })
         })
     }
 }
@@ -452,8 +441,6 @@ where
         let TransitiveRelation {
             ref elements,
             ref edges,
-            // "map" is just a copy of elements vec
-            map: _,
             // "closure" is just a copy of the data above
             closure: _,
         } = *self;
diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs
index 7af640c109e..cc954cee907 100644
--- a/src/librustc_driver/lib.rs
+++ b/src/librustc_driver/lib.rs
@@ -677,7 +677,7 @@ impl RustcDefaultCalls {
                     let t_outputs = rustc_interface::util::build_output_filenames(
                         input, odir, ofile, attrs, sess,
                     );
-                    let id = rustc_session::output::find_crate_name(Some(sess), attrs, input);
+                    let id = rustc_session::output::find_crate_name(sess, attrs, input);
                     if *req == PrintRequest::CrateName {
                         println!("{}", id);
                         continue;
diff --git a/src/librustc_error_codes/error_codes/E0271.md b/src/librustc_error_codes/error_codes/E0271.md
index 31334069ed8..ddd245b1a2b 100644
--- a/src/librustc_error_codes/error_codes/E0271.md
+++ b/src/librustc_error_codes/error_codes/E0271.md
@@ -6,25 +6,6 @@ Erroneous code example:
 trait Trait { type AssociatedType; }
 
 fn foo<T>(t: T) where T: Trait<AssociatedType=u32> {
-    println!("in foo");
-}
-
-impl Trait for i8 { type AssociatedType = &'static str; }
-
-foo(3_i8);
-```
-
-This is because of a type mismatch between the associated type of some
-trait (e.g., `T::Bar`, where `T` implements `trait Quux { type Bar; }`)
-and another type `U` that is required to be equal to `T::Bar`, but is not.
-Examples follow.
-
-Here is that same example again, with some explanatory comments:
-
-```compile_fail,E0271
-trait Trait { type AssociatedType; }
-
-fn foo<T>(t: T) where T: Trait<AssociatedType=u32> {
 //                    ~~~~~~~~ ~~~~~~~~~~~~~~~~~~
 //                        |            |
 //         This says `foo` can         |
@@ -56,11 +37,9 @@ foo(3_i8);
 // therefore the type-checker complains with this error code.
 ```
 
-To avoid those issues, you have to make the types match correctly.
-So we can fix the previous examples like this:
-
+The issue can be resolved by changing the associated type:
+1) in the `foo` implementation:
 ```
-// Basic Example:
 trait Trait { type AssociatedType; }
 
 fn foo<T>(t: T) where T: Trait<AssociatedType = &'static str> {
@@ -70,13 +49,17 @@ fn foo<T>(t: T) where T: Trait<AssociatedType = &'static str> {
 impl Trait for i8 { type AssociatedType = &'static str; }
 
 foo(3_i8);
+```
 
-// For-Loop Example:
-let vs = vec![1, 2, 3, 4];
-for v in &vs {
-    match v {
-        &1 => {}
-        _ => {}
-    }
+2) in the `Trait` implementation for `i8`:
+```
+trait Trait { type AssociatedType; }
+
+fn foo<T>(t: T) where T: Trait<AssociatedType = u32> {
+    println!("in foo");
 }
+
+impl Trait for i8 { type AssociatedType = u32; }
+
+foo(3_i8);
 ```
diff --git a/src/librustc_error_codes/error_codes/E0502.md b/src/librustc_error_codes/error_codes/E0502.md
index b90c59f5807..dc3ffdfddd9 100644
--- a/src/librustc_error_codes/error_codes/E0502.md
+++ b/src/librustc_error_codes/error_codes/E0502.md
@@ -5,7 +5,7 @@ Erroneous code example:
 ```compile_fail,E0502
 fn bar(x: &mut i32) {}
 fn foo(a: &mut i32) {
-    let ref y = a; // a is borrowed as immutable.
+    let y = &a; // a is borrowed as immutable.
     bar(a); // error: cannot borrow `*a` as mutable because `a` is also borrowed
             //        as immutable
     println!("{}", y);
@@ -19,7 +19,7 @@ variable before trying to access it mutably:
 fn bar(x: &mut i32) {}
 fn foo(a: &mut i32) {
     bar(a);
-    let ref y = a; // ok!
+    let y = &a; // ok!
     println!("{}", y);
 }
 ```
diff --git a/src/librustc_error_codes/error_codes/E0746.md b/src/librustc_error_codes/error_codes/E0746.md
index 305667e58f8..90755d47f67 100644
--- a/src/librustc_error_codes/error_codes/E0746.md
+++ b/src/librustc_error_codes/error_codes/E0746.md
@@ -1,4 +1,4 @@
-Return types cannot be `dyn Trait`s as they must be `Sized`.
+An unboxed trait object was used as a return value.
 
 Erroneous code example:
 
@@ -13,11 +13,13 @@ impl T for S {
 
 // Having the trait `T` as return type is invalid because
 // unboxed trait objects do not have a statically known size:
-fn foo() -> dyn T {
+fn foo() -> dyn T { // error!
     S(42)
 }
 ```
 
+Return types cannot be `dyn Trait`s as they must be `Sized`.
+
 To avoid the error there are a couple of options.
 
 If there is a single type involved, you can use [`impl Trait`]:
@@ -32,7 +34,7 @@ If there is a single type involved, you can use [`impl Trait`]:
 # }
 // The compiler will select `S(usize)` as the materialized return type of this
 // function, but callers will only know that the return type implements `T`.
-fn foo() -> impl T {
+fn foo() -> impl T { // ok!
     S(42)
 }
 ```
@@ -57,7 +59,7 @@ impl T for O {
 
 // This now returns a "trait object" and callers are only be able to access
 // associated items from `T`.
-fn foo(x: bool) -> Box<dyn T> {
+fn foo(x: bool) -> Box<dyn T> { // ok!
     if x {
         Box::new(S(42))
     } else {
diff --git a/src/librustc_error_codes/error_codes/E0747.md b/src/librustc_error_codes/error_codes/E0747.md
index df1afbfef46..caf7e0fba07 100644
--- a/src/librustc_error_codes/error_codes/E0747.md
+++ b/src/librustc_error_codes/error_codes/E0747.md
@@ -1,4 +1,4 @@
-Generic arguments must be provided in the same order as the corresponding
+Generic arguments were not provided in the same order as the corresponding
 generic parameters are declared.
 
 Erroneous code example:
@@ -11,7 +11,7 @@ type X = S<(), 'static>; // error: the type argument is provided before the
 ```
 
 The argument order should be changed to match the parameter declaration
-order, as in the following.
+order, as in the following:
 
 ```
 struct S<'a, T>(&'a T);
diff --git a/src/librustc_error_codes/error_codes/E0749.md b/src/librustc_error_codes/error_codes/E0749.md
index 9eb8ee4e3fd..7a1a745b53c 100644
--- a/src/librustc_error_codes/error_codes/E0749.md
+++ b/src/librustc_error_codes/error_codes/E0749.md
@@ -1,4 +1,19 @@
-Negative impls are not allowed to have any items. Negative impls
-declare that a trait is **not** implemented (and never will be) and
-hence there is no need to specify the values for trait methods or
-other items.
+An item was added on a negative impl.
+
+Erroneous code example:
+
+```compile_fail,E0749
+# #![feature(negative_impls)]
+trait MyTrait {
+    type Foo;
+}
+
+impl !MyTrait for u32 {
+    type Foo = i32; // error!
+}
+# fn main() {}
+```
+
+Negative impls are not allowed to have any items. Negative impls declare that a
+trait is **not** implemented (and never will be) and hence there is no need to
+specify the values for trait methods or other items.
diff --git a/src/librustc_error_codes/error_codes/E0750.md b/src/librustc_error_codes/error_codes/E0750.md
index e0cf56f716f..905e852f8d5 100644
--- a/src/librustc_error_codes/error_codes/E0750.md
+++ b/src/librustc_error_codes/error_codes/E0750.md
@@ -1,4 +1,18 @@
-Negative impls cannot be default impls. A default impl supplies
-default values for the items within to be used by other impls, whereas
-a negative impl declares that there are no other impls. These don't
-make sense to combine.
+A negative impl was made default impl.
+
+Erroneous code example:
+
+```compile_fail,E0750
+# #![feature(negative_impls)]
+# #![feature(specialization)]
+trait MyTrait {
+    type Foo;
+}
+
+default impl !MyTrait for u32 {} // error!
+# fn main() {}
+```
+
+Negative impls cannot be default impls. A default impl supplies default values
+for the items within to be used by other impls, whereas a negative impl declares
+that there are no other impls. Combining it does not make sense.
diff --git a/src/librustc_expand/base.rs b/src/librustc_expand/base.rs
index daa75d42324..5a36df8299c 100644
--- a/src/librustc_expand/base.rs
+++ b/src/librustc_expand/base.rs
@@ -12,7 +12,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{self, Lrc};
 use rustc_errors::{DiagnosticBuilder, ErrorReported};
 use rustc_parse::{self, nt_to_tokenstream, parser, MACRO_ARGUMENTS};
-use rustc_session::{parse::ParseSess, Limit};
+use rustc_session::{parse::ParseSess, Limit, Session};
 use rustc_span::def_id::{DefId, LOCAL_CRATE};
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::{AstPass, ExpnData, ExpnId, ExpnKind};
@@ -790,7 +790,7 @@ impl SyntaxExtension {
     /// Constructs a syntax extension with the given properties
     /// and other properties converted from attributes.
     pub fn new(
-        sess: &ParseSess,
+        sess: &Session,
         kind: SyntaxExtensionKind,
         span: Span,
         helper_attrs: Vec<Symbol>,
@@ -798,27 +798,29 @@ impl SyntaxExtension {
         name: Symbol,
         attrs: &[ast::Attribute],
     ) -> SyntaxExtension {
-        let allow_internal_unstable = attr::allow_internal_unstable(&attrs, &sess.span_diagnostic)
+        let allow_internal_unstable = attr::allow_internal_unstable(sess, &attrs)
             .map(|features| features.collect::<Vec<Symbol>>().into());
 
         let mut local_inner_macros = false;
-        if let Some(macro_export) = attr::find_by_name(attrs, sym::macro_export) {
+        if let Some(macro_export) = sess.find_by_name(attrs, sym::macro_export) {
             if let Some(l) = macro_export.meta_item_list() {
                 local_inner_macros = attr::list_contains_name(&l, sym::local_inner_macros);
             }
         }
 
-        let is_builtin = attr::contains_name(attrs, sym::rustc_builtin_macro);
+        let is_builtin = sess.contains_name(attrs, sym::rustc_builtin_macro);
         let (stability, const_stability) = attr::find_stability(&sess, attrs, span);
         if const_stability.is_some() {
-            sess.span_diagnostic.span_err(span, "macros cannot have const stability attributes");
+            sess.parse_sess
+                .span_diagnostic
+                .span_err(span, "macros cannot have const stability attributes");
         }
 
         SyntaxExtension {
             kind,
             span,
             allow_internal_unstable,
-            allow_internal_unsafe: attr::contains_name(attrs, sym::allow_internal_unsafe),
+            allow_internal_unsafe: sess.contains_name(attrs, sym::allow_internal_unsafe),
             local_inner_macros,
             stability,
             deprecation: attr::find_deprecation(&sess, attrs, span),
@@ -941,7 +943,7 @@ pub struct ExpansionData {
 /// when a macro expansion occurs, the resulting nodes have the `backtrace()
 /// -> expn_data` of their expansion context stored into their span.
 pub struct ExtCtxt<'a> {
-    pub parse_sess: &'a ParseSess,
+    pub sess: &'a Session,
     pub ecfg: expand::ExpansionConfig<'a>,
     pub reduced_recursion_limit: Option<Limit>,
     pub root_path: PathBuf,
@@ -954,13 +956,13 @@ pub struct ExtCtxt<'a> {
 
 impl<'a> ExtCtxt<'a> {
     pub fn new(
-        parse_sess: &'a ParseSess,
+        sess: &'a Session,
         ecfg: expand::ExpansionConfig<'a>,
         resolver: &'a mut dyn ResolverExpand,
         extern_mod_loaded: Option<&'a dyn Fn(&ast::Crate)>,
     ) -> ExtCtxt<'a> {
         ExtCtxt {
-            parse_sess,
+            sess,
             ecfg,
             reduced_recursion_limit: None,
             resolver,
@@ -988,13 +990,13 @@ impl<'a> ExtCtxt<'a> {
         expand::MacroExpander::new(self, true)
     }
     pub fn new_parser_from_tts(&self, stream: TokenStream) -> parser::Parser<'a> {
-        rustc_parse::stream_to_parser(self.parse_sess, stream, MACRO_ARGUMENTS)
+        rustc_parse::stream_to_parser(&self.sess.parse_sess, stream, MACRO_ARGUMENTS)
     }
     pub fn source_map(&self) -> &'a SourceMap {
-        self.parse_sess.source_map()
+        self.sess.parse_sess.source_map()
     }
     pub fn parse_sess(&self) -> &'a ParseSess {
-        self.parse_sess
+        &self.sess.parse_sess
     }
     pub fn call_site(&self) -> Span {
         self.current_expansion.id.expn_data().call_site
@@ -1026,7 +1028,7 @@ impl<'a> ExtCtxt<'a> {
     }
 
     pub fn struct_span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> DiagnosticBuilder<'a> {
-        self.parse_sess.span_diagnostic.struct_span_err(sp, msg)
+        self.sess.parse_sess.span_diagnostic.struct_span_err(sp, msg)
     }
 
     /// Emit `msg` attached to `sp`, without immediately stopping
@@ -1035,17 +1037,17 @@ impl<'a> ExtCtxt<'a> {
     /// Compilation will be stopped in the near future (at the end of
     /// the macro expansion phase).
     pub fn span_err<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.parse_sess.span_diagnostic.span_err(sp, msg);
+        self.sess.parse_sess.span_diagnostic.span_err(sp, msg);
     }
     pub fn span_warn<S: Into<MultiSpan>>(&self, sp: S, msg: &str) {
-        self.parse_sess.span_diagnostic.span_warn(sp, msg);
+        self.sess.parse_sess.span_diagnostic.span_warn(sp, msg);
     }
     pub fn span_bug<S: Into<MultiSpan>>(&self, sp: S, msg: &str) -> ! {
-        self.parse_sess.span_diagnostic.span_bug(sp, msg);
+        self.sess.parse_sess.span_diagnostic.span_bug(sp, msg);
     }
     pub fn trace_macros_diag(&mut self) {
         for (sp, notes) in self.expansions.iter() {
-            let mut db = self.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro");
+            let mut db = self.sess.parse_sess.span_diagnostic.span_note_diag(*sp, "trace_macro");
             for note in notes {
                 db.note(note);
             }
@@ -1055,7 +1057,7 @@ impl<'a> ExtCtxt<'a> {
         self.expansions.clear();
     }
     pub fn bug(&self, msg: &str) -> ! {
-        self.parse_sess.span_diagnostic.bug(msg);
+        self.sess.parse_sess.span_diagnostic.bug(msg);
     }
     pub fn trace_macros(&self) -> bool {
         self.ecfg.trace_mac
diff --git a/src/librustc_expand/config.rs b/src/librustc_expand/config.rs
index d79dabb5092..f6ddcd35068 100644
--- a/src/librustc_expand/config.rs
+++ b/src/librustc_expand/config.rs
@@ -13,7 +13,8 @@ use rustc_feature::{
     ACCEPTED_FEATURES, ACTIVE_FEATURES, REMOVED_FEATURES, STABLE_REMOVED_FEATURES,
 };
 use rustc_parse::{parse_in, validate_attr};
-use rustc_session::parse::{feature_err, ParseSess};
+use rustc_session::parse::feature_err;
+use rustc_session::Session;
 use rustc_span::edition::{Edition, ALL_EDITIONS};
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::{Span, DUMMY_SP};
@@ -22,15 +23,14 @@ use smallvec::SmallVec;
 
 /// A folder that strips out items that do not belong in the current configuration.
 pub struct StripUnconfigured<'a> {
-    pub sess: &'a ParseSess,
+    pub sess: &'a Session,
     pub features: Option<&'a Features>,
 }
 
 fn get_features(
+    sess: &Session,
     span_handler: &Handler,
     krate_attrs: &[ast::Attribute],
-    crate_edition: Edition,
-    allow_features: &Option<Vec<String>>,
 ) -> Features {
     fn feature_removed(span_handler: &Handler, span: Span, reason: Option<&str>) {
         let mut err = struct_span_err!(span_handler, span, E0557, "feature has been removed");
@@ -53,6 +53,7 @@ fn get_features(
 
     let mut features = Features::default();
     let mut edition_enabled_features = FxHashMap::default();
+    let crate_edition = sess.edition();
 
     for &edition in ALL_EDITIONS {
         if edition <= crate_edition {
@@ -70,7 +71,7 @@ fn get_features(
     // Process the edition umbrella feature-gates first, to ensure
     // `edition_enabled_features` is completed before it's queried.
     for attr in krate_attrs {
-        if !attr.check_name(sym::feature) {
+        if !sess.check_name(attr, sym::feature) {
             continue;
         }
 
@@ -103,7 +104,7 @@ fn get_features(
     }
 
     for attr in krate_attrs {
-        if !attr.check_name(sym::feature) {
+        if !sess.check_name(attr, sym::feature) {
             continue;
         }
 
@@ -165,7 +166,7 @@ fn get_features(
                 continue;
             }
 
-            if let Some(allowed) = allow_features.as_ref() {
+            if let Some(allowed) = sess.opts.debugging_opts.allow_features.as_ref() {
                 if allowed.iter().find(|&f| name.as_str() == *f).is_none() {
                     struct_span_err!(
                         span_handler,
@@ -193,16 +194,11 @@ fn get_features(
 }
 
 // `cfg_attr`-process the crate's attributes and compute the crate's features.
-pub fn features(
-    mut krate: ast::Crate,
-    sess: &ParseSess,
-    edition: Edition,
-    allow_features: &Option<Vec<String>>,
-) -> (ast::Crate, Features) {
+pub fn features(sess: &Session, mut krate: ast::Crate) -> (ast::Crate, Features) {
     let mut strip_unconfigured = StripUnconfigured { sess, features: None };
 
     let unconfigured_attrs = krate.attrs.clone();
-    let diag = &sess.span_diagnostic;
+    let diag = &sess.parse_sess.span_diagnostic;
     let err_count = diag.err_count();
     let features = match strip_unconfigured.configure(krate.attrs) {
         None => {
@@ -213,7 +209,7 @@ pub fn features(
         }
         Some(attrs) => {
             krate.attrs = attrs;
-            let features = get_features(diag, &krate.attrs, edition, allow_features);
+            let features = get_features(sess, diag, &krate.attrs);
             if err_count == diag.err_count() {
                 // Avoid reconfiguring malformed `cfg_attr`s.
                 strip_unconfigured.features = Some(&features);
@@ -281,9 +277,9 @@ impl<'a> StripUnconfigured<'a> {
         }
 
         // At this point we know the attribute is considered used.
-        attr::mark_used(&attr);
+        self.sess.mark_attr_used(&attr);
 
-        if !attr::cfg_matches(&cfg_predicate, self.sess, self.features) {
+        if !attr::cfg_matches(&cfg_predicate, &self.sess.parse_sess, self.features) {
             return vec![];
         }
 
@@ -303,8 +299,10 @@ impl<'a> StripUnconfigured<'a> {
         match attr.get_normal_item().args {
             ast::MacArgs::Delimited(dspan, delim, ref tts) if !tts.is_empty() => {
                 let msg = "wrong `cfg_attr` delimiters";
-                validate_attr::check_meta_bad_delim(self.sess, dspan, delim, msg);
-                match parse_in(self.sess, tts.clone(), "`cfg_attr` input", |p| p.parse_cfg_attr()) {
+                validate_attr::check_meta_bad_delim(&self.sess.parse_sess, dspan, delim, msg);
+                match parse_in(&self.sess.parse_sess, tts.clone(), "`cfg_attr` input", |p| {
+                    p.parse_cfg_attr()
+                }) {
                     Ok(r) => return Some(r),
                     Err(mut e) => {
                         e.help(&format!("the valid syntax is `{}`", CFG_ATTR_GRAMMAR_HELP))
@@ -320,6 +318,7 @@ impl<'a> StripUnconfigured<'a> {
 
     fn error_malformed_cfg_attr_missing(&self, span: Span) {
         self.sess
+            .parse_sess
             .span_diagnostic
             .struct_span_err(span, "malformed `cfg_attr` attribute input")
             .span_suggestion(
@@ -335,10 +334,10 @@ impl<'a> StripUnconfigured<'a> {
     /// Determines if a node with the given attributes should be included in this configuration.
     pub fn in_cfg(&self, attrs: &[Attribute]) -> bool {
         attrs.iter().all(|attr| {
-            if !is_cfg(attr) {
+            if !is_cfg(self.sess, attr) {
                 return true;
             }
-            let meta_item = match validate_attr::parse_meta(self.sess, attr) {
+            let meta_item = match validate_attr::parse_meta(&self.sess.parse_sess, attr) {
                 Ok(meta_item) => meta_item,
                 Err(mut err) => {
                     err.emit();
@@ -346,7 +345,7 @@ impl<'a> StripUnconfigured<'a> {
                 }
             };
             let error = |span, msg, suggestion: &str| {
-                let mut err = self.sess.span_diagnostic.struct_span_err(span, msg);
+                let mut err = self.sess.parse_sess.span_diagnostic.struct_span_err(span, msg);
                 if !suggestion.is_empty() {
                     err.span_suggestion(
                         span,
@@ -364,7 +363,9 @@ impl<'a> StripUnconfigured<'a> {
                 Some([]) => error(span, "`cfg` predicate is not specified", ""),
                 Some([_, .., l]) => error(l.span(), "multiple `cfg` predicates are specified", ""),
                 Some([single]) => match single.meta_item() {
-                    Some(meta_item) => attr::cfg_matches(meta_item, self.sess, self.features),
+                    Some(meta_item) => {
+                        attr::cfg_matches(meta_item, &self.sess.parse_sess, self.features)
+                    }
                     None => error(single.span(), "`cfg` predicate key cannot be a literal", ""),
                 },
             }
@@ -383,7 +384,7 @@ impl<'a> StripUnconfigured<'a> {
     pub fn maybe_emit_expr_attr_err(&self, attr: &Attribute) {
         if !self.features.map(|features| features.stmt_expr_attributes).unwrap_or(true) {
             let mut err = feature_err(
-                self.sess,
+                &self.sess.parse_sess,
                 sym::stmt_expr_attributes,
                 attr.span,
                 "attributes on expressions are experimental",
@@ -452,9 +453,9 @@ impl<'a> StripUnconfigured<'a> {
         //
         // N.B., this is intentionally not part of the visit_expr() function
         //     in order for filter_map_expr() to be able to avoid this check
-        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(a)) {
+        if let Some(attr) = expr.attrs().iter().find(|a| is_cfg(self.sess, a)) {
             let msg = "removing an expression is not supported in this position";
-            self.sess.span_diagnostic.span_err(attr.span, msg);
+            self.sess.parse_sess.span_diagnostic.span_err(attr.span, msg);
         }
 
         self.process_cfg_attrs(expr)
@@ -527,6 +528,6 @@ impl<'a> MutVisitor for StripUnconfigured<'a> {
     }
 }
 
-fn is_cfg(attr: &Attribute) -> bool {
-    attr.check_name(sym::cfg)
+fn is_cfg(sess: &Session, attr: &Attribute) -> bool {
+    sess.check_name(attr, sym::cfg)
 }
diff --git a/src/librustc_expand/expand.rs b/src/librustc_expand/expand.rs
index 0cc340c205a..8da56dc67e5 100644
--- a/src/librustc_expand/expand.rs
+++ b/src/librustc_expand/expand.rs
@@ -527,7 +527,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     }
 
     fn error_derive_forbidden_on_non_adt(&self, derives: &[Path], item: &Annotatable) {
-        let attr = attr::find_by_name(item.attrs(), sym::derive);
+        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
@@ -566,10 +566,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
 
         let invocations = {
             let mut collector = InvocationCollector {
-                cfg: StripUnconfigured {
-                    sess: self.cx.parse_sess,
-                    features: self.cx.ecfg.features,
-                },
+                cfg: StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features },
                 cx: self.cx,
                 invocations: Vec::new(),
                 monotonic: self.monotonic,
@@ -589,8 +586,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     }
 
     fn fully_configure(&mut self, item: Annotatable) -> Annotatable {
-        let mut cfg =
-            StripUnconfigured { sess: self.cx.parse_sess, features: self.cx.ecfg.features };
+        let mut cfg = StripUnconfigured { sess: &self.cx.sess, features: self.cx.ecfg.features };
         // Since the item itself has already been configured by the InvocationCollector,
         // we know that fold result vector will contain exactly one element
         match item {
@@ -706,7 +702,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                 SyntaxExtensionKind::Attr(expander) => {
                     self.gate_proc_macro_input(&item);
                     self.gate_proc_macro_attr_item(span, &item);
-                    let tokens = item.into_tokens(self.cx.parse_sess);
+                    let tokens = item.into_tokens(&self.cx.sess.parse_sess);
                     let attr_item = attr.unwrap_normal_item();
                     if let MacArgs::Eq(..) = attr_item.args {
                         self.cx.span_err(span, "key-value macro attributes are not supported");
@@ -719,7 +715,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     self.parse_ast_fragment(tok_result, fragment_kind, &attr_item.path, span)
                 }
                 SyntaxExtensionKind::LegacyAttr(expander) => {
-                    match validate_attr::parse_meta(self.cx.parse_sess, &attr) {
+                    match validate_attr::parse_meta(&self.cx.sess.parse_sess, &attr) {
                         Ok(meta) => {
                             let items = match expander.expand(self.cx, span, &meta, item) {
                                 ExpandResult::Ready(items) => items,
@@ -748,9 +744,9 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                     }
                 }
                 SyntaxExtensionKind::NonMacroAttr { mark_used } => {
-                    attr::mark_known(&attr);
+                    self.cx.sess.mark_attr_known(&attr);
                     if *mark_used {
-                        attr::mark_used(&attr);
+                        self.cx.sess.mark_attr_used(&attr);
                     }
                     item.visit_attrs(|attrs| attrs.push(attr));
                     fragment_kind.expect_from_annotatables(iter::once(item))
@@ -808,7 +804,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             return;
         }
         feature_err(
-            self.cx.parse_sess,
+            &self.cx.sess.parse_sess,
             sym::proc_macro_hygiene,
             span,
             &format!("custom attributes cannot be applied to {}", kind),
@@ -843,7 +839,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
         }
 
         if !self.cx.ecfg.proc_macro_hygiene() {
-            annotatable.visit_with(&mut GateProcMacroInput { parse_sess: self.cx.parse_sess });
+            annotatable
+                .visit_with(&mut GateProcMacroInput { parse_sess: &self.cx.sess.parse_sess });
         }
     }
 
@@ -989,7 +986,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                 ..ExpnData::default(
                     ExpnKind::Macro(MacroKind::Attr, sym::derive),
                     item.span(),
-                    self.cx.parse_sess.edition,
+                    self.cx.sess.parse_sess.edition,
                     None,
                 )
             }),
@@ -1049,7 +1046,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                 if a.has_name(sym::derive) {
                     *after_derive = true;
                 }
-                !attr::is_known(a) && !is_builtin_attr(a)
+                !self.cx.sess.is_attr_known(a) && !is_builtin_attr(a)
             })
             .map(|i| attrs.remove(i));
         if let Some(attr) = &attr {
@@ -1058,7 +1055,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
                 && !attr.has_name(sym::test)
             {
                 feature_err(
-                    &self.cx.parse_sess,
+                    &self.cx.sess.parse_sess,
                     sym::custom_inner_attributes,
                     attr.span,
                     "non-builtin inner attributes are unstable",
@@ -1109,8 +1106,8 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
     fn check_attributes(&mut self, attrs: &[ast::Attribute]) {
         let features = self.cx.ecfg.features.unwrap();
         for attr in attrs.iter() {
-            rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.parse_sess, features);
-            validate_attr::check_meta(self.cx.parse_sess, attr);
+            rustc_ast_passes::feature_gate::check_attribute(attr, self.cx.sess, features);
+            validate_attr::check_meta(&self.cx.sess.parse_sess, attr);
 
             // macros are expanded before any lint passes so this warning has to be hardcoded
             if attr.has_name(sym::derive) {
@@ -1123,7 +1120,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
             }
 
             if attr.doc_str().is_some() {
-                self.cx.parse_sess.buffer_lint_with_diagnostic(
+                self.cx.sess.parse_sess.buffer_lint_with_diagnostic(
                     &UNUSED_DOC_COMMENTS,
                     attr.span,
                     ast::CRATE_NODE_ID,
@@ -1429,7 +1426,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 })
             }
             ast::ItemKind::Mod(ref mut old_mod @ ast::Mod { .. }) if ident != Ident::invalid() => {
-                let sess = self.cx.parse_sess;
+                let sess = &self.cx.sess.parse_sess;
                 let orig_ownership = self.cx.current_expansion.directory_ownership;
                 let mut module = (*self.cx.current_expansion.module).clone();
 
@@ -1438,11 +1435,11 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 let Directory { ownership, path } = if old_mod.inline {
                     // Inline `mod foo { ... }`, but we still need to push directories.
                     item.attrs = attrs;
-                    push_directory(ident, &item.attrs, dir)
+                    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(sess, ident, span, dir, &mut attrs, pushed);
+                        parse_external_mod(&self.cx.sess, ident, span, dir, &mut attrs, pushed);
 
                     let krate = ast::Crate {
                         span: new_mod.inner,
@@ -1639,7 +1636,7 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
     fn visit_attribute(&mut self, at: &mut ast::Attribute) {
         // turn `#[doc(include="filename")]` attributes into `#[doc(include(file="filename",
         // contents="file contents")]` attributes
-        if !at.check_name(sym::doc) {
+        if !self.cx.sess.check_name(at, sym::doc) {
             return noop_visit_attribute(at, self);
         }
 
@@ -1660,9 +1657,9 @@ impl<'a, 'b> MutVisitor for InvocationCollector<'a, 'b> {
                 }
 
                 if let Some(file) = it.value_str() {
-                    let err_count = self.cx.parse_sess.span_diagnostic.err_count();
+                    let err_count = self.cx.sess.parse_sess.span_diagnostic.err_count();
                     self.check_attributes(slice::from_ref(at));
-                    if self.cx.parse_sess.span_diagnostic.err_count() > err_count {
+                    if self.cx.sess.parse_sess.span_diagnostic.err_count() > err_count {
                         // avoid loading the file if they haven't enabled the feature
                         return noop_visit_attribute(at, self);
                     }
diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs
index 74d4023b410..15b2c14a257 100644
--- a/src/librustc_expand/mbe/macro_rules.rs
+++ b/src/librustc_expand/mbe/macro_rules.rs
@@ -19,6 +19,7 @@ use rustc_errors::{Applicability, DiagnosticBuilder};
 use rustc_feature::Features;
 use rustc_parse::parser::Parser;
 use rustc_session::parse::ParseSess;
+use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::hygiene::Transparency;
 use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent};
@@ -217,7 +218,7 @@ fn generic_extension<'cx>(
     lhses: &[mbe::TokenTree],
     rhses: &[mbe::TokenTree],
 ) -> Box<dyn MacResult + 'cx> {
-    let sess = cx.parse_sess;
+    let sess = &cx.sess.parse_sess;
 
     if cx.trace_macros() {
         let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
@@ -378,7 +379,7 @@ fn generic_extension<'cx>(
 
 /// Converts a macro item into a syntax extension.
 pub fn compile_declarative_macro(
-    sess: &ParseSess,
+    sess: &Session,
     features: &Features,
     def: &ast::Item,
     edition: Edition,
@@ -396,7 +397,7 @@ pub fn compile_declarative_macro(
         )
     };
 
-    let diag = &sess.span_diagnostic;
+    let diag = &sess.parse_sess.span_diagnostic;
     let lhs_nm = Ident::new(sym::lhs, def.span);
     let rhs_nm = Ident::new(sym::rhs, def.span);
     let tt_spec = Some(NonterminalKind::TT);
@@ -444,17 +445,20 @@ pub fn compile_declarative_macro(
         ),
     ];
 
-    let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS);
+    let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS);
     let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
         Success(m) => m,
         Failure(token, msg) => {
             let s = parse_failure_msg(&token);
             let sp = token.span.substitute_dummy(def.span);
-            sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
+            sess.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
             return mk_syn_ext(Box::new(macro_rules_dummy_expander));
         }
         Error(sp, msg) => {
-            sess.span_diagnostic.struct_span_err(sp.substitute_dummy(def.span), &msg).emit();
+            sess.parse_sess
+                .span_diagnostic
+                .struct_span_err(sp.substitute_dummy(def.span), &msg)
+                .emit();
             return mk_syn_ext(Box::new(macro_rules_dummy_expander));
         }
         ErrorReported => {
@@ -471,17 +475,18 @@ pub fn compile_declarative_macro(
             .map(|m| {
                 if let MatchedNonterminal(ref nt) = *m {
                     if let NtTT(ref tt) = **nt {
-                        let tt = mbe::quoted::parse(tt.clone().into(), true, sess, def.id)
-                            .pop()
-                            .unwrap();
-                        valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt);
+                        let tt =
+                            mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id)
+                                .pop()
+                                .unwrap();
+                        valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt);
                         return tt;
                     }
                 }
-                sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
+                sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
             })
             .collect::<Vec<mbe::TokenTree>>(),
-        _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
+        _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
     };
 
     let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
@@ -490,29 +495,34 @@ pub fn compile_declarative_macro(
             .map(|m| {
                 if let MatchedNonterminal(ref nt) = *m {
                     if let NtTT(ref tt) = **nt {
-                        return mbe::quoted::parse(tt.clone().into(), false, sess, def.id)
-                            .pop()
-                            .unwrap();
+                        return mbe::quoted::parse(
+                            tt.clone().into(),
+                            false,
+                            &sess.parse_sess,
+                            def.id,
+                        )
+                        .pop()
+                        .unwrap();
                     }
                 }
-                sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
+                sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
             })
             .collect::<Vec<mbe::TokenTree>>(),
-        _ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
+        _ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
     };
 
     for rhs in &rhses {
-        valid &= check_rhs(sess, rhs);
+        valid &= check_rhs(&sess.parse_sess, rhs);
     }
 
     // don't abort iteration early, so that errors for multiple lhses can be reported
     for lhs in &lhses {
-        valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
+        valid &= check_lhs_no_empty_seq(&sess.parse_sess, slice::from_ref(lhs));
     }
 
-    valid &= macro_check::check_meta_variables(sess, def.id, def.span, &lhses, &rhses);
+    valid &= macro_check::check_meta_variables(&sess.parse_sess, def.id, def.span, &lhses, &rhses);
 
-    let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
+    let (transparency, transparency_error) = attr::find_transparency(sess, &def.attrs, macro_rules);
     match transparency_error {
         Some(TransparencyError::UnknownTransparency(value, span)) => {
             diag.span_err(span, &format!("unknown macro transparency: `{}`", value))
diff --git a/src/librustc_expand/module.rs b/src/librustc_expand/module.rs
index 535c1dbad04..12fe49ed585 100644
--- a/src/librustc_expand/module.rs
+++ b/src/librustc_expand/module.rs
@@ -1,8 +1,9 @@
 use rustc_ast::ast::{Attribute, Mod};
-use rustc_ast::{attr, token};
+use rustc_ast::token;
 use rustc_errors::{struct_span_err, PResult};
 use rustc_parse::new_parser_from_file;
 use rustc_session::parse::ParseSess;
+use rustc_session::Session;
 use rustc_span::source_map::{FileName, Span};
 use rustc_span::symbol::{sym, Ident};
 
@@ -39,7 +40,7 @@ pub struct ModulePathSuccess {
 }
 
 crate fn parse_external_mod(
-    sess: &ParseSess,
+    sess: &Session,
     id: Ident,
     span: Span, // The span to blame on errors.
     Directory { mut ownership, path }: Directory,
@@ -53,14 +54,15 @@ crate fn parse_external_mod(
         ownership = mp.ownership;
 
         // Ensure file paths are acyclic.
-        let mut included_mod_stack = sess.included_mod_stack.borrow_mut();
-        error_on_circular_module(sess, span, &mp.path, &included_mod_stack)?;
+        let mut included_mod_stack = sess.parse_sess.included_mod_stack.borrow_mut();
+        error_on_circular_module(&sess.parse_sess, span, &mp.path, &included_mod_stack)?;
         included_mod_stack.push(mp.path.clone());
         *pop_mod_stack = true; // We have pushed, so notify caller.
         drop(included_mod_stack);
 
         // Actually parse the external file as a module.
-        let mut module = new_parser_from_file(sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
+        let mut module =
+            new_parser_from_file(&sess.parse_sess, &mp.path, Some(span)).parse_mod(&token::Eof)?;
         module.0.inline = false;
         module
     };
@@ -98,11 +100,12 @@ fn error_on_circular_module<'a>(
 }
 
 crate fn push_directory(
+    sess: &Session,
     id: Ident,
     attrs: &[Attribute],
     Directory { mut ownership, mut path }: Directory,
 ) -> Directory {
-    if let Some(filename) = attr::first_attr_value_str_by_name(attrs, sym::path) {
+    if let Some(filename) = sess.first_attr_value_str_by_name(attrs, sym::path) {
         path.push(&*filename.as_str());
         ownership = DirectoryOwnership::Owned { relative: None };
     } else {
@@ -124,14 +127,14 @@ crate fn push_directory(
 }
 
 fn submod_path<'a>(
-    sess: &'a ParseSess,
+    sess: &'a Session,
     id: Ident,
     span: Span,
     attrs: &[Attribute],
     ownership: DirectoryOwnership,
     dir_path: &Path,
 ) -> PResult<'a, ModulePathSuccess> {
-    if let Some(path) = submod_path_from_attr(attrs, dir_path) {
+    if let Some(path) = submod_path_from_attr(sess, attrs, dir_path) {
         let ownership = match path.file_name().and_then(|s| s.to_str()) {
             // All `#[path]` files are treated as though they are a `mod.rs` file.
             // This means that `mod foo;` declarations inside `#[path]`-included
@@ -151,16 +154,16 @@ fn submod_path<'a>(
         DirectoryOwnership::UnownedViaBlock | DirectoryOwnership::UnownedViaMod => None,
     };
     let ModulePath { path_exists, name, result } =
-        default_submod_path(sess, id, span, relative, dir_path);
+        default_submod_path(&sess.parse_sess, id, span, relative, dir_path);
     match ownership {
         DirectoryOwnership::Owned { .. } => Ok(result?),
         DirectoryOwnership::UnownedViaBlock => {
             let _ = result.map_err(|mut err| err.cancel());
-            error_decl_mod_in_block(sess, span, path_exists, &name)
+            error_decl_mod_in_block(&sess.parse_sess, span, path_exists, &name)
         }
         DirectoryOwnership::UnownedViaMod => {
             let _ = result.map_err(|mut err| err.cancel());
-            error_cannot_declare_mod_here(sess, span, path_exists, &name)
+            error_cannot_declare_mod_here(&sess.parse_sess, span, path_exists, &name)
         }
     }
 }
@@ -218,9 +221,13 @@ 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(attrs: &[Attribute], dir_path: &Path) -> Option<PathBuf> {
+pub fn submod_path_from_attr(
+    sess: &Session,
+    attrs: &[Attribute],
+    dir_path: &Path,
+) -> Option<PathBuf> {
     // Extract path string from first `#[path = "path_string"]` attribute.
-    let path_string = attr::first_attr_value_str_by_name(attrs, sym::path)?;
+    let path_string = sess.first_attr_value_str_by_name(attrs, sym::path)?;
     let path_string = path_string.as_str();
 
     // On windows, the base path might have the form
diff --git a/src/librustc_expand/mut_visit/tests.rs b/src/librustc_expand/mut_visit/tests.rs
index c22d2a100c3..0608ccfffb8 100644
--- a/src/librustc_expand/mut_visit/tests.rs
+++ b/src/librustc_expand/mut_visit/tests.rs
@@ -2,9 +2,9 @@ use crate::tests::{matches_codepattern, string_to_crate};
 
 use rustc_ast::ast;
 use rustc_ast::mut_visit::{self, MutVisitor};
-use rustc_ast::with_default_session_globals;
 use rustc_ast_pretty::pprust;
 use rustc_span::symbol::Ident;
+use rustc_span::with_default_session_globals;
 
 // This version doesn't care about getting comments or doc-strings in.
 fn fake_print_crate(s: &mut pprust::State<'_>, krate: &ast::Crate) {
diff --git a/src/librustc_expand/parse/lexer/tests.rs b/src/librustc_expand/parse/lexer/tests.rs
index b3775c78e73..87184444283 100644
--- a/src/librustc_expand/parse/lexer/tests.rs
+++ b/src/librustc_expand/parse/lexer/tests.rs
@@ -1,12 +1,12 @@
-use rustc_ast::token::{self, Token, TokenKind};
-use rustc_ast::util::comments::is_doc_comment;
-use rustc_ast::with_default_session_globals;
+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;
@@ -224,13 +224,6 @@ fn literal_suffixes() {
 }
 
 #[test]
-fn line_doc_comments() {
-    assert!(is_doc_comment("///"));
-    assert!(is_doc_comment("/// blah"));
-    assert!(!is_doc_comment("////"));
-}
-
-#[test]
 fn nested_block_comments() {
     with_default_session_globals(|| {
         let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
@@ -251,6 +244,9 @@ fn crlf_comments() {
         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(Symbol::intern("/// test")));
+        assert_eq!(
+            lexer.next_token(),
+            token::DocComment(CommentKind::Line, AttrStyle::Outer, Symbol::intern(" test"))
+        );
     })
 }
diff --git a/src/librustc_expand/parse/tests.rs b/src/librustc_expand/parse/tests.rs
index fc9b9f2dab0..5c9116b2f13 100644
--- a/src/librustc_expand/parse/tests.rs
+++ b/src/librustc_expand/parse/tests.rs
@@ -5,13 +5,13 @@ use rustc_ast::ptr::P;
 use rustc_ast::token::{self, Token};
 use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
 use rustc_ast::visit;
-use rustc_ast::with_default_session_globals;
 use rustc_ast_pretty::pprust::item_to_string;
 use rustc_errors::PResult;
 use rustc_parse::new_parser_from_source_str;
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::FilePathMapping;
 use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::with_default_session_globals;
 use rustc_span::{BytePos, FileName, Pos, Span};
 
 use std::path::PathBuf;
@@ -244,20 +244,20 @@ fn crlf_doc_comments() {
         let source = "/// doc comment\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_1, source, &sess).unwrap().unwrap();
         let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap();
-        assert_eq!(doc.as_str(), "/// doc comment");
+        assert_eq!(doc.as_str(), " doc comment");
 
         let name_2 = FileName::Custom("crlf_source_2".to_string());
         let source = "/// doc comment\r\n/// line 2\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_2, source, &sess).unwrap().unwrap();
         let docs = item.attrs.iter().filter_map(|at| at.doc_str()).collect::<Vec<_>>();
-        let b: &[_] = &[Symbol::intern("/// doc comment"), Symbol::intern("/// line 2")];
+        let b: &[_] = &[Symbol::intern(" doc comment"), Symbol::intern(" line 2")];
         assert_eq!(&docs[..], b);
 
         let name_3 = FileName::Custom("clrf_source_3".to_string());
         let source = "/** doc comment\r\n *  with CRLF */\r\nfn foo() {}".to_string();
         let item = parse_item_from_source_str(name_3, source, &sess).unwrap().unwrap();
         let doc = item.attrs.iter().filter_map(|at| at.doc_str()).next().unwrap();
-        assert_eq!(doc.as_str(), "/** doc comment\n *  with CRLF */");
+        assert_eq!(doc.as_str(), " doc comment\n *  with CRLF ");
     });
 }
 
diff --git a/src/librustc_expand/proc_macro.rs b/src/librustc_expand/proc_macro.rs
index 54012d62a72..85fbf3bc0d0 100644
--- a/src/librustc_expand/proc_macro.rs
+++ b/src/librustc_expand/proc_macro.rs
@@ -107,7 +107,7 @@ impl MultiItemModifier for ProcMacroDerive {
         let input = if item.pretty_printing_compatibility_hack() {
             TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
         } else {
-            nt_to_tokenstream(&item, ecx.parse_sess, DUMMY_SP)
+            nt_to_tokenstream(&item, &ecx.sess.parse_sess, DUMMY_SP)
         };
 
         let server = proc_macro_server::Rustc::new(ecx);
@@ -123,9 +123,9 @@ impl MultiItemModifier for ProcMacroDerive {
             }
         };
 
-        let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
+        let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
         let mut parser =
-            rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
+            rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
         let mut items = vec![];
 
         loop {
@@ -140,7 +140,7 @@ impl MultiItemModifier for ProcMacroDerive {
         }
 
         // fail if there have been errors emitted
-        if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
+        if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
             ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
         }
 
diff --git a/src/librustc_expand/proc_macro_server.rs b/src/librustc_expand/proc_macro_server.rs
index 881d7b84b70..dc7ba2d0424 100644
--- a/src/librustc_expand/proc_macro_server.rs
+++ b/src/librustc_expand/proc_macro_server.rs
@@ -3,7 +3,6 @@ use crate::base::ExtCtxt;
 use rustc_ast::ast;
 use rustc_ast::token;
 use rustc_ast::tokenstream::{self, DelimSpan, IsJoint::*, TokenStream, TreeAndJoint};
-use rustc_ast::util::comments;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Diagnostic;
@@ -148,11 +147,9 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
                 tt!(Punct::new('\'', true))
             }
             Literal(lit) => tt!(Literal { lit }),
-            DocComment(c) => {
-                let style = comments::doc_comment_style(c);
-                let stripped = comments::strip_doc_comment_decoration(c);
+            DocComment(_, attr_style, data) => {
                 let mut escaped = String::new();
-                for ch in stripped.chars() {
+                for ch in data.as_str().chars() {
                     escaped.extend(ch.escape_debug());
                 }
                 let stream = vec![
@@ -169,7 +166,7 @@ impl FromInternal<(TreeAndJoint, &'_ ParseSess, &'_ mut Vec<Self>)>
                     span: DelimSpan::from_single(span),
                     flatten: false,
                 }));
-                if style == ast::AttrStyle::Inner {
+                if attr_style == ast::AttrStyle::Inner {
                     stack.push(tt!(Punct::new('!', false)));
                 }
                 tt!(Punct::new('#', false))
@@ -367,7 +364,7 @@ impl<'a> Rustc<'a> {
     pub fn new(cx: &'a ExtCtxt<'_>) -> Self {
         let expn_data = cx.current_expansion.id.expn_data();
         Rustc {
-            sess: cx.parse_sess,
+            sess: &cx.sess.parse_sess,
             def_site: cx.with_def_site_ctxt(expn_data.def_site),
             call_site: cx.with_call_site_ctxt(expn_data.call_site),
             mixed_site: cx.with_mixed_site_ctxt(expn_data.call_site),
diff --git a/src/librustc_expand/tests.rs b/src/librustc_expand/tests.rs
index 283ea0f68d9..b562a690f83 100644
--- a/src/librustc_expand/tests.rs
+++ b/src/librustc_expand/tests.rs
@@ -1,9 +1,9 @@
 use rustc_ast::ast;
 use rustc_ast::tokenstream::TokenStream;
-use rustc_ast::with_default_session_globals;
 use rustc_parse::{new_parser_from_source_str, parser::Parser, source_file_to_stream};
 use rustc_session::parse::ParseSess;
 use rustc_span::source_map::{FilePathMapping, SourceMap};
+use rustc_span::with_default_session_globals;
 use rustc_span::{BytePos, MultiSpan, Span};
 
 use rustc_data_structures::sync::Lrc;
diff --git a/src/librustc_expand/tokenstream/tests.rs b/src/librustc_expand/tokenstream/tests.rs
index bc171bec6ff..4e818e9feb0 100644
--- a/src/librustc_expand/tokenstream/tests.rs
+++ b/src/librustc_expand/tokenstream/tests.rs
@@ -2,7 +2,7 @@ use crate::tests::string_to_stream;
 
 use rustc_ast::token;
 use rustc_ast::tokenstream::{TokenStream, TokenStreamBuilder, TokenTree};
-use rustc_ast::with_default_session_globals;
+use rustc_span::with_default_session_globals;
 use rustc_span::{BytePos, Span, Symbol};
 use smallvec::smallvec;
 
diff --git a/src/librustc_feature/active.rs b/src/librustc_feature/active.rs
index d7c310a8b4c..31aba8aba25 100644
--- a/src/librustc_feature/active.rs
+++ b/src/librustc_feature/active.rs
@@ -576,9 +576,12 @@ declare_features! (
     /// Lazily evaluate constants. This allows constants to depend on type parameters.
     (active, lazy_normalization_consts, "1.46.0", Some(72219), None),
 
-    /// Alloc calling `transmute` in const fn
+    /// Allows calling `transmute` in const fn
     (active, const_fn_transmute, "1.46.0", Some(53605), None),
 
+    /// The smallest useful subset of `const_generics`.
+    (active, min_const_generics, "1.47.0", Some(74878), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
diff --git a/src/librustc_hir/def.rs b/src/librustc_hir/def.rs
index af1860ca6bf..618f3e99c3f 100644
--- a/src/librustc_hir/def.rs
+++ b/src/librustc_hir/def.rs
@@ -150,7 +150,7 @@ impl DefKind {
         }
     }
 
-    pub fn matches_ns(&self, ns: Namespace) -> bool {
+    pub fn ns(&self) -> Option<Namespace> {
         match self {
             DefKind::Mod
             | DefKind::Struct
@@ -163,7 +163,7 @@ impl DefKind {
             | DefKind::ForeignTy
             | DefKind::TraitAlias
             | DefKind::AssocTy
-            | DefKind::TyParam => ns == Namespace::TypeNS,
+            | DefKind::TyParam => Some(Namespace::TypeNS),
 
             DefKind::Fn
             | DefKind::Const
@@ -171,9 +171,9 @@ impl DefKind {
             | DefKind::Static
             | DefKind::Ctor(..)
             | DefKind::AssocFn
-            | DefKind::AssocConst => ns == Namespace::ValueNS,
+            | DefKind::AssocConst => Some(Namespace::ValueNS),
 
-            DefKind::Macro(..) => ns == Namespace::MacroNS,
+            DefKind::Macro(..) => Some(Namespace::MacroNS),
 
             // Not namespaced.
             DefKind::AnonConst
@@ -185,7 +185,7 @@ impl DefKind {
             | DefKind::Use
             | DefKind::ForeignMod
             | DefKind::GlobalAsm
-            | DefKind::Impl => false,
+            | DefKind::Impl => None,
         }
     }
 }
@@ -453,7 +453,7 @@ impl<Id> Res<Id> {
 
     pub fn matches_ns(&self, ns: Namespace) -> bool {
         match self {
-            Res::Def(kind, ..) => kind.matches_ns(ns),
+            Res::Def(kind, ..) => kind.ns() == Some(ns),
             Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => ns == Namespace::TypeNS,
             Res::SelfCtor(..) | Res::Local(..) => ns == Namespace::ValueNS,
             Res::NonMacroAttr(..) => ns == Namespace::MacroNS,
diff --git a/src/librustc_hir/lang_items.rs b/src/librustc_hir/lang_items.rs
index 4b71407acfb..7f473a45848 100644
--- a/src/librustc_hir/lang_items.rs
+++ b/src/librustc_hir/lang_items.rs
@@ -141,12 +141,20 @@ impl<CTX> HashStable<CTX> for LangItem {
 /// Extracts the first `lang = "$name"` out of a list of attributes.
 /// The attributes `#[panic_handler]` and `#[alloc_error_handler]`
 /// are also extracted out when found.
-pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
+///
+/// About the `check_name` argument: passing in a `Session` would be simpler,
+/// because then we could call `Session::check_name` directly. But we want to
+/// avoid the need for `librustc_hir` to depend on `librustc_session`, so we
+/// use a closure instead.
+pub fn extract<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<(Symbol, Span)>
+where
+    F: Fn(&'a ast::Attribute, Symbol) -> bool,
+{
     attrs.iter().find_map(|attr| {
         Some(match attr {
-            _ if attr.check_name(sym::lang) => (attr.value_str()?, attr.span),
-            _ if attr.check_name(sym::panic_handler) => (sym::panic_impl, attr.span),
-            _ if attr.check_name(sym::alloc_error_handler) => (sym::oom, attr.span),
+            _ if check_name(attr, sym::lang) => (attr.value_str()?, attr.span),
+            _ if check_name(attr, sym::panic_handler) => (sym::panic_impl, attr.span),
+            _ if check_name(attr, sym::alloc_error_handler) => (sym::oom, attr.span),
             _ => return None,
         })
     })
diff --git a/src/librustc_hir/weak_lang_items.rs b/src/librustc_hir/weak_lang_items.rs
index c0560eb8d45..fd64361cb35 100644
--- a/src/librustc_hir/weak_lang_items.rs
+++ b/src/librustc_hir/weak_lang_items.rs
@@ -20,8 +20,13 @@ lazy_static! {
     };
 }
 
-pub fn link_name(attrs: &[ast::Attribute]) -> Option<Symbol> {
-    lang_items::extract(attrs).and_then(|(name, _)| {
+/// The `check_name` argument avoids the need for `librustc_hir` to depend on
+/// `librustc_session`.
+pub fn link_name<'a, F>(check_name: F, attrs: &'a [ast::Attribute]) -> Option<Symbol>
+where
+    F: Fn(&'a ast::Attribute, Symbol) -> bool
+{
+    lang_items::extract(check_name, attrs).and_then(|(name, _)| {
         $(if name == sym::$name {
             Some(sym::$sym)
         } else)* {
diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs
index b1665d9e1ae..df602d8bd1b 100644
--- a/src/librustc_incremental/assert_dep_graph.rs
+++ b/src/librustc_incremental/assert_dep_graph.rs
@@ -117,7 +117,7 @@ impl IfThisChanged<'tcx> {
         let def_id = self.tcx.hir().local_def_id(hir_id);
         let def_path_hash = self.tcx.def_path_hash(def_id.to_def_id());
         for attr in attrs {
-            if attr.check_name(sym::rustc_if_this_changed) {
+            if self.tcx.sess.check_name(attr, sym::rustc_if_this_changed) {
                 let dep_node_interned = self.argument(attr);
                 let dep_node = match dep_node_interned {
                     None => DepNode::from_def_path_hash(def_path_hash, DepKind::hir_owner),
@@ -132,7 +132,7 @@ impl IfThisChanged<'tcx> {
                     },
                 };
                 self.if_this_changed.push((attr.span, def_id.to_def_id(), dep_node));
-            } else if attr.check_name(sym::rustc_then_this_would_need) {
+            } else if self.tcx.sess.check_name(attr, sym::rustc_then_this_would_need) {
                 let dep_node_interned = self.argument(attr);
                 let dep_node = match dep_node_interned {
                     Some(n) => match DepNode::from_label_string(&n.as_str(), def_path_hash) {
diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs
index d451d9a22a4..ed04a947485 100644
--- a/src/librustc_incremental/assert_module_sources.rs
+++ b/src/librustc_incremental/assert_module_sources.rs
@@ -39,8 +39,8 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
             .collect_and_partition_mono_items(LOCAL_CRATE)
             .1
             .iter()
-            .map(|cgu| cgu.name())
-            .collect::<BTreeSet<Symbol>>();
+            .map(|cgu| cgu.name().to_string())
+            .collect::<BTreeSet<String>>();
 
         let ams = AssertModuleSource { tcx, available_cgus };
 
@@ -52,31 +52,32 @@ pub fn assert_module_sources(tcx: TyCtxt<'_>) {
 
 struct AssertModuleSource<'tcx> {
     tcx: TyCtxt<'tcx>,
-    available_cgus: BTreeSet<Symbol>,
+    available_cgus: BTreeSet<String>,
 }
 
 impl AssertModuleSource<'tcx> {
     fn check_attr(&self, attr: &ast::Attribute) {
-        let (expected_reuse, comp_kind) = if attr.check_name(sym::rustc_partition_reused) {
-            (CguReuse::PreLto, ComparisonKind::AtLeast)
-        } else if attr.check_name(sym::rustc_partition_codegened) {
-            (CguReuse::No, ComparisonKind::Exact)
-        } else if attr.check_name(sym::rustc_expected_cgu_reuse) {
-            match self.field(attr, sym::kind) {
-                sym::no => (CguReuse::No, ComparisonKind::Exact),
-                sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
-                sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
-                sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
-                other => {
-                    self.tcx.sess.span_fatal(
-                        attr.span,
-                        &format!("unknown cgu-reuse-kind `{}` specified", other),
-                    );
+        let (expected_reuse, comp_kind) =
+            if self.tcx.sess.check_name(attr, sym::rustc_partition_reused) {
+                (CguReuse::PreLto, ComparisonKind::AtLeast)
+            } else if self.tcx.sess.check_name(attr, sym::rustc_partition_codegened) {
+                (CguReuse::No, ComparisonKind::Exact)
+            } else if self.tcx.sess.check_name(attr, sym::rustc_expected_cgu_reuse) {
+                match self.field(attr, sym::kind) {
+                    sym::no => (CguReuse::No, ComparisonKind::Exact),
+                    sym::pre_dash_lto => (CguReuse::PreLto, ComparisonKind::Exact),
+                    sym::post_dash_lto => (CguReuse::PostLto, ComparisonKind::Exact),
+                    sym::any => (CguReuse::PreLto, ComparisonKind::AtLeast),
+                    other => {
+                        self.tcx.sess.span_fatal(
+                            attr.span,
+                            &format!("unknown cgu-reuse-kind `{}` specified", other),
+                        );
+                    }
                 }
-            }
-        } else {
-            return;
-        };
+            } else {
+                return;
+            };
 
         if !self.tcx.sess.opts.debugging_opts.query_dep_graph {
             self.tcx.sess.span_fatal(
@@ -121,12 +122,11 @@ impl AssertModuleSource<'tcx> {
 
         debug!("mapping '{}' to cgu name '{}'", self.field(attr, sym::module), cgu_name);
 
-        if !self.available_cgus.contains(&cgu_name) {
+        if !self.available_cgus.contains(&*cgu_name.as_str()) {
             self.tcx.sess.span_err(
                 attr.span,
                 &format!(
-                    "no module named `{}` (mangled: {}). \
-                          Available modules: {}",
+                    "no module named `{}` (mangled: {}). Available modules: {}",
                     user_path,
                     cgu_name,
                     self.available_cgus
diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs
index 02f37f82352..0f254aee8e3 100644
--- a/src/librustc_incremental/persist/dirty_clean.rs
+++ b/src/librustc_incremental/persist/dirty_clean.rs
@@ -180,9 +180,9 @@ pub struct DirtyCleanVisitor<'tcx> {
 impl DirtyCleanVisitor<'tcx> {
     /// Possibly "deserialize" the attribute into a clean/dirty assertion
     fn assertion_maybe(&mut self, item_id: hir::HirId, attr: &Attribute) -> Option<Assertion> {
-        let is_clean = if attr.check_name(sym::rustc_dirty) {
+        let is_clean = if self.tcx.sess.check_name(attr, sym::rustc_dirty) {
             false
-        } else if attr.check_name(sym::rustc_clean) {
+        } else if self.tcx.sess.check_name(attr, sym::rustc_clean) {
             true
         } else {
             // skip: not rustc_clean/dirty
@@ -523,7 +523,7 @@ pub struct FindAllAttrs<'tcx> {
 impl FindAllAttrs<'tcx> {
     fn is_active_attr(&mut self, attr: &Attribute) -> bool {
         for attr_name in &self.attr_names {
-            if attr.check_name(*attr_name) && check_config(self.tcx, attr) {
+            if self.tcx.sess.check_name(attr, *attr_name) && check_config(self.tcx, attr) {
                 return true;
             }
         }
diff --git a/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index 72deba990b0..89142edb2dc 100644
--- a/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/src/librustc_infer/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -85,11 +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()
-                        .filter(|t| t.span.desugaring_kind().is_none())
-                        .next()
-                        .is_some()
+                    && v.0.into_iter().find(|t| t.span.desugaring_kind().is_none()).is_some()
                 {
                     // 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/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 0125e0f48e8..7493b8b0a9f 100644
--- a/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/src/librustc_infer/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -257,7 +257,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                             param.param_ty.to_string(),
                             Applicability::MaybeIncorrect,
                         );
-                    } else if let Some(_) = opaque
+                    } else if opaque
                         .bounds
                         .iter()
                         .filter_map(|arg| match arg {
@@ -269,6 +269,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
                             _ => None,
                         })
                         .next()
+                        .is_some()
                     {
                     } else {
                         err.span_suggestion_verbose(
diff --git a/src/librustc_infer/infer/glb.rs b/src/librustc_infer/infer/glb.rs
index 8a0ab52f383..ccba904df9e 100644
--- a/src/librustc_infer/infer/glb.rs
+++ b/src/librustc_infer/infer/glb.rs
@@ -50,7 +50,7 @@ impl TypeRelation<'tcx> for Glb<'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.lub(self.a_is_expected).relate(a, b),
         }
     }
@@ -97,7 +97,7 @@ impl TypeRelation<'tcx> for Glb<'combine, 'infcx, 'tcx> {
         // very challenging, switch to invariance. This is obviously
         // overly conservative but works ok in practice.
         self.relate_with_variance(ty::Variance::Invariant, a, b)?;
-        Ok(a.clone())
+        Ok(a)
     }
 }
 
diff --git a/src/librustc_infer/infer/nll_relate/mod.rs b/src/librustc_infer/infer/nll_relate/mod.rs
index cb1f1c08d88..3f5ed36035c 100644
--- a/src/librustc_infer/infer/nll_relate/mod.rs
+++ b/src/librustc_infer/infer/nll_relate/mod.rs
@@ -719,7 +719,7 @@ where
             self.a_scopes.pop().unwrap();
         }
 
-        Ok(a.clone())
+        Ok(a)
     }
 }
 
diff --git a/src/librustc_infer/infer/region_constraints/leak_check.rs b/src/librustc_infer/infer/region_constraints/leak_check.rs
index 32e708bf52b..2d4c1e5d050 100644
--- a/src/librustc_infer/infer/region_constraints/leak_check.rs
+++ b/src/librustc_infer/infer/region_constraints/leak_check.rs
@@ -288,9 +288,9 @@ impl<'me, 'tcx> LeakCheck<'me, 'tcx> {
     ) -> TypeError<'tcx> {
         debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region);
         if self.overly_polymorphic {
-            return TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region);
+            TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region)
         } else {
-            return TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region);
+            TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region)
         }
     }
 }
diff --git a/src/librustc_infer/infer/sub.rs b/src/librustc_infer/infer/sub.rs
index 4f860c77d65..308f884f9a6 100644
--- a/src/librustc_infer/infer/sub.rs
+++ b/src/librustc_infer/infer/sub.rs
@@ -68,7 +68,7 @@ impl TypeRelation<'tcx> for Sub<'combine, 'infcx, 'tcx> {
         match variance {
             ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b),
             ty::Covariant => self.relate(a, b),
-            ty::Bivariant => Ok(a.clone()),
+            ty::Bivariant => Ok(a),
             ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)),
         }
     }
diff --git a/src/librustc_interface/interface.rs b/src/librustc_interface/interface.rs
index ad30f5eda4d..4dccf273dd9 100644
--- a/src/librustc_interface/interface.rs
+++ b/src/librustc_interface/interface.rs
@@ -73,7 +73,7 @@ impl Compiler {
 
 /// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.
 pub fn parse_cfgspecs(cfgspecs: Vec<String>) -> FxHashSet<(String, Option<String>)> {
-    rustc_ast::with_default_session_globals(move || {
+    rustc_span::with_default_session_globals(move || {
         let cfg = cfgspecs
             .into_iter()
             .map(|s| {
diff --git a/src/librustc_interface/passes.rs b/src/librustc_interface/passes.rs
index 3a562847d3e..6c0343330c8 100644
--- a/src/librustc_interface/passes.rs
+++ b/src/librustc_interface/passes.rs
@@ -162,12 +162,7 @@ pub fn register_plugins<'a>(
         )
     });
 
-    let (krate, features) = rustc_expand::config::features(
-        krate,
-        &sess.parse_sess,
-        sess.edition(),
-        &sess.opts.debugging_opts.allow_features,
-    );
+    let (krate, features) = rustc_expand::config::features(sess, krate);
     // these need to be set "early" so that expansion sees `quote` if enabled.
     sess.init_features(features);
 
@@ -244,7 +239,7 @@ fn configure_and_expand_inner<'a>(
         let (krate, name) = rustc_builtin_macros::standard_library_imports::inject(
             krate,
             &mut resolver,
-            &sess.parse_sess,
+            &sess,
             alt_std_name,
         );
         if let Some(name) = name {
@@ -253,7 +248,7 @@ fn configure_and_expand_inner<'a>(
         krate
     });
 
-    util::check_attr_crate_type(&krate.attrs, &mut resolver.lint_buffer());
+    util::check_attr_crate_type(&sess, &krate.attrs, &mut resolver.lint_buffer());
 
     // Expand all macros
     krate = sess.time("macro_expand_crate", || {
@@ -300,7 +295,7 @@ fn configure_and_expand_inner<'a>(
         };
 
         let extern_mod_loaded = |k: &ast::Crate| pre_expansion_lint(sess, lint_store, k);
-        let mut ecx = ExtCtxt::new(&sess.parse_sess, cfg, &mut resolver, Some(&extern_mod_loaded));
+        let mut ecx = ExtCtxt::new(&sess, cfg, &mut resolver, Some(&extern_mod_loaded));
 
         // Expand macros now!
         let krate = sess.time("expand_crate", || ecx.monotonic_expander().expand_crate(krate));
@@ -312,6 +307,7 @@ fn configure_and_expand_inner<'a>(
         });
 
         let mut missing_fragment_specifiers: Vec<_> = ecx
+            .sess
             .parse_sess
             .missing_fragment_specifiers
             .borrow()
@@ -341,17 +337,7 @@ fn configure_and_expand_inner<'a>(
     })?;
 
     sess.time("maybe_building_test_harness", || {
-        rustc_builtin_macros::test_harness::inject(
-            &sess.parse_sess,
-            &mut resolver,
-            sess.opts.test,
-            &mut krate,
-            sess.diagnostic(),
-            &sess.features_untracked(),
-            sess.panic_strategy(),
-            sess.target.target.options.panic_strategy,
-            sess.opts.debugging_opts.panic_abort_tests,
-        )
+        rustc_builtin_macros::test_harness::inject(&sess, &mut resolver, &mut krate)
     });
 
     if let Some(PpMode::PpmSource(PpSourceMode::PpmEveryBodyLoops)) = sess.opts.pretty {
@@ -385,7 +371,7 @@ fn configure_and_expand_inner<'a>(
             let num_crate_types = crate_types.len();
             let is_test_crate = sess.opts.test;
             rustc_builtin_macros::proc_macro_harness::inject(
-                &sess.parse_sess,
+                &sess,
                 &mut resolver,
                 krate,
                 is_proc_macro_crate,
@@ -415,12 +401,7 @@ fn configure_and_expand_inner<'a>(
 
     // Needs to go *after* expansion to be able to check the results of macro expansion.
     sess.time("complete_gated_feature_checking", || {
-        rustc_ast_passes::feature_gate::check_crate(
-            &krate,
-            &sess.parse_sess,
-            &sess.features_untracked(),
-            sess.opts.unstable_features,
-        );
+        rustc_ast_passes::feature_gate::check_crate(&krate, sess);
     });
 
     // Add all buffered lints from the `ParseSess` to the `Session`.
diff --git a/src/librustc_interface/proc_macro_decls.rs b/src/librustc_interface/proc_macro_decls.rs
index e91003b450c..d56115fd6ac 100644
--- a/src/librustc_interface/proc_macro_decls.rs
+++ b/src/librustc_interface/proc_macro_decls.rs
@@ -1,4 +1,3 @@
-use rustc_ast::attr;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
@@ -13,19 +12,20 @@ pub fn find(tcx: TyCtxt<'_>) -> Option<DefId> {
 fn proc_macro_decls_static(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<DefId> {
     assert_eq!(cnum, LOCAL_CRATE);
 
-    let mut finder = Finder { decls: None };
+    let mut finder = Finder { tcx, decls: None };
     tcx.hir().krate().visit_all_item_likes(&mut finder);
 
     finder.decls.map(|id| tcx.hir().local_def_id(id).to_def_id())
 }
 
-struct Finder {
+struct Finder<'tcx> {
+    tcx: TyCtxt<'tcx>,
     decls: Option<hir::HirId>,
 }
 
-impl<'v> ItemLikeVisitor<'v> for Finder {
+impl<'v> ItemLikeVisitor<'v> for Finder<'_> {
     fn visit_item(&mut self, item: &hir::Item<'_>) {
-        if attr::contains_name(&item.attrs, sym::rustc_proc_macro_decls) {
+        if self.tcx.sess.contains_name(&item.attrs, sym::rustc_proc_macro_decls) {
             self.decls = Some(item.hir_id);
         }
     }
diff --git a/src/librustc_interface/queries.rs b/src/librustc_interface/queries.rs
index 4265e6dca6a..d1a22c69ee0 100644
--- a/src/librustc_interface/queries.rs
+++ b/src/librustc_interface/queries.rs
@@ -159,7 +159,7 @@ impl<'tcx> Queries<'tcx> {
                 None => {
                     let parse_result = self.parse()?;
                     let krate = parse_result.peek();
-                    find_crate_name(Some(self.session()), &krate.attrs, &self.compiler.input)
+                    find_crate_name(self.session(), &krate.attrs, &self.compiler.input)
                 }
             })
         })
@@ -294,7 +294,7 @@ impl<'tcx> Queries<'tcx> {
         };
 
         let attrs = &*tcx.get_attrs(def_id.to_def_id());
-        let attrs = attrs.iter().filter(|attr| attr.check_name(sym::rustc_error));
+        let attrs = attrs.iter().filter(|attr| tcx.sess.check_name(attr, sym::rustc_error));
         for attr in attrs {
             match attr.meta_item_list() {
                 // Check if there is a `#[rustc_error(delay_span_bug_from_inside_query)]`.
diff --git a/src/librustc_interface/tests.rs b/src/librustc_interface/tests.rs
index 22197a66530..e94745519a4 100644
--- a/src/librustc_interface/tests.rs
+++ b/src/librustc_interface/tests.rs
@@ -73,7 +73,7 @@ fn mk_map<K: Ord, V>(entries: Vec<(K, V)>) -> BTreeMap<K, V> {
 // When the user supplies --test we should implicitly supply --cfg test
 #[test]
 fn test_switch_implies_cfg_test() {
-    rustc_ast::with_default_session_globals(|| {
+    rustc_span::with_default_session_globals(|| {
         let matches = optgroups().parse(&["--test".to_string()]).unwrap();
         let (sess, cfg) = mk_session(matches);
         let cfg = build_configuration(&sess, to_crate_config(cfg));
@@ -84,7 +84,7 @@ fn test_switch_implies_cfg_test() {
 // When the user supplies --test and --cfg test, don't implicitly add another --cfg test
 #[test]
 fn test_switch_implies_cfg_test_unless_cfg_test() {
-    rustc_ast::with_default_session_globals(|| {
+    rustc_span::with_default_session_globals(|| {
         let matches = optgroups().parse(&["--test".to_string(), "--cfg=test".to_string()]).unwrap();
         let (sess, cfg) = mk_session(matches);
         let cfg = build_configuration(&sess, to_crate_config(cfg));
@@ -96,20 +96,20 @@ fn test_switch_implies_cfg_test_unless_cfg_test() {
 
 #[test]
 fn test_can_print_warnings() {
-    rustc_ast::with_default_session_globals(|| {
+    rustc_span::with_default_session_globals(|| {
         let matches = optgroups().parse(&["-Awarnings".to_string()]).unwrap();
         let (sess, _) = mk_session(matches);
         assert!(!sess.diagnostic().can_emit_warnings());
     });
 
-    rustc_ast::with_default_session_globals(|| {
+    rustc_span::with_default_session_globals(|| {
         let matches =
             optgroups().parse(&["-Awarnings".to_string(), "-Dwarnings".to_string()]).unwrap();
         let (sess, _) = mk_session(matches);
         assert!(sess.diagnostic().can_emit_warnings());
     });
 
-    rustc_ast::with_default_session_globals(|| {
+    rustc_span::with_default_session_globals(|| {
         let matches = optgroups().parse(&["-Adead_code".to_string()]).unwrap();
         let (sess, _) = mk_session(matches);
         assert!(sess.diagnostic().can_emit_warnings());
diff --git a/src/librustc_interface/util.rs b/src/librustc_interface/util.rs
index bbb2f9d8b25..e403a60ff32 100644
--- a/src/librustc_interface/util.rs
+++ b/src/librustc_interface/util.rs
@@ -142,7 +142,7 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se
     crate::callbacks::setup_callbacks();
 
     let main_handler = move || {
-        rustc_ast::with_session_globals(edition, || {
+        rustc_span::with_session_globals(edition, || {
             if let Some(stderr) = stderr {
                 io::set_panic(Some(box Sink(stderr.clone())));
             }
@@ -176,27 +176,21 @@ pub fn setup_callbacks_and_run_in_thread_pool_with_globals<F: FnOnce() -> R + Se
 
     let with_pool = move |pool: &rayon::ThreadPool| pool.install(move || f());
 
-    rustc_ast::with_session_globals(edition, || {
-        rustc_ast::SESSION_GLOBALS.with(|ast_session_globals| {
-            rustc_span::SESSION_GLOBALS.with(|span_session_globals| {
-                // The main handler runs for each Rayon worker thread and sets
-                // up the thread local rustc uses. ast_session_globals and
-                // span_session_globals are captured and set on the new
-                // threads. ty::tls::with_thread_locals sets up thread local
-                // callbacks from librustc_ast.
-                let main_handler = move |thread: rayon::ThreadBuilder| {
-                    rustc_ast::SESSION_GLOBALS.set(ast_session_globals, || {
-                        rustc_span::SESSION_GLOBALS.set(span_session_globals, || {
-                            if let Some(stderr) = stderr {
-                                io::set_panic(Some(box Sink(stderr.clone())));
-                            }
-                            thread.run()
-                        })
-                    })
-                };
+    rustc_span::with_session_globals(edition, || {
+        rustc_span::SESSION_GLOBALS.with(|session_globals| {
+            // The main handler runs for each Rayon worker thread and sets up
+            // the thread local rustc uses. `session_globals` is captured and set
+            // on the new threads.
+            let main_handler = move |thread: rayon::ThreadBuilder| {
+                rustc_span::SESSION_GLOBALS.set(session_globals, || {
+                    if let Some(stderr) = stderr {
+                        io::set_panic(Some(box Sink(stderr.clone())));
+                    }
+                    thread.run()
+                })
+            };
 
-                config.build_scoped(main_handler, with_pool).unwrap()
-            })
+            config.build_scoped(main_handler, with_pool).unwrap()
         })
     })
 }
@@ -407,10 +401,14 @@ pub(crate) fn compute_crate_disambiguator(session: &Session) -> CrateDisambiguat
     CrateDisambiguator::from(hasher.finish::<Fingerprint>())
 }
 
-pub(crate) fn check_attr_crate_type(attrs: &[ast::Attribute], lint_buffer: &mut LintBuffer) {
+pub(crate) fn check_attr_crate_type(
+    sess: &Session,
+    attrs: &[ast::Attribute],
+    lint_buffer: &mut LintBuffer,
+) {
     // Unconditionally collect crate types from attributes to make them used
     for a in attrs.iter() {
-        if a.check_name(sym::crate_type) {
+        if sess.check_name(a, sym::crate_type) {
             if let Some(n) = a.value_str() {
                 if categorize_crate_type(n).is_some() {
                     return;
@@ -465,7 +463,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec<C
     let attr_types: Vec<CrateType> = attrs
         .iter()
         .filter_map(|a| {
-            if a.check_name(sym::crate_type) {
+            if session.check_name(a, sym::crate_type) {
                 match a.value_str() {
                     Some(s) => categorize_crate_type(s),
                     _ => None,
@@ -531,7 +529,7 @@ pub fn build_output_filenames(
                 .opts
                 .crate_name
                 .clone()
-                .or_else(|| rustc_attr::find_crate_name(attrs).map(|n| n.to_string()))
+                .or_else(|| rustc_attr::find_crate_name(&sess, attrs).map(|n| n.to_string()))
                 .unwrap_or_else(|| input.filestem().to_owned());
 
             OutputFilenames::new(
diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs
index 6515708e115..c42794e00b4 100644
--- a/src/librustc_lint/builtin.rs
+++ b/src/librustc_lint/builtin.rs
@@ -41,6 +41,7 @@ use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::ty::subst::{GenericArgKind, Subst};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::lint::FutureIncompatibleInfo;
+use rustc_session::Session;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -237,7 +238,7 @@ impl UnsafeCode {
 
 impl EarlyLintPass for UnsafeCode {
     fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        if attr.check_name(sym::allow_internal_unsafe) {
+        if cx.sess().check_name(attr, sym::allow_internal_unsafe) {
             self.report_unsafe(cx, attr.span, |lint| {
                 lint.build(
                     "`allow_internal_unsafe` allows defining \
@@ -315,12 +316,12 @@ pub struct MissingDoc {
 
 impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
 
-fn has_doc(attr: &ast::Attribute) -> bool {
+fn has_doc(sess: &Session, attr: &ast::Attribute) -> bool {
     if attr.is_doc_comment() {
         return true;
     }
 
-    if !attr.check_name(sym::doc) {
+    if !sess.check_name(attr, sym::doc) {
         return false;
     }
 
@@ -377,7 +378,7 @@ impl MissingDoc {
             }
         }
 
-        let has_doc = attrs.iter().any(|a| has_doc(a));
+        let has_doc = attrs.iter().any(|a| has_doc(cx.sess(), a));
         if !has_doc {
             cx.struct_span_lint(
                 MISSING_DOCS,
@@ -391,10 +392,10 @@ impl MissingDoc {
 }
 
 impl<'tcx> LateLintPass<'tcx> for MissingDoc {
-    fn enter_lint_attrs(&mut self, _: &LateContext<'_>, attrs: &[ast::Attribute]) {
+    fn enter_lint_attrs(&mut self, cx: &LateContext<'_>, attrs: &[ast::Attribute]) {
         let doc_hidden = self.doc_hidden()
             || attrs.iter().any(|attr| {
-                attr.check_name(sym::doc)
+                cx.sess().check_name(attr, sym::doc)
                     && match attr.meta_item_list() {
                         None => false,
                         Some(l) => attr::list_contains_name(&l, sym::hidden),
@@ -411,7 +412,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
         self.check_missing_docs_attrs(cx, None, &krate.item.attrs, krate.item.span, "the", "crate");
 
         for macro_def in krate.exported_macros {
-            let has_doc = macro_def.attrs.iter().any(|a| has_doc(a));
+            let has_doc = macro_def.attrs.iter().any(|a| has_doc(cx.sess(), a));
             if !has_doc {
                 cx.struct_span_lint(
                     MISSING_DOCS,
@@ -737,7 +738,7 @@ impl EarlyLintPass for DeprecatedAttr {
                 return;
             }
         }
-        if attr.check_name(sym::no_start) || attr.check_name(sym::crate_id) {
+        if cx.sess().check_name(attr, sym::no_start) || cx.sess().check_name(attr, sym::crate_id) {
             let path_str = pprust::path_to_string(&attr.get_normal_item().path);
             let msg = format!("use of deprecated attribute `{}`: no longer used.", path_str);
             lint_deprecated_attr(cx, attr, &msg, None);
@@ -763,7 +764,7 @@ fn warn_if_doc(cx: &EarlyContext<'_>, node_span: Span, node_kind: &str, attrs: &
 
         let span = sugared_span.take().unwrap_or_else(|| attr.span);
 
-        if attr.is_doc_comment() || attr.check_name(sym::doc) {
+        if attr.is_doc_comment() || cx.sess().check_name(attr, sym::doc) {
             cx.struct_span_lint(UNUSED_DOC_COMMENTS, span, |lint| {
                 let mut err = lint.build("unused doc comment");
                 err.span_label(
@@ -819,7 +820,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         match it.kind {
             hir::ItemKind::Fn(.., ref generics, _) => {
-                if let Some(no_mangle_attr) = attr::find_by_name(&it.attrs, sym::no_mangle) {
+                if let Some(no_mangle_attr) = cx.sess().find_by_name(&it.attrs, sym::no_mangle) {
                     for param in generics.params {
                         match param.kind {
                             GenericParamKind::Lifetime { .. } => {}
@@ -845,7 +846,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidNoMangleItems {
                 }
             }
             hir::ItemKind::Const(..) => {
-                if attr::contains_name(&it.attrs, sym::no_mangle) {
+                if cx.sess().contains_name(&it.attrs, sym::no_mangle) {
                     // Const items do not refer to a particular location in memory, and therefore
                     // don't have anything to attach a symbol to
                     cx.struct_span_lint(NO_MANGLE_CONST_ITEMS, it.span, |lint| {
@@ -938,11 +939,11 @@ declare_lint_pass!(
 );
 
 impl<'tcx> LateLintPass<'tcx> for UnstableFeatures {
-    fn check_attribute(&mut self, ctx: &LateContext<'_>, attr: &ast::Attribute) {
-        if attr.check_name(sym::feature) {
+    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
+        if cx.sess().check_name(attr, sym::feature) {
             if let Some(items) = attr.meta_item_list() {
                 for item in items {
-                    ctx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
+                    cx.struct_span_lint(UNSTABLE_FEATURES, item.span(), |lint| {
                         lint.build("unstable feature").emit()
                     });
                 }
@@ -1381,7 +1382,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems {
             return;
         }
 
-        if let Some(attr) = attr::find_by_name(&it.attrs, sym::rustc_test_marker) {
+        if let Some(attr) = cx.sess().find_by_name(&it.attrs, sym::rustc_test_marker) {
             cx.struct_span_lint(UNNAMEABLE_TEST_ITEMS, attr.span, |lint| {
                 lint.build("cannot test inner items").emit()
             });
@@ -2131,7 +2132,7 @@ impl ClashingExternDeclarations {
                     overridden_link_name,
                     tcx.get_attrs(did.to_def_id())
                         .iter()
-                        .find(|at| at.check_name(sym::link_name))
+                        .find(|at| tcx.sess.check_name(at, sym::link_name))
                         .unwrap()
                         .span,
                 )
diff --git a/src/librustc_lint/levels.rs b/src/librustc_lint/levels.rs
index 2e9cd962a74..145a07d8dc8 100644
--- a/src/librustc_lint/levels.rs
+++ b/src/librustc_lint/levels.rs
@@ -125,7 +125,7 @@ impl<'s> LintLevelsBuilder<'s> {
             };
 
             let meta = unwrap_or!(attr.meta(), continue);
-            attr::mark_used(attr);
+            self.sess.mark_attr_used(attr);
 
             let mut metas = unwrap_or!(meta.meta_item_list(), continue);
 
diff --git a/src/librustc_lint/nonstandard_style.rs b/src/librustc_lint/nonstandard_style.rs
index dc6b8670498..5ca6f461048 100644
--- a/src/librustc_lint/nonstandard_style.rs
+++ b/src/librustc_lint/nonstandard_style.rs
@@ -127,7 +127,7 @@ impl EarlyLintPass for NonCamelCaseTypes {
         let has_repr_c = it
             .attrs
             .iter()
-            .any(|attr| attr::find_repr_attrs(&cx.sess.parse_sess, attr).contains(&attr::ReprC));
+            .any(|attr| attr::find_repr_attrs(&cx.sess, attr).contains(&attr::ReprC));
 
         if has_repr_c {
             return;
@@ -263,7 +263,8 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
         let crate_ident = if let Some(name) = &cx.tcx.sess.opts.crate_name {
             Some(Ident::from_str(name))
         } else {
-            attr::find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
+            cx.sess()
+                .find_by_name(&cx.tcx.hir().attrs(hir::CRATE_HIR_ID), sym::crate_name)
                 .and_then(|attr| attr.meta())
                 .and_then(|meta| {
                     meta.name_value_literal().and_then(|lit| {
@@ -327,7 +328,7 @@ impl<'tcx> LateLintPass<'tcx> for NonSnakeCase {
             },
             FnKind::ItemFn(ident, _, header, _, attrs) => {
                 // Skip foreign-ABI #[no_mangle] functions (Issue #31924)
-                if header.abi != Abi::Rust && attr::contains_name(attrs, sym::no_mangle) {
+                if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) {
                     return;
                 }
                 self.check_snake_case(cx, "function", ident);
@@ -407,7 +408,7 @@ impl NonUpperCaseGlobals {
 impl<'tcx> LateLintPass<'tcx> for NonUpperCaseGlobals {
     fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
         match it.kind {
-            hir::ItemKind::Static(..) if !attr::contains_name(&it.attrs, sym::no_mangle) => {
+            hir::ItemKind::Static(..) if !cx.sess().contains_name(&it.attrs, sym::no_mangle) => {
                 NonUpperCaseGlobals::check_upper_case(cx, "static variable", &it.ident);
             }
             hir::ItemKind::Const(..) => {
diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs
index de750010ed1..5c53e435fd7 100644
--- a/src/librustc_lint/types.rs
+++ b/src/librustc_lint/types.rs
@@ -538,7 +538,7 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi
             let guaranteed_nonnull_optimization = tcx
                 .get_attrs(def.did)
                 .iter()
-                .any(|a| a.check_name(sym::rustc_nonnull_optimization_guaranteed));
+                .any(|a| tcx.sess.check_name(a, sym::rustc_nonnull_optimization_guaranteed));
 
             if guaranteed_nonnull_optimization {
                 return true;
@@ -556,6 +556,7 @@ fn ty_is_known_nonnull<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, mode: CItemKi
         _ => false,
     }
 }
+
 /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type.
 /// If the type passed in was not scalar, returns None.
 fn get_nullable_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
@@ -1074,7 +1075,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
             }
             // If `ty` is a `repr(transparent)` newtype, and the non-zero-sized type is a generic
             // argument, which after substitution, is `()`, then this branch can be hit.
-            FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => return,
+            FfiResult::FfiUnsafe { ty, .. } if is_return_type && ty.is_unit() => {}
             FfiResult::FfiUnsafe { ty, reason, help } => {
                 self.emit_ffi_unsafe_type_lint(ty, sp, &reason, help.as_deref());
             }
diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs
index 1e98ddbd7db..5de9a16e098 100644
--- a/src/librustc_lint/unused.rs
+++ b/src/librustc_lint/unused.rs
@@ -2,7 +2,6 @@ use crate::Lint;
 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_ast::ast;
 use rustc_ast::ast::{ExprKind, StmtKind};
-use rustc_ast::attr;
 use rustc_ast::util::parser;
 use rustc_ast_pretty::pprust;
 use rustc_data_structures::fx::FxHashMap;
@@ -242,7 +241,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
             descr_post_path: &str,
         ) -> bool {
             for attr in cx.tcx.get_attrs(def_id).iter() {
-                if attr.check_name(sym::must_use) {
+                if cx.sess().check_name(attr, sym::must_use) {
                     cx.struct_span_lint(UNUSED_MUST_USE, span, |lint| {
                         let msg = format!(
                             "unused {}`{}`{} that must be used",
@@ -331,7 +330,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAttributes {
             }
         }
 
-        if !attr::is_used(attr) {
+        if !cx.sess().is_attr_used(attr) {
             debug!("emitting warning for: {:?}", attr);
             cx.struct_span_lint(UNUSED_ATTRIBUTES, attr.span, |lint| {
                 lint.build("unused attribute").emit()
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index 9bc6c054e4d..e15655e3794 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -4,8 +4,8 @@ use crate::dynamic_lib::DynamicLibrary;
 use crate::locator::{CrateError, CrateLocator, CratePaths};
 use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
 
-use rustc_ast::expand::allocator::{global_allocator_spans, AllocatorKind};
-use rustc_ast::{ast, attr};
+use rustc_ast::expand::allocator::AllocatorKind;
+use rustc_ast::{ast, visit};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::Lrc;
@@ -636,7 +636,8 @@ impl<'a> CrateLoader<'a> {
         // compilation mode also comes into play.
         let desired_strategy = self.sess.panic_strategy();
         let mut runtime_found = false;
-        let mut needs_panic_runtime = attr::contains_name(&krate.attrs, sym::needs_panic_runtime);
+        let mut needs_panic_runtime =
+            self.sess.contains_name(&krate.attrs, sym::needs_panic_runtime);
 
         self.cstore.iter_crate_data(|cnum, data| {
             needs_panic_runtime = needs_panic_runtime || data.needs_panic_runtime();
@@ -716,7 +717,7 @@ impl<'a> CrateLoader<'a> {
     }
 
     fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
-        self.cstore.has_global_allocator = match &*global_allocator_spans(krate) {
+        self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) {
             [span1, span2, ..] => {
                 self.sess
                     .struct_span_err(*span2, "cannot define multiple global allocators")
@@ -731,7 +732,7 @@ impl<'a> CrateLoader<'a> {
         // Check to see if we actually need an allocator. This desire comes
         // about through the `#![needs_allocator]` attribute and is typically
         // written down in liballoc.
-        let mut needs_allocator = attr::contains_name(&krate.attrs, sym::needs_allocator);
+        let mut needs_allocator = self.sess.contains_name(&krate.attrs, sym::needs_allocator);
         self.cstore.iter_crate_data(|_, data| {
             needs_allocator = needs_allocator || data.needs_allocator();
         });
@@ -785,7 +786,7 @@ impl<'a> CrateLoader<'a> {
         // allocator. At this point our allocator request is typically fulfilled
         // by the standard library, denoted by the `#![default_lib_allocator]`
         // attribute.
-        let mut has_default = attr::contains_name(&krate.attrs, sym::default_lib_allocator);
+        let mut has_default = self.sess.contains_name(&krate.attrs, sym::default_lib_allocator);
         self.cstore.iter_crate_data(|_, data| {
             if data.has_default_lib_allocator() {
                 has_default = true;
@@ -895,12 +896,12 @@ impl<'a> CrateLoader<'a> {
                 );
                 let name = match orig_name {
                     Some(orig_name) => {
-                        validate_crate_name(Some(self.sess), &orig_name.as_str(), Some(item.span));
+                        validate_crate_name(self.sess, &orig_name.as_str(), Some(item.span));
                         orig_name
                     }
                     None => item.ident.name,
                 };
-                let dep_kind = if attr::contains_name(&item.attrs, sym::no_link) {
+                let dep_kind = if self.sess.contains_name(&item.attrs, sym::no_link) {
                     CrateDepKind::MacrosOnly
                 } else {
                     CrateDepKind::Explicit
@@ -945,3 +946,26 @@ impl<'a> CrateLoader<'a> {
         self.maybe_resolve_crate(name, CrateDepKind::Explicit, None).ok()
     }
 }
+
+fn global_allocator_spans(sess: &Session, krate: &ast::Crate) -> Vec<Span> {
+    struct Finder<'a> {
+        sess: &'a Session,
+        name: Symbol,
+        spans: Vec<Span>,
+    }
+    impl<'ast, 'a> visit::Visitor<'ast> for Finder<'a> {
+        fn visit_item(&mut self, item: &'ast ast::Item) {
+            if item.ident.name == self.name
+                && self.sess.contains_name(&item.attrs, sym::rustc_std_internal_symbol)
+            {
+                self.spans.push(item.span);
+            }
+            visit::walk_item(self, item)
+        }
+    }
+
+    let name = Symbol::intern(&AllocatorKind::Global.fn_name(sym::alloc));
+    let mut f = Finder { sess, name, spans: Vec::new() };
+    visit::walk_crate(&mut f, krate);
+    f.spans
+}
diff --git a/src/librustc_metadata/link_args.rs b/src/librustc_metadata/link_args.rs
index c5a43b91b5e..d8f16796083 100644
--- a/src/librustc_metadata/link_args.rs
+++ b/src/librustc_metadata/link_args.rs
@@ -5,7 +5,7 @@ use rustc_span::symbol::{sym, Symbol};
 use rustc_target::spec::abi::Abi;
 
 crate fn collect(tcx: TyCtxt<'_>) -> Vec<String> {
-    let mut collector = Collector { args: Vec::new() };
+    let mut collector = Collector { tcx, args: Vec::new() };
     tcx.hir().krate().visit_all_item_likes(&mut collector);
 
     for attr in tcx.hir().krate().item.attrs.iter() {
@@ -19,11 +19,12 @@ crate fn collect(tcx: TyCtxt<'_>) -> Vec<String> {
     collector.args
 }
 
-struct Collector {
+struct Collector<'tcx> {
+    tcx: TyCtxt<'tcx>,
     args: Vec<String>,
 }
 
-impl<'tcx> ItemLikeVisitor<'tcx> for Collector {
+impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
     fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
         let fm = match it.kind {
             hir::ItemKind::ForeignMod(ref fm) => fm,
@@ -34,7 +35,8 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector {
         }
 
         // First, add all of the custom #[link_args] attributes
-        for m in it.attrs.iter().filter(|a| a.check_name(sym::link_args)) {
+        let sess = &self.tcx.sess;
+        for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link_args)) {
             if let Some(linkarg) = m.value_str() {
                 self.add_link_args(linkarg);
             }
@@ -45,7 +47,7 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector {
     fn visit_impl_item(&mut self, _it: &'tcx hir::ImplItem<'tcx>) {}
 }
 
-impl Collector {
+impl<'tcx> Collector<'tcx> {
     fn add_link_args(&mut self, args: Symbol) {
         self.args.extend(args.as_str().split(' ').filter(|s| !s.is_empty()).map(|s| s.to_string()))
     }
diff --git a/src/librustc_metadata/native_libs.rs b/src/librustc_metadata/native_libs.rs
index d01c598d059..3976475cb06 100644
--- a/src/librustc_metadata/native_libs.rs
+++ b/src/librustc_metadata/native_libs.rs
@@ -43,7 +43,8 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
         }
 
         // Process all of the #[link(..)]-style arguments
-        for m in it.attrs.iter().filter(|a| a.check_name(sym::link)) {
+        let sess = &self.tcx.sess;
+        for m in it.attrs.iter().filter(|a| sess.check_name(a, sym::link)) {
             let items = match m.meta_item_list() {
                 Some(item) => item,
                 None => continue,
@@ -71,16 +72,10 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
                         "framework" => NativeLibKind::Framework,
                         "raw-dylib" => NativeLibKind::RawDylib,
                         k => {
-                            struct_span_err!(
-                                self.tcx.sess,
-                                item.span(),
-                                E0458,
-                                "unknown kind: `{}`",
-                                k
-                            )
-                            .span_label(item.span(), "unknown kind")
-                            .span_label(m.span, "")
-                            .emit();
+                            struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
+                                .span_label(item.span(), "unknown kind")
+                                .span_label(m.span, "")
+                                .emit();
                             NativeLibKind::Unspecified
                         }
                     };
@@ -92,18 +87,18 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
                         None => continue, // skip like historical compilers
                     };
                     if cfg.is_empty() {
-                        self.tcx.sess.span_err(item.span(), "`cfg()` must have an argument");
+                        sess.span_err(item.span(), "`cfg()` must have an argument");
                     } else if let cfg @ Some(..) = cfg[0].meta_item() {
                         lib.cfg = cfg.cloned();
                     } else {
-                        self.tcx.sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
+                        sess.span_err(cfg[0].span(), "invalid argument for `cfg(..)`");
                     }
                 } else if item.has_name(sym::wasm_import_module) {
                     match item.value_str() {
                         Some(s) => lib.wasm_import_module = Some(s),
                         None => {
                             let msg = "must be of the form `#[link(wasm_import_module = \"...\")]`";
-                            self.tcx.sess.span_err(item.span(), msg);
+                            sess.span_err(item.span(), msg);
                         }
                     }
                 } else {
@@ -117,7 +112,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
             let requires_name = kind_specified || lib.wasm_import_module.is_none();
             if lib.name.is_none() && requires_name {
                 struct_span_err!(
-                    self.tcx.sess,
+                    sess,
                     m.span,
                     E0459,
                     "`#[link(...)]` specified without \
diff --git a/src/librustc_metadata/rmeta/decoder.rs b/src/librustc_metadata/rmeta/decoder.rs
index 9fb4e08e284..8aea9a9f588 100644
--- a/src/librustc_metadata/rmeta/decoder.rs
+++ b/src/librustc_metadata/rmeta/decoder.rs
@@ -742,7 +742,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
         };
 
         SyntaxExtension::new(
-            &sess.parse_sess,
+            sess,
             kind,
             self.get_span(id, sess),
             helper_attrs,
@@ -1102,7 +1102,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                                 // for other constructors correct visibilities
                                 // were already encoded in metadata.
                                 let attrs = self.get_item_attrs(def_id.index, sess);
-                                if attr::contains_name(&attrs, sym::non_exhaustive) {
+                                if sess.contains_name(&attrs, sym::non_exhaustive) {
                                     let crate_def_id = self.local_def_id(CRATE_DEF_INDEX);
                                     vis = ty::Visibility::Restricted(crate_def_id);
                                 }
diff --git a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
index e51862be9a8..10b89cdd15a 100644
--- a/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
+++ b/src/librustc_metadata/rmeta/decoder/cstore_impl.rs
@@ -5,7 +5,6 @@ use crate::native_libs;
 use crate::rmeta::{self, encoder};
 
 use rustc_ast::ast;
-use rustc_ast::attr;
 use rustc_ast::expand::allocator::AllocatorKind;
 use rustc_data_structures::svh::Svh;
 use rustc_hir as hir;
@@ -37,7 +36,7 @@ macro_rules! provide {
                 def_id_arg: ty::query::query_keys::$name<$lt>,
             ) -> ty::query::query_values::$name<$lt> {
                 let _prof_timer =
-                    $tcx.prof.generic_activity("metadata_decode_entry");
+                    $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name)));
 
                 #[allow(unused_variables)]
                 let ($def_id, $other) = def_id_arg.into_args();
@@ -415,7 +414,7 @@ impl CStore {
         // Mark the attrs as used
         let attrs = data.get_item_attrs(id.index, sess);
         for attr in attrs.iter() {
-            attr::mark_used(attr);
+            sess.mark_attr_used(attr);
         }
 
         let ident = data.item_ident(id.index, sess);
diff --git a/src/librustc_metadata/rmeta/encoder.rs b/src/librustc_metadata/rmeta/encoder.rs
index acae44e6bf7..6723e236a1f 100644
--- a/src/librustc_metadata/rmeta/encoder.rs
+++ b/src/librustc_metadata/rmeta/encoder.rs
@@ -3,9 +3,8 @@ use crate::rmeta::*;
 
 use log::{debug, trace};
 use rustc_ast::ast;
-use rustc_ast::attr;
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_data_structures::stable_hasher::StableHasher;
 use rustc_data_structures::sync::{join, Lrc};
 use rustc_hir as hir;
@@ -49,8 +48,7 @@ struct EncodeContext<'a, 'tcx> {
     type_shorthands: FxHashMap<Ty<'tcx>, usize>,
     predicate_shorthands: FxHashMap<ty::Predicate<'tcx>, usize>,
 
-    interpret_allocs: FxHashMap<interpret::AllocId, usize>,
-    interpret_allocs_inverse: Vec<interpret::AllocId>,
+    interpret_allocs: FxIndexSet<interpret::AllocId>,
 
     // This is used to speed up Span encoding.
     // The `usize` is an index into the `MonotonicVec`
@@ -162,7 +160,7 @@ impl<'a, 'tcx> SpecializedEncoder<ExpnId> for EncodeContext<'a, 'tcx> {
     fn specialized_encode(&mut self, expn: &ExpnId) -> Result<(), Self::Error> {
         rustc_span::hygiene::raw_encode_expn_id(
             *expn,
-            &mut self.hygiene_ctxt,
+            &self.hygiene_ctxt,
             ExpnDataEncodeMode::Metadata,
             self,
         )
@@ -280,6 +278,10 @@ impl<'a, 'tcx> SpecializedEncoder<Span> for EncodeContext<'a, 'tcx> {
         // cross-crate inconsistencies (getting one behavior in the same
         // crate, and a different behavior in another crate) due to the
         // limited surface that proc-macros can expose.
+        //
+        // IMPORTANT: If this is ever changed, be sure to update
+        // `rustc_span::hygiene::raw_encode_expn_id` to handle
+        // encoding `ExpnData` for proc-macro crates.
         if self.is_proc_macro {
             SyntaxContext::root().encode(self)?;
         } else {
@@ -328,17 +330,7 @@ impl<'a, 'b, 'tcx> SpecializedEncoder<ty::Predicate<'b>> for EncodeContext<'a, '
 
 impl<'a, 'tcx> SpecializedEncoder<interpret::AllocId> for EncodeContext<'a, 'tcx> {
     fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> {
-        use std::collections::hash_map::Entry;
-        let index = match self.interpret_allocs.entry(*alloc_id) {
-            Entry::Occupied(e) => *e.get(),
-            Entry::Vacant(e) => {
-                let idx = self.interpret_allocs_inverse.len();
-                self.interpret_allocs_inverse.push(*alloc_id);
-                e.insert(idx);
-                idx
-            }
-        };
-
+        let (index, _) = self.interpret_allocs.insert_full(*alloc_id);
         index.encode(self)
     }
 }
@@ -580,7 +572,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             let mut n = 0;
             trace!("beginning to encode alloc ids");
             loop {
-                let new_n = self.interpret_allocs_inverse.len();
+                let new_n = self.interpret_allocs.len();
                 // if we have found new ids, serialize those, too
                 if n == new_n {
                     // otherwise, abort
@@ -588,7 +580,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
                 }
                 trace!("encoding {} further alloc ids", new_n - n);
                 for idx in n..new_n {
-                    let id = self.interpret_allocs_inverse[idx];
+                    let id = self.interpret_allocs[idx];
                     let pos = self.position() as u32;
                     interpret_alloc_index.push(pos);
                     interpret::specialized_encode_alloc_id(self, tcx, id).unwrap();
@@ -633,7 +625,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
         let source_map_bytes = self.position() - i;
 
         let attrs = tcx.hir().krate_attrs();
-        let has_default_lib_allocator = attr::contains_name(&attrs, sym::default_lib_allocator);
+        let has_default_lib_allocator = tcx.sess.contains_name(&attrs, sym::default_lib_allocator);
 
         let root = self.lazy(CrateRoot {
             name: tcx.crate_name(LOCAL_CRATE),
@@ -659,12 +651,12 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
             } else {
                 None
             },
-            compiler_builtins: attr::contains_name(&attrs, sym::compiler_builtins),
-            needs_allocator: attr::contains_name(&attrs, sym::needs_allocator),
-            needs_panic_runtime: attr::contains_name(&attrs, sym::needs_panic_runtime),
-            no_builtins: attr::contains_name(&attrs, sym::no_builtins),
-            panic_runtime: attr::contains_name(&attrs, sym::panic_runtime),
-            profiler_runtime: attr::contains_name(&attrs, sym::profiler_runtime),
+            compiler_builtins: tcx.sess.contains_name(&attrs, sym::compiler_builtins),
+            needs_allocator: tcx.sess.contains_name(&attrs, sym::needs_allocator),
+            needs_panic_runtime: tcx.sess.contains_name(&attrs, sym::needs_panic_runtime),
+            no_builtins: tcx.sess.contains_name(&attrs, sym::no_builtins),
+            panic_runtime: tcx.sess.contains_name(&attrs, sym::panic_runtime),
+            profiler_runtime: tcx.sess.contains_name(&attrs, sym::profiler_runtime),
             symbol_mangling_version: tcx.sess.opts.debugging_opts.symbol_mangling_version,
 
             crate_deps,
@@ -2016,7 +2008,6 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>) -> EncodedMetadata {
         predicate_shorthands: Default::default(),
         source_file_cache: (source_map_files[0].clone(), 0),
         interpret_allocs: Default::default(),
-        interpret_allocs_inverse: Default::default(),
         required_source_files: Some(GrowableBitSet::with_capacity(source_map_files.len())),
         is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro),
         hygiene_ctxt: &hygiene_ctxt,
diff --git a/src/librustc_middle/middle/limits.rs b/src/librustc_middle/middle/limits.rs
index 85198482bd3..e9820539484 100644
--- a/src/librustc_middle/middle/limits.rs
+++ b/src/librustc_middle/middle/limits.rs
@@ -27,7 +27,7 @@ fn update_limit(
     default: usize,
 ) {
     for attr in &krate.attrs {
-        if !attr.check_name(name) {
+        if !sess.check_name(attr, name) {
             continue;
         }
 
diff --git a/src/librustc_middle/traits/query.rs b/src/librustc_middle/traits/query.rs
index 69696ac9e93..4b7663e9ade 100644
--- a/src/librustc_middle/traits/query.rs
+++ b/src/librustc_middle/traits/query.rs
@@ -128,7 +128,7 @@ pub struct DropckOutlivesResult<'tcx> {
 
 impl<'tcx> DropckOutlivesResult<'tcx> {
     pub fn report_overflows(&self, tcx: TyCtxt<'tcx>, span: Span, ty: Ty<'tcx>) {
-        if let Some(overflow_ty) = self.overflows.iter().next() {
+        if let Some(overflow_ty) = self.overflows.get(0) {
             let mut err = struct_span_err!(
                 tcx.sess,
                 span,
diff --git a/src/librustc_middle/ty/context.rs b/src/librustc_middle/ty/context.rs
index 6cbf5db8373..d6bcfbf49cf 100644
--- a/src/librustc_middle/ty/context.rs
+++ b/src/librustc_middle/ty/context.rs
@@ -1040,7 +1040,7 @@ impl<'tcx> TyCtxt<'tcx> {
     pub fn layout_scalar_valid_range(self, def_id: DefId) -> (Bound<u128>, Bound<u128>) {
         let attrs = self.get_attrs(def_id);
         let get = |name| {
-            let attr = match attrs.iter().find(|a| a.check_name(name)) {
+            let attr = match attrs.iter().find(|a| self.sess.check_name(a, name)) {
                 Some(attr) => attr,
                 None => return Bound::Unbounded,
             };
@@ -1380,7 +1380,9 @@ impl<'tcx> TyCtxt<'tcx> {
     /// we still evaluate them eagerly.
     #[inline]
     pub fn lazy_normalization(self) -> bool {
-        self.features().const_generics || self.features().lazy_normalization_consts
+        let features = self.features();
+        // Note: We do not enable lazy normalization for `features.min_const_generics`.
+        features.const_generics || features.lazy_normalization_consts
     }
 
     #[inline]
@@ -2736,11 +2738,11 @@ pub fn provide(providers: &mut ty::query::Providers) {
     };
     providers.is_panic_runtime = |tcx, cnum| {
         assert_eq!(cnum, LOCAL_CRATE);
-        attr::contains_name(tcx.hir().krate_attrs(), sym::panic_runtime)
+        tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::panic_runtime)
     };
     providers.is_compiler_builtins = |tcx, cnum| {
         assert_eq!(cnum, LOCAL_CRATE);
-        attr::contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins)
+        tcx.sess.contains_name(tcx.hir().krate_attrs(), sym::compiler_builtins)
     };
     providers.has_panic_handler = |tcx, cnum| {
         assert_eq!(cnum, LOCAL_CRATE);
diff --git a/src/librustc_middle/ty/flags.rs b/src/librustc_middle/ty/flags.rs
index 27f50c240db..81f7474962c 100644
--- a/src/librustc_middle/ty/flags.rs
+++ b/src/librustc_middle/ty/flags.rs
@@ -85,6 +85,8 @@ impl FlagComputation {
             }
 
             &ty::Generator(_, ref substs, _) => {
+                self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
                 let substs = substs.as_generator();
                 let should_remove_further_specializable =
                     !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
@@ -107,6 +109,8 @@ impl FlagComputation {
             }
 
             &ty::Closure(_, substs) => {
+                self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
                 let substs = substs.as_closure();
                 let should_remove_further_specializable =
                     !self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
@@ -192,6 +196,8 @@ impl FlagComputation {
             }
 
             &ty::FnDef(_, substs) => {
+                self.add_flags(TypeFlags::MAY_POLYMORPHIZE);
+
                 self.add_substs(substs);
             }
 
diff --git a/src/librustc_middle/ty/fold.rs b/src/librustc_middle/ty/fold.rs
index 492f8ce9ef1..87434f3f267 100644
--- a/src/librustc_middle/ty/fold.rs
+++ b/src/librustc_middle/ty/fold.rs
@@ -150,6 +150,12 @@ pub trait TypeFoldable<'tcx>: fmt::Debug + Clone {
         self.has_type_flags(TypeFlags::STILL_FURTHER_SPECIALIZABLE)
     }
 
+    /// Does this value contain closures, generators or functions such that it may require
+    /// polymorphization?
+    fn may_polymorphize(&self) -> bool {
+        self.has_type_flags(TypeFlags::MAY_POLYMORPHIZE)
+    }
+
     /// A visitor that does not recurse into types, works like `fn walk_shallow` in `Ty`.
     fn visit_tys_shallow(&self, visit: impl FnMut(Ty<'tcx>) -> bool) -> bool {
         pub struct Visitor<F>(F);
diff --git a/src/librustc_middle/ty/instance.rs b/src/librustc_middle/ty/instance.rs
index 289a9db7327..cf876db26bc 100644
--- a/src/librustc_middle/ty/instance.rs
+++ b/src/librustc_middle/ty/instance.rs
@@ -474,26 +474,7 @@ impl<'tcx> Instance<'tcx> {
         }
 
         if let InstanceDef::Item(def) = self.def {
-            let unused = tcx.unused_generic_params(def.did);
-
-            if unused.is_empty() {
-                // Exit early if every parameter was used.
-                return self;
-            }
-
-            debug!("polymorphize: unused={:?}", unused);
-            let polymorphized_substs =
-                InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind {
-                // If parameter is a const or type parameter..
-                ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
-                    // ..and is within range and unused..
-                    unused.contains(param.index).unwrap_or(false) =>
-                        // ..then use the identity for this parameter.
-                        tcx.mk_param_from_def(param),
-                // Otherwise, use the parameter as before.
-                _ => self.substs[param.index as usize],
-            });
-
+            let polymorphized_substs = polymorphize(tcx, def.did, self.substs);
             debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
             Self { def: self.def, substs: polymorphized_substs }
         } else {
@@ -502,6 +483,82 @@ impl<'tcx> Instance<'tcx> {
     }
 }
 
+fn polymorphize<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: DefId,
+    substs: SubstsRef<'tcx>,
+) -> SubstsRef<'tcx> {
+    debug!("polymorphize({:?}, {:?})", def_id, substs);
+    let unused = tcx.unused_generic_params(def_id);
+    debug!("polymorphize: unused={:?}", unused);
+
+    struct PolymorphizationFolder<'tcx> {
+        tcx: TyCtxt<'tcx>,
+    };
+
+    impl ty::TypeFolder<'tcx> for PolymorphizationFolder<'tcx> {
+        fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+            self.tcx
+        }
+
+        fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+            debug!("fold_ty: ty={:?}", ty);
+            match ty.kind {
+                ty::Closure(def_id, substs) => {
+                    let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+                    if substs == polymorphized_substs {
+                        ty
+                    } else {
+                        self.tcx.mk_closure(def_id, polymorphized_substs)
+                    }
+                }
+                ty::FnDef(def_id, substs) => {
+                    let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+                    if substs == polymorphized_substs {
+                        ty
+                    } else {
+                        self.tcx.mk_fn_def(def_id, polymorphized_substs)
+                    }
+                }
+                ty::Generator(def_id, substs, movability) => {
+                    let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
+                    if substs == polymorphized_substs {
+                        ty
+                    } else {
+                        self.tcx.mk_generator(def_id, polymorphized_substs, movability)
+                    }
+                }
+                _ => ty.super_fold_with(self),
+            }
+        }
+    }
+
+    InternalSubsts::for_item(tcx, def_id, |param, _| {
+        let is_unused = unused.contains(param.index).unwrap_or(false);
+        debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
+        match param.kind {
+            // If parameter is a const or type parameter..
+            ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
+                // ..and is within range and unused..
+                unused.contains(param.index).unwrap_or(false) =>
+                    // ..then use the identity for this parameter.
+                    tcx.mk_param_from_def(param),
+
+            // If the parameter does not contain any closures or generators, then use the
+            // substitution directly.
+            _ if !substs.may_polymorphize() => substs[param.index as usize],
+
+            // Otherwise, use the substitution after polymorphizing.
+            _ => {
+                let arg = substs[param.index as usize];
+                let polymorphized_arg = arg.fold_with(&mut PolymorphizationFolder { tcx });
+                debug!("polymorphize: arg={:?} polymorphized_arg={:?}", arg, polymorphized_arg);
+                ty::GenericArg::from(polymorphized_arg)
+            }
+        }
+    })
+}
+
 fn needs_fn_once_adapter_shim(
     actual_closure_kind: ty::ClosureKind,
     trait_closure_kind: ty::ClosureKind,
diff --git a/src/librustc_middle/ty/mod.rs b/src/librustc_middle/ty/mod.rs
index bd45f866abc..0102225b9b5 100644
--- a/src/librustc_middle/ty/mod.rs
+++ b/src/librustc_middle/ty/mod.rs
@@ -575,6 +575,10 @@ bitflags! {
         /// Does this value have parameters/placeholders/inference variables which could be
         /// replaced later, in a way that would change the results of `impl` specialization?
         const STILL_FURTHER_SPECIALIZABLE = 1 << 17;
+
+        /// Does this value contain closures, generators or functions such that it may require
+        /// polymorphization?
+        const MAY_POLYMORPHIZE = 1 << 18;
     }
 }
 
@@ -2261,7 +2265,7 @@ impl ReprOptions {
         let mut max_align: Option<Align> = None;
         let mut min_pack: Option<Align> = None;
         for attr in tcx.get_attrs(did).iter() {
-            for r in attr::find_repr_attrs(&tcx.sess.parse_sess, attr) {
+            for r in attr::find_repr_attrs(&tcx.sess, attr) {
                 flags.insert(match r {
                     attr::ReprC => ReprFlags::IS_C,
                     attr::ReprPacked(pack) => {
@@ -2378,7 +2382,7 @@ impl<'tcx> AdtDef {
         }
 
         let attrs = tcx.get_attrs(did);
-        if attr::contains_name(&attrs, sym::fundamental) {
+        if tcx.sess.contains_name(&attrs, sym::fundamental) {
             flags |= AdtFlags::IS_FUNDAMENTAL;
         }
         if Some(did) == tcx.lang_items().phantom_data() {
@@ -3017,7 +3021,7 @@ impl<'tcx> TyCtxt<'tcx> {
 
     /// Determines whether an item is annotated with an attribute.
     pub fn has_attr(self, did: DefId, attr: Symbol) -> bool {
-        attr::contains_name(&self.get_attrs(did), attr)
+        self.sess.contains_name(&self.get_attrs(did), attr)
     }
 
     /// Returns `true` if this is an `auto trait`.
diff --git a/src/librustc_middle/ty/query/job.rs b/src/librustc_middle/ty/query/job.rs
index 1811d945a1d..bd2e7747b7d 100644
--- a/src/librustc_middle/ty/query/job.rs
+++ b/src/librustc_middle/ty/query/job.rs
@@ -15,16 +15,12 @@ pub unsafe fn handle_deadlock() {
     rustc_data_structures::sync::assert_sync::<tls::ImplicitCtxt<'_, '_>>();
     let icx: &tls::ImplicitCtxt<'_, '_> = &*(context as *const tls::ImplicitCtxt<'_, '_>);
 
-    let span_session_globals = rustc_span::SESSION_GLOBALS.with(|ssg| ssg as *const _);
-    let span_session_globals = &*span_session_globals;
-    let ast_session_globals = rustc_ast::attr::SESSION_GLOBALS.with(|asg| asg as *const _);
-    let ast_session_globals = &*ast_session_globals;
+    let session_globals = rustc_span::SESSION_GLOBALS.with(|sg| sg as *const _);
+    let session_globals = &*session_globals;
     thread::spawn(move || {
         tls::enter_context(icx, |_| {
-            rustc_ast::attr::SESSION_GLOBALS.set(ast_session_globals, || {
-                rustc_span::SESSION_GLOBALS
-                    .set(span_session_globals, || tls::with(|tcx| deadlock(tcx, &registry)))
-            });
+            rustc_span::SESSION_GLOBALS
+                .set(session_globals, || tls::with(|tcx| deadlock(tcx, &registry)))
         })
     });
 }
diff --git a/src/librustc_middle/ty/query/on_disk_cache.rs b/src/librustc_middle/ty/query/on_disk_cache.rs
index 643fbe793ab..08b0bfecf49 100644
--- a/src/librustc_middle/ty/query/on_disk_cache.rs
+++ b/src/librustc_middle/ty/query/on_disk_cache.rs
@@ -5,7 +5,7 @@ use crate::ty::codec::{self as ty_codec, TyDecoder, TyEncoder};
 use crate::ty::context::TyCtxt;
 use crate::ty::{self, Ty};
 use rustc_data_structures::fingerprint::Fingerprint;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_data_structures::sync::{HashMapExt, Lock, Lrc, OnceCell};
 use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::Diagnostic;
@@ -212,7 +212,6 @@ impl<'sess> OnDiskCache<'sess> {
                 type_shorthands: Default::default(),
                 predicate_shorthands: Default::default(),
                 interpret_allocs: Default::default(),
-                interpret_allocs_inverse: Vec::new(),
                 source_map: CachingSourceMapView::new(tcx.sess.source_map()),
                 file_to_file_index,
                 hygiene_context: &hygiene_encode_context,
@@ -267,7 +266,7 @@ impl<'sess> OnDiskCache<'sess> {
                 let mut interpret_alloc_index = Vec::new();
                 let mut n = 0;
                 loop {
-                    let new_n = encoder.interpret_allocs_inverse.len();
+                    let new_n = encoder.interpret_allocs.len();
                     // If we have found new IDs, serialize those too.
                     if n == new_n {
                         // Otherwise, abort.
@@ -275,7 +274,7 @@ impl<'sess> OnDiskCache<'sess> {
                     }
                     interpret_alloc_index.reserve(new_n - n);
                     for idx in n..new_n {
-                        let id = encoder.interpret_allocs_inverse[idx];
+                        let id = encoder.interpret_allocs[idx];
                         let pos = encoder.position() as u32;
                         interpret_alloc_index.push(pos);
                         interpret::specialized_encode_alloc_id(&mut encoder, tcx, id)?;
@@ -767,8 +766,7 @@ struct CacheEncoder<'a, 'tcx, E: ty_codec::TyEncoder> {
     encoder: &'a mut E,
     type_shorthands: FxHashMap<Ty<'tcx>, usize>,
     predicate_shorthands: FxHashMap<ty::Predicate<'tcx>, usize>,
-    interpret_allocs: FxHashMap<interpret::AllocId, usize>,
-    interpret_allocs_inverse: Vec<interpret::AllocId>,
+    interpret_allocs: FxIndexSet<interpret::AllocId>,
     source_map: CachingSourceMapView<'tcx>,
     file_to_file_index: FxHashMap<*const SourceFile, SourceFileIndex>,
     hygiene_context: &'a HygieneEncodeContext,
@@ -807,17 +805,7 @@ where
     E: 'a + TyEncoder,
 {
     fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> {
-        use std::collections::hash_map::Entry;
-        let index = match self.interpret_allocs.entry(*alloc_id) {
-            Entry::Occupied(e) => *e.get(),
-            Entry::Vacant(e) => {
-                let idx = self.interpret_allocs_inverse.len();
-                self.interpret_allocs_inverse.push(*alloc_id);
-                e.insert(idx);
-                idx
-            }
-        };
-
+        let (index, _) = self.interpret_allocs.insert_full(*alloc_id);
         index.encode(self)
     }
 }
diff --git a/src/librustc_mir/borrow_check/borrow_set.rs b/src/librustc_mir/borrow_check/borrow_set.rs
index ef9af7bace9..b4299fbc5a1 100644
--- a/src/librustc_mir/borrow_check/borrow_set.rs
+++ b/src/librustc_mir/borrow_check/borrow_set.rs
@@ -3,9 +3,8 @@ use crate::borrow_check::path_utils::allow_two_phase_borrow;
 use crate::borrow_check::place_ext::PlaceExt;
 use crate::dataflow::indexes::BorrowIndex;
 use crate::dataflow::move_paths::MoveData;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
 use rustc_middle::mir::traversal;
 use rustc_middle::mir::visit::{MutatingUseContext, NonUseContext, PlaceContext, Visitor};
 use rustc_middle::mir::{self, Body, Local, Location};
@@ -15,14 +14,11 @@ use std::ops::Index;
 
 crate struct BorrowSet<'tcx> {
     /// The fundamental map relating bitvector indexes to the borrows
-    /// in the MIR.
-    crate borrows: IndexVec<BorrowIndex, BorrowData<'tcx>>,
-
-    /// Each borrow is also uniquely identified in the MIR by the
-    /// `Location` of the assignment statement in which it appears on
-    /// the right hand side; we map each such location to the
-    /// corresponding `BorrowIndex`.
-    crate location_map: FxHashMap<Location, BorrowIndex>,
+    /// in the MIR. Each borrow is also uniquely identified in the MIR
+    /// by the `Location` of the assignment statement in which it
+    /// appears on the right hand side. Thus the location is the map
+    /// key, and its position in the map corresponds to `BorrowIndex`.
+    crate location_map: FxIndexMap<Location, BorrowData<'tcx>>,
 
     /// Locations which activate borrows.
     /// NOTE: a given location may activate more than one borrow in the future
@@ -40,7 +36,7 @@ impl<'tcx> Index<BorrowIndex> for BorrowSet<'tcx> {
     type Output = BorrowData<'tcx>;
 
     fn index(&self, index: BorrowIndex) -> &BorrowData<'tcx> {
-        &self.borrows[index]
+        &self.location_map[index.as_usize()]
     }
 }
 
@@ -129,7 +125,6 @@ impl<'tcx> BorrowSet<'tcx> {
         let mut visitor = GatherBorrows {
             tcx,
             body: &body,
-            idx_vec: IndexVec::new(),
             location_map: Default::default(),
             activation_map: Default::default(),
             local_map: Default::default(),
@@ -146,7 +141,6 @@ impl<'tcx> BorrowSet<'tcx> {
         }
 
         BorrowSet {
-            borrows: visitor.idx_vec,
             location_map: visitor.location_map,
             activation_map: visitor.activation_map,
             local_map: visitor.local_map,
@@ -157,13 +151,32 @@ impl<'tcx> BorrowSet<'tcx> {
     crate fn activations_at_location(&self, location: Location) -> &[BorrowIndex] {
         self.activation_map.get(&location).map(|activations| &activations[..]).unwrap_or(&[])
     }
+
+    crate fn len(&self) -> usize {
+        self.location_map.len()
+    }
+
+    crate fn indices(&self) -> impl Iterator<Item = BorrowIndex> {
+        BorrowIndex::from_usize(0)..BorrowIndex::from_usize(self.len())
+    }
+
+    crate fn iter_enumerated(&self) -> impl Iterator<Item = (BorrowIndex, &BorrowData<'tcx>)> {
+        self.indices().zip(self.location_map.values())
+    }
+
+    crate fn get_index_of(&self, location: &Location) -> Option<BorrowIndex> {
+        self.location_map.get_index_of(location).map(BorrowIndex::from)
+    }
+
+    crate fn contains(&self, location: &Location) -> bool {
+        self.location_map.contains_key(location)
+    }
 }
 
 struct GatherBorrows<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
     body: &'a Body<'tcx>,
-    idx_vec: IndexVec<BorrowIndex, BorrowData<'tcx>>,
-    location_map: FxHashMap<Location, BorrowIndex>,
+    location_map: FxIndexMap<Location, BorrowData<'tcx>>,
     activation_map: FxHashMap<Location, Vec<BorrowIndex>>,
     local_map: FxHashMap<mir::Local, FxHashSet<BorrowIndex>>,
 
@@ -203,8 +216,8 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
                 borrowed_place: *borrowed_place,
                 assigned_place: *assigned_place,
             };
-            let idx = self.idx_vec.push(borrow);
-            self.location_map.insert(location, idx);
+            let (idx, _) = self.location_map.insert_full(location, borrow);
+            let idx = BorrowIndex::from(idx);
 
             self.insert_as_pending_if_two_phase(location, assigned_place, kind, idx);
 
@@ -224,7 +237,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
         //
         //     TMP = &mut place
         if let Some(&borrow_index) = self.pending_activations.get(temp) {
-            let borrow_data = &mut self.idx_vec[borrow_index];
+            let borrow_data = &mut self.location_map[borrow_index.as_usize()];
 
             // Watch out: the use of TMP in the borrow itself
             // doesn't count as an activation. =)
@@ -265,8 +278,7 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherBorrows<'a, 'tcx> {
         if let mir::Rvalue::Ref(region, kind, ref place) = *rvalue {
             // double-check that we already registered a BorrowData for this
 
-            let borrow_index = self.location_map[&location];
-            let borrow_data = &self.idx_vec[borrow_index];
+            let borrow_data = &self.location_map[&location];
             assert_eq!(borrow_data.reserve_location, location);
             assert_eq!(borrow_data.kind, kind);
             assert_eq!(borrow_data.region, region.to_region_vid());
@@ -316,7 +328,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
         // Consider the borrow not activated to start. When we find an activation, we'll update
         // this field.
         {
-            let borrow_data = &mut self.idx_vec[borrow_index];
+            let borrow_data = &mut self.location_map[borrow_index.as_usize()];
             borrow_data.activation_location = TwoPhaseActivation::NotActivated;
         }
 
@@ -332,7 +344,7 @@ impl<'a, 'tcx> GatherBorrows<'a, 'tcx> {
                        at borrow_index: {:?} with associated data {:?}",
                 temp,
                 old_index,
-                self.idx_vec[old_index]
+                self.location_map[old_index.as_usize()]
             );
         }
     }
diff --git a/src/librustc_mir/borrow_check/constraint_generation.rs b/src/librustc_mir/borrow_check/constraint_generation.rs
index e0420d974fb..33b09dcb888 100644
--- a/src/librustc_mir/borrow_check/constraint_generation.rs
+++ b/src/librustc_mir/borrow_check/constraint_generation.rs
@@ -217,7 +217,7 @@ impl<'cx, 'cg, 'tcx> ConstraintGeneration<'cx, 'cg, 'tcx> {
                             let places_conflict = places_conflict::places_conflict(
                                 self.infcx.tcx,
                                 self.body,
-                                self.borrow_set.borrows[borrow_index].borrowed_place,
+                                self.borrow_set[borrow_index].borrowed_place,
                                 place,
                                 places_conflict::PlaceConflictBias::NoOverlap,
                             );
diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs
index d8f6abd92f6..ba74ffaa8d6 100644
--- a/src/librustc_mir/borrow_check/diagnostics/mod.rs
+++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs
@@ -868,7 +868,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 }
             }
         }
-        return normal_ret;
+        normal_ret
     }
 
     /// Finds the span of arguments of a closure (within `maybe_closure_span`)
diff --git a/src/librustc_mir/borrow_check/invalidation.rs b/src/librustc_mir/borrow_check/invalidation.rs
index fd8f17718e7..2de2124dc5e 100644
--- a/src/librustc_mir/borrow_check/invalidation.rs
+++ b/src/librustc_mir/borrow_check/invalidation.rs
@@ -166,8 +166,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
                 // Invalidate all borrows of local places
                 let borrow_set = self.borrow_set.clone();
                 let resume = self.location_table.start_index(resume.start_location());
-                for i in borrow_set.borrows.indices() {
-                    if borrow_of_local_data(borrow_set.borrows[i].borrowed_place) {
+                for (i, data) in borrow_set.iter_enumerated() {
+                    if borrow_of_local_data(data.borrowed_place) {
                         self.all_facts.invalidates.push((resume, i));
                     }
                 }
@@ -178,8 +178,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
                 // Invalidate all borrows of local places
                 let borrow_set = self.borrow_set.clone();
                 let start = self.location_table.start_index(location);
-                for i in borrow_set.borrows.indices() {
-                    if borrow_of_local_data(borrow_set.borrows[i].borrowed_place) {
+                for (i, data) in borrow_set.iter_enumerated() {
+                    if borrow_of_local_data(data.borrowed_place) {
                         self.all_facts.invalidates.push((start, i));
                     }
                 }
@@ -369,7 +369,7 @@ impl<'cx, 'tcx> InvalidationGenerator<'cx, 'tcx> {
         let tcx = self.tcx;
         let body = self.body;
         let borrow_set = self.borrow_set.clone();
-        let indices = self.borrow_set.borrows.indices();
+        let indices = self.borrow_set.indices();
         each_borrow_involving_path(
             self,
             tcx,
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
index 76cc03fa609..6e211b42a05 100644
--- a/src/librustc_mir/borrow_check/mod.rs
+++ b/src/librustc_mir/borrow_check/mod.rs
@@ -1131,11 +1131,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                 (
                     Reservation(WriteKind::MutableBorrow(bk)),
                     BorrowKind::Shallow | BorrowKind::Shared,
-                ) if {
-                    tcx.migrate_borrowck() && this.borrow_set.location_map.contains_key(&location)
-                } =>
-                {
-                    let bi = this.borrow_set.location_map[&location];
+                ) if { tcx.migrate_borrowck() && this.borrow_set.contains(&location) } => {
+                    let bi = this.borrow_set.get_index_of(&location).unwrap();
                     debug!(
                         "recording invalid reservation of place: {:?} with \
                          borrow index {:?} as warning",
diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs
index f6b3be59d95..66a17cba6bb 100644
--- a/src/librustc_mir/borrow_check/nll.rs
+++ b/src/librustc_mir/borrow_check/nll.rs
@@ -206,7 +206,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>(
         //   the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
         //   added to the existing number of loans, as if they succeeded them in the set.
         //
-        let borrow_count = borrow_set.borrows.len();
+        let borrow_count = borrow_set.len();
         debug!(
             "compute_regions: polonius placeholders, num_universals={}, borrow_count={}",
             universal_regions.len(),
diff --git a/src/librustc_mir/borrow_check/region_infer/values.rs b/src/librustc_mir/borrow_check/region_infer/values.rs
index 6cd814962c6..8a5a600cfdd 100644
--- a/src/librustc_mir/borrow_check/region_infer/values.rs
+++ b/src/librustc_mir/borrow_check/region_infer/values.rs
@@ -1,4 +1,4 @@
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_index::bit_set::{HybridBitSet, SparseBitMatrix};
 use rustc_index::vec::Idx;
 use rustc_index::vec::IndexVec;
@@ -193,26 +193,25 @@ impl<N: Idx> LivenessValues<N> {
 /// NLL.
 #[derive(Default)]
 crate struct PlaceholderIndices {
-    to_index: FxHashMap<ty::PlaceholderRegion, PlaceholderIndex>,
-    from_index: IndexVec<PlaceholderIndex, ty::PlaceholderRegion>,
+    indices: FxIndexSet<ty::PlaceholderRegion>,
 }
 
 impl PlaceholderIndices {
     crate fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
-        let PlaceholderIndices { to_index, from_index } = self;
-        *to_index.entry(placeholder).or_insert_with(|| from_index.push(placeholder))
+        let (index, _) = self.indices.insert_full(placeholder);
+        index.into()
     }
 
     crate fn lookup_index(&self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
-        self.to_index[&placeholder]
+        self.indices.get_index_of(&placeholder).unwrap().into()
     }
 
     crate fn lookup_placeholder(&self, placeholder: PlaceholderIndex) -> ty::PlaceholderRegion {
-        self.from_index[placeholder]
+        self.indices[placeholder.index()]
     }
 
     crate fn len(&self) -> usize {
-        self.from_index.len()
+        self.indices.len()
     }
 }
 
diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs
index bc5c144cd74..ff98de5475e 100644
--- a/src/librustc_mir/borrow_check/type_check/mod.rs
+++ b/src/librustc_mir/borrow_check/type_check/mod.rs
@@ -2469,11 +2469,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
         // example).
         if let Some(all_facts) = all_facts {
             let _prof_timer = self.infcx.tcx.prof.generic_activity("polonius_fact_generation");
-            if let Some(borrow_index) = borrow_set.location_map.get(&location) {
+            if let Some(borrow_index) = borrow_set.get_index_of(&location) {
                 let region_vid = borrow_region.to_region_vid();
                 all_facts.borrow_region.push((
                     region_vid,
-                    *borrow_index,
+                    borrow_index,
                     location_table.mid_index(location),
                 ));
             }
diff --git a/src/librustc_mir/dataflow/framework/engine.rs b/src/librustc_mir/dataflow/framework/engine.rs
index 2113d40a594..89e2d7088f7 100644
--- a/src/librustc_mir/dataflow/framework/engine.rs
+++ b/src/librustc_mir/dataflow/framework/engine.rs
@@ -335,7 +335,7 @@ impl RustcMirAttrs {
 
         let rustc_mir_attrs = attrs
             .iter()
-            .filter(|attr| attr.check_name(sym::rustc_mir))
+            .filter(|attr| tcx.sess.check_name(attr, sym::rustc_mir))
             .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter()));
 
         for attr in rustc_mir_attrs {
diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs
index dfca270396d..7e7b7f2cc76 100644
--- a/src/librustc_mir/dataflow/impls/borrows.rs
+++ b/src/librustc_mir/dataflow/impls/borrows.rs
@@ -136,9 +136,9 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
         borrow_set: &Rc<BorrowSet<'tcx>>,
     ) -> Self {
         let mut borrows_out_of_scope_at_location = FxHashMap::default();
-        for (borrow_index, borrow_data) in borrow_set.borrows.iter_enumerated() {
+        for (borrow_index, borrow_data) in borrow_set.iter_enumerated() {
             let borrow_region = borrow_data.region.to_region_vid();
-            let location = borrow_set.borrows[borrow_index].reserve_location;
+            let location = borrow_data.reserve_location;
 
             precompute_borrows_out_of_scope(
                 body,
@@ -160,7 +160,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
     }
 
     pub fn location(&self, idx: BorrowIndex) -> &Location {
-        &self.borrow_set.borrows[idx].reserve_location
+        &self.borrow_set[idx].reserve_location
     }
 
     /// Add all borrows to the kill set, if those borrows are out of scope at `location`.
@@ -216,7 +216,7 @@ impl<'a, 'tcx> Borrows<'a, 'tcx> {
             places_conflict(
                 self.tcx,
                 self.body,
-                self.borrow_set.borrows[i].borrowed_place,
+                self.borrow_set[i].borrowed_place,
                 place,
                 PlaceConflictBias::NoOverlap,
             )
@@ -232,7 +232,7 @@ impl<'tcx> dataflow::AnalysisDomain<'tcx> for Borrows<'_, 'tcx> {
     const NAME: &'static str = "borrows";
 
     fn bits_per_block(&self, _: &mir::Body<'tcx>) -> usize {
-        self.borrow_set.borrows.len() * 2
+        self.borrow_set.len() * 2
     }
 
     fn initialize_start_block(&self, _: &mir::Body<'tcx>, _: &mut BitSet<Self::Idx>) {
@@ -271,11 +271,11 @@ impl<'tcx> dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
                     ) {
                         return;
                     }
-                    let index = self.borrow_set.location_map.get(&location).unwrap_or_else(|| {
+                    let index = self.borrow_set.get_index_of(&location).unwrap_or_else(|| {
                         panic!("could not find BorrowIndex for location {:?}", location);
                     });
 
-                    trans.gen(*index);
+                    trans.gen(index);
                 }
 
                 // Make sure there are no remaining borrows for variables
diff --git a/src/librustc_mir/dataflow/mod.rs b/src/librustc_mir/dataflow/mod.rs
index 8a9edb23a10..fdec5729a54 100644
--- a/src/librustc_mir/dataflow/mod.rs
+++ b/src/librustc_mir/dataflow/mod.rs
@@ -1,5 +1,6 @@
 use rustc_ast::ast::{self, MetaItem};
 use rustc_middle::ty;
+use rustc_session::Session;
 use rustc_span::symbol::{sym, Symbol};
 
 pub(crate) use self::drop_flag_effects::*;
@@ -28,9 +29,13 @@ pub struct MoveDataParamEnv<'tcx> {
     pub(crate) param_env: ty::ParamEnv<'tcx>,
 }
 
-pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: Symbol) -> Option<MetaItem> {
+pub(crate) fn has_rustc_mir_with(
+    sess: &Session,
+    attrs: &[ast::Attribute],
+    name: Symbol,
+) -> Option<MetaItem> {
     for attr in attrs {
-        if attr.check_name(sym::rustc_mir) {
+        if sess.check_name(attr, sym::rustc_mir) {
             let items = attr.meta_item_list();
             for item in items.iter().flat_map(|l| l.iter()) {
                 match item.meta_item() {
diff --git a/src/librustc_mir/monomorphize/polymorphize.rs b/src/librustc_mir/monomorphize/polymorphize.rs
index 8fc1458f592..a996b6fb9d9 100644
--- a/src/librustc_mir/monomorphize/polymorphize.rs
+++ b/src/librustc_mir/monomorphize/polymorphize.rs
@@ -15,6 +15,7 @@ use rustc_middle::ty::{
     self,
     fold::{TypeFoldable, TypeVisitor},
     query::Providers,
+    subst::SubstsRef,
     Const, Ty, TyCtxt,
 };
 use rustc_span::symbol::sym;
@@ -170,7 +171,11 @@ fn emit_unused_generic_params_error<'tcx>(
 ) {
     debug!("emit_unused_generic_params_error: def_id={:?}", def_id);
     let base_def_id = tcx.closure_base_def_id(def_id);
-    if !tcx.get_attrs(base_def_id).iter().any(|a| a.check_name(sym::rustc_polymorphize_error)) {
+    if !tcx
+        .get_attrs(base_def_id)
+        .iter()
+        .any(|a| tcx.sess.check_name(a, sym::rustc_polymorphize_error))
+    {
         return;
     }
 
@@ -205,6 +210,25 @@ struct UsedGenericParametersVisitor<'a, 'tcx> {
     unused_parameters: &'a mut FiniteBitSet<u32>,
 }
 
+impl<'a, 'tcx> UsedGenericParametersVisitor<'a, 'tcx> {
+    /// Invoke `unused_generic_params` on a body contained within the current item (e.g.
+    /// a closure, generator or constant).
+    fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
+        let unused = self.tcx.unused_generic_params(def_id);
+        debug!(
+            "visit_child_body: unused_parameters={:?} unused={:?}",
+            self.unused_parameters, unused
+        );
+        for (i, arg) in substs.iter().enumerate() {
+            let i = i.try_into().unwrap();
+            if !unused.contains(i).unwrap_or(false) {
+                arg.visit_with(self);
+            }
+        }
+        debug!("visit_child_body: unused_parameters={:?}", self.unused_parameters);
+    }
+}
+
 impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
     fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
         debug!("visit_local_decl: local_decl={:?}", local_decl);
@@ -245,6 +269,17 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
                 self.unused_parameters.clear(param.index);
                 false
             }
+            ty::ConstKind::Unevaluated(_, _, Some(p)) => {
+                // If there is a promoted, don't look at the substs - since it will always contain
+                // the generic parameters, instead, traverse the promoted MIR.
+                let promoted = self.tcx.promoted_mir(self.def_id);
+                self.visit_body(&promoted[p]);
+                false
+            }
+            ty::ConstKind::Unevaluated(def_id, unevaluated_substs, None) => {
+                self.visit_child_body(def_id.did, unevaluated_substs);
+                false
+            }
             _ => c.super_visit_with(self),
         }
     }
@@ -265,19 +300,7 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
 
                 // Consider any generic parameters used by any closures/generators as used in the
                 // parent.
-                let unused = self.tcx.unused_generic_params(def_id);
-                debug!(
-                    "visit_ty: unused_parameters={:?} unused={:?}",
-                    self.unused_parameters, unused
-                );
-                for (i, arg) in substs.iter().enumerate() {
-                    let i = i.try_into().unwrap();
-                    if !unused.contains(i).unwrap_or(false) {
-                        arg.visit_with(self);
-                    }
-                }
-                debug!("visit_ty: unused_parameters={:?}", self.unused_parameters);
-
+                self.visit_child_body(def_id, substs);
                 false
             }
             ty::Param(param) => {
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 8e2fd709d66..db0b0415728 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -34,7 +34,9 @@ use crate::interpret::{
 };
 use crate::transform::{MirPass, MirSource};
 
-/// The maximum number of bytes that we'll allocate space for a return value.
+/// The maximum number of bytes that we'll allocate space for a local or the return value.
+/// Needed for #66397, because otherwise we eval into large places and that can cause OOM or just
+/// Severely regress performance.
 const MAX_ALLOC_LIMIT: u64 = 1024;
 
 /// Macro for machine-specific `InterpError` without allocation.
@@ -155,14 +157,19 @@ struct ConstPropMachine<'mir, 'tcx> {
     written_only_inside_own_block_locals: FxHashSet<Local>,
     /// Locals that need to be cleared after every block terminates.
     only_propagate_inside_block_locals: BitSet<Local>,
+    can_const_prop: IndexVec<Local, ConstPropMode>,
 }
 
 impl<'mir, 'tcx> ConstPropMachine<'mir, 'tcx> {
-    fn new(only_propagate_inside_block_locals: BitSet<Local>) -> Self {
+    fn new(
+        only_propagate_inside_block_locals: BitSet<Local>,
+        can_const_prop: IndexVec<Local, ConstPropMode>,
+    ) -> Self {
         Self {
             stack: Vec::new(),
             written_only_inside_own_block_locals: Default::default(),
             only_propagate_inside_block_locals,
+            can_const_prop,
         }
     }
 }
@@ -241,6 +248,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
         local: Local,
     ) -> InterpResult<'tcx, Result<&'a mut LocalValue<Self::PointerTag>, MemPlace<Self::PointerTag>>>
     {
+        if ecx.machine.can_const_prop[local] == ConstPropMode::NoPropagation {
+            throw_machine_stop_str!("tried to write to a local that is marked as not propagatable")
+        }
         if frame == 0 && ecx.machine.only_propagate_inside_block_locals.contains(local) {
             ecx.machine.written_only_inside_own_block_locals.insert(local);
         }
@@ -285,7 +295,6 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for ConstPropMachine<'mir, 'tcx>
 struct ConstPropagator<'mir, 'tcx> {
     ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
     tcx: TyCtxt<'tcx>,
-    can_const_prop: IndexVec<Local, ConstPropMode>,
     param_env: ParamEnv<'tcx>,
     // FIXME(eddyb) avoid cloning these two fields more than once,
     // by accessing them through `ecx` instead.
@@ -331,7 +340,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
         let param_env = tcx.param_env_reveal_all_normalized(def_id);
 
         let span = tcx.def_span(def_id);
-        let can_const_prop = CanConstProp::check(body);
+        // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
+        // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
+        // `layout_of` query invocations.
+        let can_const_prop = CanConstProp::check(tcx, param_env, body);
         let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
         for (l, mode) in can_const_prop.iter_enumerated() {
             if *mode == ConstPropMode::OnlyInsideOwnBlock {
@@ -342,7 +354,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             tcx,
             span,
             param_env,
-            ConstPropMachine::new(only_propagate_inside_block_locals),
+            ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
             (),
         );
 
@@ -368,7 +380,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
             ecx,
             tcx,
             param_env,
-            can_const_prop,
             // FIXME(eddyb) avoid cloning these two fields more than once,
             // by accessing them through `ecx` instead.
             source_scopes: body.source_scopes.clone(),
@@ -612,15 +623,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
     fn const_prop(
         &mut self,
         rvalue: &Rvalue<'tcx>,
-        place_layout: TyAndLayout<'tcx>,
         source_info: SourceInfo,
         place: Place<'tcx>,
     ) -> Option<()> {
-        // #66397: Don't try to eval into large places as that can cause an OOM
-        if place_layout.size >= Size::from_bytes(MAX_ALLOC_LIMIT) {
-            return None;
-        }
-
         // Perform any special handling for specific Rvalue types.
         // Generally, checks here fall into one of two categories:
         //   1. Additional checking to provide useful lints to the user
@@ -893,7 +898,11 @@ struct CanConstProp {
 
 impl CanConstProp {
     /// Returns true if `local` can be propagated
-    fn check(body: &Body<'_>) -> IndexVec<Local, ConstPropMode> {
+    fn check(
+        tcx: TyCtxt<'tcx>,
+        param_env: ParamEnv<'tcx>,
+        body: &Body<'tcx>,
+    ) -> IndexVec<Local, ConstPropMode> {
         let mut cpv = CanConstProp {
             can_const_prop: IndexVec::from_elem(ConstPropMode::FullConstProp, &body.local_decls),
             found_assignment: BitSet::new_empty(body.local_decls.len()),
@@ -903,6 +912,16 @@ impl CanConstProp {
             ),
         };
         for (local, val) in cpv.can_const_prop.iter_enumerated_mut() {
+            let ty = body.local_decls[local].ty;
+            match tcx.layout_of(param_env.and(ty)) {
+                Ok(layout) if layout.size < Size::from_bytes(MAX_ALLOC_LIMIT) => {}
+                // Either the layout fails to compute, then we can't use this local anyway
+                // or the local is too large, then we don't want to.
+                _ => {
+                    *val = ConstPropMode::NoPropagation;
+                    continue;
+                }
+            }
             // Cannot use args at all
             // Cannot use locals because if x < y { y - x } else { x - y } would
             //        lint for x != y
@@ -1018,61 +1037,52 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
         let source_info = statement.source_info;
         self.source_info = Some(source_info);
         if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
-            let place_ty: Ty<'tcx> = place.ty(&self.local_decls, self.tcx).ty;
-            if let Ok(place_layout) = self.tcx.layout_of(self.param_env.and(place_ty)) {
-                let can_const_prop = self.can_const_prop[place.local];
-                if let Some(()) = self.const_prop(rval, place_layout, source_info, place) {
-                    // This will return None if the above `const_prop` invocation only "wrote" a
-                    // type whose creation requires no write. E.g. a generator whose initial state
-                    // consists solely of uninitialized memory (so it doesn't capture any locals).
-                    if let Some(value) = self.get_const(place) {
-                        if self.should_const_prop(value) {
-                            trace!("replacing {:?} with {:?}", rval, value);
-                            self.replace_with_const(rval, value, source_info);
-                            if can_const_prop == ConstPropMode::FullConstProp
-                                || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
-                            {
-                                trace!("propagated into {:?}", place);
-                            }
+            let can_const_prop = self.ecx.machine.can_const_prop[place.local];
+            if let Some(()) = self.const_prop(rval, source_info, place) {
+                // This will return None if the above `const_prop` invocation only "wrote" a
+                // type whose creation requires no write. E.g. a generator whose initial state
+                // consists solely of uninitialized memory (so it doesn't capture any locals).
+                if let Some(value) = self.get_const(place) {
+                    if self.should_const_prop(value) {
+                        trace!("replacing {:?} with {:?}", rval, value);
+                        self.replace_with_const(rval, value, source_info);
+                        if can_const_prop == ConstPropMode::FullConstProp
+                            || can_const_prop == ConstPropMode::OnlyInsideOwnBlock
+                        {
+                            trace!("propagated into {:?}", place);
                         }
                     }
-                    match can_const_prop {
-                        ConstPropMode::OnlyInsideOwnBlock => {
-                            trace!(
-                                "found local restricted to its block. \
+                }
+                match can_const_prop {
+                    ConstPropMode::OnlyInsideOwnBlock => {
+                        trace!(
+                            "found local restricted to its block. \
                                 Will remove it from const-prop after block is finished. Local: {:?}",
-                                place.local
-                            );
-                        }
-                        ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
-                            trace!("can't propagate into {:?}", place);
-                            if place.local != RETURN_PLACE {
-                                Self::remove_const(&mut self.ecx, place.local);
-                            }
+                            place.local
+                        );
+                    }
+                    ConstPropMode::OnlyPropagateInto | ConstPropMode::NoPropagation => {
+                        trace!("can't propagate into {:?}", place);
+                        if place.local != RETURN_PLACE {
+                            Self::remove_const(&mut self.ecx, place.local);
                         }
-                        ConstPropMode::FullConstProp => {}
                     }
-                } else {
-                    // Const prop failed, so erase the destination, ensuring that whatever happens
-                    // from here on, does not know about the previous value.
-                    // This is important in case we have
-                    // ```rust
-                    // let mut x = 42;
-                    // x = SOME_MUTABLE_STATIC;
-                    // // x must now be undefined
-                    // ```
-                    // FIXME: we overzealously erase the entire local, because that's easier to
-                    // implement.
-                    trace!(
-                        "propagation into {:?} failed.
-                        Nuking the entire site from orbit, it's the only way to be sure",
-                        place,
-                    );
-                    Self::remove_const(&mut self.ecx, place.local);
+                    ConstPropMode::FullConstProp => {}
                 }
             } else {
+                // Const prop failed, so erase the destination, ensuring that whatever happens
+                // from here on, does not know about the previous value.
+                // This is important in case we have
+                // ```rust
+                // let mut x = 42;
+                // x = SOME_MUTABLE_STATIC;
+                // // x must now be undefined
+                // ```
+                // FIXME: we overzealously erase the entire local, because that's easier to
+                // implement.
                 trace!(
-                    "cannot propagate into {:?}, because the type of the local is generic.",
+                    "propagation into {:?} failed.
+                        Nuking the entire site from orbit, it's the only way to be sure",
                     place,
                 );
                 Self::remove_const(&mut self.ecx, place.local);
diff --git a/src/librustc_mir/transform/promote_consts.rs b/src/librustc_mir/transform/promote_consts.rs
index f1a7338d11f..c0564105701 100644
--- a/src/librustc_mir/transform/promote_consts.rs
+++ b/src/librustc_mir/transform/promote_consts.rs
@@ -130,7 +130,7 @@ impl Candidate {
 
 fn args_required_const(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Vec<usize>> {
     let attrs = tcx.get_attrs(def_id);
-    let attr = attrs.iter().find(|a| a.check_name(sym::rustc_args_required_const))?;
+    let attr = attrs.iter().find(|a| tcx.sess.check_name(a, sym::rustc_args_required_const))?;
     let mut ret = vec![];
     for meta in attr.meta_item_list()? {
         match meta.literal()?.kind {
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 34fa840408f..dfd01e27d57 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -342,7 +342,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.get_attrs(def_id), &tcx.sess.diagnostic())
+    attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
         .map_or(false, |mut features| features.any(|name| name == feature_gate))
 }
 
@@ -362,7 +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.get_attrs(def_id), &tcx.sess.diagnostic())
+    attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
         .map_or(false, |mut features| features.any(|name| name == feature_gate))
 }
 
diff --git a/src/librustc_mir/transform/rustc_peek.rs b/src/librustc_mir/transform/rustc_peek.rs
index 5eb374e7ee2..729e22a94dc 100644
--- a/src/librustc_mir/transform/rustc_peek.rs
+++ b/src/librustc_mir/transform/rustc_peek.rs
@@ -35,8 +35,9 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
         let param_env = tcx.param_env(def_id);
         let move_data = MoveData::gather_moves(body, tcx, param_env).unwrap();
         let mdpe = MoveDataParamEnv { move_data, param_env };
+        let sess = &tcx.sess;
 
-        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
+        if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_init).is_some() {
             let flow_inits = MaybeInitializedPlaces::new(tcx, body, &mdpe)
                 .into_engine(tcx, body, def_id)
                 .iterate_to_fixpoint();
@@ -44,7 +45,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
         }
 
-        if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_uninit).is_some() {
+        if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_maybe_uninit).is_some() {
             let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &mdpe)
                 .into_engine(tcx, body, def_id)
                 .iterate_to_fixpoint();
@@ -52,7 +53,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_uninits);
         }
 
-        if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
+        if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_definite_init).is_some() {
             let flow_def_inits = DefinitelyInitializedPlaces::new(tcx, body, &mdpe)
                 .into_engine(tcx, body, def_id)
                 .iterate_to_fixpoint();
@@ -60,7 +61,7 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
         }
 
-        if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
+        if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_indirectly_mutable).is_some() {
             let flow_mut_borrowed = MaybeMutBorrowedLocals::mut_borrows_only(tcx, body, param_env)
                 .into_engine(tcx, body, def_id)
                 .iterate_to_fixpoint();
@@ -68,14 +69,14 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_mut_borrowed);
         }
 
-        if has_rustc_mir_with(&attributes, sym::rustc_peek_liveness).is_some() {
+        if has_rustc_mir_with(sess, &attributes, sym::rustc_peek_liveness).is_some() {
             let flow_liveness =
                 MaybeLiveLocals.into_engine(tcx, body, def_id).iterate_to_fixpoint();
 
             sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_liveness);
         }
 
-        if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
+        if has_rustc_mir_with(sess, &attributes, sym::stop_after_dataflow).is_some() {
             tcx.sess.fatal("stop_after_dataflow ended compilation");
         }
     }
diff --git a/src/librustc_mir/transform/simplify_try.rs b/src/librustc_mir/transform/simplify_try.rs
index 97a01de867e..02896d7de35 100644
--- a/src/librustc_mir/transform/simplify_try.rs
+++ b/src/librustc_mir/transform/simplify_try.rs
@@ -361,7 +361,7 @@ fn optimization_applies<'tcx>(
     }
 
     trace!("SUCCESS: optimization applies!");
-    return true;
+    true
 }
 
 impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
diff --git a/src/librustc_mir/transform/validate.rs b/src/librustc_mir/transform/validate.rs
index b8a74f09409..9296e2ca700 100644
--- a/src/librustc_mir/transform/validate.rs
+++ b/src/librustc_mir/transform/validate.rs
@@ -115,7 +115,7 @@ pub fn equal_up_to_regions(
             T: Relate<'tcx>,
         {
             self.relate(a.skip_binder(), b.skip_binder())?;
-            Ok(a.clone())
+            Ok(a)
         }
     }
 
diff --git a/src/librustc_mir_build/build/matches/mod.rs b/src/librustc_mir_build/build/matches/mod.rs
index 77c0fe8dda5..5f87cb364b8 100644
--- a/src/librustc_mir_build/build/matches/mod.rs
+++ b/src/librustc_mir_build/build/matches/mod.rs
@@ -11,7 +11,7 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder};
 use crate::build::{GuardFrame, GuardFrameLocal, LocalsForNode};
 use crate::thir::{self, *};
 use rustc_data_structures::{
-    fx::{FxHashMap, FxHashSet},
+    fx::{FxHashSet, FxIndexMap},
     stack::ensure_sufficient_stack,
 };
 use rustc_hir::HirId;
@@ -817,9 +817,7 @@ enum TestKind<'tcx> {
         ///
         /// For `bool` we always generate two edges, one for `true` and one for
         /// `false`.
-        options: Vec<u128>,
-        /// Reverse map used to ensure that the values in `options` are unique.
-        indices: FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+        options: FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
     },
 
     /// Test for equality with value, possibly after an unsizing coercion to
@@ -1396,14 +1394,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         // may want to add cases based on the candidates that are
         // available
         match test.kind {
-            TestKind::SwitchInt { switch_ty, ref mut options, ref mut indices } => {
+            TestKind::SwitchInt { switch_ty, ref mut options } => {
                 for candidate in candidates.iter() {
                     if !self.add_cases_to_switch(
                         &match_place,
                         candidate,
                         switch_ty,
                         options,
-                        indices,
                     ) {
                         break;
                     }
diff --git a/src/librustc_mir_build/build/matches/test.rs b/src/librustc_mir_build/build/matches/test.rs
index 158ad78a1bf..87977d6fe89 100644
--- a/src/librustc_mir_build/build/matches/test.rs
+++ b/src/librustc_mir_build/build/matches/test.rs
@@ -9,7 +9,7 @@ use crate::build::matches::{Candidate, MatchPair, Test, TestKind};
 use crate::build::Builder;
 use crate::thir::pattern::compare_const_vals;
 use crate::thir::*;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_hir::RangeEnd;
 use rustc_index::bit_set::BitSet;
 use rustc_middle::mir::*;
@@ -44,8 +44,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
                         // these maps are empty to start; cases are
                         // added below in add_cases_to_switch
-                        options: vec![],
-                        indices: Default::default(),
+                        options: Default::default(),
                     },
                 }
             }
@@ -83,8 +82,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         test_place: &Place<'tcx>,
         candidate: &Candidate<'pat, 'tcx>,
         switch_ty: Ty<'tcx>,
-        options: &mut Vec<u128>,
-        indices: &mut FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+        options: &mut FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
     ) -> bool {
         let match_pair = match candidate.match_pairs.iter().find(|mp| mp.place == *test_place) {
             Some(match_pair) => match_pair,
@@ -95,9 +93,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         match *match_pair.pattern.kind {
             PatKind::Constant { value } => {
-                indices.entry(value).or_insert_with(|| {
-                    options.push(value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty));
-                    options.len() - 1
+                options.entry(value).or_insert_with(|| {
+                    value.eval_bits(self.hir.tcx(), self.hir.param_env, switch_ty)
                 });
                 true
             }
@@ -106,7 +103,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
             PatKind::Range(range) => {
                 // Check that none of the switch values are in the range.
-                self.values_not_contained_in_range(range, indices).unwrap_or(false)
+                self.values_not_contained_in_range(range, options).unwrap_or(false)
             }
             PatKind::Slice { .. }
             | PatKind::Array { .. }
@@ -216,7 +213,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 );
             }
 
-            TestKind::SwitchInt { switch_ty, ref options, indices: _ } => {
+            TestKind::SwitchInt { switch_ty, ref options } => {
                 let target_blocks = make_target_blocks(self);
                 let terminator = if switch_ty.kind == ty::Bool {
                     assert!(!options.is_empty() && options.len() <= 2);
@@ -236,7 +233,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                     TerminatorKind::SwitchInt {
                         discr: Operand::Copy(place),
                         switch_ty,
-                        values: options.clone().into(),
+                        values: options.values().copied().collect(),
                         targets: target_blocks,
                     }
                 };
@@ -532,20 +529,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             // FIXME(#29623) we could use PatKind::Range to rule
             // things out here, in some cases.
             (
-                &TestKind::SwitchInt { switch_ty: _, options: _, ref indices },
+                &TestKind::SwitchInt { switch_ty: _, ref options },
                 &PatKind::Constant { ref value },
             ) if is_switch_ty(match_pair.pattern.ty) => {
-                let index = indices[value];
+                let index = options.get_index_of(value).unwrap();
                 self.candidate_without_match_pair(match_pair_index, candidate);
                 Some(index)
             }
 
             (
-                &TestKind::SwitchInt { switch_ty: _, ref options, ref indices },
+                &TestKind::SwitchInt { switch_ty: _, ref options },
                 &PatKind::Range(range),
             ) => {
                 let not_contained =
-                    self.values_not_contained_in_range(range, indices).unwrap_or(false);
+                    self.values_not_contained_in_range(range, options).unwrap_or(false);
 
                 if not_contained {
                     // No switch values are contained in the pattern range,
@@ -777,9 +774,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     fn values_not_contained_in_range(
         &self,
         range: PatRange<'tcx>,
-        indices: &FxHashMap<&'tcx ty::Const<'tcx>, usize>,
+        options: &FxIndexMap<&'tcx ty::Const<'tcx>, u128>,
     ) -> Option<bool> {
-        for &val in indices.keys() {
+        for &val in options.keys() {
             if self.const_range_contains(range, val)? {
                 return Some(false);
             }
diff --git a/src/librustc_mir_build/build/mod.rs b/src/librustc_mir_build/build/mod.rs
index 3c4587119cd..f3f3c3e33a4 100644
--- a/src/librustc_mir_build/build/mod.rs
+++ b/src/librustc_mir_build/build/mod.rs
@@ -537,7 +537,7 @@ macro_rules! unpack {
 fn should_abort_on_panic(tcx: TyCtxt<'_>, fn_def_id: LocalDefId, _abi: Abi) -> bool {
     // Validate `#[unwind]` syntax regardless of platform-specific panic strategy.
     let attrs = &tcx.get_attrs(fn_def_id.to_def_id());
-    let unwind_attr = attr::find_unwind_attr(Some(tcx.sess.diagnostic()), attrs);
+    let unwind_attr = attr::find_unwind_attr(&tcx.sess, attrs);
 
     // We never unwind, so it's not relevant to stop an unwind.
     if tcx.sess.panic_strategy() != PanicStrategy::Unwind {
diff --git a/src/librustc_mir_build/thir/cx/mod.rs b/src/librustc_mir_build/thir/cx/mod.rs
index 21736df7b07..bb814ab8248 100644
--- a/src/librustc_mir_build/thir/cx/mod.rs
+++ b/src/librustc_mir_build/thir/cx/mod.rs
@@ -6,7 +6,6 @@ use crate::thir::util::UserAnnotatedTyHelpers;
 use crate::thir::*;
 
 use rustc_ast::ast;
-use rustc_ast::attr;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::Node;
@@ -69,7 +68,7 @@ impl<'a, 'tcx> Cx<'a, 'tcx> {
         // Some functions always have overflow checks enabled,
         // however, they may not get codegen'd, depending on
         // the settings for the crate they are codegened in.
-        let mut check_overflow = attr::contains_name(attrs, sym::rustc_inherit_overflow_checks);
+        let mut check_overflow = tcx.sess.contains_name(attrs, sym::rustc_inherit_overflow_checks);
 
         // Respect -C overflow-checks.
         check_overflow |= tcx.sess.overflow_checks();
diff --git a/src/librustc_parse/lexer/mod.rs b/src/librustc_parse/lexer/mod.rs
index 2b0e637c74e..c3a79660eb9 100644
--- a/src/librustc_parse/lexer/mod.rs
+++ b/src/librustc_parse/lexer/mod.rs
@@ -1,4 +1,4 @@
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Token, TokenKind};
 use rustc_ast::util::comments;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::{error_code, Applicability, DiagnosticBuilder, FatalError};
@@ -170,22 +170,20 @@ impl<'a> StringReader<'a> {
         match token {
             rustc_lexer::TokenKind::LineComment => {
                 let string = self.str_from(start);
-                // comments with only more "/"s are not doc comments
-                if comments::is_line_doc_comment(string) {
+                if let Some(attr_style) = comments::line_doc_comment_style(string) {
                     self.forbid_bare_cr(start, string, "bare CR not allowed in doc-comment");
-                    token::DocComment(Symbol::intern(string))
+                    // Opening delimiter of the length 3 is not included into the symbol.
+                    token::DocComment(CommentKind::Line, attr_style, Symbol::intern(&string[3..]))
                 } else {
                     token::Comment
                 }
             }
             rustc_lexer::TokenKind::BlockComment { terminated } => {
                 let string = self.str_from(start);
-                // block comments starting with "/**" or "/*!" are doc-comments
-                // but comments with only "*"s between two "/"s are not
-                let is_doc_comment = comments::is_block_doc_comment(string);
+                let attr_style = comments::block_doc_comment_style(string, terminated);
 
                 if !terminated {
-                    let msg = if is_doc_comment {
+                    let msg = if attr_style.is_some() {
                         "unterminated block doc-comment"
                     } else {
                         "unterminated block comment"
@@ -202,9 +200,15 @@ impl<'a> StringReader<'a> {
                     FatalError.raise();
                 }
 
-                if is_doc_comment {
+                if let Some(attr_style) = attr_style {
                     self.forbid_bare_cr(start, string, "bare CR not allowed in block doc-comment");
-                    token::DocComment(Symbol::intern(string))
+                    // Opening delimiter of the length 3 and closing delimiter of the length 2
+                    // are not included into the symbol.
+                    token::DocComment(
+                        CommentKind::Block,
+                        attr_style,
+                        Symbol::intern(&string[3..string.len() - if terminated { 2 } else { 0 }]),
+                    )
                 } else {
                     token::Comment
                 }
diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs
index 3319ca44da4..723e4333790 100644
--- a/src/librustc_parse/lib.rs
+++ b/src/librustc_parse/lib.rs
@@ -486,7 +486,9 @@ fn token_probably_equal_for_proc_macro(first: &Token, other: &Token) -> bool {
 
         (&OpenDelim(a), &OpenDelim(b)) | (&CloseDelim(a), &CloseDelim(b)) => a == b,
 
-        (&DocComment(a), &DocComment(b)) | (&Shebang(a), &Shebang(b)) => a == b,
+        (&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,
 
@@ -524,7 +526,7 @@ fn prepend_attrs(
 
         let item = match attr.kind {
             ast::AttrKind::Normal(ref item) => item,
-            ast::AttrKind::DocComment(_) => {
+            ast::AttrKind::DocComment(..) => {
                 let stream = parse_stream_from_source_str(macro_filename, source, sess, Some(span));
                 builder.push(stream);
                 continue;
diff --git a/src/librustc_parse/parser/attr.rs b/src/librustc_parse/parser/attr.rs
index 8b67f4743c6..b6a8ee71beb 100644
--- a/src/librustc_parse/parser/attr.rs
+++ b/src/librustc_parse/parser/attr.rs
@@ -2,10 +2,9 @@ use super::{Parser, PathStyle};
 use rustc_ast::ast;
 use rustc_ast::attr;
 use rustc_ast::token::{self, Nonterminal};
-use rustc_ast::util::comments;
 use rustc_ast_pretty::pprust;
 use rustc_errors::{error_code, PResult};
-use rustc_span::{Span, Symbol};
+use rustc_span::Span;
 
 use log::debug;
 
@@ -47,8 +46,8 @@ impl<'a> Parser<'a> {
                 let attr = self.parse_attribute_with_inner_parse_policy(inner_parse_policy)?;
                 attrs.push(attr);
                 just_parsed_doc_comment = false;
-            } else if let token::DocComment(s) = self.token.kind {
-                let attr = self.mk_doc_comment(s);
+            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
+                let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
                 if attr.style != ast::AttrStyle::Outer {
                     self.sess
                         .span_diagnostic
@@ -73,10 +72,6 @@ impl<'a> Parser<'a> {
         Ok(attrs)
     }
 
-    fn mk_doc_comment(&self, s: Symbol) -> ast::Attribute {
-        attr::mk_doc_comment(comments::doc_comment_style(s), s, self.token.span)
-    }
-
     /// Matches `attribute = # ! [ meta_item ]`.
     ///
     /// If `permit_inner` is `true`, then a leading `!` indicates an inner
@@ -184,9 +179,9 @@ impl<'a> Parser<'a> {
                 let attr = self.parse_attribute(true)?;
                 assert_eq!(attr.style, ast::AttrStyle::Inner);
                 attrs.push(attr);
-            } else if let token::DocComment(s) = self.token.kind {
+            } else if let token::DocComment(comment_kind, attr_style, data) = self.token.kind {
                 // We need to get the position of this token before we bump.
-                let attr = self.mk_doc_comment(s);
+                let attr = attr::mk_doc_comment(comment_kind, attr_style, data, self.token.span);
                 if attr.style == ast::AttrStyle::Inner {
                     attrs.push(attr);
                     self.bump();
diff --git a/src/librustc_parse/parser/diagnostics.rs b/src/librustc_parse/parser/diagnostics.rs
index 5e9411327ca..2854356ab0f 100644
--- a/src/librustc_parse/parser/diagnostics.rs
+++ b/src/librustc_parse/parser/diagnostics.rs
@@ -1419,7 +1419,7 @@ impl<'a> Parser<'a> {
     }
 
     pub(super) fn eat_incorrect_doc_comment_for_param_type(&mut self) {
-        if let token::DocComment(_) = self.token.kind {
+        if let token::DocComment(..) = self.token.kind {
             self.struct_span_err(
                 self.token.span,
                 "documentation comments cannot be applied to a function parameter's type",
diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs
index 3aec300d86d..55a134ae091 100644
--- a/src/librustc_parse/parser/expr.rs
+++ b/src/librustc_parse/parser/expr.rs
@@ -1733,13 +1733,20 @@ impl<'a> Parser<'a> {
         Ok(self.mk_expr(lo.to(self.prev_token.span), kind, attrs))
     }
 
-    fn error_missing_in_for_loop(&self) {
-        let in_span = self.prev_token.span.between(self.token.span);
-        self.struct_span_err(in_span, "missing `in` in `for` loop")
+    fn error_missing_in_for_loop(&mut self) {
+        let (span, msg, sugg) = if self.token.is_ident_named(sym::of) {
+            // Possibly using JS syntax (#75311).
+            let span = self.token.span;
+            self.bump();
+            (span, "try using `in` here instead", "in")
+        } else {
+            (self.prev_token.span.between(self.token.span), "try adding `in` here", " in ")
+        };
+        self.struct_span_err(span, "missing `in` in `for` loop")
             .span_suggestion_short(
-                in_span,
-                "try adding `in` here",
-                " in ".into(),
+                span,
+                msg,
+                sugg.into(),
                 // Has been misleading, at least in the past (closed Issue #48492).
                 Applicability::MaybeIncorrect,
             )
diff --git a/src/librustc_parse/parser/generics.rs b/src/librustc_parse/parser/generics.rs
index 47794746126..be72ed6dffb 100644
--- a/src/librustc_parse/parser/generics.rs
+++ b/src/librustc_parse/parser/generics.rs
@@ -54,7 +54,7 @@ impl<'a> Parser<'a> {
         self.expect(&token::Colon)?;
         let ty = self.parse_ty()?;
 
-        self.sess.gated_spans.gate(sym::const_generics, const_span.to(self.prev_token.span));
+        self.sess.gated_spans.gate(sym::min_const_generics, const_span.to(self.prev_token.span));
 
         Ok(GenericParam {
             ident,
diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs
index 5923a185dcf..10d214e52ab 100644
--- a/src/librustc_parse/parser/item.rs
+++ b/src/librustc_parse/parser/item.rs
@@ -610,7 +610,7 @@ impl<'a> Parser<'a> {
 
     /// Recover on a doc comment before `}`.
     fn recover_doc_comment_before_brace(&mut self) -> bool {
-        if let token::DocComment(_) = self.token.kind {
+        if let token::DocComment(..) = self.token.kind {
             if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
                 struct_span_err!(
                     self.diagnostic(),
@@ -1231,7 +1231,7 @@ impl<'a> Parser<'a> {
                 self.bump();
             }
             token::CloseDelim(token::Brace) => {}
-            token::DocComment(_) => {
+            token::DocComment(..) => {
                 let previous_span = self.prev_token.span;
                 let mut err = self.span_fatal_err(self.token.span, Error::UselessDocComment);
                 self.bump(); // consume the doc comment
diff --git a/src/librustc_parse/parser/mod.rs b/src/librustc_parse/parser/mod.rs
index 2509a979221..b33ae4bed82 100644
--- a/src/librustc_parse/parser/mod.rs
+++ b/src/librustc_parse/parser/mod.rs
@@ -22,7 +22,6 @@ use rustc_ast::ast::{
 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::util::comments::{doc_comment_style, strip_doc_comment_decoration};
 use rustc_ast_pretty::pprust;
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, FatalError, PResult};
 use rustc_session::parse::ParseSess;
@@ -104,6 +103,8 @@ pub struct Parser<'a> {
     /// error.
     pub(super) unclosed_delims: Vec<UnmatchedBrace>,
     last_unexpected_token_span: Option<Span>,
+    /// Span pointing at the `:` for the last type ascription the parser has seen, and whether it
+    /// looked like it could have been a mistyped path or literal `Option:Some(42)`).
     pub last_type_ascription: Option<(Span, bool /* likely path typo */)>,
     /// If present, this `Parser` is not parsing Rust code but rather a macro call.
     subparser_name: Option<&'static str>,
@@ -209,18 +210,18 @@ impl TokenCursor {
     }
 
     fn next_desugared(&mut self) -> Token {
-        let (name, sp) = match self.next() {
-            Token { kind: token::DocComment(name), span } => (name, span),
+        let (data, attr_style, sp) = match self.next() {
+            Token { kind: token::DocComment(_, attr_style, data), span } => {
+                (data, attr_style, span)
+            }
             tok => return tok,
         };
 
-        let stripped = strip_doc_comment_decoration(name);
-
         // Searches for the occurrences of `"#*` and returns the minimum number of `#`s
         // required to wrap the text.
         let mut num_of_hashes = 0;
         let mut count = 0;
-        for ch in stripped.chars() {
+        for ch in data.as_str().chars() {
             count = match ch {
                 '"' => 1,
                 '#' if count > 0 => count + 1,
@@ -236,10 +237,7 @@ impl TokenCursor {
             [
                 TokenTree::token(token::Ident(sym::doc, false), sp),
                 TokenTree::token(token::Eq, sp),
-                TokenTree::token(
-                    TokenKind::lit(token::StrRaw(num_of_hashes), Symbol::intern(&stripped), None),
-                    sp,
-                ),
+                TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), sp),
             ]
             .iter()
             .cloned()
@@ -251,7 +249,7 @@ impl TokenCursor {
             TokenCursorFrame::new(
                 delim_span,
                 token::NoDelim,
-                &if doc_comment_style(name) == AttrStyle::Inner {
+                &if attr_style == AttrStyle::Inner {
                     [TokenTree::token(token::Pound, sp), TokenTree::token(token::Not, sp), body]
                         .iter()
                         .cloned()
diff --git a/src/librustc_parse_format/lib.rs b/src/librustc_parse_format/lib.rs
index 7db62f3493e..ebb3aa3866e 100644
--- a/src/librustc_parse_format/lib.rs
+++ b/src/librustc_parse_format/lib.rs
@@ -820,7 +820,7 @@ fn find_skips_from_snippet(
     }
 
     let r_start = str_style.map(|r| r + 1).unwrap_or(0);
-    let r_end = str_style.map(|r| r).unwrap_or(0);
+    let r_end = str_style.unwrap_or(0);
     let s = &snippet[r_start + 1..snippet.len() - r_end - 1];
     (find_skips(s, str_style.is_some()), true)
 }
diff --git a/src/librustc_passes/check_attr.rs b/src/librustc_passes/check_attr.rs
index 1ff47ee038d..d438fe35ff4 100644
--- a/src/librustc_passes/check_attr.rs
+++ b/src/librustc_passes/check_attr.rs
@@ -9,7 +9,6 @@ use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
 
 use rustc_ast::ast::{Attribute, NestedMetaItem};
-use rustc_ast::attr;
 use rustc_errors::struct_span_err;
 use rustc_hir as hir;
 use rustc_hir::def_id::LocalDefId;
@@ -60,17 +59,17 @@ impl CheckAttrVisitor<'tcx> {
     ) {
         let mut is_valid = true;
         for attr in attrs {
-            is_valid &= if attr.check_name(sym::inline) {
+            is_valid &= if self.tcx.sess.check_name(attr, sym::inline) {
                 self.check_inline(hir_id, attr, span, target)
-            } else if attr.check_name(sym::non_exhaustive) {
+            } else if self.tcx.sess.check_name(attr, sym::non_exhaustive) {
                 self.check_non_exhaustive(attr, span, target)
-            } else if attr.check_name(sym::marker) {
+            } else if self.tcx.sess.check_name(attr, sym::marker) {
                 self.check_marker(attr, span, target)
-            } else if attr.check_name(sym::target_feature) {
+            } else if self.tcx.sess.check_name(attr, sym::target_feature) {
                 self.check_target_feature(attr, span, target)
-            } else if attr.check_name(sym::track_caller) {
+            } else if self.tcx.sess.check_name(attr, sym::track_caller) {
                 self.check_track_caller(&attr.span, attrs, span, target)
-            } else if attr.check_name(sym::doc) {
+            } else if self.tcx.sess.check_name(attr, sym::doc) {
                 self.check_doc_alias(attr)
             } else {
                 true
@@ -144,7 +143,7 @@ impl CheckAttrVisitor<'tcx> {
         target: Target,
     ) -> bool {
         match target {
-            _ if attr::contains_name(attrs, sym::naked) => {
+            _ if self.tcx.sess.contains_name(attrs, sym::naked) => {
                 struct_span_err!(
                     self.tcx.sess,
                     *attr_span,
@@ -262,7 +261,7 @@ impl CheckAttrVisitor<'tcx> {
         // ```
         let hints: Vec<_> = attrs
             .iter()
-            .filter(|attr| attr.check_name(sym::repr))
+            .filter(|attr| self.tcx.sess.check_name(attr, sym::repr))
             .filter_map(|attr| attr.meta_item_list())
             .flatten()
             .collect();
@@ -391,10 +390,10 @@ impl CheckAttrVisitor<'tcx> {
         // When checking statements ignore expressions, they will be checked later
         if let hir::StmtKind::Local(ref l) = stmt.kind {
             for attr in l.attrs.iter() {
-                if attr.check_name(sym::inline) {
+                if self.tcx.sess.check_name(attr, sym::inline) {
                     self.check_inline(l.hir_id, attr, &stmt.span, Target::Statement);
                 }
-                if attr.check_name(sym::repr) {
+                if self.tcx.sess.check_name(attr, sym::repr) {
                     self.emit_repr_error(
                         attr.span,
                         stmt.span,
@@ -412,10 +411,10 @@ impl CheckAttrVisitor<'tcx> {
             _ => Target::Expression,
         };
         for attr in expr.attrs.iter() {
-            if attr.check_name(sym::inline) {
+            if self.tcx.sess.check_name(attr, sym::inline) {
                 self.check_inline(expr.hir_id, attr, &expr.span, target);
             }
-            if attr.check_name(sym::repr) {
+            if self.tcx.sess.check_name(attr, sym::repr) {
                 self.emit_repr_error(
                     attr.span,
                     expr.span,
@@ -431,7 +430,7 @@ impl CheckAttrVisitor<'tcx> {
 
     fn check_used(&self, attrs: &'hir [Attribute], target: Target) {
         for attr in attrs {
-            if attr.check_name(sym::used) && target != Target::Static {
+            if self.tcx.sess.check_name(attr, sym::used) && target != Target::Static {
                 self.tcx
                     .sess
                     .span_err(attr.span, "attribute must be applied to a `static` variable");
diff --git a/src/librustc_passes/check_const.rs b/src/librustc_passes/check_const.rs
index b1ebab3f2f8..dd0bcbf208d 100644
--- a/src/librustc_passes/check_const.rs
+++ b/src/librustc_passes/check_const.rs
@@ -106,7 +106,7 @@ impl<'tcx> CheckConstVisitor<'tcx> {
 
             // 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.get_attrs(def_id), &tcx.sess.diagnostic())
+            attr::allow_internal_unstable(&tcx.sess, &tcx.get_attrs(def_id))
                 .map_or(false, |mut features| features.any(|name| name == feature_gate))
         };
 
diff --git a/src/librustc_passes/dead.rs b/src/librustc_passes/dead.rs
index ab75c8ebf4c..0e5298acc2c 100644
--- a/src/librustc_passes/dead.rs
+++ b/src/librustc_passes/dead.rs
@@ -15,7 +15,7 @@ use rustc_middle::middle::privacy;
 use rustc_middle::ty::{self, DefIdTree, TyCtxt};
 use rustc_session::lint;
 
-use rustc_ast::{ast, attr};
+use rustc_ast::ast;
 use rustc_span::symbol::{sym, Symbol};
 
 // Any local node that may call something in its body block should be
@@ -331,17 +331,17 @@ fn has_allow_dead_code_or_lang_attr(
     id: hir::HirId,
     attrs: &[ast::Attribute],
 ) -> bool {
-    if attr::contains_name(attrs, sym::lang) {
+    if tcx.sess.contains_name(attrs, sym::lang) {
         return true;
     }
 
     // Stable attribute for #[lang = "panic_impl"]
-    if attr::contains_name(attrs, sym::panic_handler) {
+    if tcx.sess.contains_name(attrs, sym::panic_handler) {
         return true;
     }
 
     // (To be) stable attribute for #[lang = "oom"]
-    if attr::contains_name(attrs, sym::alloc_error_handler) {
+    if tcx.sess.contains_name(attrs, sym::alloc_error_handler) {
         return true;
     }
 
diff --git a/src/librustc_passes/diagnostic_items.rs b/src/librustc_passes/diagnostic_items.rs
index 3cce4b8d00e..fa59337b0f6 100644
--- a/src/librustc_passes/diagnostic_items.rs
+++ b/src/librustc_passes/diagnostic_items.rs
@@ -16,6 +16,7 @@ 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::symbol::{sym, Symbol};
 
 struct DiagnosticItemCollector<'tcx> {
@@ -44,7 +45,7 @@ impl<'tcx> DiagnosticItemCollector<'tcx> {
     }
 
     fn observe_item(&mut self, attrs: &[ast::Attribute], hir_id: hir::HirId) {
-        if let Some(name) = extract(attrs) {
+        if let Some(name) = extract(&self.tcx.sess, attrs) {
             let def_id = self.tcx.hir().local_def_id(hir_id);
             // insert into our table
             collect_item(self.tcx, &mut self.items, name, def_id.to_def_id());
@@ -86,9 +87,9 @@ fn collect_item(
 }
 
 /// Extract the first `rustc_diagnostic_item = "$name"` out of a list of attributes.
-fn extract(attrs: &[ast::Attribute]) -> Option<Symbol> {
+fn extract(sess: &Session, attrs: &[ast::Attribute]) -> Option<Symbol> {
     attrs.iter().find_map(|attr| {
-        if attr.check_name(sym::rustc_diagnostic_item) { attr.value_str() } else { None }
+        if sess.check_name(attr, sym::rustc_diagnostic_item) { attr.value_str() } else { None }
     })
 }
 
diff --git a/src/librustc_passes/entry.rs b/src/librustc_passes/entry.rs
index 11612101e37..8aa6e7936be 100644
--- a/src/librustc_passes/entry.rs
+++ b/src/librustc_passes/entry.rs
@@ -1,4 +1,3 @@
-use rustc_ast::attr;
 use rustc_ast::entry::EntryPointType;
 use rustc_errors::struct_span_err;
 use rustc_hir::def_id::{CrateNum, LocalDefId, CRATE_DEF_INDEX, LOCAL_CRATE};
@@ -58,7 +57,7 @@ fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)
     }
 
     // If the user wants no main function at all, then stop here.
-    if attr::contains_name(&tcx.hir().krate().item.attrs, sym::no_main) {
+    if tcx.sess.contains_name(&tcx.hir().krate().item.attrs, sym::no_main) {
         return None;
     }
 
@@ -76,14 +75,14 @@ fn entry_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<(LocalDefId, EntryFnType)
     configure_main(tcx, &ctxt)
 }
 
-// Beware, this is duplicated in `librustc_ast/entry.rs`, so make sure to keep
-// them in sync.
-fn entry_point_type(item: &Item<'_>, at_root: bool) -> EntryPointType {
+// Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
+// (with `ast::Item`), so make sure to keep them in sync.
+fn entry_point_type(sess: &Session, item: &Item<'_>, at_root: bool) -> EntryPointType {
     match item.kind {
         ItemKind::Fn(..) => {
-            if attr::contains_name(&item.attrs, sym::start) {
+            if sess.contains_name(&item.attrs, sym::start) {
                 EntryPointType::Start
-            } else if attr::contains_name(&item.attrs, sym::main) {
+            } else if sess.contains_name(&item.attrs, sym::main) {
                 EntryPointType::MainAttr
             } else if item.ident.name == sym::main {
                 if at_root {
@@ -101,7 +100,7 @@ fn entry_point_type(item: &Item<'_>, at_root: bool) -> EntryPointType {
 }
 
 fn find_item(item: &Item<'_>, ctxt: &mut EntryContext<'_, '_>, at_root: bool) {
-    match entry_point_type(item, at_root) {
+    match entry_point_type(&ctxt.session, item, at_root) {
         EntryPointType::MainNamed => {
             if ctxt.main_fn.is_none() {
                 ctxt.main_fn = Some((item.hir_id, item.span));
diff --git a/src/librustc_passes/lang_items.rs b/src/librustc_passes/lang_items.rs
index e07c71b41d8..07415870549 100644
--- a/src/librustc_passes/lang_items.rs
+++ b/src/librustc_passes/lang_items.rs
@@ -56,7 +56,8 @@ impl LanguageItemCollector<'tcx> {
     }
 
     fn check_for_lang(&mut self, actual_target: Target, hir_id: HirId, attrs: &[Attribute]) {
-        if let Some((value, span)) = extract(&attrs) {
+        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+        if let Some((value, span)) = extract(check_name, &attrs) {
             match ITEM_REFS.get(&value).cloned() {
                 // Known lang item with attribute on correct target.
                 Some((item_index, expected_target)) if actual_target == expected_target => {
diff --git a/src/librustc_passes/layout_test.rs b/src/librustc_passes/layout_test.rs
index 55a6d3f7645..9450d75620a 100644
--- a/src/librustc_passes/layout_test.rs
+++ b/src/librustc_passes/layout_test.rs
@@ -29,7 +29,7 @@ impl ItemLikeVisitor<'tcx> for LayoutTest<'tcx> {
             | ItemKind::Struct(..)
             | ItemKind::Union(..) => {
                 for attr in self.tcx.get_attrs(item_def_id.to_def_id()).iter() {
-                    if attr.check_name(sym::rustc_layout) {
+                    if self.tcx.sess.check_name(attr, sym::rustc_layout) {
                         self.dump_layout_of(item_def_id, item, attr);
                     }
                 }
diff --git a/src/librustc_passes/lib_features.rs b/src/librustc_passes/lib_features.rs
index 922a475e5f4..9a4aa6a68a6 100644
--- a/src/librustc_passes/lib_features.rs
+++ b/src/librustc_passes/lib_features.rs
@@ -34,7 +34,9 @@ impl LibFeatureCollector<'tcx> {
 
         // Find a stability attribute (i.e., `#[stable (..)]`, `#[unstable (..)]`,
         // `#[rustc_const_unstable (..)]`).
-        if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.check_name(**stab_attr)) {
+        if let Some(stab_attr) =
+            stab_attrs.iter().find(|stab_attr| self.tcx.sess.check_name(attr, **stab_attr))
+        {
             let meta_item = attr.meta();
             if let Some(MetaItem { kind: MetaItemKind::List(ref metas), .. }) = meta_item {
                 let mut feature = None;
diff --git a/src/librustc_passes/liveness.rs b/src/librustc_passes/liveness.rs
index 45193c45569..6477f8da008 100644
--- a/src/librustc_passes/liveness.rs
+++ b/src/librustc_passes/liveness.rs
@@ -355,7 +355,7 @@ fn visit_fn<'tcx>(
     if let FnKind::Method(..) = fk {
         let parent = ir.tcx.hir().get_parent_item(id);
         if let Some(Node::Item(i)) = ir.tcx.hir().find(parent) {
-            if i.attrs.iter().any(|a| a.check_name(sym::automatically_derived)) {
+            if i.attrs.iter().any(|a| ir.tcx.sess.check_name(a, sym::automatically_derived)) {
                 return;
             }
         }
diff --git a/src/librustc_passes/stability.rs b/src/librustc_passes/stability.rs
index 830af8d31e7..1af77ae61c7 100644
--- a/src/librustc_passes/stability.rs
+++ b/src/librustc_passes/stability.rs
@@ -65,11 +65,8 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             did_error = self.forbid_staged_api_attrs(hir_id, attrs);
         }
 
-        let depr = if did_error {
-            None
-        } else {
-            attr::find_deprecation(&self.tcx.sess.parse_sess, attrs, item_sp)
-        };
+        let depr =
+            if did_error { None } else { attr::find_deprecation(&self.tcx.sess, attrs, item_sp) };
         let mut is_deprecated = false;
         if let Some(depr) = &depr {
             is_deprecated = true;
@@ -88,7 +85,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         }
 
         if self.tcx.features().staged_api {
-            if let Some(..) = attrs.iter().find(|a| a.check_name(sym::deprecated)) {
+            if let Some(..) = attrs.iter().find(|a| self.tcx.sess.check_name(a, sym::deprecated)) {
                 self.tcx.sess.span_err(
                     item_sp,
                     "`#[deprecated]` cannot be used in staged API; \
@@ -105,7 +102,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
             return;
         }
 
-        let (stab, const_stab) = attr::find_stability(&self.tcx.sess.parse_sess, attrs, item_sp);
+        let (stab, const_stab) = attr::find_stability(&self.tcx.sess, attrs, item_sp);
 
         let const_stab = const_stab.map(|const_stab| {
             let const_stab = self.tcx.intern_const_stability(const_stab);
@@ -252,7 +249,7 @@ impl<'a, 'tcx> Annotator<'a, 'tcx> {
         for attr in attrs {
             let name = attr.name_or_empty();
             if unstable_attrs.contains(&name) {
-                attr::mark_used(attr);
+                self.tcx.sess.mark_attr_used(attr);
                 struct_span_err!(
                     self.tcx.sess,
                     attr.span,
diff --git a/src/librustc_passes/weak_lang_items.rs b/src/librustc_passes/weak_lang_items.rs
index 3b11fb37962..2749b96bc85 100644
--- a/src/librustc_passes/weak_lang_items.rs
+++ b/src/librustc_passes/weak_lang_items.rs
@@ -100,7 +100,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for Context<'a, 'tcx> {
     }
 
     fn visit_foreign_item(&mut self, i: &hir::ForeignItem<'_>) {
-        if let Some((lang_item, _)) = hir::lang_items::extract(&i.attrs) {
+        let check_name = |attr, sym| self.tcx.sess.check_name(attr, sym);
+        if let Some((lang_item, _)) = hir::lang_items::extract(check_name, &i.attrs) {
             self.register(lang_item, i.span, i.hir_id);
         }
         intravisit::walk_foreign_item(self, i)
diff --git a/src/librustc_plugin_impl/build.rs b/src/librustc_plugin_impl/build.rs
index db2363316cd..d16dd701a12 100644
--- a/src/librustc_plugin_impl/build.rs
+++ b/src/librustc_plugin_impl/build.rs
@@ -1,6 +1,5 @@
 //! Used by `rustc` when compiling a plugin crate.
 
-use rustc_ast::attr;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
@@ -9,14 +8,15 @@ use rustc_middle::ty::TyCtxt;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
-struct RegistrarFinder {
+struct RegistrarFinder<'tcx> {
+    tcx: TyCtxt<'tcx>,
     registrars: Vec<(hir::HirId, Span)>,
 }
 
-impl<'v> ItemLikeVisitor<'v> for RegistrarFinder {
+impl<'v, 'tcx> ItemLikeVisitor<'v> for RegistrarFinder<'tcx> {
     fn visit_item(&mut self, item: &hir::Item<'_>) {
         if let hir::ItemKind::Fn(..) = item.kind {
-            if attr::contains_name(&item.attrs, sym::plugin_registrar) {
+            if self.tcx.sess.contains_name(&item.attrs, sym::plugin_registrar) {
                 self.registrars.push((item.hir_id, item.span));
             }
         }
@@ -35,7 +35,7 @@ pub fn find_plugin_registrar(tcx: TyCtxt<'_>) -> Option<DefId> {
 fn plugin_registrar_fn(tcx: TyCtxt<'_>, cnum: CrateNum) -> Option<DefId> {
     assert_eq!(cnum, LOCAL_CRATE);
 
-    let mut finder = RegistrarFinder { registrars: Vec::new() };
+    let mut finder = RegistrarFinder { tcx, registrars: Vec::new() };
     tcx.hir().krate().visit_all_item_likes(&mut finder);
 
     match finder.registrars.len() {
diff --git a/src/librustc_plugin_impl/lib.rs b/src/librustc_plugin_impl/lib.rs
index 10712eb60b9..1eb65dd96ba 100644
--- a/src/librustc_plugin_impl/lib.rs
+++ b/src/librustc_plugin_impl/lib.rs
@@ -8,6 +8,7 @@
 
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
 #![feature(nll)]
+#![recursion_limit = "256"]
 
 use rustc_lint::LintStore;
 
diff --git a/src/librustc_plugin_impl/load.rs b/src/librustc_plugin_impl/load.rs
index 62a87b47a2f..2f307302db0 100644
--- a/src/librustc_plugin_impl/load.rs
+++ b/src/librustc_plugin_impl/load.rs
@@ -32,7 +32,7 @@ pub fn load_plugins(
     let mut plugins = Vec::new();
 
     for attr in &krate.attrs {
-        if !attr.check_name(sym::plugin) {
+        if !sess.check_name(attr, sym::plugin) {
             continue;
         }
 
diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs
index fc00050f405..2aa7780aaaf 100644
--- a/src/librustc_privacy/lib.rs
+++ b/src/librustc_privacy/lib.rs
@@ -232,7 +232,7 @@ fn def_id_visibility<'tcx>(
                 Node::Item(item) => &item.vis,
                 Node::ForeignItem(foreign_item) => &foreign_item.vis,
                 Node::MacroDef(macro_def) => {
-                    if attr::contains_name(&macro_def.attrs, sym::macro_export) {
+                    if tcx.sess.contains_name(&macro_def.attrs, sym::macro_export) {
                         return (ty::Visibility::Public, macro_def.span, "public");
                     } else {
                         &macro_def.vis
@@ -271,8 +271,11 @@ fn def_id_visibility<'tcx>(
                                 ctor_vis =
                                     ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
                                 let attrs = tcx.get_attrs(variant.def_id);
-                                span =
-                                    attr::find_by_name(&attrs, sym::non_exhaustive).unwrap().span;
+                                span = tcx
+                                    .sess
+                                    .find_by_name(&attrs, sym::non_exhaustive)
+                                    .unwrap()
+                                    .span;
                                 descr = "crate-visible";
                             }
 
@@ -305,7 +308,9 @@ fn def_id_visibility<'tcx>(
                                 if adt_def.non_enum_variant().is_field_list_non_exhaustive() {
                                     ctor_vis =
                                         ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
-                                    span = attr::find_by_name(&item.attrs, sym::non_exhaustive)
+                                    span = tcx
+                                        .sess
+                                        .find_by_name(&item.attrs, sym::non_exhaustive)
                                         .unwrap()
                                         .span;
                                     descr = "crate-visible";
@@ -914,7 +919,9 @@ impl Visitor<'tcx> for EmbargoVisitor<'tcx> {
     }
 
     fn visit_macro_def(&mut self, md: &'tcx hir::MacroDef<'tcx>) {
-        if attr::find_transparency(&md.attrs, md.ast.macro_rules).0 != Transparency::Opaque {
+        if attr::find_transparency(&self.tcx.sess, &md.attrs, md.ast.macro_rules).0
+            != Transparency::Opaque
+        {
             self.update(md.hir_id, Some(AccessLevel::Public));
             return;
         }
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs
index 737fd138120..11c7793b3ad 100644
--- a/src/librustc_resolve/build_reduced_graph.rs
+++ b/src/librustc_resolve/build_reduced_graph.rs
@@ -541,7 +541,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
             }
             ast::UseTreeKind::Glob => {
                 let kind = ImportKind::Glob {
-                    is_prelude: attr::contains_name(&item.attrs, sym::prelude_import),
+                    is_prelude: self.r.session.contains_name(&item.attrs, sym::prelude_import),
                     max_vis: Cell::new(ty::Visibility::Invisible),
                 };
                 self.add_import(prefix, kind, use_tree.span, id, item, root_span, item.id, vis);
@@ -712,7 +712,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 let module_kind = ModuleKind::Def(DefKind::Mod, def_id.to_def_id(), ident.name);
                 let module = self.r.arenas.alloc_module(ModuleData {
                     no_implicit_prelude: parent.no_implicit_prelude || {
-                        attr::contains_name(&item.attrs, sym::no_implicit_prelude)
+                        self.r.session.contains_name(&item.attrs, sym::no_implicit_prelude)
                     },
                     ..ModuleData::new(
                         Some(parent),
@@ -789,7 +789,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                     // If the structure is marked as non_exhaustive then lower the visibility
                     // to within the crate.
                     let mut ctor_vis = if vis == ty::Visibility::Public
-                        && attr::contains_name(&item.attrs, sym::non_exhaustive)
+                        && self.r.session.contains_name(&item.attrs, sym::non_exhaustive)
                     {
                         ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX))
                     } else {
@@ -991,7 +991,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         let mut import_all = None;
         let mut single_imports = Vec::new();
         for attr in &item.attrs {
-            if attr.check_name(sym::macro_use) {
+            if self.r.session.check_name(attr, sym::macro_use) {
                 if self.parent_scope.module.parent.is_some() {
                     struct_span_err!(
                         self.r.session,
@@ -1097,7 +1097,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
     /// Returns `true` if this attribute list contains `macro_use`.
     fn contains_macro_use(&mut self, attrs: &[ast::Attribute]) -> bool {
         for attr in attrs {
-            if attr.check_name(sym::macro_escape) {
+            if self.r.session.check_name(attr, sym::macro_escape) {
                 let msg = "`#[macro_escape]` is a deprecated synonym for `#[macro_use]`";
                 let mut err = self.r.session.struct_span_warn(attr.span, msg);
                 if let ast::AttrStyle::Inner = attr.style {
@@ -1105,7 +1105,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 } else {
                     err.emit();
                 }
-            } else if !attr.check_name(sym::macro_use) {
+            } else if !self.r.session.check_name(attr, sym::macro_use) {
                 continue;
             }
 
@@ -1129,12 +1129,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         MacroRulesScope::Invocation(invoc_id)
     }
 
-    fn proc_macro_stub(item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
-        if attr::contains_name(&item.attrs, sym::proc_macro) {
+    fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
+        if self.r.session.contains_name(&item.attrs, sym::proc_macro) {
             return Some((MacroKind::Bang, item.ident, item.span));
-        } else if attr::contains_name(&item.attrs, sym::proc_macro_attribute) {
+        } else if self.r.session.contains_name(&item.attrs, sym::proc_macro_attribute) {
             return Some((MacroKind::Attr, item.ident, item.span));
-        } else if let Some(attr) = attr::find_by_name(&item.attrs, sym::proc_macro_derive) {
+        } else if let Some(attr) = self.r.session.find_by_name(&item.attrs, sym::proc_macro_derive)
+        {
             if let Some(nested_meta) = attr.meta_item_list().and_then(|list| list.get(0).cloned()) {
                 if let Some(ident) = nested_meta.ident() {
                     return Some((MacroKind::Derive, ident, ident.span));
@@ -1168,7 +1169,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
                 let ext = Lrc::new(self.r.compile_macro(item, self.r.session.edition()));
                 (ext, item.ident, item.span, def.macro_rules)
             }
-            ItemKind::Fn(..) => match Self::proc_macro_stub(item) {
+            ItemKind::Fn(..) => match self.proc_macro_stub(item) {
                 Some((macro_kind, ident, span)) => {
                     self.r.proc_macro_stubs.insert(def_id);
                     (self.r.dummy_ext(macro_kind), ident, span, false)
@@ -1185,7 +1186,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         if macro_rules {
             let ident = ident.normalize_to_macros_2_0();
             self.r.macro_names.insert(ident);
-            let is_macro_export = attr::contains_name(&item.attrs, sym::macro_export);
+            let is_macro_export = self.r.session.contains_name(&item.attrs, sym::macro_export);
             let vis = if is_macro_export {
                 ty::Visibility::Public
             } else {
@@ -1416,7 +1417,7 @@ impl<'a, 'b> Visitor<'b> for BuildReducedGraphVisitor<'a, 'b> {
         // If the variant is marked as non_exhaustive then lower the visibility to within the
         // crate.
         let mut ctor_vis = vis;
-        let has_non_exhaustive = attr::contains_name(&variant.attrs, sym::non_exhaustive);
+        let has_non_exhaustive = self.r.session.contains_name(&variant.attrs, sym::non_exhaustive);
         if has_non_exhaustive && vis == ty::Visibility::Public {
             ctor_vis = ty::Visibility::Restricted(DefId::local(CRATE_DEF_INDEX));
         }
diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs
index 81e29047dc5..9502be728de 100644
--- a/src/librustc_resolve/diagnostics.rs
+++ b/src/librustc_resolve/diagnostics.rs
@@ -466,6 +466,23 @@ impl<'a> Resolver<'a> {
                 );
                 err
             }
+            ResolutionError::ParamInNonTrivialAnonConst(name) => {
+                let mut err = self.session.struct_span_err(
+                    span,
+                    "generic parameters must not be used inside of non trivial constant values",
+                );
+                err.span_label(
+                    span,
+                    &format!(
+                        "non-trivial anonymous constants must not depend on the parameter `{}`",
+                        name
+                    ),
+                );
+                err.help(
+                    &format!("it is currently only allowed to use either `{0}` or `{{ {0} }}` as generic constants", name)
+                );
+                err
+            }
             ResolutionError::SelfInTyParamDefault => {
                 let mut err = struct_span_err!(
                     self.session,
@@ -1075,10 +1092,9 @@ impl<'a> Resolver<'a> {
         ) = binding.kind
         {
             let def_id = (&*self).parent(ctor_def_id).expect("no parent for a constructor");
-            if let Some(fields) = self.field_names.get(&def_id) {
-                let first_field = fields.first().expect("empty field list in the map");
-                return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
-            }
+            let fields = self.field_names.get(&def_id)?;
+            let first_field = fields.first()?; // Handle `struct Foo()`
+            return Some(fields.iter().fold(first_field.span, |acc, field| acc.to(field.span)));
         }
         None
     }
diff --git a/src/librustc_resolve/late.rs b/src/librustc_resolve/late.rs
index 44ff4209095..7ecfe2554ec 100644
--- a/src/librustc_resolve/late.rs
+++ b/src/librustc_resolve/late.rs
@@ -111,7 +111,7 @@ crate enum RibKind<'a> {
     ItemRibKind(HasGenericParams),
 
     /// We're in a constant item. Can't refer to dynamic stuff.
-    ConstantItemRibKind,
+    ConstantItemRibKind(bool),
 
     /// We passed through a module.
     ModuleRibKind(Module<'a>),
@@ -137,7 +137,7 @@ impl RibKind<'_> {
             NormalRibKind
             | ClosureOrAsyncRibKind
             | FnItemRibKind
-            | ConstantItemRibKind
+            | ConstantItemRibKind(_)
             | ModuleRibKind(_)
             | MacroDefinition(_)
             | ConstParamTyRibKind => false,
@@ -226,7 +226,7 @@ impl<'a> PathSource<'a> {
                 ValueNS => "method or associated constant",
                 MacroNS => bug!("associated macro"),
             },
-            PathSource::Expr(parent) => match &parent.as_ref().map(|p| &p.kind) {
+            PathSource::Expr(parent) => match parent.as_ref().map(|p| &p.kind) {
                 // "function" here means "anything callable" rather than `DefKind::Fn`,
                 // this is not precise but usually more helpful than just "value".
                 Some(ExprKind::Call(call_expr, _)) => match &call_expr.kind {
@@ -426,7 +426,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
     }
     fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
         debug!("visit_anon_const {:?}", constant);
-        self.with_constant_rib(|this| {
+        self.with_constant_rib(constant.value.is_potential_trivial_const_param(), |this| {
             visit::walk_anon_const(this, constant);
         });
     }
@@ -628,7 +628,7 @@ impl<'a, 'ast> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast> {
                         if !check_ns(TypeNS) && check_ns(ValueNS) {
                             // This must be equivalent to `visit_anon_const`, but we cannot call it
                             // directly due to visitor lifetimes so we have to copy-paste some code.
-                            self.with_constant_rib(|this| {
+                            self.with_constant_rib(true, |this| {
                                 this.smart_resolve_path(
                                     ty.id,
                                     qself.as_ref(),
@@ -829,7 +829,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 | ClosureOrAsyncRibKind
                 | FnItemRibKind
                 | ItemRibKind(..)
-                | ConstantItemRibKind
+                | ConstantItemRibKind(_)
                 | ModuleRibKind(..)
                 | ForwardTyParamBanRibKind
                 | ConstParamTyRibKind => {
@@ -948,7 +948,14 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                         // Only impose the restrictions of `ConstRibKind` for an
                                         // actual constant expression in a provided default.
                                         if let Some(expr) = default {
-                                            this.with_constant_rib(|this| this.visit_expr(expr));
+                                            // We allow arbitrary const expressions inside of associated consts,
+                                            // even if they are potentially not const evaluatable.
+                                            //
+                                            // Type parameters can already be used and as associated consts are
+                                            // not used as part of the type system, this is far less surprising.
+                                            this.with_constant_rib(true, |this| {
+                                                this.visit_expr(expr)
+                                            });
                                         }
                                     }
                                     AssocItemKind::Fn(_, _, generics, _) => {
@@ -989,7 +996,9 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                 self.with_item_rib(HasGenericParams::No, |this| {
                     this.visit_ty(ty);
                     if let Some(expr) = expr {
-                        this.with_constant_rib(|this| this.visit_expr(expr));
+                        this.with_constant_rib(expr.is_potential_trivial_const_param(), |this| {
+                            this.visit_expr(expr)
+                        });
                     }
                 });
             }
@@ -1086,11 +1095,11 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
         self.with_rib(ValueNS, kind, |this| this.with_rib(TypeNS, kind, f))
     }
 
-    fn with_constant_rib(&mut self, f: impl FnOnce(&mut Self)) {
+    fn with_constant_rib(&mut self, trivial: bool, f: impl FnOnce(&mut Self)) {
         debug!("with_constant_rib");
-        self.with_rib(ValueNS, ConstantItemRibKind, |this| {
-            this.with_rib(TypeNS, ConstantItemRibKind, |this| {
-                this.with_label_rib(ConstantItemRibKind, f);
+        self.with_rib(ValueNS, ConstantItemRibKind(trivial), |this| {
+            this.with_rib(TypeNS, ConstantItemRibKind(trivial), |this| {
+                this.with_label_rib(ConstantItemRibKind(trivial), f);
             })
         });
     }
@@ -1220,7 +1229,7 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                 for item in impl_items {
                                     use crate::ResolutionError::*;
                                     match &item.kind {
-                                        AssocItemKind::Const(..) => {
+                                        AssocItemKind::Const(_default, _ty, _expr) => {
                                             debug!("resolve_implementation AssocItemKind::Const",);
                                             // If this is a trait impl, ensure the const
                                             // exists in trait
@@ -1231,7 +1240,12 @@ impl<'a, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
                                                 |n, s| ConstNotMemberOfTrait(n, s),
                                             );
 
-                                            this.with_constant_rib(|this| {
+                                            // We allow arbitrary const expressions inside of associated consts,
+                                            // even if they are potentially not const evaluatable.
+                                            //
+                                            // Type parameters can already be used and as associated consts are
+                                            // not used as part of the type system, this is far less surprising.
+                                            this.with_constant_rib(true, |this| {
                                                 visit::walk_assoc_item(this, item, AssocCtxt::Impl)
                                             });
                                         }
diff --git a/src/librustc_resolve/late/lifetimes.rs b/src/librustc_resolve/late/lifetimes.rs
index 3fbb42ece47..0b881b089de 100644
--- a/src/librustc_resolve/late/lifetimes.rs
+++ b/src/librustc_resolve/late/lifetimes.rs
@@ -6,7 +6,6 @@
 //! way. Therefore, we break lifetime name resolution into a separate pass.
 
 use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot};
-use rustc_ast::attr;
 use rustc_ast::walk_list;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
@@ -1179,7 +1178,7 @@ fn compute_object_lifetime_defaults(tcx: TyCtxt<'_>) -> HirIdMap<Vec<ObjectLifet
                 let result = object_lifetime_defaults_for_item(tcx, generics);
 
                 // Debugging aid.
-                if attr::contains_name(&item.attrs, sym::rustc_object_lifetime_default) {
+                if tcx.sess.contains_name(&item.attrs, sym::rustc_object_lifetime_default) {
                     let object_lifetime_default_reprs: String = result
                         .iter()
                         .map(|set| match *set {
@@ -1540,13 +1539,9 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
                             if let Some(def_id) = parent_def_id.as_local() {
                                 let parent_hir_id = self.tcx.hir().as_local_hir_id(def_id);
                                 // lifetimes in `derive` expansions don't count (Issue #53738)
-                                if self
-                                    .tcx
-                                    .hir()
-                                    .attrs(parent_hir_id)
-                                    .iter()
-                                    .any(|attr| attr.check_name(sym::automatically_derived))
-                                {
+                                if self.tcx.hir().attrs(parent_hir_id).iter().any(|attr| {
+                                    self.tcx.sess.check_name(attr, sym::automatically_derived)
+                                }) {
                                     continue;
                                 }
                             }
diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs
index 234fcd789ee..79f5a27bb28 100644
--- a/src/librustc_resolve/lib.rs
+++ b/src/librustc_resolve/lib.rs
@@ -23,7 +23,6 @@ use rustc_arena::TypedArena;
 use rustc_ast::ast::{self, FloatTy, IntTy, NodeId, UintTy};
 use rustc_ast::ast::{Crate, CRATE_NODE_ID};
 use rustc_ast::ast::{ItemKind, Path};
-use rustc_ast::attr;
 use rustc_ast::node_id::NodeMap;
 use rustc_ast::unwrap_or;
 use rustc_ast::visit::{self, Visitor};
@@ -218,6 +217,10 @@ enum ResolutionError<'a> {
     ParamInTyOfConstParam(Symbol),
     /// constant values inside of type parameter defaults must not depend on generic parameters.
     ParamInAnonConstInTyDefault(Symbol),
+    /// generic parameters must not be used inside of non trivial constant values.
+    ///
+    /// This error is only emitted when using `min_const_generics`.
+    ParamInNonTrivialAnonConst(Symbol),
     /// Error E0735: type parameters with a default cannot use `Self`
     SelfInTyParamDefault,
     /// Error E0767: use of unreachable label
@@ -1194,7 +1197,7 @@ impl<'a> Resolver<'a> {
         let root_def_id = DefId::local(CRATE_DEF_INDEX);
         let root_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid);
         let graph_root = arenas.alloc_module(ModuleData {
-            no_implicit_prelude: attr::contains_name(&krate.attrs, sym::no_implicit_prelude),
+            no_implicit_prelude: session.contains_name(&krate.attrs, sym::no_implicit_prelude),
             ..ModuleData::new(None, root_module_kind, root_def_id, ExpnId::root(), krate.span)
         });
         let empty_module_kind = ModuleKind::Def(DefKind::Mod, root_def_id, kw::Invalid);
@@ -1232,9 +1235,9 @@ impl<'a> Resolver<'a> {
             .map(|(name, _)| (Ident::from_str(name), Default::default()))
             .collect();
 
-        if !attr::contains_name(&krate.attrs, sym::no_core) {
+        if !session.contains_name(&krate.attrs, sym::no_core) {
             extern_prelude.insert(Ident::with_dummy_span(sym::core), Default::default());
-            if !attr::contains_name(&krate.attrs, sym::no_std) {
+            if !session.contains_name(&krate.attrs, sym::no_std) {
                 extern_prelude.insert(Ident::with_dummy_span(sym::std), Default::default());
                 if session.rust_2018() {
                     extern_prelude.insert(Ident::with_dummy_span(sym::meta), Default::default());
@@ -2507,7 +2510,7 @@ impl<'a> Resolver<'a> {
                                 res_err = Some(CannotCaptureDynamicEnvironmentInFnItem);
                             }
                         }
-                        ConstantItemRibKind => {
+                        ConstantItemRibKind(_) => {
                             // Still doesn't deal with upvars
                             if record_used {
                                 self.report_error(span, AttemptToUseNonConstantValueInConstant);
@@ -2546,7 +2549,18 @@ impl<'a> Resolver<'a> {
                             in_ty_param_default = true;
                             continue;
                         }
-                        ConstantItemRibKind => {
+                        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),
+                                    );
+                                }
+                                return Res::Err;
+                            }
+
                             if in_ty_param_default {
                                 if record_used {
                                     self.report_error(
@@ -2612,7 +2626,18 @@ impl<'a> Resolver<'a> {
                             in_ty_param_default = true;
                             continue;
                         }
-                        ConstantItemRibKind => {
+                        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),
+                                    );
+                                }
+                                return Res::Err;
+                            }
+
                             if in_ty_param_default {
                                 if record_used {
                                     self.report_error(
diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs
index ccc7e16ae4c..542025ac1f4 100644
--- a/src/librustc_resolve/macros.rs
+++ b/src/librustc_resolve/macros.rs
@@ -9,7 +9,7 @@ use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, T
 use rustc_ast::ast::{self, NodeId};
 use rustc_ast_lowering::ResolverAstLowering;
 use rustc_ast_pretty::pprust;
-use rustc_attr::{self as attr, StabilityLevel};
+use rustc_attr::StabilityLevel;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_expand::base::{Indeterminate, InvocationRes, ResolverExpand, SyntaxExtension};
 use rustc_expand::compile_declarative_macro;
@@ -105,7 +105,7 @@ fn registered_idents(
     descr: &str,
 ) -> FxHashSet<Ident> {
     let mut registered = FxHashSet::default();
-    for attr in attr::filter_by_name(attrs, attr_name) {
+    for attr in sess.filter_by_name(attrs, attr_name) {
         for nested_meta in attr.meta_item_list().unwrap_or_default() {
             match nested_meta.ident() {
                 Some(ident) => {
@@ -1068,7 +1068,7 @@ impl<'a> Resolver<'a> {
     /// its expander to a pre-defined one for built-in macros.
     crate fn compile_macro(&mut self, item: &ast::Item, edition: Edition) -> SyntaxExtension {
         let mut result = compile_declarative_macro(
-            &self.session.parse_sess,
+            &self.session,
             self.session.features_untracked(),
             item,
             edition,
diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs
index 8e379a35100..6469971fce8 100644
--- a/src/librustc_save_analysis/lib.rs
+++ b/src/librustc_save_analysis/lib.rs
@@ -10,7 +10,7 @@ mod span_utils;
 mod sig;
 
 use rustc_ast::ast::{self};
-use rustc_ast::util::comments::strip_doc_comment_decoration;
+use rustc_ast::util::comments::beautify_doc_string;
 use rustc_ast_pretty::pprust::attribute_to_string;
 use rustc_hir as hir;
 use rustc_hir::def::{DefKind as HirDefKind, Res};
@@ -702,7 +702,7 @@ impl<'tcx> SaveContext<'tcx> {
             Res::Def(HirDefKind::ConstParam, def_id) => {
                 Some(Ref { kind: RefKind::Variable, span, ref_id: id_from_def_id(def_id) })
             }
-            Res::Def(HirDefKind::Ctor(_, ..), def_id) => {
+            Res::Def(HirDefKind::Ctor(..), def_id) => {
                 // This is a reference to a tuple struct or an enum variant where the def_id points
                 // to an invisible constructor function. That is not a very useful
                 // def, so adjust to point to the tuple struct or enum variant itself.
@@ -822,13 +822,10 @@ impl<'tcx> SaveContext<'tcx> {
 
         for attr in attrs {
             if let Some(val) = attr.doc_str() {
-                if attr.is_doc_comment() {
-                    result.push_str(&strip_doc_comment_decoration(val));
-                } else {
-                    result.push_str(&val.as_str());
-                }
+                // FIXME: Should save-analysis beautify doc strings itself or leave it to users?
+                result.push_str(&beautify_doc_string(val));
                 result.push('\n');
-            } else if attr.check_name(sym::doc) {
+            } else if self.tcx.sess.check_name(attr, sym::doc) {
                 if let Some(meta_list) = attr.meta_item_list() {
                     meta_list
                         .into_iter()
diff --git a/src/librustc_session/filesearch.rs b/src/librustc_session/filesearch.rs
index 504490d938c..8fe71b71cae 100644
--- a/src/librustc_session/filesearch.rs
+++ b/src/librustc_session/filesearch.rs
@@ -98,7 +98,7 @@ impl<'a> FileSearch<'a> {
         p.push(RUST_LIB_DIR);
         p.push(&self.triple);
         p.push("bin");
-        if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p.clone()] }
+        if self_contained { vec![p.clone(), p.join("self-contained")] } else { vec![p] }
     }
 }
 
diff --git a/src/librustc_session/output.rs b/src/librustc_session/output.rs
index 52216188397..e4bce435c4b 100644
--- a/src/librustc_session/output.rs
+++ b/src/librustc_session/output.rs
@@ -1,7 +1,7 @@
 //! Related to out filenames of compilation (e.g. save analysis, binaries).
 use crate::config::{CrateType, Input, OutputFilenames, OutputType};
 use crate::Session;
-use rustc_ast::{ast, attr};
+use rustc_ast::ast;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use std::path::{Path, PathBuf};
@@ -45,7 +45,7 @@ fn is_writeable(p: &Path) -> bool {
     }
 }
 
-pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input: &Input) -> String {
+pub fn find_crate_name(sess: &Session, attrs: &[ast::Attribute], input: &Input) -> String {
     let validate = |s: String, span: Option<Span>| {
         validate_crate_name(sess, &s, span);
         s
@@ -56,22 +56,20 @@ pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input:
     // the command line over one found in the #[crate_name] attribute. If we
     // find both we ensure that they're the same later on as well.
     let attr_crate_name =
-        attr::find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
-
-    if let Some(sess) = sess {
-        if let Some(ref s) = sess.opts.crate_name {
-            if let Some((attr, name)) = attr_crate_name {
-                if name.as_str() != *s {
-                    let msg = format!(
-                        "`--crate-name` and `#[crate_name]` are \
-                                       required to match, but `{}` != `{}`",
-                        s, name
-                    );
-                    sess.span_err(attr.span, &msg);
-                }
+        sess.find_by_name(attrs, sym::crate_name).and_then(|at| at.value_str().map(|s| (at, s)));
+
+    if let Some(ref s) = sess.opts.crate_name {
+        if let Some((attr, name)) = attr_crate_name {
+            if name.as_str() != *s {
+                let msg = format!(
+                    "`--crate-name` and `#[crate_name]` are \
+                                   required to match, but `{}` != `{}`",
+                    s, name
+                );
+                sess.span_err(attr.span, &msg);
             }
-            return validate(s.clone(), None);
         }
+        return validate(s.clone(), None);
     }
 
     if let Some((attr, s)) = attr_crate_name {
@@ -85,9 +83,7 @@ pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input:
                                    `{}` has a leading hyphen",
                     s
                 );
-                if let Some(sess) = sess {
-                    sess.err(&msg);
-                }
+                sess.err(&msg);
             } else {
                 return validate(s.replace("-", "_"), None);
             }
@@ -97,14 +93,13 @@ pub fn find_crate_name(sess: Option<&Session>, attrs: &[ast::Attribute], input:
     "rust_out".to_string()
 }
 
-pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
+pub fn validate_crate_name(sess: &Session, s: &str, sp: Option<Span>) {
     let mut err_count = 0;
     {
         let mut say = |s: &str| {
-            match (sp, sess) {
-                (_, None) => panic!("{}", s),
-                (Some(sp), Some(sess)) => sess.span_err(sp, s),
-                (None, Some(sess)) => sess.err(s),
+            match sp {
+                Some(sp) => sess.span_err(sp, s),
+                None => sess.err(s),
             }
             err_count += 1;
         };
@@ -123,7 +118,7 @@ pub fn validate_crate_name(sess: Option<&Session>, s: &str, sp: Option<Span>) {
     }
 
     if err_count > 0 {
-        sess.unwrap().abort_if_errors();
+        sess.abort_if_errors();
     }
 }
 
diff --git a/src/librustc_session/session.rs b/src/librustc_session/session.rs
index e9077f40859..9191f7e8d76 100644
--- a/src/librustc_session/session.rs
+++ b/src/librustc_session/session.rs
@@ -7,6 +7,8 @@ use crate::lint;
 use crate::parse::ParseSess;
 use crate::search_paths::{PathKind, SearchPath};
 
+pub use rustc_ast::ast::Attribute;
+pub use rustc_ast::attr::MarkedAttrs;
 pub use rustc_ast::crate_disambiguator::CrateDisambiguator;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -22,7 +24,7 @@ use rustc_errors::registry::Registry;
 use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorReported};
 use rustc_span::edition::Edition;
 use rustc_span::source_map::{FileLoader, MultiSpan, RealFileLoader, SourceMap, Span};
-use rustc_span::{SourceFileHashAlgorithm, Symbol};
+use rustc_span::{sym, SourceFileHashAlgorithm, Symbol};
 use rustc_target::asm::InlineAsmArch;
 use rustc_target::spec::{CodeModel, PanicStrategy, RelocModel, RelroLevel};
 use rustc_target::spec::{Target, TargetTriple, TlsModel};
@@ -208,6 +210,9 @@ pub struct Session {
 
     /// Set of enabled features for the current target.
     pub target_features: FxHashSet<Symbol>,
+
+    known_attrs: Lock<MarkedAttrs>,
+    used_attrs: Lock<MarkedAttrs>,
 }
 
 pub struct PerfStats {
@@ -1020,6 +1025,76 @@ impl Session {
         // MemorySanitizer uses lifetimes to detect use of uninitialized stack variables.
         || self.opts.debugging_opts.sanitizer.intersects(SanitizerSet::ADDRESS | SanitizerSet::MEMORY)
     }
+
+    pub fn mark_attr_known(&self, attr: &Attribute) {
+        self.known_attrs.lock().mark(attr)
+    }
+
+    pub fn is_attr_known(&self, attr: &Attribute) -> bool {
+        self.known_attrs.lock().is_marked(attr)
+    }
+
+    pub fn mark_attr_used(&self, attr: &Attribute) {
+        self.used_attrs.lock().mark(attr)
+    }
+
+    pub fn is_attr_used(&self, attr: &Attribute) -> bool {
+        self.used_attrs.lock().is_marked(attr)
+    }
+
+    /// Returns `true` if the attribute's path matches the argument. If it matches, then the
+    /// attribute is marked as used.
+
+    /// Returns `true` if the attribute's path matches the argument. If it
+    /// matches, then the attribute is marked as used.
+    ///
+    /// This method should only be used by rustc, other tools can use
+    /// `Attribute::has_name` instead, because only rustc is supposed to report
+    /// the `unused_attributes` lint. (`MetaItem` and `NestedMetaItem` are
+    /// produced by lowering an `Attribute` and don't have identity, so they
+    /// only have the `has_name` method, and you need to mark the original
+    /// `Attribute` as used when necessary.)
+    pub fn check_name(&self, attr: &Attribute, name: Symbol) -> bool {
+        let matches = attr.has_name(name);
+        if matches {
+            self.mark_attr_used(attr);
+        }
+        matches
+    }
+
+    pub fn is_proc_macro_attr(&self, attr: &Attribute) -> bool {
+        [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
+            .iter()
+            .any(|kind| self.check_name(attr, *kind))
+    }
+
+    pub fn contains_name(&self, attrs: &[Attribute], name: Symbol) -> bool {
+        attrs.iter().any(|item| self.check_name(item, name))
+    }
+
+    pub fn find_by_name<'a>(
+        &'a self,
+        attrs: &'a [Attribute],
+        name: Symbol,
+    ) -> Option<&'a Attribute> {
+        attrs.iter().find(|attr| self.check_name(attr, name))
+    }
+
+    pub fn filter_by_name<'a>(
+        &'a self,
+        attrs: &'a [Attribute],
+        name: Symbol,
+    ) -> impl Iterator<Item = &'a Attribute> {
+        attrs.iter().filter(move |attr| self.check_name(attr, name))
+    }
+
+    pub fn first_attr_value_str_by_name(
+        &self,
+        attrs: &[Attribute],
+        name: Symbol,
+    ) -> Option<Symbol> {
+        attrs.iter().find(|at| self.check_name(at, name)).and_then(|at| at.value_str())
+    }
 }
 
 fn default_emitter(
@@ -1283,6 +1358,8 @@ pub fn build_session(
         real_rust_source_base_dir,
         asm_arch,
         target_features: FxHashSet::default(),
+        known_attrs: Lock::new(MarkedAttrs::new()),
+        used_attrs: Lock::new(MarkedAttrs::new()),
     };
 
     validate_commandline_args_with_session_available(&sess);
diff --git a/src/librustc_span/hygiene.rs b/src/librustc_span/hygiene.rs
index a03ac4e1fdb..f52b2195c2f 100644
--- a/src/librustc_span/hygiene.rs
+++ b/src/librustc_span/hygiene.rs
@@ -80,8 +80,6 @@ pub enum Transparency {
     Opaque,
 }
 
-pub(crate) const NUM_TRANSPARENCIES: usize = 3;
-
 impl ExpnId {
     pub fn fresh(expn_data: Option<ExpnData>) -> Self {
         HygieneData::with(|data| data.fresh_expn(expn_data))
@@ -619,6 +617,11 @@ impl SyntaxContext {
     }
 
     #[inline]
+    pub fn outer_mark(self) -> (ExpnId, Transparency) {
+        HygieneData::with(|data| data.outer_mark(self))
+    }
+
+    #[inline]
     pub fn outer_mark_with_data(self) -> (ExpnId, Transparency, ExpnData) {
         HygieneData::with(|data| {
             let (expn_id, transparency) = data.outer_mark(self);
@@ -667,7 +670,6 @@ pub struct ExpnData {
     /// The kind of this expansion - macro or compiler desugaring.
     pub kind: ExpnKind,
     /// The expansion that produced this expansion.
-    #[stable_hasher(ignore)]
     pub parent: ExpnId,
     /// The location of the actual macro invocation or syntax sugar , e.g.
     /// `let x = foo!();` or `if let Some(y) = x {}`
@@ -1030,7 +1032,7 @@ pub fn decode_expn_id<
         drop(expns);
         expn_id
     });
-    return Ok(expn_id);
+    Ok(expn_id)
 }
 
 // Decodes `SyntaxContext`, using the provided `HygieneDecodeContext`
@@ -1103,7 +1105,7 @@ pub fn decode_syntax_context<
         assert_eq!(dummy.dollar_crate_name, kw::Invalid);
     });
 
-    return Ok(new_ctxt);
+    Ok(new_ctxt)
 }
 
 pub fn num_syntax_ctxts() -> usize {
@@ -1170,13 +1172,30 @@ pub fn raw_encode_expn_id<E: Encoder>(
     mode: ExpnDataEncodeMode,
     e: &mut E,
 ) -> Result<(), E::Error> {
-    if !context.serialized_expns.lock().contains(&expn) {
-        context.latest_expns.lock().insert(expn);
-    }
+    // Record the fact that we need to serialize the corresponding
+    // `ExpnData`
+    let needs_data = || {
+        if !context.serialized_expns.lock().contains(&expn) {
+            context.latest_expns.lock().insert(expn);
+        }
+    };
+
     match mode {
-        ExpnDataEncodeMode::IncrComp => expn.0.encode(e),
+        ExpnDataEncodeMode::IncrComp => {
+            // Always serialize the `ExpnData` in incr comp mode
+            needs_data();
+            expn.0.encode(e)
+        }
         ExpnDataEncodeMode::Metadata => {
             let data = expn.expn_data();
+            // We only need to serialize the ExpnData
+            // if it comes from this crate.
+            // We currently don't serialize any hygiene information data for
+            // proc-macro crates: see the `SpecializedEncoder<Span>` impl
+            // for crate metadata.
+            if data.krate == LOCAL_CRATE {
+                needs_data();
+            }
             data.orig_id.expect("Missing orig_id").encode(e)?;
             data.krate.encode(e)
         }
diff --git a/src/librustc_span/lib.rs b/src/librustc_span/lib.rs
index 7087dc80b1d..697d88ad063 100644
--- a/src/librustc_span/lib.rs
+++ b/src/librustc_span/lib.rs
@@ -32,8 +32,8 @@ pub mod edition;
 use edition::Edition;
 pub mod hygiene;
 pub use hygiene::SyntaxContext;
+use hygiene::Transparency;
 pub use hygiene::{DesugaringKind, ExpnData, ExpnId, ExpnKind, ForLoopLoc, MacroKind};
-use hygiene::{Transparency, NUM_TRANSPARENCIES};
 pub mod def_id;
 use def_id::{CrateNum, DefId, LOCAL_CRATE};
 mod span_encoding;
@@ -87,6 +87,15 @@ impl SessionGlobals {
     }
 }
 
+pub fn with_session_globals<R>(edition: Edition, f: impl FnOnce() -> R) -> R {
+    let session_globals = SessionGlobals::new(edition);
+    SESSION_GLOBALS.set(&session_globals, f)
+}
+
+pub fn with_default_session_globals<R>(f: impl FnOnce() -> R) -> R {
+    with_session_globals(edition::DEFAULT_EDITION, f)
+}
+
 // If this ever becomes non thread-local, `decode_syntax_context`
 // and `decode_expn_id` will need to be updated to handle concurrent
 // deserialization.
@@ -1814,47 +1823,51 @@ impl<CTX: HashStableContext> HashStable<CTX> for SyntaxContext {
             TAG_NO_EXPANSION.hash_stable(ctx, hasher);
         } else {
             TAG_EXPANSION.hash_stable(ctx, hasher);
+            let (expn_id, transparency) = self.outer_mark();
+            expn_id.hash_stable(ctx, hasher);
+            transparency.hash_stable(ctx, hasher);
+        }
+    }
+}
 
-            // Since the same expansion context is usually referenced many
-            // times, we cache a stable hash of it and hash that instead of
-            // recursing every time.
-            thread_local! {
-                static CACHE: RefCell<Vec<Option<[Option<u64>; NUM_TRANSPARENCIES]>>> = Default::default();
-            }
+impl<CTX: HashStableContext> HashStable<CTX> for ExpnId {
+    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
+        // Since the same expansion context is usually referenced many
+        // times, we cache a stable hash of it and hash that instead of
+        // recursing every time.
+        thread_local! {
+            static CACHE: RefCell<Vec<Option<Fingerprint>>> = Default::default();
+        }
 
-            let sub_hash: u64 = CACHE.with(|cache| {
-                let (expn_id, transparency, _) = self.outer_mark_with_data();
-                let index = expn_id.as_u32() as usize;
+        const TAG_ROOT: u8 = 0;
+        const TAG_NOT_ROOT: u8 = 1;
 
-                if let Some(sub_hash_cache) = cache.borrow().get(index).copied().flatten() {
-                    if let Some(sub_hash) = sub_hash_cache[transparency as usize] {
-                        return sub_hash;
-                    }
-                }
+        if *self == ExpnId::root() {
+            TAG_ROOT.hash_stable(ctx, hasher);
+            return;
+        }
 
-                let new_len = index + 1;
+        TAG_NOT_ROOT.hash_stable(ctx, hasher);
+        let index = self.as_u32() as usize;
 
-                let mut hasher = StableHasher::new();
-                expn_id.expn_data().hash_stable(ctx, &mut hasher);
-                transparency.hash_stable(ctx, &mut hasher);
+        let res = CACHE.with(|cache| cache.borrow().get(index).copied().flatten());
 
-                let sub_hash: Fingerprint = hasher.finish();
-                let sub_hash = sub_hash.to_smaller_hash();
+        if let Some(res) = res {
+            res.hash_stable(ctx, hasher);
+        } else {
+            let new_len = index + 1;
 
+            let mut sub_hasher = StableHasher::new();
+            self.expn_data().hash_stable(ctx, &mut sub_hasher);
+            let sub_hash: Fingerprint = sub_hasher.finish();
+
+            CACHE.with(|cache| {
                 let mut cache = cache.borrow_mut();
                 if cache.len() < new_len {
                     cache.resize(new_len, None);
                 }
-                if let Some(mut sub_hash_cache) = cache[index] {
-                    sub_hash_cache[transparency as usize] = Some(sub_hash);
-                } else {
-                    let mut sub_hash_cache = [None; NUM_TRANSPARENCIES];
-                    sub_hash_cache[transparency as usize] = Some(sub_hash);
-                    cache[index] = Some(sub_hash_cache);
-                }
-                sub_hash
+                cache[index].replace(sub_hash).expect_none("Cache slot was filled");
             });
-
             sub_hash.hash_stable(ctx, hasher);
         }
     }
diff --git a/src/librustc_span/span_encoding.rs b/src/librustc_span/span_encoding.rs
index 6b672d344fa..b05e01d666b 100644
--- a/src/librustc_span/span_encoding.rs
+++ b/src/librustc_span/span_encoding.rs
@@ -8,7 +8,7 @@ use crate::hygiene::SyntaxContext;
 use crate::SESSION_GLOBALS;
 use crate::{BytePos, SpanData};
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexSet;
 
 /// A compressed span.
 ///
@@ -111,25 +111,18 @@ impl Span {
 
 #[derive(Default)]
 pub struct SpanInterner {
-    spans: FxHashMap<SpanData, u32>,
-    span_data: Vec<SpanData>,
+    spans: FxIndexSet<SpanData>,
 }
 
 impl SpanInterner {
     fn intern(&mut self, span_data: &SpanData) -> u32 {
-        if let Some(index) = self.spans.get(span_data) {
-            return *index;
-        }
-
-        let index = self.spans.len() as u32;
-        self.span_data.push(*span_data);
-        self.spans.insert(*span_data, index);
-        index
+        let (index, _) = self.spans.insert_full(*span_data);
+        index as u32
     }
 
     #[inline]
     fn get(&self, index: u32) -> &SpanData {
-        &self.span_data[index as usize]
+        &self.spans[index as usize]
     }
 }
 
diff --git a/src/librustc_span/symbol.rs b/src/librustc_span/symbol.rs
index 98776a04782..caa6de09664 100644
--- a/src/librustc_span/symbol.rs
+++ b/src/librustc_span/symbol.rs
@@ -672,6 +672,7 @@ symbols! {
         min_align_of,
         min_align_of_val,
         min_const_fn,
+        min_const_generics,
         min_const_unsafe_fn,
         min_specialization,
         minnumf32,
@@ -736,6 +737,7 @@ symbols! {
         not,
         note,
         object_safe_for_dispatch,
+        of,
         offset,
         omit_gdb_pretty_printer_section,
         on,
@@ -1480,6 +1482,10 @@ impl<CTX> ToStableHashKey<CTX> for Symbol {
 }
 
 // The `&'static str`s in this type actually point into the arena.
+//
+// The `FxHashMap`+`Vec` pair could be replaced by `FxIndexSet`, but #75278
+// found that to regress performance up to 2% in some cases. This might be
+// revisited after further improvements to `indexmap`.
 #[derive(Default)]
 pub struct Interner {
     arena: DroplessArena,
diff --git a/src/librustc_symbol_mangling/test.rs b/src/librustc_symbol_mangling/test.rs
index 2f1c896ce2f..24850a8a0d2 100644
--- a/src/librustc_symbol_mangling/test.rs
+++ b/src/librustc_symbol_mangling/test.rs
@@ -34,16 +34,16 @@ impl SymbolNamesTest<'tcx> {
         let tcx = self.tcx;
         let def_id = tcx.hir().local_def_id(hir_id);
         for attr in tcx.get_attrs(def_id.to_def_id()).iter() {
-            if attr.check_name(SYMBOL_NAME) {
+            if tcx.sess.check_name(attr, SYMBOL_NAME) {
                 // for now, can only use on monomorphic names
                 let instance = Instance::mono(tcx, def_id.to_def_id());
-                let mangled = self.tcx.symbol_name(instance);
+                let mangled = tcx.symbol_name(instance);
                 tcx.sess.span_err(attr.span, &format!("symbol-name({})", mangled));
                 if let Ok(demangling) = rustc_demangle::try_demangle(mangled.name) {
                     tcx.sess.span_err(attr.span, &format!("demangling({})", demangling));
                     tcx.sess.span_err(attr.span, &format!("demangling-alt({:#})", demangling));
                 }
-            } else if attr.check_name(DEF_PATH) {
+            } else if tcx.sess.check_name(attr, DEF_PATH) {
                 let path = tcx.def_path_str(def_id.to_def_id());
                 tcx.sess.span_err(attr.span, &format!("def-path({})", path));
             }
diff --git a/src/librustc_trait_selection/autoderef.rs b/src/librustc_trait_selection/autoderef.rs
index cc971440fea..02eefe56223 100644
--- a/src/librustc_trait_selection/autoderef.rs
+++ b/src/librustc_trait_selection/autoderef.rs
@@ -187,7 +187,7 @@ impl<'a, 'tcx> Autoderef<'a, 'tcx> {
     }
 
     pub fn span(&self) -> Span {
-        self.span.clone()
+        self.span
     }
 
     pub fn reached_recursion_limit(&self) -> bool {
diff --git a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
index 13f8c71a629..e29e740f136 100644
--- a/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
+++ b/src/librustc_trait_selection/traits/error_reporting/suggestions.rs
@@ -495,7 +495,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                     if let Ok(src) = self.tcx.sess.source_map().span_to_snippet(span) {
                         // Don't care about `&mut` because `DerefMut` is used less
                         // often and user will not expect autoderef happens.
-                        if src.starts_with("&") && !src.starts_with("&mut ") {
+                        if src.starts_with('&') && !src.starts_with("&mut ") {
                             let derefs = "*".repeat(steps);
                             err.span_suggestion(
                                 span,
diff --git a/src/librustc_trait_selection/traits/on_unimplemented.rs b/src/librustc_trait_selection/traits/on_unimplemented.rs
index 446d5a489df..7a9ed4b72dd 100644
--- a/src/librustc_trait_selection/traits/on_unimplemented.rs
+++ b/src/librustc_trait_selection/traits/on_unimplemented.rs
@@ -164,7 +164,7 @@ impl<'tcx> OnUnimplementedDirective {
     ) -> Result<Option<Self>, ErrorReported> {
         let attrs = tcx.get_attrs(impl_def_id);
 
-        let attr = if let Some(item) = attr::find_by_name(&attrs, sym::rustc_on_unimplemented) {
+        let attr = if let Some(item) = tcx.sess.find_by_name(&attrs, sym::rustc_on_unimplemented) {
             item
         } else {
             return Ok(None);
diff --git a/src/librustc_trait_selection/traits/select/mod.rs b/src/librustc_trait_selection/traits/select/mod.rs
index 75e11619924..4c575f1c6ac 100644
--- a/src/librustc_trait_selection/traits/select/mod.rs
+++ b/src/librustc_trait_selection/traits/select/mod.rs
@@ -24,7 +24,6 @@ use super::{Overflow, SelectionError, Unimplemented};
 use crate::infer::{InferCtxt, InferOk, TypeFreshener};
 use crate::traits::error_reporting::InferCtxtExt;
 use crate::traits::project::ProjectionCacheKeyExt;
-use rustc_ast::attr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_errors::ErrorReported;
@@ -440,7 +439,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             obligation
         );
 
-        // `previous_stack` stores a `TraitObligatiom`, while `obligation` is
+        // `previous_stack` stores a `TraitObligation`, while `obligation` is
         // a `PredicateObligation`. These are distinct types, so we can't
         // use any `Option` combinator method that would force them to be
         // the same.
@@ -980,7 +979,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                         &mut self.intercrate_ambiguity_causes
                     {
                         let attrs = tcx.get_attrs(def_id);
-                        let attr = attr::find_by_name(&attrs, sym::rustc_reservation_impl);
+                        let attr = tcx.sess.find_by_name(&attrs, sym::rustc_reservation_impl);
                         let value = attr.and_then(|a| a.value_str());
                         if let Some(value) = value {
                             debug!(
diff --git a/src/librustc_traits/chalk/db.rs b/src/librustc_traits/chalk/db.rs
index 715e5299a37..4c8be8eb610 100644
--- a/src/librustc_traits/chalk/db.rs
+++ b/src/librustc_traits/chalk/db.rs
@@ -141,7 +141,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
 
         let predicates = self.tcx.predicates_of(adt_def.did).predicates;
         let where_clauses: Vec<_> = predicates
-            .into_iter()
+            .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();
@@ -174,7 +174,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
                 phantom_data: adt_def.is_phantom_data(),
             },
         });
-        return struct_datum;
+        struct_datum
     }
 
     fn fn_def_datum(
@@ -187,7 +187,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
 
         let predicates = self.tcx.predicates_defined_on(def_id).predicates;
         let where_clauses: Vec<_> = predicates
-            .into_iter()
+            .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();
 
@@ -276,7 +276,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             parameters[0].assert_ty_ref(&self.interner).could_match(&self.interner, &lowered_ty)
         });
 
-        let impls = matched_impls.map(|matched_impl| chalk_ir::ImplId(matched_impl)).collect();
+        let impls = matched_impls.map(chalk_ir::ImplId).collect();
         impls
     }
 
@@ -379,7 +379,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
                         ty::AdtKind::Struct | ty::AdtKind::Union => None,
                         ty::AdtKind::Enum => {
                             let constraint = self.tcx.adt_sized_constraint(adt_def.did);
-                            if constraint.0.len() > 0 { unimplemented!() } else { Some(true) }
+                            if !constraint.0.is_empty() { unimplemented!() } else { Some(true) }
                         }
                     },
                     _ => None,
@@ -398,7 +398,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
                         ty::AdtKind::Struct | ty::AdtKind::Union => None,
                         ty::AdtKind::Enum => {
                             let constraint = self.tcx.adt_sized_constraint(adt_def.did);
-                            if constraint.0.len() > 0 { unimplemented!() } else { Some(true) }
+                            if !constraint.0.is_empty() { unimplemented!() } else { Some(true) }
                         }
                     },
                     _ => None,
@@ -440,7 +440,7 @@ impl<'tcx> chalk_solve::RustIrDatabase<RustInterner<'tcx>> for RustIrDatabase<'t
             FnOnce => self.tcx.lang_items().fn_once_trait(),
             Unsize => self.tcx.lang_items().unsize_trait(),
         };
-        def_id.map(|t| chalk_ir::TraitId(t))
+        def_id.map(chalk_ir::TraitId)
     }
 
     fn is_object_safe(&self, trait_id: chalk_ir::TraitId<RustInterner<'tcx>>) -> bool {
diff --git a/src/librustc_ty/ty.rs b/src/librustc_ty/ty.rs
index dfb28b473ff..b31f9f3c7b1 100644
--- a/src/librustc_ty/ty.rs
+++ b/src/librustc_ty/ty.rs
@@ -443,7 +443,7 @@ fn opaque_type_projection_predicates(
 
     let bounds = tcx.predicates_of(def_id);
     let predicates =
-        util::elaborate_predicates(tcx, bounds.predicates.into_iter().map(|&(pred, _)| pred));
+        util::elaborate_predicates(tcx, bounds.predicates.iter().map(|&(pred, _)| pred));
 
     let filtered_predicates = predicates.filter_map(|obligation| {
         let pred = obligation.predicate;
diff --git a/src/librustc_typeck/check/demand.rs b/src/librustc_typeck/check/demand.rs
index be83ab259c2..258c5b77df2 100644
--- a/src/librustc_typeck/check/demand.rs
+++ b/src/librustc_typeck/check/demand.rs
@@ -246,7 +246,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     //
                     // FIXME? Other potential candidate methods: `as_ref` and
                     // `as_mut`?
-                    .any(|a| a.check_name(sym::rustc_conversion_suggestion))
+                    .any(|a| self.sess().check_name(a, sym::rustc_conversion_suggestion))
         });
 
         methods
diff --git a/src/librustc_typeck/check/dropck.rs b/src/librustc_typeck/check/dropck.rs
index 88c47b38ccc..9ef91641916 100644
--- a/src/librustc_typeck/check/dropck.rs
+++ b/src/librustc_typeck/check/dropck.rs
@@ -368,6 +368,6 @@ impl TypeRelation<'tcx> for SimpleEqRelation<'tcx> {
         let anon_b = self.tcx.anonymize_late_bound_regions(&b);
         self.relate(anon_a.skip_binder(), anon_b.skip_binder())?;
 
-        Ok(a.clone())
+        Ok(a)
     }
 }
diff --git a/src/librustc_typeck/check/generator_interior.rs b/src/librustc_typeck/check/generator_interior.rs
index c5004e4ce11..93fdf93e9e3 100644
--- a/src/librustc_typeck/check/generator_interior.rs
+++ b/src/librustc_typeck/check/generator_interior.rs
@@ -4,7 +4,7 @@
 //! types computed here.
 
 use super::FnCtxt;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -16,7 +16,7 @@ use rustc_span::Span;
 
 struct InteriorVisitor<'a, 'tcx> {
     fcx: &'a FnCtxt<'a, 'tcx>,
-    types: FxHashMap<ty::GeneratorInteriorTypeCause<'tcx>, usize>,
+    types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>,
     region_scope_tree: &'tcx region::ScopeTree,
     expr_count: usize,
     kind: hir::GeneratorKind,
@@ -88,18 +88,15 @@ impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> {
                     .span_note(yield_data.span, &*note)
                     .emit();
             } else {
-                // Map the type to the number of types added before it
-                let entries = self.types.len();
+                // Insert the type into the ordered set.
                 let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree));
-                self.types
-                    .entry(ty::GeneratorInteriorTypeCause {
-                        span: source_span,
-                        ty: &ty,
-                        scope_span,
-                        yield_span: yield_data.span,
-                        expr: expr.map(|e| e.hir_id),
-                    })
-                    .or_insert(entries);
+                self.types.insert(ty::GeneratorInteriorTypeCause {
+                    span: source_span,
+                    ty: &ty,
+                    scope_span,
+                    yield_span: yield_data.span,
+                    expr: expr.map(|e| e.hir_id),
+                });
             }
         } else {
             debug!(
@@ -132,7 +129,7 @@ pub fn resolve_interior<'a, 'tcx>(
     let body = fcx.tcx.hir().body(body_id);
     let mut visitor = InteriorVisitor {
         fcx,
-        types: FxHashMap::default(),
+        types: FxIndexSet::default(),
         region_scope_tree: fcx.tcx.region_scope_tree(def_id),
         expr_count: 0,
         kind,
@@ -144,10 +141,8 @@ pub fn resolve_interior<'a, 'tcx>(
     let region_expr_count = visitor.region_scope_tree.body_expr_count(body_id).unwrap();
     assert_eq!(region_expr_count, visitor.expr_count);
 
-    let mut types: Vec<_> = visitor.types.drain().collect();
-
-    // Sort types by insertion order
-    types.sort_by_key(|t| t.1);
+    // The types are already kept in insertion order.
+    let types = visitor.types;
 
     // The types in the generator interior contain lifetimes local to the generator itself,
     // which should not be exposed outside of the generator. Therefore, we replace these
@@ -164,7 +159,7 @@ pub fn resolve_interior<'a, 'tcx>(
     let mut captured_tys = FxHashSet::default();
     let type_causes: Vec<_> = types
         .into_iter()
-        .filter_map(|(mut cause, _)| {
+        .filter_map(|mut cause| {
             // Erase regions and canonicalize late-bound regions to deduplicate as many types as we
             // can.
             let erased = fcx.tcx.erase_regions(&cause.ty);
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 6cefc99f7b1..3d58fb30d91 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -2603,7 +2603,7 @@ 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.parse_sess, attr) {
+            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() {
@@ -2814,7 +2814,7 @@ pub fn check_enum<'tcx>(
 
     if vs.is_empty() {
         let attributes = tcx.get_attrs(def_id.to_def_id());
-        if let Some(attr) = attr::find_by_name(&attributes, sym::repr) {
+        if let Some(attr) = tcx.sess.find_by_name(&attributes, sym::repr) {
             struct_span_err!(
                 tcx.sess,
                 attr.span,
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index 9c7ea34bf51..f598ada900f 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -1114,7 +1114,7 @@ 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.span, &unmentioned_fields));
+            unmentioned_err = Some(self.error_unmentioned_fields(pat, &unmentioned_fields));
         }
         match (inexistent_fields_err, unmentioned_err) {
             (Some(mut i), Some(mut u)) => {
@@ -1237,13 +1237,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if tcx.sess.teach(&err.get_code().unwrap()) {
             err.note(
                 "This error indicates that a struct pattern attempted to \
-                    extract a non-existent field from a struct. Struct fields \
-                    are identified by the name used before the colon : so struct \
-                    patterns should resemble the declaration of the struct type \
-                    being matched.\n\n\
-                    If you are using shorthand field patterns but want to refer \
-                    to the struct field by a different name, you should rename \
-                    it explicitly.",
+                 extract a non-existent field from a struct. Struct fields \
+                 are identified by the name used before the colon : so struct \
+                 patterns should resemble the declaration of the struct type \
+                 being matched.\n\n\
+                 If you are using shorthand field patterns but want to refer \
+                 to the struct field by a different name, you should rename \
+                 it explicitly.",
             );
         }
         err
@@ -1299,7 +1299,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn error_unmentioned_fields(
         &self,
-        span: Span,
+        pat: &Pat<'_>,
         unmentioned_fields: &[Ident],
     ) -> DiagnosticBuilder<'tcx> {
         let field_names = if unmentioned_fields.len() == 1 {
@@ -1312,23 +1312,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 .join(", ");
             format!("fields {}", fields)
         };
-        let mut diag = struct_span_err!(
+        let mut err = struct_span_err!(
             self.tcx.sess,
-            span,
+            pat.span,
             E0027,
             "pattern does not mention {}",
             field_names
         );
-        diag.span_label(span, format!("missing {}", field_names));
-        if self.tcx.sess.teach(&diag.get_code().unwrap()) {
-            diag.note(
+        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.",
+                 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.",
             );
         }
-        diag
+        err
     }
 
     fn check_pat_box(
diff --git a/src/librustc_typeck/check/place_op.rs b/src/librustc_typeck/check/place_op.rs
index 12468750923..84f34c0039a 100644
--- a/src/librustc_typeck/check/place_op.rs
+++ b/src/librustc_typeck/check/place_op.rs
@@ -200,13 +200,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // Gather up expressions we want to munge.
         let mut exprs = vec![expr];
 
-        loop {
-            match exprs.last().unwrap().kind {
-                hir::ExprKind::Field(ref expr, _)
-                | hir::ExprKind::Index(ref expr, _)
-                | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) => exprs.push(&expr),
-                _ => break,
-            }
+        while let hir::ExprKind::Field(ref expr, _)
+        | hir::ExprKind::Index(ref expr, _)
+        | hir::ExprKind::Unary(hir::UnOp::UnDeref, ref expr) = exprs.last().unwrap().kind
+        {
+            exprs.push(&expr);
         }
 
         debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs);
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 97df065500a..b47ef346004 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -20,9 +20,9 @@ use crate::constrained_generic_params as cgp;
 use crate::middle::resolve_lifetime as rl;
 use rustc_ast::ast;
 use rustc_ast::ast::MetaItemKind;
-use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr};
+use rustc_attr::{list_contains_name, InlineAttr, OptimizeAttr};
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -1238,6 +1238,9 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
                 // HACK(eddyb) this provides the correct generics when
                 // `feature(const_generics)` is enabled, so that const expressions
                 // used with const generics, e.g. `Foo<{N+1}>`, can work at all.
+                //
+                // Note that we do not supply the parent generics when using
+                // `feature(min_const_generics)`.
                 Some(parent_def_id.to_def_id())
             } else {
                 let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
@@ -1715,21 +1718,17 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
     /// 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.
-    // FIXME(eddyb) just use `IndexSet` from `indexmap`.
     struct UniquePredicates<'tcx> {
-        predicates: Vec<(ty::Predicate<'tcx>, Span)>,
-        uniques: FxHashSet<(ty::Predicate<'tcx>, Span)>,
+        predicates: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
     }
 
     impl<'tcx> UniquePredicates<'tcx> {
         fn new() -> Self {
-            UniquePredicates { predicates: vec![], uniques: FxHashSet::default() }
+            UniquePredicates { predicates: FxIndexSet::default() }
         }
 
         fn push(&mut self, value: (ty::Predicate<'tcx>, Span)) {
-            if self.uniques.insert(value) {
-                self.predicates.push(value);
-            }
+            self.predicates.insert(value);
         }
 
         fn extend<I: IntoIterator<Item = (ty::Predicate<'tcx>, Span)>>(&mut self, iter: I) {
@@ -2011,7 +2010,7 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
         }))
     }
 
-    let mut predicates = predicates.predicates;
+    let mut predicates: Vec<_> = predicates.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
@@ -2351,13 +2350,13 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     let mut link_ordinal_span = None;
     let mut no_sanitize_span = None;
     for attr in attrs.iter() {
-        if attr.check_name(sym::cold) {
+        if tcx.sess.check_name(attr, sym::cold) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
-        } else if attr.check_name(sym::rustc_allocator) {
+        } else if tcx.sess.check_name(attr, sym::rustc_allocator) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
-        } else if attr.check_name(sym::unwind) {
+        } else if tcx.sess.check_name(attr, sym::unwind) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND;
-        } else if attr.check_name(sym::ffi_returns_twice) {
+        } else if tcx.sess.check_name(attr, sym::ffi_returns_twice) {
             if tcx.is_foreign_item(id) {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
             } else {
@@ -2370,9 +2369,9 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if attr.check_name(sym::ffi_pure) {
+        } else if tcx.sess.check_name(attr, sym::ffi_pure) {
             if tcx.is_foreign_item(id) {
-                if attrs.iter().any(|a| a.check_name(sym::ffi_const)) {
+                if attrs.iter().any(|a| tcx.sess.check_name(a, sym::ffi_const)) {
                     // `#[ffi_const]` functions cannot be `#[ffi_pure]`
                     struct_span_err!(
                         tcx.sess,
@@ -2394,7 +2393,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if attr.check_name(sym::ffi_const) {
+        } else if tcx.sess.check_name(attr, sym::ffi_const) {
             if tcx.is_foreign_item(id) {
                 codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
             } else {
@@ -2407,25 +2406,25 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 )
                 .emit();
             }
-        } else if attr.check_name(sym::rustc_allocator_nounwind) {
+        } else if tcx.sess.check_name(attr, sym::rustc_allocator_nounwind) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND;
-        } else if attr.check_name(sym::naked) {
+        } else if tcx.sess.check_name(attr, sym::naked) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
-        } else if attr.check_name(sym::no_mangle) {
+        } else if tcx.sess.check_name(attr, sym::no_mangle) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
-        } else if attr.check_name(sym::rustc_std_internal_symbol) {
+        } else if tcx.sess.check_name(attr, sym::rustc_std_internal_symbol) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
-        } else if attr.check_name(sym::used) {
+        } else if tcx.sess.check_name(attr, sym::used) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
-        } else if attr.check_name(sym::thread_local) {
+        } else if tcx.sess.check_name(attr, sym::thread_local) {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
-        } else if attr.check_name(sym::track_caller) {
+        } else if tcx.sess.check_name(attr, sym::track_caller) {
             if tcx.is_closure(id) || tcx.fn_sig(id).abi() != abi::Abi::Rust {
                 struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
                     .emit();
             }
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
-        } else if attr.check_name(sym::export_name) {
+        } else if tcx.sess.check_name(attr, sym::export_name) {
             if let Some(s) = attr.value_str() {
                 if s.as_str().contains('\0') {
                     // `#[export_name = ...]` will be converted to a null-terminated string,
@@ -2440,7 +2439,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 }
                 codegen_fn_attrs.export_name = Some(s);
             }
-        } else if attr.check_name(sym::target_feature) {
+        } 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() {
@@ -2455,11 +2454,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 &supported_target_features,
                 &mut codegen_fn_attrs.target_features,
             );
-        } else if attr.check_name(sym::linkage) {
+        } else if tcx.sess.check_name(attr, sym::linkage) {
             if let Some(val) = attr.value_str() {
                 codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str()));
             }
-        } else if attr.check_name(sym::link_section) {
+        } else if tcx.sess.check_name(attr, sym::link_section) {
             if let Some(val) = attr.value_str() {
                 if val.as_str().bytes().any(|b| b == 0) {
                     let msg = format!(
@@ -2472,14 +2471,14 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                     codegen_fn_attrs.link_section = Some(val);
                 }
             }
-        } else if attr.check_name(sym::link_name) {
+        } else if tcx.sess.check_name(attr, sym::link_name) {
             codegen_fn_attrs.link_name = attr.value_str();
-        } else if attr.check_name(sym::link_ordinal) {
+        } else if tcx.sess.check_name(attr, sym::link_ordinal) {
             link_ordinal_span = Some(attr.span);
             if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
                 codegen_fn_attrs.link_ordinal = ordinal;
             }
-        } else if attr.check_name(sym::no_sanitize) {
+        } else if tcx.sess.check_name(attr, sym::no_sanitize) {
             no_sanitize_span = Some(attr.span);
             if let Some(list) = attr.meta_item_list() {
                 for item in list.iter() {
@@ -2506,11 +2505,11 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
         }
         match attr.meta().map(|i| i.kind) {
             Some(MetaItemKind::Word) => {
-                mark_used(attr);
+                tcx.sess.mark_attr_used(attr);
                 InlineAttr::Hint
             }
             Some(MetaItemKind::List(ref items)) => {
-                mark_used(attr);
+                tcx.sess.mark_attr_used(attr);
                 inline_span = Some(attr.span);
                 if items.len() != 1 {
                     struct_span_err!(
@@ -2553,7 +2552,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
                 ia
             }
             Some(MetaItemKind::List(ref items)) => {
-                mark_used(attr);
+                tcx.sess.mark_attr_used(attr);
                 inline_span = Some(attr.span);
                 if items.len() != 1 {
                     err(attr.span, "expected one argument");
@@ -2614,7 +2613,8 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs {
     if tcx.is_weak_lang_item(id) {
         codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
     }
-    if let Some(name) = weak_lang_items::link_name(&attrs) {
+    let check_name = |attr, sym| tcx.sess.check_name(attr, sym);
+    if let Some(name) = weak_lang_items::link_name(check_name, &attrs) {
         codegen_fn_attrs.export_name = Some(name);
         codegen_fn_attrs.link_name = Some(name);
     }
diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs
index 8c9cd50a17d..17444c6d0ac 100644
--- a/src/librustc_typeck/collect/type_of.rs
+++ b/src/librustc_typeck/collect/type_of.rs
@@ -326,21 +326,39 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
             GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty),
             GenericParamKind::Const { ty: ref hir_ty, .. } => {
                 let ty = icx.to_ty(hir_ty);
-                let err = match ty.peel_refs().kind {
-                    ty::FnPtr(_) => Some("function pointers"),
-                    ty::RawPtr(_) => Some("raw pointers"),
-                    _ => None,
+                let err_ty_str;
+                let err = if tcx.features().min_const_generics {
+                    match ty.kind {
+                        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None,
+                        ty::FnPtr(_) => Some("function pointers"),
+                        ty::RawPtr(_) => Some("raw pointers"),
+                        _ => {
+                            err_ty_str = format!("`{}`", ty);
+                            Some(err_ty_str.as_str())
+                        }
+                    }
+                } else {
+                    match ty.peel_refs().kind {
+                        ty::FnPtr(_) => Some("function pointers"),
+                        ty::RawPtr(_) => Some("raw pointers"),
+                        _ => None,
+                    }
                 };
                 if let Some(unsupported_type) = err {
-                    tcx.sess
-                        .struct_span_err(
-                            hir_ty.span,
-                            &format!(
-                                "using {} as const generic parameters is forbidden",
-                                unsupported_type
-                            ),
-                        )
-                        .emit();
+                    let mut err = tcx.sess.struct_span_err(
+                        hir_ty.span,
+                        &format!(
+                            "using {} as const generic parameters is forbidden",
+                            unsupported_type
+                        ),
+                    );
+
+                    if tcx.features().min_const_generics {
+                        err.note("the only supported types are integers, `bool` and `char`")
+                        .note("more complex types are supported with `#[feature(const_generics)]`").emit()
+                    } else {
+                        err.emit();
+                    }
                 };
                 if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty)
                     .is_some()
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index e203d51f612..a8247e2f494 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -196,7 +196,7 @@ fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) {
                     }
 
                     for attr in it.attrs {
-                        if attr.check_name(sym::track_caller) {
+                        if tcx.sess.check_name(attr, sym::track_caller) {
                             tcx.sess
                                 .struct_span_err(
                                     attr.span,
@@ -293,7 +293,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) {
                     }
 
                     for attr in it.attrs {
-                        if attr.check_name(sym::track_caller) {
+                        if tcx.sess.check_name(attr, sym::track_caller) {
                             tcx.sess
                                 .struct_span_err(
                                     attr.span,
diff --git a/src/librustc_typeck/mem_categorization.rs b/src/librustc_typeck/mem_categorization.rs
index afc7cb346eb..8a6fe620af7 100644
--- a/src/librustc_typeck/mem_categorization.rs
+++ b/src/librustc_typeck/mem_categorization.rs
@@ -583,7 +583,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
                 self.tcx()
                     .sess
                     .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT");
-                return Err(());
+                Err(())
             }
         }
     }
@@ -596,7 +596,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
             ty::Tuple(substs) => Ok(substs.len()),
             _ => {
                 self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple");
-                return Err(());
+                Err(())
             }
         }
     }
diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs
index 8d1193e7f82..b4ced412e5e 100644
--- a/src/librustdoc/clean/cfg/tests.rs
+++ b/src/librustdoc/clean/cfg/tests.rs
@@ -2,8 +2,8 @@ use super::*;
 
 use rustc_ast::ast::*;
 use rustc_ast::attr;
-use rustc_ast::with_default_session_globals;
 use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::with_default_session_globals;
 use rustc_span::DUMMY_SP;
 
 fn word_cfg(s: &str) -> Cfg {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 2a090d6efa5..7b1dd5b11ed 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1395,10 +1395,13 @@ impl Clean<Type> for hir::Ty<'_> {
                                             _ => None,
                                         });
                                     if let Some(lt) = lifetime.cloned() {
-                                        if !lt.is_elided() {
-                                            let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id);
-                                            lt_substs.insert(lt_def_id.to_def_id(), lt.clean(cx));
-                                        }
+                                        let lt_def_id = cx.tcx.hir().local_def_id(param.hir_id);
+                                        let cleaned = if !lt.is_elided() {
+                                            lt.clean(cx)
+                                        } else {
+                                            self::types::Lifetime::elided()
+                                        };
+                                        lt_substs.insert(lt_def_id.to_def_id(), cleaned);
                                     }
                                     indices.lifetimes += 1;
                                 }
@@ -1957,21 +1960,17 @@ impl Clean<GenericArgs> for hir::GenericArgs<'_> {
                 output: if output != Type::Tuple(Vec::new()) { Some(output) } else { None },
             }
         } else {
-            let elide_lifetimes = self.args.iter().all(|arg| match arg {
-                hir::GenericArg::Lifetime(lt) => lt.is_elided(),
-                _ => true,
-            });
             GenericArgs::AngleBracketed {
                 args: self
                     .args
                     .iter()
-                    .filter_map(|arg| match arg {
-                        hir::GenericArg::Lifetime(lt) if !elide_lifetimes => {
-                            Some(GenericArg::Lifetime(lt.clean(cx)))
+                    .map(|arg| match arg {
+                        hir::GenericArg::Lifetime(lt) if !lt.is_elided() => {
+                            GenericArg::Lifetime(lt.clean(cx))
                         }
-                        hir::GenericArg::Lifetime(_) => None,
-                        hir::GenericArg::Type(ty) => Some(GenericArg::Type(ty.clean(cx))),
-                        hir::GenericArg::Const(ct) => Some(GenericArg::Const(ct.clean(cx))),
+                        hir::GenericArg::Lifetime(_) => GenericArg::Lifetime(Lifetime::elided()),
+                        hir::GenericArg::Type(ty) => GenericArg::Type(ty.clean(cx)),
+                        hir::GenericArg::Const(ct) => GenericArg::Const(ct.clean(cx)),
                     })
                     .collect(),
                 bindings: self.bindings.clean(cx),
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 1bea41b6585..50eca75d7ca 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -10,7 +10,7 @@ use std::{slice, vec};
 
 use rustc_ast::ast::{self, AttrStyle};
 use rustc_ast::attr;
-use rustc_ast::util::comments::strip_doc_comment_decoration;
+use rustc_ast::util::comments::beautify_doc_string;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def::Res;
@@ -506,10 +506,11 @@ impl Attributes {
             .iter()
             .filter_map(|attr| {
                 if let Some(value) = attr.doc_str() {
-                    let (value, mk_fragment): (_, fn(_, _, _) -> _) = if attr.is_doc_comment() {
-                        (strip_doc_comment_decoration(value), DocFragment::SugaredDoc)
+                    let value = beautify_doc_string(value);
+                    let mk_fragment: fn(_, _, _) -> _ = if attr.is_doc_comment() {
+                        DocFragment::SugaredDoc
                     } else {
-                        (value.to_string(), DocFragment::RawDoc)
+                        DocFragment::RawDoc
                     };
 
                     let line = doc_line;
@@ -749,6 +750,10 @@ impl Lifetime {
     pub fn statik() -> Lifetime {
         Lifetime("'static".to_string())
     }
+
+    pub fn elided() -> Lifetime {
+        Lifetime("'_".to_string())
+    }
 }
 
 #[derive(Clone, Debug)]
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index 52c30668826..c22538f21f6 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -2,9 +2,9 @@ use crate::clean::auto_trait::AutoTraitFinder;
 use crate::clean::blanket_impl::BlanketImplFinder;
 use crate::clean::{
     inline, Clean, Crate, Deprecation, ExternalCrate, FnDecl, FnRetTy, Generic, GenericArg,
-    GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, MacroKind, Path,
-    PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type, TypeBinding,
-    TypeKind, Visibility, WherePredicate,
+    GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemEnum, Lifetime,
+    MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Stability, Type,
+    TypeBinding, TypeKind, Visibility, WherePredicate,
 };
 use crate::core::DocContext;
 
@@ -121,7 +121,10 @@ pub fn external_generic_args(
     let args: Vec<_> = substs
         .iter()
         .filter_map(|kind| match kind.unpack() {
-            GenericArgKind::Lifetime(lt) => lt.clean(cx).map(GenericArg::Lifetime),
+            GenericArgKind::Lifetime(lt) => match lt {
+                ty::ReLateBound(_, ty::BrAnon(_)) => Some(GenericArg::Lifetime(Lifetime::elided())),
+                _ => lt.clean(cx).map(GenericArg::Lifetime),
+            },
             GenericArgKind::Type(_) if skip_self => {
                 skip_self = false;
                 None
@@ -607,6 +610,9 @@ pub fn register_res(cx: &DocContext<'_>, res: Res) -> DefId {
         Res::Def(DefKind::TyAlias, i) => (i, TypeKind::Typedef),
         Res::Def(DefKind::Enum, i) => (i, TypeKind::Enum),
         Res::Def(DefKind::Trait, i) => (i, TypeKind::Trait),
+        Res::Def(DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst, i) => {
+            (cx.tcx.parent(i).unwrap(), TypeKind::Trait)
+        }
         Res::Def(DefKind::Struct, i) => (i, TypeKind::Struct),
         Res::Def(DefKind::Union, i) => (i, TypeKind::Union),
         Res::Def(DefKind::Mod, i) => (i, TypeKind::Module),
diff --git a/src/librustdoc/docfs.rs b/src/librustdoc/docfs.rs
index 4ce6bcbe274..8b52ce710a4 100644
--- a/src/librustdoc/docfs.rs
+++ b/src/librustdoc/docfs.rs
@@ -69,14 +69,14 @@ impl DocFS {
             let sender = self.errors.clone().expect("can't write after closing");
             rayon::spawn(move || {
                 fs::write(&path, contents).unwrap_or_else(|e| {
-                    sender
-                        .send(format!("\"{}\": {}", path.display(), e))
-                        .expect(&format!("failed to send error on \"{}\"", path.display()));
+                    sender.send(format!("\"{}\": {}", path.display(), e)).unwrap_or_else(|_| {
+                        panic!("failed to send error on \"{}\"", path.display())
+                    })
                 });
             });
-            Ok(())
         } else {
-            Ok(try_err!(fs::write(&path, contents), path))
+            try_err!(fs::write(&path, contents), path);
         }
+        Ok(())
     }
 }
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index 01b25fd6be4..21e476cbe06 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -1,6 +1,6 @@
-use rustc_ast::attr::with_session_globals;
 use rustc_session::parse::ParseSess;
 use rustc_span::edition::Edition;
+use rustc_span::with_session_globals;
 use rustc_span::FileName;
 
 use super::Classifier;
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5fb2d9f6f91..4cbc56333b1 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -3450,7 +3450,7 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String {
                 if impl_.trait_.def_id().map_or(false, |d| c.traits[&d].is_spotlight) {
                     if out.is_empty() {
                         out.push_str(&format!(
-                            "<h3 class=\"important\">Important traits for {}</h3>\
+                            "<h3 class=\"notable\">Notable traits for {}</h3>\
                                       <code class=\"content\">",
                             impl_.for_.print()
                         ));
@@ -3485,7 +3485,7 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String {
     if !out.is_empty() {
         out.insert_str(
             0,
-            "<span class=\"important-traits\"><span class=\"important-traits-tooltip\">ⓘ<div class='important-traits-tooltiptext'><span class=\"docblock\">"
+            "<span class=\"notable-traits\"><span class=\"notable-traits-tooltip\">ⓘ<div class='notable-traits-tooltiptext'><span class=\"docblock\">"
 
         );
         out.push_str("</code></span></div></span></span>");
diff --git a/src/librustdoc/html/static/main.js b/src/librustdoc/html/static/main.js
index 082f9cca064..19284018a30 100644
--- a/src/librustdoc/html/static/main.js
+++ b/src/librustdoc/html/static/main.js
@@ -2636,9 +2636,9 @@ function defocusSearchBar() {
         });
     }());
 
-    onEachLazy(document.getElementsByClassName("important-traits"), function(e) {
+    onEachLazy(document.getElementsByClassName("notable-traits"), function(e) {
         e.onclick = function() {
-            this.getElementsByClassName('important-traits-tooltiptext')[0]
+            this.getElementsByClassName('notable-traits-tooltiptext')[0]
                 .classList.toggle("force-tooltip");
         };
     });
diff --git a/src/librustdoc/html/static/rustdoc.css b/src/librustdoc/html/static/rustdoc.css
index d0312d77a7c..db0e4f4d31d 100644
--- a/src/librustdoc/html/static/rustdoc.css
+++ b/src/librustdoc/html/static/rustdoc.css
@@ -91,7 +91,7 @@ h2 {
 h3 {
 	font-size: 1.3em;
 }
-h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.important),
+h1, h2, h3:not(.impl):not(.method):not(.type):not(.tymethod):not(.notable),
 h4:not(.method):not(.type):not(.tymethod):not(.associatedconstant) {
 	font-weight: 500;
 	margin: 20px 0 15px 0;
@@ -528,7 +528,7 @@ h4 > code, h3 > code, .invisible > code {
 	font-size: 0.8em;
 }
 
-.content .methods > div:not(.important-traits) {
+.content .methods > div:not(.notable-traits) {
 	margin-left: 40px;
 	margin-bottom: 15px;
 }
@@ -1099,17 +1099,17 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
 	font-size: 20px;
 }
 
-.important-traits-tooltip {
+.notable-traits-tooltip {
 	display: inline-block;
 	cursor: pointer;
 }
 
-.important-traits:hover .important-traits-tooltiptext,
-.important-traits .important-traits-tooltiptext.force-tooltip {
+.notable-traits:hover .notable-traits-tooltiptext,
+.notable-traits .notable-traits-tooltiptext.force-tooltip {
 	display: inline-block;
 }
 
-.important-traits .important-traits-tooltiptext {
+.notable-traits .notable-traits-tooltiptext {
 	display: none;
 	padding: 5px 3px 3px 3px;
 	border-radius: 6px;
@@ -1121,18 +1121,18 @@ h3 > .collapse-toggle, h4 > .collapse-toggle {
 	border: 1px solid;
 }
 
-.important-traits-tooltip::after {
+.notable-traits-tooltip::after {
 	/* The margin on the tooltip does not capture hover events,
 	   this extends the area of hover enough so that mouse hover is not
 	   lost when moving the mouse to the tooltip */
 	content: "\00a0\00a0\00a0";
 }
 
-.important-traits .important, .important-traits .docblock {
+.notable-traits .notable, .notable-traits .docblock {
 	margin: 0;
 }
 
-.important-traits .docblock code.content{
+.notable-traits .docblock code.content{
 	margin: 0;
 	padding: 0;
 	font-size: 20px;
@@ -1183,13 +1183,13 @@ pre.rust {
 	font-size: 16px;
 }
 
-.important-traits {
+.notable-traits {
 	cursor: pointer;
 	z-index: 2;
 	margin-left: 5px;
 }
 
-h4 > .important-traits {
+h4 > .notable-traits {
 	position: absolute;
 	left: -44px;
 	top: 2px;
@@ -1431,7 +1431,7 @@ h4 > .important-traits {
 		z-index: 1;
 	}
 
-	h4 > .important-traits {
+	h4 > .notable-traits {
 		position: absolute;
 		left: -22px;
 		top: 24px;
@@ -1522,7 +1522,7 @@ h4 > .important-traits {
 		margin-top: 0;
 	}
 
-	.important-traits .important-traits-tooltiptext {
+	.notable-traits .notable-traits-tooltiptext {
 		left: 0;
 		top: 100%;
 	}
@@ -1544,7 +1544,7 @@ h4 > .important-traits {
 	}
 }
 
-h3.important {
+h3.notable {
 	margin: 0;
 	margin-bottom: 13px;
 	font-size: 19px;
diff --git a/src/librustdoc/html/static/themes/ayu.css b/src/librustdoc/html/static/themes/ayu.css
index f4710f6ae87..60f0d25b219 100644
--- a/src/librustdoc/html/static/themes/ayu.css
+++ b/src/librustdoc/html/static/themes/ayu.css
@@ -389,7 +389,7 @@ pre.ignore:hover, .information:hover + pre.ignore {
 	border-color: transparent #314559 transparent transparent;
 }
 
-.important-traits-tooltiptext {
+.notable-traits-tooltiptext {
 	background-color: #314559;
 	border-color: #5c6773;
 }
diff --git a/src/librustdoc/html/static/themes/dark.css b/src/librustdoc/html/static/themes/dark.css
index b3b586ba362..34c6cbbf4fa 100644
--- a/src/librustdoc/html/static/themes/dark.css
+++ b/src/librustdoc/html/static/themes/dark.css
@@ -339,7 +339,7 @@ pre.ignore:hover, .information:hover + pre.ignore {
 	border-color: transparent black transparent transparent;
 }
 
-.important-traits-tooltiptext {
+.notable-traits-tooltiptext {
 	background-color: #111;
 	border-color: #777;
 }
diff --git a/src/librustdoc/html/static/themes/light.css b/src/librustdoc/html/static/themes/light.css
index b0c5715604b..137aad4ed43 100644
--- a/src/librustdoc/html/static/themes/light.css
+++ b/src/librustdoc/html/static/themes/light.css
@@ -333,7 +333,7 @@ pre.ignore:hover, .information:hover + pre.ignore {
 	border-color: transparent black transparent transparent;
 }
 
-.important-traits-tooltiptext {
+.notable-traits-tooltiptext {
 	background-color: #eee;
 	border-color: #999;
 }
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 98300385c8f..b722cfc8f75 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -4,7 +4,6 @@ use crate::core::DocContext;
 use crate::fold::{self, DocFolder};
 use crate::passes::Pass;
 
-use rustc_ast::attr;
 use rustc_span::symbol::sym;
 use rustc_span::FileName;
 use serde::Serialize;
@@ -155,7 +154,10 @@ impl fold::DocFolder for CoverageCalculator {
                 return Some(i);
             }
             clean::ImplItem(ref impl_)
-                if attr::contains_name(&i.attrs.other_attrs, sym::automatically_derived)
+                if i.attrs
+                    .other_attrs
+                    .iter()
+                    .any(|item| item.has_name(sym::automatically_derived))
                     || impl_.synthetic
                     || impl_.blanket_impl.is_some() =>
             {
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index bf7a43236e0..062bd61a7d0 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -17,6 +17,7 @@ use rustc_span::symbol::Ident;
 use rustc_span::symbol::Symbol;
 use rustc_span::DUMMY_SP;
 
+use std::cell::Cell;
 use std::ops::Range;
 
 use crate::clean::*;
@@ -62,11 +63,15 @@ struct LinkCollector<'a, 'tcx> {
     cx: &'a DocContext<'tcx>,
     // NOTE: this may not necessarily be a module in the current crate
     mod_ids: Vec<DefId>,
+    /// This is used to store the kind of associated items,
+    /// because `clean` and the disambiguator code expect them to be different.
+    /// See the code for associated items on inherent impls for details.
+    kind_side_channel: Cell<Option<DefKind>>,
 }
 
 impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
     fn new(cx: &'a DocContext<'tcx>) -> Self {
-        LinkCollector { cx, mod_ids: Vec::new() }
+        LinkCollector { cx, mod_ids: Vec::new(), kind_side_channel: Cell::new(None) }
     }
 
     fn variant_field(
@@ -174,7 +179,7 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
     fn resolve(
         &self,
         path_str: &str,
-        disambiguator: Option<&str>,
+        disambiguator: Option<Disambiguator>,
         ns: Namespace,
         current_item: &Option<String>,
         parent_id: Option<DefId>,
@@ -212,9 +217,11 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                         return Ok((res, Some(path_str.to_owned())));
                     }
                     Res::Def(DefKind::Mod, _) => {
-                        // This resolved to a module, but if we were passed `type@`,
-                        // we want primitive types to take precedence instead.
-                        if disambiguator == Some("type") {
+                        // This resolved to a module, but we want primitive types to take precedence instead.
+                        if matches!(
+                            disambiguator,
+                            None | Some(Disambiguator::Namespace(Namespace::TypeNS))
+                        ) {
                             if let Some(prim) = is_primitive(path_str, ns) {
                                 if extra_fragment.is_some() {
                                     return Err(ErrorKind::AnchorFailure(AnchorFailure::Primitive));
@@ -347,6 +354,10 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                                 AnchorFailure::AssocConstant
                             }))
                         } else {
+                            // HACK(jynelson): `clean` expects the type, not the associated item.
+                            // but the disambiguator logic expects the associated item.
+                            // Store the kind in a side channel so that only the disambiguator logic looks at it.
+                            self.kind_side_channel.replace(Some(item.kind.as_def_kind()));
                             Ok((ty_res, Some(format!("{}.{}", out, item_name))))
                         }
                     } else {
@@ -415,7 +426,8 @@ impl<'a, 'tcx> LinkCollector<'a, 'tcx> {
                                 AnchorFailure::Method
                             }))
                         } else {
-                            Ok((ty_res, Some(format!("{}.{}", kind, item_name))))
+                            let res = Res::Def(item.kind.as_def_kind(), item.def_id);
+                            Ok((res, Some(format!("{}.{}", kind, item_name))))
                         }
                     } else {
                         self.variant_field(path_str, current_item, module_id)
@@ -574,46 +586,14 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             };
             let resolved_self;
             let mut path_str;
+            let disambiguator;
             let (res, fragment) = {
-                let mut kind = None;
-                let mut disambiguator = None;
-                path_str = if let Some(prefix) =
-                    ["struct@", "enum@", "type@", "trait@", "union@", "module@", "mod@"]
-                        .iter()
-                        .find(|p| link.starts_with(**p))
-                {
-                    kind = Some(TypeNS);
-                    disambiguator = Some(&prefix[..prefix.len() - 1]);
-                    link.trim_start_matches(prefix)
-                } else if let Some(prefix) =
-                    ["const@", "static@", "value@", "function@", "fn@", "method@"]
-                        .iter()
-                        .find(|p| link.starts_with(**p))
-                {
-                    kind = Some(ValueNS);
-                    disambiguator = Some(&prefix[..prefix.len() - 1]);
-                    link.trim_start_matches(prefix)
-                } else if link.ends_with("!()") {
-                    kind = Some(MacroNS);
-                    link.trim_end_matches("!()")
-                } else if link.ends_with("()") {
-                    kind = Some(ValueNS);
-                    disambiguator = Some("fn");
-                    link.trim_end_matches("()")
-                } else if link.starts_with("macro@") {
-                    kind = Some(MacroNS);
-                    disambiguator = Some("macro");
-                    link.trim_start_matches("macro@")
-                } else if link.starts_with("derive@") {
-                    kind = Some(MacroNS);
-                    disambiguator = Some("derive");
-                    link.trim_start_matches("derive@")
-                } else if link.ends_with('!') {
-                    kind = Some(MacroNS);
-                    disambiguator = Some("macro");
-                    link.trim_end_matches('!')
+                path_str = if let Ok((d, path)) = Disambiguator::from_str(&link) {
+                    disambiguator = Some(d);
+                    path
                 } else {
-                    &link[..]
+                    disambiguator = None;
+                    &link
                 }
                 .trim();
 
@@ -646,7 +626,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
                     }
                 }
 
-                match kind {
+                match disambiguator.map(Disambiguator::ns) {
                     Some(ns @ ValueNS) => {
                         match self.resolve(
                             path_str,
@@ -789,6 +769,42 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             } else {
                 debug!("intra-doc link to {} resolved to {:?}", path_str, res);
 
+                // Disallow e.g. linking to enums with `struct@`
+                if let Res::Def(kind, id) = res {
+                    debug!("saw kind {:?} with disambiguator {:?}", kind, disambiguator);
+                    match (self.kind_side_channel.take().unwrap_or(kind), disambiguator) {
+                        | (DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst, Some(Disambiguator::Kind(DefKind::Const)))
+                        // NOTE: this allows 'method' to mean both normal functions and associated functions
+                        // This can't cause ambiguity because both are in the same namespace.
+                        | (DefKind::Fn | DefKind::AssocFn, Some(Disambiguator::Kind(DefKind::Fn)))
+                        // These are namespaces; allow anything in the namespace to match
+                        | (_, Some(Disambiguator::Namespace(_)))
+                        // If no disambiguator given, allow anything
+                        | (_, None)
+                        // All of these are valid, so do nothing
+                        => {}
+                        (actual, Some(Disambiguator::Kind(expected))) if actual == expected => {}
+                        (_, Some(Disambiguator::Kind(expected))) => {
+                            // The resolved item did not match the disambiguator; give a better error than 'not found'
+                            let msg = format!("incompatible link kind for `{}`", path_str);
+                            report_diagnostic(cx, &msg, &item, &dox, link_range, |diag, sp| {
+                                // HACK(jynelson): by looking at the source I saw the DefId we pass
+                                // for `expected.descr()` doesn't matter, since it's not a crate
+                                let note = format!("this link resolved to {} {}, which is not {} {}", kind.article(), kind.descr(id), expected.article(), expected.descr(id));
+                                let suggestion = Disambiguator::display_for(kind, path_str);
+                                let help_msg = format!("to link to the {}, use its disambiguator", kind.descr(id));
+                                diag.note(&note);
+                                if let Some(sp) = sp {
+                                    diag.span_suggestion(sp, &help_msg, suggestion, Applicability::MaybeIncorrect);
+                                } else {
+                                    diag.help(&format!("{}: {}", help_msg, suggestion));
+                                }
+                            });
+                            continue;
+                        }
+                    }
+                }
+
                 // item can be non-local e.g. when using #[doc(primitive = "pointer")]
                 if let Some((src_id, dst_id)) = res
                     .opt_def_id()
@@ -837,6 +853,94 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
     }
 }
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum Disambiguator {
+    Kind(DefKind),
+    Namespace(Namespace),
+}
+
+impl Disambiguator {
+    /// (disambiguator, path_str)
+    fn from_str(link: &str) -> Result<(Self, &str), ()> {
+        use Disambiguator::{Kind, Namespace as NS};
+
+        let find_suffix = || {
+            let suffixes = [
+                ("!()", DefKind::Macro(MacroKind::Bang)),
+                ("()", DefKind::Fn),
+                ("!", DefKind::Macro(MacroKind::Bang)),
+            ];
+            for &(suffix, kind) in &suffixes {
+                if link.ends_with(suffix) {
+                    return Ok((Kind(kind), link.trim_end_matches(suffix)));
+                }
+            }
+            Err(())
+        };
+
+        if let Some(idx) = link.find('@') {
+            let (prefix, rest) = link.split_at(idx);
+            let d = match prefix {
+                "struct" => Kind(DefKind::Struct),
+                "enum" => Kind(DefKind::Enum),
+                "trait" => Kind(DefKind::Trait),
+                "union" => Kind(DefKind::Union),
+                "module" | "mod" => Kind(DefKind::Mod),
+                "const" | "constant" => Kind(DefKind::Const),
+                "static" => Kind(DefKind::Static),
+                "function" | "fn" | "method" => Kind(DefKind::Fn),
+                "derive" => Kind(DefKind::Macro(MacroKind::Derive)),
+                "type" => NS(Namespace::TypeNS),
+                "value" => NS(Namespace::ValueNS),
+                "macro" => NS(Namespace::MacroNS),
+                _ => return find_suffix(),
+            };
+            Ok((d, &rest[1..]))
+        } else {
+            find_suffix()
+        }
+    }
+
+    fn display_for(kind: DefKind, path_str: &str) -> String {
+        if kind == DefKind::Macro(MacroKind::Bang) {
+            return format!("{}!", path_str);
+        } else if kind == DefKind::Fn || kind == DefKind::AssocFn {
+            return format!("{}()", path_str);
+        }
+        let prefix = match kind {
+            DefKind::Struct => "struct",
+            DefKind::Enum => "enum",
+            DefKind::Trait => "trait",
+            DefKind::Union => "union",
+            DefKind::Mod => "mod",
+            DefKind::Const | DefKind::ConstParam | DefKind::AssocConst | DefKind::AnonConst => {
+                "const"
+            }
+            DefKind::Static => "static",
+            DefKind::Macro(MacroKind::Derive) => "derive",
+            // Now handle things that don't have a specific disambiguator
+            _ => match kind
+                .ns()
+                .expect("tried to calculate a disambiguator for a def without a namespace?")
+            {
+                Namespace::TypeNS => "type",
+                Namespace::ValueNS => "value",
+                Namespace::MacroNS => "macro",
+            },
+        };
+        format!("{}@{}", prefix, path_str)
+    }
+
+    fn ns(self) -> Namespace {
+        match self {
+            Self::Namespace(n) => n,
+            Self::Kind(k) => {
+                k.ns().expect("only DefKinds with a valid namespace can be disambiguators")
+            }
+        }
+    }
+}
+
 /// Reports a diagnostic for an intra-doc link.
 ///
 /// If no link range is provided, or the source span of the link cannot be determined, the span of
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index b86a105ff76..62f52ea5213 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -398,7 +398,7 @@ pub fn make_test(
     // Uses librustc_ast to parse the doctest and find if there's a main fn and the extern
     // crate already is included.
     let result = rustc_driver::catch_fatal_errors(|| {
-        rustc_ast::with_session_globals(edition, || {
+        rustc_span::with_session_globals(edition, || {
             use rustc_errors::emitter::EmitterWriter;
             use rustc_errors::Handler;
             use rustc_parse::maybe_new_parser_from_source_str;
@@ -943,7 +943,12 @@ impl<'a, 'hir, 'tcx> HirCollector<'a, 'hir, 'tcx> {
         // The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
         // anything else, this will combine them for us.
         if let Some(doc) = attrs.collapsed_doc_value() {
-            self.collector.set_position(attrs.span.unwrap_or(DUMMY_SP));
+            // Use the outermost invocation, so that doctest names come from where the docs were written.
+            let span = attrs
+                .span
+                .map(|span| span.ctxt().outer_expn().expansion_cause().unwrap_or(span))
+                .unwrap_or(DUMMY_SP);
+            self.collector.set_position(span);
             markdown::find_testable_code(
                 &doc,
                 self.collector,
diff --git a/src/test/codegen-units/polymorphization/pr-75255.rs b/src/test/codegen-units/polymorphization/pr-75255.rs
new file mode 100644
index 00000000000..af47b440640
--- /dev/null
+++ b/src/test/codegen-units/polymorphization/pr-75255.rs
@@ -0,0 +1,52 @@
+// compile-flags:-Zpolymorphize=on -Zprint-mono-items=lazy -Copt-level=1
+// ignore-tidy-linelength
+
+#![crate_type = "rlib"]
+
+// Test that only one copy of `Iter::map` and `iter::repeat` are generated.
+
+fn unused<T>() -> u64 {
+    42
+}
+
+fn foo<T>() {
+    let x = [1, 2, 3, std::mem::size_of::<T>()];
+    x.iter().map(|_| ());
+}
+
+//~ MONO_ITEM fn core::iter[0]::adapters[0]::{{impl}}[29]::new[0]<core::slice[0]::Iter[0]<usize>, pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.0[External]
+//~ MONO_ITEM fn core::iter[0]::traits[0]::iterator[0]::Iterator[0]::map[0]<core::slice[0]::Iter[0]<usize>, (), pr_75255::foo[0]::{{closure}}[0]<T>> @@ pr_75255-cgu.1[Internal]
+
+fn bar<T>() {
+    std::iter::repeat(unused::<T>);
+}
+
+//~ MONO_ITEM fn core::iter[0]::sources[0]::repeat[0]<fn() -> u64> @@ pr_75255-cgu.1[Internal]
+
+pub fn dispatch() {
+    foo::<String>();
+    foo::<Vec<String>>();
+
+    bar::<String>();
+    bar::<Vec<String>>();
+}
+
+// These are all the items that aren't relevant to the test.
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::mem[0]::size_of[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::add[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::is_null[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::offset[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_add[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::const_ptr[0]::{{impl}}[0]::wrapping_offset[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::non_null[0]::{{impl}}[3]::new_unchecked[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::ptr[0]::null[0]<u8> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::as_ptr[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::iter[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn core::slice[0]::{{impl}}[0]::len[0]<usize> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::dispatch[0] @@ pr_75255-cgu.1[External]
+//~ MONO_ITEM fn pr_75255::foo[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::foo[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::bar[0]<alloc::string[0]::String[0]> @@ pr_75255-cgu.1[Internal]
+//~ MONO_ITEM fn pr_75255::bar[0]<alloc::vec[0]::Vec[0]<alloc::string[0]::String[0]>> @@ pr_75255-cgu.1[Internal]
diff --git a/src/test/codegen/some-global-nonnull.rs b/src/test/codegen/some-global-nonnull.rs
new file mode 100644
index 00000000000..59c47de4129
--- /dev/null
+++ b/src/test/codegen/some-global-nonnull.rs
@@ -0,0 +1,25 @@
+// compile-flags: -O
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @test
+// CHECK-NEXT: start:
+// CHECK-NEXT: tail call void @ext_fn0()
+#[no_mangle]
+pub fn test() {
+    test_inner(Some(inner0));
+}
+
+fn test_inner(f_maybe: Option<fn()>) {
+    if let Some(f) = f_maybe {
+        f();
+    }
+}
+
+fn inner0() {
+    unsafe { ext_fn0() };
+}
+
+extern "C" {
+    fn ext_fn0();
+}
diff --git a/src/test/debuginfo/function-arguments-naked.rs b/src/test/debuginfo/function-arguments-naked.rs
index e88a99b322e..5f3a1eb44e4 100644
--- a/src/test/debuginfo/function-arguments-naked.rs
+++ b/src/test/debuginfo/function-arguments-naked.rs
@@ -3,6 +3,9 @@
 // We have to ignore android because of this issue:
 // https://github.com/rust-lang/rust/issues/74847
 // ignore-android
+//
+// We need to use inline assembly, so just use one platform
+// only-x86_64
 
 // compile-flags:-g
 
@@ -24,6 +27,7 @@
 // lldb-command:continue
 
 
+#![feature(asm)]
 #![feature(naked_functions)]
 #![feature(omit_gdb_pretty_printer_section)]
 #![omit_gdb_pretty_printer_section]
@@ -33,8 +37,6 @@ fn main() {
 }
 
 #[naked]
-fn naked(x: usize, y: usize) {
-    zzz(); // #break
+extern "C" fn naked(x: usize, y: usize) {
+    unsafe { asm!("ret"); } // #break
 }
-
-fn zzz() { () }
diff --git a/src/test/debuginfo/pretty-std-collections-hash.rs b/src/test/debuginfo/pretty-std-collections-hash.rs
index 361b300f28c..e8f52deabd8 100644
--- a/src/test/debuginfo/pretty-std-collections-hash.rs
+++ b/src/test/debuginfo/pretty-std-collections-hash.rs
@@ -9,35 +9,35 @@
 // cdb-check:hash_set,d [...] : { size=15 } [Type: [...]::HashSet<u64, [...]>]
 // cdb-check:    [size]           : 15 [Type: [...]]
 // cdb-check:    [capacity]       : [...]
-// cdb-check:    [[...]] [...]    : 0 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 0 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 1 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 1 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 2 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 2 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 3 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 3 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 4 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 4 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 5 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 5 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 6 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 6 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 7 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 7 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 8 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 8 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 9 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 9 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 10 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 10 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 11 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 11 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 12 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 12 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 13 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 13 [Type: u64]
 // cdb-command: dx hash_set,d
-// cdb-check:    [[...]] [...]    : 14 [Type: unsigned __int64]
+// cdb-check:    [[...]] [...]    : 14 [Type: u64]
 
 // cdb-command: dx hash_map,d
 // cdb-check:hash_map,d [...] : { size=15 } [Type: [...]::HashMap<u64, u64, [...]>]
diff --git a/src/test/mir-opt/const-promotion-extern-static.rs b/src/test/mir-opt/const-promotion-extern-static.rs
index e63309a9bd2..9c30e040031 100644
--- a/src/test/mir-opt/const-promotion-extern-static.rs
+++ b/src/test/mir-opt/const-promotion-extern-static.rs
@@ -1,7 +1,7 @@
+// ignore-endian-big
 extern "C" {
     static X: i32;
 }
-
 static Y: i32 = 42;
 
 // EMIT_MIR const_promotion_extern_static.BAR.PromoteTemps.diff
diff --git a/src/test/mir-opt/const_allocation.rs b/src/test/mir-opt/const_allocation.rs
index bb1c48e8e3c..b0fcb86fcee 100644
--- a/src/test/mir-opt/const_allocation.rs
+++ b/src/test/mir-opt/const_allocation.rs
@@ -1,5 +1,5 @@
+// ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
-
 static FOO: &[(Option<i32>, &[&str])] =
     &[(None, &[]), (None, &["foo", "bar"]), (Some(42), &["meh", "mop", "möp"])];
 
diff --git a/src/test/mir-opt/const_allocation2.rs b/src/test/mir-opt/const_allocation2.rs
index 56839255c0e..30afedffb39 100644
--- a/src/test/mir-opt/const_allocation2.rs
+++ b/src/test/mir-opt/const_allocation2.rs
@@ -1,5 +1,5 @@
+// ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
-
 // EMIT_MIR const_allocation2.main.ConstProp.after.mir
 fn main() {
     FOO;
diff --git a/src/test/mir-opt/const_allocation3.rs b/src/test/mir-opt/const_allocation3.rs
index 2ce289aea3f..ddeb32ab9a5 100644
--- a/src/test/mir-opt/const_allocation3.rs
+++ b/src/test/mir-opt/const_allocation3.rs
@@ -1,5 +1,5 @@
+// ignore-endian-big
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
-
 // EMIT_MIR const_allocation3.main.ConstProp.after.mir
 fn main() {
     FOO;
diff --git a/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit
new file mode 100644
index 00000000000..721766f9849
--- /dev/null
+++ b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.32bit
@@ -0,0 +1,85 @@
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
+      let _1: u8;                          // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+      let mut _2: [u8; 5000];              // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+      let _3: usize;                       // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+      let mut _4: usize;                   // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      let mut _5: bool;                    // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+          StorageLive(_2);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+          _2 = [const 0_u8; 5000];         // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+                                           // ty::Const
+                                           // + ty: u8
+                                           // + val: Value(Scalar(0x00))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:18: 6:22
+                                           // + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
+          StorageLive(_3);                 // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+          _3 = const 2_usize;              // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x00000002))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:30: 6:31
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
+          _4 = const 5000_usize;           // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x00001388))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:17: 6:32
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
+-         _5 = Lt(_3, _4);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+-         assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++         _5 = const true;                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++         assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x00001388))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x00001388)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x00000002))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x00000002)) }
+      }
+  
+      bb1: {
+          _1 = _2[_3];                     // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+          StorageDead(_3);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          StorageDead(_2);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          _0 = const ();                   // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:4:11: 7:2
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          StorageDead(_1);                 // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
+          return;                          // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit
new file mode 100644
index 00000000000..eae2ce6671c
--- /dev/null
+++ b/src/test/mir-opt/const_prop/large_array_index.main.ConstProp.diff.64bit
@@ -0,0 +1,85 @@
+- // MIR for `main` before ConstProp
++ // MIR for `main` after ConstProp
+  
+  fn main() -> () {
+      let mut _0: ();                      // return place in scope 0 at $DIR/large_array_index.rs:4:11: 4:11
+      let _1: u8;                          // in scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+      let mut _2: [u8; 5000];              // in scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+      let _3: usize;                       // in scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+      let mut _4: usize;                   // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      let mut _5: bool;                    // in scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+      scope 1 {
+          debug x => _1;                   // in scope 1 at $DIR/large_array_index.rs:6:9: 6:10
+      }
+  
+      bb0: {
+          StorageLive(_1);                 // scope 0 at $DIR/large_array_index.rs:6:9: 6:10
+          StorageLive(_2);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+          _2 = [const 0_u8; 5000];         // scope 0 at $DIR/large_array_index.rs:6:17: 6:29
+                                           // ty::Const
+                                           // + ty: u8
+                                           // + val: Value(Scalar(0x00))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:18: 6:22
+                                           // + literal: Const { ty: u8, val: Value(Scalar(0x00)) }
+          StorageLive(_3);                 // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+          _3 = const 2_usize;              // scope 0 at $DIR/large_array_index.rs:6:30: 6:31
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x0000000000000002))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:30: 6:31
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) }
+          _4 = const 5000_usize;           // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+                                           // ty::Const
+                                           // + ty: usize
+                                           // + val: Value(Scalar(0x0000000000001388))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:6:17: 6:32
+                                           // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000001388)) }
+-         _5 = Lt(_3, _4);                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+-         assert(move _5, "index out of bounds: the len is {} but the index is {}", move _4, _3) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++         _5 = const true;                 // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++         assert(const true, "index out of bounds: the len is {} but the index is {}", const 5000_usize, const 2_usize) -> bb1; // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
++                                          // ty::Const
++                                          // + ty: bool
++                                          // + val: Value(Scalar(0x01))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: bool, val: Value(Scalar(0x01)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x0000000000001388))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000001388)) }
++                                          // ty::Const
++                                          // + ty: usize
++                                          // + val: Value(Scalar(0x0000000000000002))
++                                          // mir::Constant
++                                          // + span: $DIR/large_array_index.rs:6:17: 6:32
++                                          // + literal: Const { ty: usize, val: Value(Scalar(0x0000000000000002)) }
+      }
+  
+      bb1: {
+          _1 = _2[_3];                     // scope 0 at $DIR/large_array_index.rs:6:17: 6:32
+          StorageDead(_3);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          StorageDead(_2);                 // scope 0 at $DIR/large_array_index.rs:6:32: 6:33
+          _0 = const ();                   // scope 0 at $DIR/large_array_index.rs:4:11: 7:2
+                                           // ty::Const
+                                           // + ty: ()
+                                           // + val: Value(Scalar(<ZST>))
+                                           // mir::Constant
+                                           // + span: $DIR/large_array_index.rs:4:11: 7:2
+                                           // + literal: Const { ty: (), val: Value(Scalar(<ZST>)) }
+          StorageDead(_1);                 // scope 0 at $DIR/large_array_index.rs:7:1: 7:2
+          return;                          // scope 0 at $DIR/large_array_index.rs:7:2: 7:2
+      }
+  }
+  
diff --git a/src/test/mir-opt/const_prop/large_array_index.rs b/src/test/mir-opt/const_prop/large_array_index.rs
new file mode 100644
index 00000000000..48d134376db
--- /dev/null
+++ b/src/test/mir-opt/const_prop/large_array_index.rs
@@ -0,0 +1,7 @@
+// EMIT_MIR_FOR_EACH_BIT_WIDTH
+
+// EMIT_MIR large_array_index.main.ConstProp.diff
+fn main() {
+    // check that we don't propagate this, because it's too large
+    let x: u8 = [0_u8; 5000][2];
+}
diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff
index e78bc31b774..6e2ee0957ab 100644
--- a/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate_mut_ref.main.ConstProp.diff
@@ -23,15 +23,13 @@
                                            // + ty: i32
                                            // + val: Value(Scalar(0x0000002a))
                                            // mir::Constant
--                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20
-+                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25
+                                           // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:18: 5:20
                                            // + literal: Const { ty: i32, val: Value(Scalar(0x0000002a)) }
                                            // ty::Const
                                            // + ty: i32
                                            // + val: Value(Scalar(0x0000002b))
                                            // mir::Constant
--                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24
-+                                          // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:17: 5:25
+                                           // + span: $DIR/mutable_variable_aggregate_mut_ref.rs:5:22: 5:24
                                            // + literal: Const { ty: i32, val: Value(Scalar(0x0000002b)) }
           StorageLive(_2);                 // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:9: 6:10
           _2 = &mut _1;                    // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:6:13: 6:19
diff --git a/src/test/mir-opt/inline/inline-into-box-place.rs b/src/test/mir-opt/inline/inline-into-box-place.rs
index 30c9a5d6b8f..57298605b18 100644
--- a/src/test/mir-opt/inline/inline-into-box-place.rs
+++ b/src/test/mir-opt/inline/inline-into-box-place.rs
@@ -1,8 +1,8 @@
+// ignore-endian-big
 // ignore-wasm32-bare compiled with panic=abort by default
 // compile-flags: -Z mir-opt-level=3
 // EMIT_MIR_FOR_EACH_BIT_WIDTH
 #![feature(box_syntax)]
-
 // EMIT_MIR inline_into_box_place.main.Inline.diff
 fn main() {
     let _x: Box<Vec<u32>> = box Vec::new();
diff --git a/src/test/rustdoc-ui/auxiliary/extern_macros.rs b/src/test/rustdoc-ui/auxiliary/extern_macros.rs
new file mode 100644
index 00000000000..ee1fec4c5c2
--- /dev/null
+++ b/src/test/rustdoc-ui/auxiliary/extern_macros.rs
@@ -0,0 +1,7 @@
+#[macro_export]
+macro_rules! attrs_on_struct {
+    ( $( #[$attr:meta] )* ) => {
+        $( #[$attr] )*
+        pub struct ExpandedStruct;
+    }
+}
diff --git a/src/test/rustdoc-ui/doctest-output.rs b/src/test/rustdoc-ui/doctest-output.rs
index f812263c252..e0e1e061ac7 100644
--- a/src/test/rustdoc-ui/doctest-output.rs
+++ b/src/test/rustdoc-ui/doctest-output.rs
@@ -1,3 +1,5 @@
+// edition:2018
+// aux-build:extern_macros.rs
 // compile-flags:--test --test-args=--test-threads=1
 // normalize-stdout-test: "src/test/rustdoc-ui" -> "$$DIR"
 // check-pass
@@ -6,6 +8,10 @@
 //! assert_eq!(1 + 1, 2);
 //! ```
 
+extern crate extern_macros as macros;
+
+use macros::attrs_on_struct;
+
 pub mod foo {
 
     /// ```
@@ -13,3 +19,9 @@ pub mod foo {
     /// ```
     pub fn bar() {}
 }
+
+attrs_on_struct! {
+    /// ```
+    /// assert!(true);
+    /// ```
+}
diff --git a/src/test/rustdoc-ui/doctest-output.stdout b/src/test/rustdoc-ui/doctest-output.stdout
index 9a55bf50196..c72bd91d1dd 100644
--- a/src/test/rustdoc-ui/doctest-output.stdout
+++ b/src/test/rustdoc-ui/doctest-output.stdout
@@ -1,7 +1,8 @@
 
-running 2 tests
-test $DIR/doctest-output.rs - (line 5) ... ok
-test $DIR/doctest-output.rs - foo::bar (line 11) ... ok
+running 3 tests
+test $DIR/doctest-output.rs - (line 7) ... ok
+test $DIR/doctest-output.rs - ExpandedStruct (line 23) ... ok
+test $DIR/doctest-output.rs - foo::bar (line 17) ... ok
 
-test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
+test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
 
diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
new file mode 100644
index 00000000000..1a7a2fce7a3
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.rs
@@ -0,0 +1,68 @@
+#![deny(broken_intra_doc_links)]
+//~^ NOTE lint level is defined
+pub enum S {}
+
+macro_rules! m {
+    () => {};
+}
+
+static s: usize = 0;
+const c: usize = 0;
+
+trait T {}
+
+/// Link to [struct@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [mod@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [union@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [trait@S]
+//~^ ERROR incompatible link kind for `S`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [struct@T]
+//~^ ERROR incompatible link kind for `T`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [derive@m]
+//~^ ERROR incompatible link kind for `m`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [const@s]
+//~^ ERROR incompatible link kind for `s`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [static@c]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [fn@c]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [c()]
+//~^ ERROR incompatible link kind for `c`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+
+/// Link to [const@f]
+//~^ ERROR incompatible link kind for `f`
+//~| NOTE this link resolved
+//~| HELP use its disambiguator
+pub fn f() {}
diff --git a/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
new file mode 100644
index 00000000000..9edf838f9d8
--- /dev/null
+++ b/src/test/rustdoc-ui/intra-links-disambiguator-mismatch.stderr
@@ -0,0 +1,95 @@
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:14:14
+   |
+LL | /// Link to [struct@S]
+   |              ^^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+note: the lint level is defined here
+  --> $DIR/intra-links-disambiguator-mismatch.rs:1:9
+   |
+LL | #![deny(broken_intra_doc_links)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   = note: this link resolved to an enum, which is not a struct
+
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:19:14
+   |
+LL | /// Link to [mod@S]
+   |              ^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+   = note: this link resolved to an enum, which is not a module
+
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:24:14
+   |
+LL | /// Link to [union@S]
+   |              ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+   = note: this link resolved to an enum, which is not a union
+
+error: incompatible link kind for `S`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:29:14
+   |
+LL | /// Link to [trait@S]
+   |              ^^^^^^^ help: to link to the enum, use its disambiguator: `enum@S`
+   |
+   = note: this link resolved to an enum, which is not a trait
+
+error: incompatible link kind for `T`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:34:14
+   |
+LL | /// Link to [struct@T]
+   |              ^^^^^^^^ help: to link to the trait, use its disambiguator: `trait@T`
+   |
+   = note: this link resolved to a trait, which is not a struct
+
+error: incompatible link kind for `m`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:39:14
+   |
+LL | /// Link to [derive@m]
+   |              ^^^^^^^^ help: to link to the macro, use its disambiguator: `m!`
+   |
+   = note: this link resolved to a macro, which is not a derive macro
+
+error: incompatible link kind for `s`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:44:14
+   |
+LL | /// Link to [const@s]
+   |              ^^^^^^^ help: to link to the static, use its disambiguator: `static@s`
+   |
+   = note: this link resolved to a static, which is not a constant
+
+error: incompatible link kind for `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:49:14
+   |
+LL | /// Link to [static@c]
+   |              ^^^^^^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |
+   = note: this link resolved to a constant, which is not a static
+
+error: incompatible link kind for `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:54:14
+   |
+LL | /// Link to [fn@c]
+   |              ^^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |
+   = note: this link resolved to a constant, which is not a function
+
+error: incompatible link kind for `c`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:59:14
+   |
+LL | /// Link to [c()]
+   |              ^^^ help: to link to the constant, use its disambiguator: `const@c`
+   |
+   = note: this link resolved to a constant, which is not a function
+
+error: incompatible link kind for `f`
+  --> $DIR/intra-links-disambiguator-mismatch.rs:64:14
+   |
+LL | /// Link to [const@f]
+   |              ^^^^^^^ help: to link to the function, use its disambiguator: `f()`
+   |
+   = note: this link resolved to a function, which is not a constant
+
+error: aborting due to 11 previous errors
+
diff --git a/src/test/rustdoc/auxiliary/elided-lifetime.rs b/src/test/rustdoc/auxiliary/elided-lifetime.rs
new file mode 100644
index 00000000000..4f2c93379d8
--- /dev/null
+++ b/src/test/rustdoc/auxiliary/elided-lifetime.rs
@@ -0,0 +1,11 @@
+#![crate_name = "bar"]
+
+pub struct Ref<'a>(&'a u32);
+
+pub fn test5(a: &u32) -> Ref {
+    Ref(a)
+}
+
+pub fn test6(a: &u32) -> Ref<'_> {
+    Ref(a)
+}
diff --git a/src/test/rustdoc/elided-lifetime.rs b/src/test/rustdoc/elided-lifetime.rs
new file mode 100644
index 00000000000..5a32554f972
--- /dev/null
+++ b/src/test/rustdoc/elided-lifetime.rs
@@ -0,0 +1,43 @@
+// aux-build:elided-lifetime.rs
+//
+// rust-lang/rust#75225
+//
+// Since Rust 2018 we encourage writing out <'_> explicitly to make it clear
+// that borrowing is occuring. Make sure rustdoc is following the same idiom.
+
+#![crate_name = "foo"]
+
+pub struct Ref<'a>(&'a u32);
+type ARef<'a> = Ref<'a>;
+
+// @has foo/fn.test1.html
+// @matches - "Ref</a>&lt;'_&gt;"
+pub fn test1(a: &u32) -> Ref {
+    Ref(a)
+}
+
+// @has foo/fn.test2.html
+// @matches - "Ref</a>&lt;'_&gt;"
+pub fn test2(a: &u32) -> Ref<'_> {
+    Ref(a)
+}
+
+// @has foo/fn.test3.html
+// @matches - "Ref</a>&lt;'_&gt;"
+pub fn test3(a: &u32) -> ARef {
+    Ref(a)
+}
+
+// @has foo/fn.test4.html
+// @matches - "Ref</a>&lt;'_&gt;"
+pub fn test4(a: &u32) -> ARef<'_> {
+    Ref(a)
+}
+
+// Ensure external paths in inlined docs also display elided lifetime
+// @has foo/bar/fn.test5.html
+// @matches - "Ref</a>&lt;'_&gt;"
+// @has foo/bar/fn.test6.html
+// @matches - "Ref</a>&lt;'_&gt;"
+#[doc(inline)]
+pub extern crate bar;
diff --git a/src/test/rustdoc/intra-link-prim-precedence.rs b/src/test/rustdoc/intra-link-prim-precedence.rs
index 5f10c1ec4a7..15ea1232fd6 100644
--- a/src/test/rustdoc/intra-link-prim-precedence.rs
+++ b/src/test/rustdoc/intra-link-prim-precedence.rs
@@ -8,5 +8,10 @@ pub mod char {}
 pub struct MyString;
 
 /// See also [char]
-// @has intra_link_prim_precedence/struct.MyString2.html '//a/@href' 'intra_link_prim_precedence/char/index.html'
+// @has intra_link_prim_precedence/struct.MyString2.html '//a/@href' 'https://doc.rust-lang.org/nightly/std/primitive.char.html'
 pub struct MyString2;
+
+/// See also [crate::char] and [mod@char]
+// @has intra_link_prim_precedence/struct.MyString3.html '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'crate::char'
+// @has - '//*[@href="../intra_link_prim_precedence/char/index.html"]' 'mod@char'
+pub struct MyString3;
diff --git a/src/test/rustdoc/intra-link-trait-item.rs b/src/test/rustdoc/intra-link-trait-item.rs
new file mode 100644
index 00000000000..54270414c9d
--- /dev/null
+++ b/src/test/rustdoc/intra-link-trait-item.rs
@@ -0,0 +1,12 @@
+// ignore-tidy-linelength
+#![deny(broken_intra_doc_links)]
+
+/// Link to [S::assoc_fn()]
+/// Link to [Default::default()]
+// @has intra_link_trait_item/struct.S.html '//*[@href="../intra_link_trait_item/struct.S.html#method.assoc_fn"]' 'S::assoc_fn()'
+// @has - '//*[@href="https://doc.rust-lang.org/nightly/core/default/trait.Default.html#tymethod.default"]' 'Default::default()'
+pub struct S;
+
+impl S {
+    pub fn assoc_fn() {}
+}
diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs
index bfb4da4c8f5..736a8633dac 100644
--- a/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs
+++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate-rpass.rs
@@ -29,7 +29,7 @@ macro_rules! fake_lint_pass {
         impl LateLintPass<'_> for $struct {
             fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
                 $(
-                    if !attr::contains_name(&krate.item.attrs, $attr) {
+                    if !cx.sess().contains_name(&krate.item.attrs, $attr) {
                         cx.lint(CRATE_NOT_OKAY, |lint| {
                              let msg = format!("crate is not marked with #![{}]", $attr);
                              lint.build(&msg).set_span(krate.item.span).emit()
diff --git a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs
index e6a6f73bd47..bd477b793fc 100644
--- a/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs
+++ b/src/test/ui-fulldeps/auxiliary/lint-for-crate.rs
@@ -27,7 +27,7 @@ declare_lint_pass!(Pass => [CRATE_NOT_OKAY]);
 
 impl<'tcx> LateLintPass<'tcx> for Pass {
     fn check_crate(&mut self, cx: &LateContext, krate: &rustc_hir::Crate) {
-        if !attr::contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) {
+        if !cx.sess().contains_name(&krate.item.attrs, Symbol::intern("crate_okay")) {
             cx.lint(CRATE_NOT_OKAY, |lint| {
                 lint.build("crate is not marked with #![crate_okay]")
                     .set_span(krate.item.span)
diff --git a/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs b/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs
index 836cb07d5d1..448c57da754 100644
--- a/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs
+++ b/src/test/ui-fulldeps/mod_dir_path_canonicalized.rs
@@ -19,7 +19,7 @@ use std::path::Path;
 mod gravy;
 
 pub fn main() {
-    rustc_ast::with_default_session_globals(|| parse());
+    rustc_span::with_default_session_globals(|| parse());
 
     assert_eq!(gravy::foo(), 10);
 }
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index 8286b7fdb66..f0e3ab308e9 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -208,7 +208,7 @@ impl MutVisitor for AddParens {
 }
 
 fn main() {
-    rustc_ast::with_default_session_globals(|| run());
+    rustc_span::with_default_session_globals(|| run());
 }
 
 fn run() {
diff --git a/src/test/ui/asm/bad-arch.rs b/src/test/ui/asm/bad-arch.rs
new file mode 100644
index 00000000000..1d21ae8df24
--- /dev/null
+++ b/src/test/ui/asm/bad-arch.rs
@@ -0,0 +1,19 @@
+// compile-flags: --target wasm32-unknown-unknown
+// needs-llvm-components: webassembly
+
+#![feature(no_core, lang_items, rustc_attrs)]
+#![no_core]
+
+#[rustc_builtin_macro]
+macro_rules! asm {
+    () => {};
+}
+#[lang = "sized"]
+trait Sized {}
+
+fn main() {
+    unsafe {
+        asm!("");
+        //~^ ERROR asm! is unsupported on this target
+    }
+}
diff --git a/src/test/ui/asm/bad-arch.stderr b/src/test/ui/asm/bad-arch.stderr
new file mode 100644
index 00000000000..cb876f28650
--- /dev/null
+++ b/src/test/ui/asm/bad-arch.stderr
@@ -0,0 +1,8 @@
+error[E0472]: asm! is unsupported on this target
+  --> $DIR/bad-arch.rs:16:9
+   |
+LL |         asm!("");
+   |         ^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs b/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs
new file mode 100644
index 00000000000..fda825bc65e
--- /dev/null
+++ b/src/test/ui/btreemap/btreemap_into_iterator_lifetime.rs
@@ -0,0 +1,23 @@
+// check-pass
+
+use std::collections::{BTreeMap, HashMap};
+
+trait Map
+where
+    for<'a> &'a Self: IntoIterator<Item = (&'a Self::Key, &'a Self::Value)>,
+{
+    type Key;
+    type Value;
+}
+
+impl<K, V> Map for HashMap<K, V> {
+    type Key = K;
+    type Value = V;
+}
+
+impl<K, V> Map for BTreeMap<K, V> {
+  type Key = K;
+  type Value = V;
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/const-param-in-trait-ungated.stderr b/src/test/ui/const-generics/const-param-in-trait-ungated.stderr
index bdae6bc362c..d53a4ac2d4c 100644
--- a/src/test/ui/const-generics/const-param-in-trait-ungated.stderr
+++ b/src/test/ui/const-generics/const-param-in-trait-ungated.stderr
@@ -4,8 +4,8 @@ error[E0658]: const generics are unstable
 LL | trait Trait<const T: ()> {}
    |                   ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr
index 616f0fa8f1a..5d379ff083c 100644
--- a/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr
+++ b/src/test/ui/const-generics/const-param-type-depends-on-type-param-ungated.stderr
@@ -10,8 +10,8 @@ error[E0658]: const generics are unstable
 LL | struct B<T, const N: T>(PhantomData<[T; N]>);
    |                   ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/const-generics/defaults/wrong-order.stderr b/src/test/ui/const-generics/defaults/wrong-order.full.stderr
index 283f6656121..c51028d5b20 100644
--- a/src/test/ui/const-generics/defaults/wrong-order.stderr
+++ b/src/test/ui/const-generics/defaults/wrong-order.full.stderr
@@ -1,5 +1,5 @@
 error: type parameters with a default must be trailing
-  --> $DIR/wrong-order.rs:3:10
+  --> $DIR/wrong-order.rs:5:10
    |
 LL | struct A<T = u32, const N: usize> {
    |          ^
@@ -7,10 +7,10 @@ LL | struct A<T = u32, const N: usize> {
    = note: using type defaults and const parameters in the same parameter list is currently not permitted
 
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/wrong-order.rs:1:12
+  --> $DIR/wrong-order.rs:2:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
diff --git a/src/test/ui/const-generics/defaults/wrong-order.min.stderr b/src/test/ui/const-generics/defaults/wrong-order.min.stderr
new file mode 100644
index 00000000000..29a46367004
--- /dev/null
+++ b/src/test/ui/const-generics/defaults/wrong-order.min.stderr
@@ -0,0 +1,10 @@
+error: type parameters with a default must be trailing
+  --> $DIR/wrong-order.rs:5:10
+   |
+LL | struct A<T = u32, const N: usize> {
+   |          ^
+   |
+   = note: using type defaults and const parameters in the same parameter list is currently not permitted
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/const-generics/defaults/wrong-order.rs b/src/test/ui/const-generics/defaults/wrong-order.rs
index 7f17c6358b7..cb36d456f38 100644
--- a/src/test/ui/const-generics/defaults/wrong-order.rs
+++ b/src/test/ui/const-generics/defaults/wrong-order.rs
@@ -1,4 +1,6 @@
-#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 struct A<T = u32, const N: usize> {
     //~^ ERROR type parameters with a default must be trailing
diff --git a/src/test/ui/const-generics/issues/issue-56445.stderr b/src/test/ui/const-generics/issues/issue-56445.full.stderr
index fba638b0b2b..d853ec5015e 100644
--- a/src/test/ui/const-generics/issues/issue-56445.stderr
+++ b/src/test/ui/const-generics/issues/issue-56445.full.stderr
@@ -1,8 +1,8 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-56445.rs:3:12
+  --> $DIR/issue-56445.rs:3:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
diff --git a/src/test/ui/const-generics/issues/issue-56445.min.stderr b/src/test/ui/const-generics/issues/issue-56445.min.stderr
new file mode 100644
index 00000000000..ca35ee5b290
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-56445.min.stderr
@@ -0,0 +1,20 @@
+error[E0771]: use of non-static lifetime `'a` in const generic
+  --> $DIR/issue-56445.rs:9:26
+   |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+   |                          ^^
+   |
+   = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
+
+error: using `&'static str` as const generic parameters is forbidden
+  --> $DIR/issue-56445.rs:9:25
+   |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+   |                         ^^^^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0771`.
diff --git a/src/test/ui/const-generics/issues/issue-56445.rs b/src/test/ui/const-generics/issues/issue-56445.rs
index 26441512e3f..174eb16abfc 100644
--- a/src/test/ui/const-generics/issues/issue-56445.rs
+++ b/src/test/ui/const-generics/issues/issue-56445.rs
@@ -1,12 +1,13 @@
 // Regression test for https://github.com/rust-lang/rust/issues/56445#issuecomment-518402995.
-
-#![feature(const_generics)]
-//~^ WARN: the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 #![crate_type = "lib"]
 
 use std::marker::PhantomData;
 
 struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
 //~^ ERROR: use of non-static lifetime `'a` in const generic
+//[min]~| ERROR: using `&'static str` as const
 
 impl Bug<'_, ""> {}
diff --git a/src/test/ui/const-generics/issues/issue-60263.stderr b/src/test/ui/const-generics/issues/issue-60263.stderr
index 7b50c442d2f..aeef296f385 100644
--- a/src/test/ui/const-generics/issues/issue-60263.stderr
+++ b/src/test/ui/const-generics/issues/issue-60263.stderr
@@ -4,8 +4,8 @@ error[E0658]: const generics are unstable
 LL | struct B<const I: u8>;
    |                ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/const-generics/issues/issue-61336-1.stderr b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr
index b2c69d57c40..c03b7252a3c 100644
--- a/src/test/ui/const-generics/issues/issue-61336-1.stderr
+++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.full.stderr
@@ -1,8 +1,8 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-61336-1.rs:1:12
+  --> $DIR/issue-60818-struct-constructors.rs:3:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs
index 26d74ffb254..ae2b0520fb1 100644
--- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs
+++ b/src/test/ui/const-generics/issues/issue-60818-struct-constructors.rs
@@ -1,7 +1,7 @@
 // check-pass
-
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 struct Generic<const V: usize>;
 
diff --git a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr b/src/test/ui/const-generics/issues/issue-61336-1.full.stderr
index 94a2b673a51..f18728eabbb 100644
--- a/src/test/ui/const-generics/issues/issue-60818-struct-constructors.stderr
+++ b/src/test/ui/const-generics/issues/issue-61336-1.full.stderr
@@ -1,8 +1,8 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-60818-struct-constructors.rs:3:12
+  --> $DIR/issue-61336-1.rs:3:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
diff --git a/src/test/ui/const-generics/issues/issue-61336-1.rs b/src/test/ui/const-generics/issues/issue-61336-1.rs
index 2135c868bbc..201c0d039d9 100644
--- a/src/test/ui/const-generics/issues/issue-61336-1.rs
+++ b/src/test/ui/const-generics/issues/issue-61336-1.rs
@@ -1,7 +1,7 @@
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
-
 // build-pass
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 fn f<T: Copy, const N: usize>(x: T) -> [T; N] {
     [x; N]
diff --git a/src/test/ui/const-generics/issues/issue-61336-2.stderr b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr
index 5f3395223f9..d21cd9df054 100644
--- a/src/test/ui/const-generics/issues/issue-61336-2.stderr
+++ b/src/test/ui/const-generics/issues/issue-61336-2.full.stderr
@@ -1,14 +1,14 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-61336-2.rs:1:12
+  --> $DIR/issue-61336-2.rs:2:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
 
 error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
-  --> $DIR/issue-61336-2.rs:9:5
+  --> $DIR/issue-61336-2.rs:10:5
    |
 LL |     [x; { N }]
    |     ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
diff --git a/src/test/ui/const-generics/issues/issue-61336-2.min.stderr b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr
new file mode 100644
index 00000000000..29ab7b1305e
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-61336-2.min.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+  --> $DIR/issue-61336-2.rs:10:5
+   |
+LL |     [x; { N }]
+   |     ^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+   |
+   = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+   |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+   |       ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/issues/issue-61336-2.rs b/src/test/ui/const-generics/issues/issue-61336-2.rs
index 52969056f00..25b9271105e 100644
--- a/src/test/ui/const-generics/issues/issue-61336-2.rs
+++ b/src/test/ui/const-generics/issues/issue-61336-2.rs
@@ -1,5 +1,6 @@
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 fn f<T: Copy, const N: usize>(x: T) -> [T; N] {
     [x; { N }]
diff --git a/src/test/ui/const-generics/issues/issue-61336.full.stderr b/src/test/ui/const-generics/issues/issue-61336.full.stderr
new file mode 100644
index 00000000000..d1b5d5eb941
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-61336.full.stderr
@@ -0,0 +1,24 @@
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/issue-61336.rs:2:27
+   |
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+  --> $DIR/issue-61336.rs:10:5
+   |
+LL |     [x; N]
+   |     ^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+   |
+   = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+   |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+   |       ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/issues/issue-61336.min.stderr b/src/test/ui/const-generics/issues/issue-61336.min.stderr
new file mode 100644
index 00000000000..bced8bbd82f
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-61336.min.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the trait bound `T: std::marker::Copy` is not satisfied
+  --> $DIR/issue-61336.rs:10:5
+   |
+LL |     [x; N]
+   |     ^^^^^^ the trait `std::marker::Copy` is not implemented for `T`
+   |
+   = note: the `Copy` trait is required because the repeated element will be copied
+help: consider restricting type parameter `T`
+   |
+LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
+   |       ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/src/test/ui/const-generics/issues/issue-61336.rs b/src/test/ui/const-generics/issues/issue-61336.rs
index eb0f3097627..fb55542a1c9 100644
--- a/src/test/ui/const-generics/issues/issue-61336.rs
+++ b/src/test/ui/const-generics/issues/issue-61336.rs
@@ -1,5 +1,6 @@
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 fn f<T: Copy, const N: usize>(x: T) -> [T; N] {
     [x; N]
diff --git a/src/test/ui/const-generics/issues/issue-61432.stderr b/src/test/ui/const-generics/issues/issue-61422.full.stderr
index 1d547b1b6c9..ac6c378295d 100644
--- a/src/test/ui/const-generics/issues/issue-61432.stderr
+++ b/src/test/ui/const-generics/issues/issue-61422.full.stderr
@@ -1,8 +1,8 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-61432.rs:3:12
+  --> $DIR/issue-61422.rs:3:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
diff --git a/src/test/ui/const-generics/issues/issue-61422.rs b/src/test/ui/const-generics/issues/issue-61422.rs
index 7e7ef6867ed..649f8b4255b 100644
--- a/src/test/ui/const-generics/issues/issue-61422.rs
+++ b/src/test/ui/const-generics/issues/issue-61422.rs
@@ -1,7 +1,7 @@
 // check-pass
-
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 use std::mem;
 
diff --git a/src/test/ui/const-generics/issues/issue-61422.stderr b/src/test/ui/const-generics/issues/issue-61432.full.stderr
index 69bbaada691..82b36de45a2 100644
--- a/src/test/ui/const-generics/issues/issue-61422.stderr
+++ b/src/test/ui/const-generics/issues/issue-61432.full.stderr
@@ -1,8 +1,8 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-61422.rs:3:12
+  --> $DIR/issue-61432.rs:3:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
diff --git a/src/test/ui/const-generics/issues/issue-61432.rs b/src/test/ui/const-generics/issues/issue-61432.rs
index 0440468e9e6..91a4794099c 100644
--- a/src/test/ui/const-generics/issues/issue-61432.rs
+++ b/src/test/ui/const-generics/issues/issue-61432.rs
@@ -1,7 +1,7 @@
 // run-pass
-
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 fn promote<const N: i32>() {
     // works:
diff --git a/src/test/ui/const-generics/issues/issue-61747.stderr b/src/test/ui/const-generics/issues/issue-61747.full.stderr
index 2685d9fdf16..3ccce5675fc 100644
--- a/src/test/ui/const-generics/issues/issue-61747.stderr
+++ b/src/test/ui/const-generics/issues/issue-61747.full.stderr
@@ -1,14 +1,14 @@
 warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
-  --> $DIR/issue-61747.rs:1:12
+  --> $DIR/issue-61747.rs:2:27
    |
-LL | #![feature(const_generics)]
-   |            ^^^^^^^^^^^^^^
+LL | #![cfg_attr(full, feature(const_generics))]
+   |                           ^^^^^^^^^^^^^^
    |
    = note: `#[warn(incomplete_features)]` on by default
    = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
 
 error: constant expression depends on a generic parameter
-  --> $DIR/issue-61747.rs:7:23
+  --> $DIR/issue-61747.rs:8:23
    |
 LL |     fn successor() -> Const<{C + 1}> {
    |                       ^^^^^^^^^^^^^^
diff --git a/src/test/ui/const-generics/issues/issue-61747.min.stderr b/src/test/ui/const-generics/issues/issue-61747.min.stderr
new file mode 100644
index 00000000000..2061b6c55bb
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-61747.min.stderr
@@ -0,0 +1,10 @@
+error: generic parameters must not be used inside of non trivial constant values
+  --> $DIR/issue-61747.rs:8:30
+   |
+LL |     fn successor() -> Const<{C + 1}> {
+   |                              ^ non-trivial anonymous constants must not depend on the parameter `C`
+   |
+   = help: it is currently only allowed to use either `C` or `{ C }` as generic constants
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/const-generics/issues/issue-61747.rs b/src/test/ui/const-generics/issues/issue-61747.rs
index cc671163e85..4e5cde17f39 100644
--- a/src/test/ui/const-generics/issues/issue-61747.rs
+++ b/src/test/ui/const-generics/issues/issue-61747.rs
@@ -1,11 +1,13 @@
-#![feature(const_generics)]
-//~^ WARN the feature `const_generics` is incomplete
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))] //[full]~WARN the feature `const_generics` is incomplete
+#![cfg_attr(min, feature(min_const_generics))]
 
 struct Const<const N: usize>;
 
 impl<const C: usize> Const<{C}> {
     fn successor() -> Const<{C + 1}> {
-        //~^ ERROR constant expression depends on a generic parameter
+        //[full]~^ ERROR constant expression depends on a generic parameter
+        //[min]~^^ ERROR generic parameters must not be used
         Const
     }
 }
diff --git a/src/test/ui/const-generics/min_const_generics/assoc_const.rs b/src/test/ui/const-generics/min_const_generics/assoc_const.rs
new file mode 100644
index 00000000000..fa75613d9dd
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/assoc_const.rs
@@ -0,0 +1,18 @@
+// check-pass
+#![feature(min_const_generics)]
+
+struct Foo<const N: usize>;
+
+impl<const N: usize> Foo<N> {
+    const VALUE: usize = N * 2;
+}
+
+trait Bar {
+    const ASSOC: usize;
+}
+
+impl<const N: usize> Bar for Foo<N> {
+    const ASSOC: usize = N * 3;
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/min_const_generics/complex-expression.rs b/src/test/ui/const-generics/min_const_generics/complex-expression.rs
new file mode 100644
index 00000000000..f9cb0d2829d
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/complex-expression.rs
@@ -0,0 +1,29 @@
+#![feature(min_const_generics)]
+
+fn test<const N: usize>() {}
+
+fn ok<const M: usize>() -> [u8; M] {
+    [0; { M }]
+}
+
+struct Break0<const N: usize>([u8; { N + 1 }]);
+//~^ ERROR generic parameters must not be used inside of non trivial constant values
+
+struct Break1<const N: usize>([u8; { { N } }]);
+//~^ ERROR generic parameters must not be used inside of non trivial constant values
+
+fn break2<const N: usize>() {
+    let _: [u8; N + 1];
+    //~^ ERROR generic parameters must not be used inside of non trivial constant values
+}
+
+fn break3<const N: usize>() {
+    let _ = [0; N + 1];
+    //~^ ERROR generic parameters must not be used inside of non trivial constant values
+}
+
+trait Foo {
+    const ASSOC: usize;
+}
+
+fn main() {}
diff --git a/src/test/ui/const-generics/min_const_generics/complex-expression.stderr b/src/test/ui/const-generics/min_const_generics/complex-expression.stderr
new file mode 100644
index 00000000000..baed8d13f00
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/complex-expression.stderr
@@ -0,0 +1,34 @@
+error: generic parameters must not be used inside of non trivial constant values
+  --> $DIR/complex-expression.rs:9:38
+   |
+LL | struct Break0<const N: usize>([u8; { N + 1 }]);
+   |                                      ^ non-trivial anonymous constants must not depend on the parameter `N`
+   |
+   = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: generic parameters must not be used inside of non trivial constant values
+  --> $DIR/complex-expression.rs:12:40
+   |
+LL | struct Break1<const N: usize>([u8; { { N } }]);
+   |                                        ^ non-trivial anonymous constants must not depend on the parameter `N`
+   |
+   = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: generic parameters must not be used inside of non trivial constant values
+  --> $DIR/complex-expression.rs:16:17
+   |
+LL |     let _: [u8; N + 1];
+   |                 ^ non-trivial anonymous constants must not depend on the parameter `N`
+   |
+   = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: generic parameters must not be used inside of non trivial constant values
+  --> $DIR/complex-expression.rs:21:17
+   |
+LL |     let _ = [0; N + 1];
+   |                 ^ non-trivial anonymous constants must not depend on the parameter `N`
+   |
+   = help: it is currently only allowed to use either `N` or `{ N }` as generic constants
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.rs b/src/test/ui/const-generics/min_const_generics/complex-types.rs
new file mode 100644
index 00000000000..a396fa83aa6
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/complex-types.rs
@@ -0,0 +1,18 @@
+#![feature(min_const_generics)]
+
+struct Foo<const N: [u8; 0]>;
+//~^ ERROR using `[u8; 0]` as const generic parameters is forbidden
+
+struct Bar<const N: ()>;
+//~^ ERROR using `()` as const generic parameters is forbidden
+
+#[derive(PartialEq, Eq)]
+struct No;
+
+struct Fez<const N: No>;
+//~^ ERROR using `No` as const generic parameters is forbidden
+
+struct Faz<const N: &'static u8>;
+//~^ ERROR using `&'static u8` as const generic parameters is forbidden
+
+fn main() {}
diff --git a/src/test/ui/const-generics/min_const_generics/complex-types.stderr b/src/test/ui/const-generics/min_const_generics/complex-types.stderr
new file mode 100644
index 00000000000..835b1f1a3e8
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/complex-types.stderr
@@ -0,0 +1,38 @@
+error: using `[u8; 0]` as const generic parameters is forbidden
+  --> $DIR/complex-types.rs:3:21
+   |
+LL | struct Foo<const N: [u8; 0]>;
+   |                     ^^^^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: using `()` as const generic parameters is forbidden
+  --> $DIR/complex-types.rs:6:21
+   |
+LL | struct Bar<const N: ()>;
+   |                     ^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: using `No` as const generic parameters is forbidden
+  --> $DIR/complex-types.rs:12:21
+   |
+LL | struct Fez<const N: No>;
+   |                     ^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: using `&'static u8` as const generic parameters is forbidden
+  --> $DIR/complex-types.rs:15:21
+   |
+LL | struct Faz<const N: &'static u8>;
+   |                     ^^^^^^^^^^^
+   |
+   = note: the only supported types are integers, `bool` and `char`
+   = note: more complex types are supported with `#[feature(const_generics)]`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs
new file mode 100644
index 00000000000..423deae4600
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.rs
@@ -0,0 +1,4 @@
+fn test<const N: usize>() {}
+//~^ ERROR const generics are unstable
+
+fn main() {}
diff --git a/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr
new file mode 100644
index 00000000000..7f82a960da2
--- /dev/null
+++ b/src/test/ui/const-generics/min_const_generics/feature-gate-min_const_generics.stderr
@@ -0,0 +1,12 @@
+error[E0658]: const generics are unstable
+  --> $DIR/feature-gate-min_const_generics.rs:1:15
+   |
+LL | fn test<const N: usize>() {}
+   |               ^
+   |
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs b/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs
index ae50252facd..3ccdd472613 100644
--- a/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs
+++ b/src/test/ui/const-generics/type-dependent/const-arg-in-const-arg.rs
@@ -1,7 +1,8 @@
 // run-pass
-#![feature(const_generics)]
+// revisions: full min
+#![cfg_attr(full, feature(const_generics))]
+#![cfg_attr(min, feature(min_const_generics))]
 #![allow(incomplete_features)]
-#![feature(const_fn)]
 
 struct Foo;
 
diff --git a/src/test/ui/feature-gates/feature-gate-asm.rs b/src/test/ui/feature-gates/feature-gate-asm.rs
index 753e924f004..59f04372fff 100644
--- a/src/test/ui/feature-gates/feature-gate-asm.rs
+++ b/src/test/ui/feature-gates/feature-gate-asm.rs
@@ -1,4 +1,4 @@
-// ignore-emscripten
+// only-x86_64
 
 fn main() {
     unsafe {
diff --git a/src/test/ui/feature-gates/feature-gate-asm2.rs b/src/test/ui/feature-gates/feature-gate-asm2.rs
index e9349acb643..aa63aff1c5e 100644
--- a/src/test/ui/feature-gates/feature-gate-asm2.rs
+++ b/src/test/ui/feature-gates/feature-gate-asm2.rs
@@ -1,4 +1,4 @@
-// ignore-emscripten
+// only-x86_64
 
 fn main() {
     unsafe {
diff --git a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr
index b2c96d3810f..eef465318a3 100644
--- a/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr
+++ b/src/test/ui/feature-gates/feature-gate-const_generics-ptr.stderr
@@ -4,8 +4,8 @@ error[E0658]: const generics are unstable
 LL | struct ConstFn<const F: fn()>;
    |                      ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error[E0658]: const generics are unstable
   --> $DIR/feature-gate-const_generics-ptr.rs:5:23
@@ -13,8 +13,8 @@ error[E0658]: const generics are unstable
 LL | struct ConstPtr<const P: *const u32>;
    |                       ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error: using function pointers as const generic parameters is forbidden
   --> $DIR/feature-gate-const_generics-ptr.rs:1:25
diff --git a/src/test/ui/feature-gates/feature-gate-const_generics.stderr b/src/test/ui/feature-gates/feature-gate-const_generics.stderr
index 02aa1f5a4d8..f80362252f9 100644
--- a/src/test/ui/feature-gates/feature-gate-const_generics.stderr
+++ b/src/test/ui/feature-gates/feature-gate-const_generics.stderr
@@ -4,8 +4,8 @@ error[E0658]: const generics are unstable
 LL | fn foo<const X: ()>() {}
    |              ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error[E0658]: const generics are unstable
   --> $DIR/feature-gate-const_generics.rs:3:18
@@ -13,8 +13,8 @@ error[E0658]: const generics are unstable
 LL | struct Foo<const X: usize>([(); X]);
    |                  ^
    |
-   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
-   = help: add `#![feature(const_generics)]` to the crate attributes to enable
+   = note: see issue #74878 <https://github.com/rust-lang/rust/issues/74878> for more information
+   = help: add `#![feature(min_const_generics)]` to the crate attributes to enable
 
 error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.rs b/src/test/ui/higher-rank-trait-bounds/issue-59311.rs
new file mode 100644
index 00000000000..1e1241c7f83
--- /dev/null
+++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.rs
@@ -0,0 +1,20 @@
+// Regression test for #59311. The test is taken from
+// rust-lang/rust/issues/71546#issuecomment-620638437
+// as they seem to have the same cause.
+
+// FIXME: It's not clear that this code ought to report
+// an error, but the regression test is here to ensure
+// that it does not ICE. See discussion on #74889 for details.
+
+pub trait T {
+    fn t<F: Fn()>(&self, _: F) {}
+}
+
+pub fn crash<V>(v: &V)
+where
+    for<'a> &'a V: T + 'static,
+{
+    v.t(|| {}); //~ ERROR: higher-ranked subtype error
+}
+
+fn main() {}
diff --git a/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr b/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr
new file mode 100644
index 00000000000..ca632629267
--- /dev/null
+++ b/src/test/ui/higher-rank-trait-bounds/issue-59311.stderr
@@ -0,0 +1,8 @@
+error: higher-ranked subtype error
+  --> $DIR/issue-59311.rs:17:9
+   |
+LL |     v.t(|| {});
+   |         ^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/issues/issue-40782.fixed b/src/test/ui/issues/issue-40782.fixed
index d61c248c6ec..305a9c3292a 100644
--- a/src/test/ui/issues/issue-40782.fixed
+++ b/src/test/ui/issues/issue-40782.fixed
@@ -3,4 +3,6 @@
 fn main() {
     for _i in 0..2 { //~ ERROR missing `in`
     }
+    for _i in 0..2 { //~ ERROR missing `in`
+    }
 }
diff --git a/src/test/ui/issues/issue-40782.rs b/src/test/ui/issues/issue-40782.rs
index 3688c69fbc6..43460ec1535 100644
--- a/src/test/ui/issues/issue-40782.rs
+++ b/src/test/ui/issues/issue-40782.rs
@@ -3,4 +3,6 @@
 fn main() {
     for _i 0..2 { //~ ERROR missing `in`
     }
+    for _i of 0..2 { //~ ERROR missing `in`
+    }
 }
diff --git a/src/test/ui/issues/issue-40782.stderr b/src/test/ui/issues/issue-40782.stderr
index 9d7776f32b3..81f419bf687 100644
--- a/src/test/ui/issues/issue-40782.stderr
+++ b/src/test/ui/issues/issue-40782.stderr
@@ -4,5 +4,11 @@ error: missing `in` in `for` loop
 LL |     for _i 0..2 {
    |           ^ help: try adding `in` here
 
-error: aborting due to previous error
+error: missing `in` in `for` loop
+  --> $DIR/issue-40782.rs:6:12
+   |
+LL |     for _i of 0..2 {
+   |            ^^ help: try using `in` here instead
+
+error: aborting due to 2 previous errors
 
diff --git a/src/test/ui/issues/issue-75283.rs b/src/test/ui/issues/issue-75283.rs
new file mode 100644
index 00000000000..d556132e47f
--- /dev/null
+++ b/src/test/ui/issues/issue-75283.rs
@@ -0,0 +1,6 @@
+extern "C" {
+    fn lol() { //~ ERROR incorrect function inside `extern` block
+        println!("");
+    }
+}
+fn main() {}
diff --git a/src/test/ui/issues/issue-75283.stderr b/src/test/ui/issues/issue-75283.stderr
new file mode 100644
index 00000000000..da3800affc0
--- /dev/null
+++ b/src/test/ui/issues/issue-75283.stderr
@@ -0,0 +1,18 @@
+error: incorrect function inside `extern` block
+  --> $DIR/issue-75283.rs:2:8
+   |
+LL |   extern "C" {
+   |   ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body
+LL |       fn lol() {
+   |  ________^^^___-
+   | |        |
+   | |        cannot have a body
+LL | |         println!("");
+LL | |     }
+   | |_____- help: remove the invalid body: `;`
+   |
+   = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block
+   = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/panics/issue-47429-short-backtraces.rs b/src/test/ui/panics/issue-47429-short-backtraces.rs
new file mode 100644
index 00000000000..1e8c38cc342
--- /dev/null
+++ b/src/test/ui/panics/issue-47429-short-backtraces.rs
@@ -0,0 +1,18 @@
+// Regression test for #47429: short backtraces were not terminating correctly
+
+// compile-flags: -O
+// run-fail
+// check-run-results
+// exec-env:RUST_BACKTRACE=1
+
+// ignore-msvc see #62897 and `backtrace-debuginfo.rs` test
+// ignore-android FIXME #17520
+// ignore-cloudabi spawning processes is not supported
+// ignore-openbsd no support for libbacktrace without filename
+// ignore-wasm no panic or subprocess support
+// ignore-emscripten no panic or subprocess support
+// ignore-sgx no subprocess support
+
+fn main() {
+    panic!()
+}
diff --git a/src/test/ui/panics/issue-47429-short-backtraces.run.stderr b/src/test/ui/panics/issue-47429-short-backtraces.run.stderr
new file mode 100644
index 00000000000..99ee26fe55e
--- /dev/null
+++ b/src/test/ui/panics/issue-47429-short-backtraces.run.stderr
@@ -0,0 +1,5 @@
+thread 'main' panicked at 'explicit panic', $DIR/issue-47429-short-backtraces.rs:17:5
+stack backtrace:
+   0: std::panicking::begin_panic
+   1: issue_47429_short_backtraces::main
+note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
diff --git a/src/test/ui/polymorphization/closure_in_upvar/fn.rs b/src/test/ui/polymorphization/closure_in_upvar/fn.rs
new file mode 100644
index 00000000000..b0b39dbd3df
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/fn.rs
@@ -0,0 +1,29 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+    let x = |_: ()| ();
+
+    // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+    // `x` that will differ for each instantiation despite polymorphisation of the varying
+    // argument.
+    let y = || x(());
+
+    // Consider `f` used in `foo`.
+    f();
+    // Use `y` so that it is visited in monomorphisation collection.
+    y();
+}
+
+fn entry_a() {
+    foo(|| ());
+}
+
+fn entry_b() {
+    foo(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs b/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs
new file mode 100644
index 00000000000..ba75f6c5a10
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/fnmut.rs
@@ -0,0 +1,34 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+    // Mutate an upvar from `x` so that it implements `FnMut`.
+    let mut outer = 3;
+    let mut x = |_: ()| {
+        outer = 4;
+        ()
+    };
+
+    // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+    // `x` that will differ for each instantiation despite polymorphisation of the varying
+    // argument.
+    let mut y = || x(());
+
+    // Consider `f` used in `foo`.
+    f();
+    // Use `y` so that it is visited in monomorphisation collection.
+    y();
+}
+
+fn entry_a() {
+    foo(|| ());
+}
+
+fn entry_b() {
+    foo(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs b/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs
new file mode 100644
index 00000000000..e9761ad0bcb
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/fnonce.rs
@@ -0,0 +1,34 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn foo(f: impl Fn()) {
+    // Move a non-copy type into `x` so that it implements `FnOnce`.
+    let outer = Vec::<u32>::new();
+    let x = move |_: ()| {
+        let inner = outer;
+        ()
+    };
+
+    // Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
+    // `x` that will differ for each instantiation despite polymorphisation of the varying
+    // argument.
+    let y = || x(());
+
+    // Consider `f` used in `foo`.
+    f();
+    // Use `y` so that it is visited in monomorphisation collection.
+    y();
+}
+
+fn entry_a() {
+    foo(|| ());
+}
+
+fn entry_b() {
+    foo(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/closure_in_upvar/other.rs b/src/test/ui/polymorphization/closure_in_upvar/other.rs
new file mode 100644
index 00000000000..7614aa83fcd
--- /dev/null
+++ b/src/test/ui/polymorphization/closure_in_upvar/other.rs
@@ -0,0 +1,38 @@
+// build-pass
+// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
+
+fn y_uses_f(f: impl Fn()) {
+    let x = |_: ()| ();
+
+    let y = || {
+        f();
+        x(());
+    };
+
+    f();
+    y();
+}
+
+fn x_uses_f(f: impl Fn()) {
+    let x = |_: ()| { f(); };
+
+    let y = || x(());
+
+    f();
+    y();
+}
+
+fn entry_a() {
+    x_uses_f(|| ());
+    y_uses_f(|| ());
+}
+
+fn entry_b() {
+    x_uses_f(|| ());
+    y_uses_f(|| ());
+}
+
+fn main() {
+    entry_a();
+    entry_b();
+}
diff --git a/src/test/ui/polymorphization/promoted-function-1.rs b/src/test/ui/polymorphization/promoted-function-1.rs
new file mode 100644
index 00000000000..2cd02673442
--- /dev/null
+++ b/src/test/ui/polymorphization/promoted-function-1.rs
@@ -0,0 +1,12 @@
+// build-fail
+// compile-flags: -Zpolymorphize=on
+#![crate_type = "lib"]
+#![feature(rustc_attrs)]
+
+fn foo<'a>(_: &'a ()) {}
+
+#[rustc_polymorphize_error]
+pub fn test<T>() {
+    //~^ ERROR item has unused generic parameters
+    foo(&());
+}
diff --git a/src/test/ui/polymorphization/promoted-function-1.stderr b/src/test/ui/polymorphization/promoted-function-1.stderr
new file mode 100644
index 00000000000..fcbb8694923
--- /dev/null
+++ b/src/test/ui/polymorphization/promoted-function-1.stderr
@@ -0,0 +1,8 @@
+error: item has unused generic parameters
+  --> $DIR/promoted-function-1.rs:9:8
+   |
+LL | pub fn test<T>() {
+   |        ^^^^ - generic parameter `T` is unused
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/polymorphization/promoted-function-2.rs b/src/test/ui/polymorphization/promoted-function-2.rs
new file mode 100644
index 00000000000..2831f861f55
--- /dev/null
+++ b/src/test/ui/polymorphization/promoted-function-2.rs
@@ -0,0 +1,16 @@
+// build-fail
+// compile-flags:-Zpolymorphize=on
+#![crate_type = "lib"]
+#![feature(lazy_normalization_consts, rustc_attrs)]
+//~^ WARN the feature `lazy_normalization_consts` is incomplete
+
+#[rustc_polymorphize_error]
+fn test<T>() {
+    //~^ ERROR item has unused generic parameters
+    let x = [0; 3 + 4];
+}
+
+pub fn caller() {
+    test::<String>();
+    test::<Vec<String>>();
+}
diff --git a/src/test/ui/polymorphization/promoted-function-2.stderr b/src/test/ui/polymorphization/promoted-function-2.stderr
new file mode 100644
index 00000000000..38d4808c48c
--- /dev/null
+++ b/src/test/ui/polymorphization/promoted-function-2.stderr
@@ -0,0 +1,17 @@
+warning: the feature `lazy_normalization_consts` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/promoted-function-2.rs:4:12
+   |
+LL | #![feature(lazy_normalization_consts, rustc_attrs)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #72219 <https://github.com/rust-lang/rust/issues/72219> for more information
+
+error: item has unused generic parameters
+  --> $DIR/promoted-function-2.rs:8:4
+   |
+LL | fn test<T>() {
+   |    ^^^^ - generic parameter `T` is unused
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/test/ui/polymorphization/promoted-function.rs b/src/test/ui/polymorphization/promoted-function.rs
index 0d3af7a89c2..a56a8e70e4c 100644
--- a/src/test/ui/polymorphization/promoted-function.rs
+++ b/src/test/ui/polymorphization/promoted-function.rs
@@ -1,4 +1,6 @@
 // run-pass
+// compile-flags:-Zpolymorphize=on
+
 fn fop<T>() {}
 
 fn bar<T>() -> &'static fn() {
diff --git a/src/test/ui/polymorphization/unsized_cast.rs b/src/test/ui/polymorphization/unsized_cast.rs
index b8facc16070..b803fec2ccf 100644
--- a/src/test/ui/polymorphization/unsized_cast.rs
+++ b/src/test/ui/polymorphization/unsized_cast.rs
@@ -17,6 +17,7 @@ fn foo<T: Default>() {
 fn foo2<T: Default>() {
     let _: T = Default::default();
     (|| {
+        //~^ ERROR item has unused generic parameters
         let call: extern "rust-call" fn(_, _) = Fn::call;
         call(&|| {}, ());
         //~^ ERROR item has unused generic parameters
diff --git a/src/test/ui/polymorphization/unsized_cast.stderr b/src/test/ui/polymorphization/unsized_cast.stderr
index d4727acca9a..b51cc5c719f 100644
--- a/src/test/ui/polymorphization/unsized_cast.stderr
+++ b/src/test/ui/polymorphization/unsized_cast.stderr
@@ -17,7 +17,7 @@ LL |     (|| Box::new(|| {}) as Box<dyn Fn()>)();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: item has unused generic parameters
-  --> $DIR/unsized_cast.rs:21:15
+  --> $DIR/unsized_cast.rs:22:15
    |
 LL | fn foo2<T: Default>() {
    |         - generic parameter `T` is unused
@@ -25,5 +25,19 @@ LL | fn foo2<T: Default>() {
 LL |         call(&|| {}, ());
    |               ^^^^^
 
-error: aborting due to 3 previous errors
+error: item has unused generic parameters
+  --> $DIR/unsized_cast.rs:19:5
+   |
+LL |   fn foo2<T: Default>() {
+   |           - generic parameter `T` is unused
+LL |       let _: T = Default::default();
+LL | /     (|| {
+LL | |
+LL | |         let call: extern "rust-call" fn(_, _) = Fn::call;
+LL | |         call(&|| {}, ());
+LL | |
+LL | |     })();
+   | |______^
+
+error: aborting due to 4 previous errors
 
diff --git a/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs
new file mode 100644
index 00000000000..4601a3d4741
--- /dev/null
+++ b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.rs
@@ -0,0 +1,10 @@
+// Regression test for issue #75062
+// Tests that we don't ICE on a privacy error for a fieldless tuple struct.
+
+mod foo {
+    struct Bar();
+}
+
+fn main() {
+    foo::Bar(); //~ ERROR tuple struct
+}
diff --git a/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr
new file mode 100644
index 00000000000..14a12003e2d
--- /dev/null
+++ b/src/test/ui/privacy/issue-75062-fieldless-tuple-struct.stderr
@@ -0,0 +1,15 @@
+error[E0603]: tuple struct `Bar` is private
+  --> $DIR/issue-75062-fieldless-tuple-struct.rs:9:10
+   |
+LL |     foo::Bar();
+   |          ^^^ private tuple struct
+   |
+note: the tuple struct `Bar` is defined here
+  --> $DIR/issue-75062-fieldless-tuple-struct.rs:5:5
+   |
+LL |     struct Bar();
+   |     ^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0603`.
diff --git a/src/test/ui/proc-macro/doc-comment-preserved.rs b/src/test/ui/proc-macro/doc-comment-preserved.rs
new file mode 100644
index 00000000000..ed8ca99bd2c
--- /dev/null
+++ b/src/test/ui/proc-macro/doc-comment-preserved.rs
@@ -0,0 +1,24 @@
+// check-pass
+// compile-flags: -Z span-debug
+// aux-build:test-macros.rs
+
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
+#[macro_use]
+extern crate test_macros;
+
+print_bang! {
+
+/**
+*******
+* DOC *
+* DOC *
+* DOC *
+*******
+*/
+pub struct S;
+
+}
+
+fn main() {}
diff --git a/src/test/ui/proc-macro/doc-comment-preserved.stdout b/src/test/ui/proc-macro/doc-comment-preserved.stdout
new file mode 100644
index 00000000000..f4160d7da80
--- /dev/null
+++ b/src/test/ui/proc-macro/doc-comment-preserved.stdout
@@ -0,0 +1,54 @@
+PRINT-BANG INPUT (DISPLAY): /**
+*******
+* DOC *
+* DOC *
+* DOC *
+*******
+*/
+ pub struct S ;
+PRINT-BANG RE-COLLECTED (DISPLAY): #[doc = "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n"] pub struct S ;
+PRINT-BANG INPUT (DEBUG): TokenStream [
+    Punct {
+        ch: '#',
+        spacing: Alone,
+        span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0),
+    },
+    Group {
+        delimiter: Bracket,
+        stream: TokenStream [
+            Ident {
+                ident: "doc",
+                span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0),
+            },
+            Punct {
+                ch: '=',
+                spacing: Alone,
+                span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0),
+            },
+            Literal {
+                kind: Str,
+                symbol: "\n*******\n* DOC *\n* DOC *\n* DOC *\n*******\n",
+                suffix: None,
+                span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0),
+            },
+        ],
+        span: $DIR/doc-comment-preserved.rs:13:1: 19:3 (#0),
+    },
+    Ident {
+        ident: "pub",
+        span: $DIR/doc-comment-preserved.rs:20:1: 20:4 (#0),
+    },
+    Ident {
+        ident: "struct",
+        span: $DIR/doc-comment-preserved.rs:20:5: 20:11 (#0),
+    },
+    Ident {
+        ident: "S",
+        span: $DIR/doc-comment-preserved.rs:20:12: 20:13 (#0),
+    },
+    Punct {
+        ch: ';',
+        spacing: Alone,
+        span: $DIR/doc-comment-preserved.rs:20:13: 20:14 (#0),
+    },
+]
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.rs b/src/test/ui/proc-macro/dollar-crate-issue-57089.rs
index 9ce90e42069..27bfa099f21 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-57089.rs
+++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.rs
@@ -1,11 +1,10 @@
 // check-pass
 // edition:2018
+// compile-flags: -Z span-debug
 // aux-build:test-macros.rs
 
-// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
-// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
-// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
-// normalize-stdout-test "#\d+" -> "#CTXT"
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
 
 #[macro_use]
 extern crate test_macros;
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
index c36c7560387..9a5afbd604f 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
+++ b/src/test/ui/proc-macro/dollar-crate-issue-57089.stdout
@@ -2,79 +2,79 @@ PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:17:13: 17:19 (#3),
     },
     Ident {
         ident: "M",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:17:20: 17:21 (#3),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:17:22: 17:28 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:17:28: 17:30 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:17:28: 17:30 (#3),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:17:30: 17:31 (#3),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:17:21: 17:32 (#3),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:17:32: 17:33 (#3),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:21:9: 21:15 (#3),
     },
     Ident {
         ident: "A",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:21:16: 21:17 (#3),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:21:18: 21:24 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:21:24: 21:26 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:21:24: 21:26 (#3),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-57089.rs:21:26: 21:27 (#3),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:21:17: 21:28 (#3),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-57089.rs:21:28: 21:29 (#3),
     },
 ]
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.rs b/src/test/ui/proc-macro/dollar-crate-issue-62325.rs
index 2a9ff4c20cd..d828fb9fd80 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-62325.rs
+++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.rs
@@ -1,12 +1,12 @@
 // check-pass
 // edition:2018
+// compile-flags: -Z span-debug
 // aux-build:test-macros.rs
 // aux-build:dollar-crate-external.rs
 
-// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
-// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
-// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
-// normalize-stdout-test "#\d+" -> "#CTXT"
+
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
 
 #[macro_use]
 extern crate test_macros;
diff --git a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
index 456940b89da..fc62eadd313 100644
--- a/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
+++ b/src/test/ui/proc-macro/dollar-crate-issue-62325.stdout
@@ -2,109 +2,109 @@ PRINT-ATTR INPUT (DISPLAY): struct A(identity ! ($crate :: S)) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-62325.rs:19:5: 19:11 (#3),
     },
     Ident {
         ident: "A",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-62325.rs:19:12: 19:13 (#3),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "identity",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-62325.rs:19:14: 19:22 (#3),
             },
             Punct {
                 ch: '!',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-62325.rs:19:22: 19:23 (#3),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
                         ident: "$crate",
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/dollar-crate-issue-62325.rs:19:24: 19:30 (#3),
                     },
                     Punct {
                         ch: ':',
                         spacing: Joint,
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/dollar-crate-issue-62325.rs:19:30: 19:32 (#3),
                     },
                     Punct {
                         ch: ':',
                         spacing: Alone,
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/dollar-crate-issue-62325.rs:19:30: 19:32 (#3),
                     },
                     Ident {
                         ident: "S",
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/dollar-crate-issue-62325.rs:19:32: 19:33 (#3),
                     },
                 ],
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate-issue-62325.rs:19:23: 19:34 (#3),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-62325.rs:19:13: 19:35 (#3),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate-issue-62325.rs:19:35: 19:36 (#3),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct B(identity ! ($crate :: S)) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:21:5: 21:11 (#10),
     },
     Ident {
         ident: "B",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:21:12: 21:13 (#10),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "identity",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:21:14: 21:22 (#10),
             },
             Punct {
                 ch: '!',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:21:22: 21:23 (#10),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [
                     Ident {
                         ident: "$crate",
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/auxiliary/dollar-crate-external.rs:21:24: 21:30 (#10),
                     },
                     Punct {
                         ch: ':',
                         spacing: Joint,
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/auxiliary/dollar-crate-external.rs:21:30: 21:32 (#10),
                     },
                     Punct {
                         ch: ':',
                         spacing: Alone,
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/auxiliary/dollar-crate-external.rs:21:30: 21:32 (#10),
                     },
                     Ident {
                         ident: "S",
-                        span: #CTXT bytes(LO..HI),
+                        span: $DIR/auxiliary/dollar-crate-external.rs:21:32: 21:33 (#10),
                     },
                 ],
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:21:23: 21:34 (#10),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:21:13: 21:35 (#10),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:21:35: 21:36 (#10),
     },
 ]
diff --git a/src/test/ui/proc-macro/dollar-crate.rs b/src/test/ui/proc-macro/dollar-crate.rs
index 1a5223d3d41..ac27dfa1aeb 100644
--- a/src/test/ui/proc-macro/dollar-crate.rs
+++ b/src/test/ui/proc-macro/dollar-crate.rs
@@ -1,12 +1,11 @@
 // check-pass
 // edition:2018
+// compile-flags: -Z span-debug
 // aux-build:test-macros.rs
 // aux-build:dollar-crate-external.rs
 
-// Anonymize unstable non-dummy spans while still showing dummy spans `0..0`.
-// normalize-stdout-test "bytes\([^0]\w*\.\.(\w+)\)" -> "bytes(LO..$1)"
-// normalize-stdout-test "bytes\((\w+)\.\.[^0]\w*\)" -> "bytes($1..HI)"
-// normalize-stdout-test "#\d+" -> "#CTXT"
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
 
 #[macro_use]
 extern crate test_macros;
diff --git a/src/test/ui/proc-macro/dollar-crate.stdout b/src/test/ui/proc-macro/dollar-crate.stdout
index deef102afb2..72fc6588583 100644
--- a/src/test/ui/proc-macro/dollar-crate.stdout
+++ b/src/test/ui/proc-macro/dollar-crate.stdout
@@ -2,239 +2,239 @@ PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:20:17: 20:23 (#3),
     },
     Ident {
         ident: "M",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:20:24: 20:25 (#3),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:20:26: 20:32 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:20:32: 20:34 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:20:32: 20:34 (#3),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:20:34: 20:35 (#3),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:20:25: 20:36 (#3),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:20:36: 20:37 (#3),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:24:13: 24:19 (#3),
     },
     Ident {
         ident: "A",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:24:20: 24:21 (#3),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:24:22: 24:28 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:24:28: 24:30 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:24:28: 24:30 (#3),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:24:30: 24:31 (#3),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:24:21: 24:32 (#3),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:24:32: 24:33 (#3),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct D($crate :: S) ;
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:27:13: 27:19 (#3),
     },
     Ident {
         ident: "D",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:27:20: 27:21 (#3),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:27:22: 27:28 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:27:28: 27:30 (#3),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:27:28: 27:30 (#3),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/dollar-crate.rs:27:30: 27:31 (#3),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:27:21: 27:32 (#3),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/dollar-crate.rs:27:32: 27:33 (#3),
     },
 ]
 PRINT-BANG INPUT (DISPLAY): struct M($crate :: S) ;
 PRINT-BANG INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:7:13: 7:19 (#13),
     },
     Ident {
         ident: "M",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:7:20: 7:21 (#13),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:7:22: 7:28 (#13),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:7:28: 7:30 (#13),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:7:28: 7:30 (#13),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:7:30: 7:31 (#13),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:7:21: 7:32 (#13),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:7:32: 7:33 (#13),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): struct A($crate :: S) ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:11:9: 11:15 (#13),
     },
     Ident {
         ident: "A",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:11:16: 11:17 (#13),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:11:18: 11:24 (#13),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:11:24: 11:26 (#13),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:11:24: 11:26 (#13),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:11:26: 11:27 (#13),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:11:17: 11:28 (#13),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:11:28: 11:29 (#13),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct D($crate :: S) ;
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:14:9: 14:15 (#13),
     },
     Ident {
         ident: "D",
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:14:16: 14:17 (#13),
     },
     Group {
         delimiter: Parenthesis,
         stream: TokenStream [
             Ident {
                 ident: "$crate",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:14:18: 14:24 (#13),
             },
             Punct {
                 ch: ':',
                 spacing: Joint,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:14:24: 14:26 (#13),
             },
             Punct {
                 ch: ':',
                 spacing: Alone,
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:14:24: 14:26 (#13),
             },
             Ident {
                 ident: "S",
-                span: #CTXT bytes(LO..HI),
+                span: $DIR/auxiliary/dollar-crate-external.rs:14:26: 14:27 (#13),
             },
         ],
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:14:17: 14:28 (#13),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(LO..HI),
+        span: $DIR/auxiliary/dollar-crate-external.rs:14:28: 14:29 (#13),
     },
 ]
diff --git a/src/test/ui/proc-macro/input-interpolated.rs b/src/test/ui/proc-macro/input-interpolated.rs
index 41c829d9d88..5e49e330cac 100644
--- a/src/test/ui/proc-macro/input-interpolated.rs
+++ b/src/test/ui/proc-macro/input-interpolated.rs
@@ -1,9 +1,12 @@
 // Check what token streams proc macros see when interpolated tokens are passed to them as input.
 
 // check-pass
-// normalize-stdout-test "#\d+" -> "#CTXT"
+// edition:2018
 // aux-build:test-macros.rs
 
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
 #[macro_use]
 extern crate test_macros;
 
diff --git a/src/test/ui/proc-macro/input-interpolated.stdout b/src/test/ui/proc-macro/input-interpolated.stdout
index d98f52249a7..9cf33ba4a9d 100644
--- a/src/test/ui/proc-macro/input-interpolated.stdout
+++ b/src/test/ui/proc-macro/input-interpolated.stdout
@@ -5,61 +5,61 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         stream: TokenStream [
             Ident {
                 ident: "A",
-                span: #CTXT bytes(445..446),
+                span: #0 bytes(503..504),
             },
         ],
-        span: #CTXT bytes(312..314),
+        span: #3 bytes(370..372),
     },
 ]
 PRINT-ATTR INPUT (DISPLAY): const A : u8 = 0 ;
 PRINT-ATTR INPUT (DEBUG): TokenStream [
     Ident {
         ident: "const",
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Ident {
         ident: "A",
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Punct {
         ch: ':',
         spacing: Alone,
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Ident {
         ident: "u8",
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Punct {
         ch: '=',
         spacing: Alone,
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Literal {
         kind: Integer,
         symbol: "0",
         suffix: None,
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Punct {
         ch: ';',
         spacing: Alone,
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
 ]
 PRINT-DERIVE INPUT (DISPLAY): struct A { }
 PRINT-DERIVE INPUT (DEBUG): TokenStream [
     Ident {
         ident: "struct",
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Ident {
         ident: "A",
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
     Group {
         delimiter: Brace,
         stream: TokenStream [],
-        span: #CTXT bytes(0..0),
+        span: #0 bytes(0..0),
     },
 ]
diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.rs b/src/test/ui/proc-macro/meta-macro-hygiene.rs
index 4b1787453cb..c11cf42956f 100644
--- a/src/test/ui/proc-macro/meta-macro-hygiene.rs
+++ b/src/test/ui/proc-macro/meta-macro-hygiene.rs
@@ -1,13 +1,16 @@
 // aux-build:make-macro.rs
 // aux-build:meta-macro.rs
 // edition:2018
-// compile-flags: -Z span-debug -Z macro-backtrace
+// compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene
 // check-pass
-// normalize-stdout-test "#\d+" -> "#CTXT"
 // normalize-stdout-test "\d+#" -> "0#"
 //
 // We don't care about symbol ids, so we set them all to 0
 // in the stdout
+
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
 extern crate meta_macro;
 
 macro_rules! produce_it {
diff --git a/src/test/ui/proc-macro/meta-macro-hygiene.stdout b/src/test/ui/proc-macro/meta-macro-hygiene.stdout
index e162bdd7fc0..dfd3e6a839a 100644
--- a/src/test/ui/proc-macro/meta-macro-hygiene.stdout
+++ b/src/test/ui/proc-macro/meta-macro-hygiene.stdout
@@ -1,3 +1,63 @@
-Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT)
-Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:20:37: 20:43 (#CTXT) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:20:43: 20:45 (#CTXT) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:20:43: 20:45 (#CTXT) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:20:45: 20:50 (#CTXT) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:20:50: 20:51 (#CTXT) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:20:51: 20:53 (#CTXT) }]
-Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT) }]
+Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4)
+Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:23:37: 23:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:23:43: 23:45 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:23:43: 23:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:23:45: 23:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:23:50: 23:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:23:51: 23:53 (#3) }]
+Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#4) }]
+#![feature /* 0#0 */(prelude_import)]
+// aux-build:make-macro.rs
+// aux-build:meta-macro.rs
+// edition:2018
+// compile-flags: -Z span-debug -Z macro-backtrace -Z unpretty=expanded,hygiene
+// check-pass
+// normalize-stdout-test "\d+#" -> "0#"
+//
+// We don't care about symbol ids, so we set them all to 0
+// in the stdout
+
+#![no_std /* 0#0 */]
+#[prelude_import /* 0#1 */]
+use core /* 0#1 */::prelude /* 0#1 */::v1 /* 0#1 */::*;
+#[macro_use /* 0#1 */]
+extern crate core /* 0#1 */;
+#[macro_use /* 0#1 */]
+extern crate compiler_builtins /* 0#1 */;
+// Don't load unnecessary hygiene information from std
+extern crate std /* 0#0 */;
+
+extern crate meta_macro /* 0#0 */;
+
+macro_rules! produce_it
+    /*
+    0#0
+    */ {
+    () =>
+    {
+        meta_macro :: print_def_site ! ($ crate :: dummy ! ()) ;
+        // `print_def_site!` will respan the `$crate` identifier
+        // with `Span::def_site()`. This should cause it to resolve
+        // relative to `meta_macro`, *not* `make_macro` (despite
+        // the fact that that `print_def_site` is produced by
+        // a `macro_rules!` macro in `make_macro`).
+    }
+}
+
+fn main /* 0#0 */() { }
+
+/*
+Expansions:
+0: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Root
+1: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: AstPass(StdImports)
+2: parent: ExpnId(0), call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "produce_it")
+3: parent: ExpnId(2), call_site_ctxt: #3, def_site_ctxt: #0, kind: Macro(Bang, "meta_macro::print_def_site")
+4: parent: ExpnId(3), call_site_ctxt: #4, def_site_ctxt: #0, kind: Macro(Bang, "$crate::dummy")
+
+SyntaxContexts:
+#0: parent: #0, outer_mark: (ExpnId(0), Opaque)
+#1: parent: #0, outer_mark: (ExpnId(1), Opaque)
+#2: parent: #0, outer_mark: (ExpnId(1), Transparent)
+#3: parent: #0, outer_mark: (ExpnId(2), SemiTransparent)
+#4: parent: #0, outer_mark: (ExpnId(3), Opaque)
+#5: parent: #3, outer_mark: (ExpnId(3), Transparent)
+#6: parent: #0, outer_mark: (ExpnId(3), SemiTransparent)
+#7: parent: #0, outer_mark: (ExpnId(4), Opaque)
+#8: parent: #4, outer_mark: (ExpnId(4), Transparent)
+#9: parent: #4, outer_mark: (ExpnId(4), SemiTransparent)
+*/
diff --git a/src/test/ui/proc-macro/meta-macro.rs b/src/test/ui/proc-macro/meta-macro.rs
index 579e232c0d9..dbac90382d1 100644
--- a/src/test/ui/proc-macro/meta-macro.rs
+++ b/src/test/ui/proc-macro/meta-macro.rs
@@ -2,9 +2,11 @@
 // aux-build:meta-macro.rs
 // edition:2018
 // compile-flags: -Z span-debug
-// normalize-stdout-test "#\d+" -> "#CTXT"
 // run-pass
 
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
 extern crate meta_macro;
 
 fn main() {
diff --git a/src/test/ui/proc-macro/meta-macro.stdout b/src/test/ui/proc-macro/meta-macro.stdout
index a9847a25d92..71aa565f4dd 100644
--- a/src/test/ui/proc-macro/meta-macro.stdout
+++ b/src/test/ui/proc-macro/meta-macro.stdout
@@ -1,3 +1,3 @@
-Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#CTXT)
+Def site: $DIR/auxiliary/make-macro.rs:7:9: 16:10 (#3)
 Input: TokenStream []
 Respanned: TokenStream []
diff --git a/src/test/ui/proc-macro/nested-macro-rules.rs b/src/test/ui/proc-macro/nested-macro-rules.rs
index 62c3dd84ce1..2fef0e5fad0 100644
--- a/src/test/ui/proc-macro/nested-macro-rules.rs
+++ b/src/test/ui/proc-macro/nested-macro-rules.rs
@@ -2,9 +2,11 @@
 // aux-build:nested-macro-rules.rs
 // aux-build:test-macros.rs
 // compile-flags: -Z span-debug
-// normalize-stdout-test "#\d+" -> "#CTXT"
 // edition:2018
 
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
 extern crate nested_macro_rules;
 extern crate test_macros;
 
diff --git a/src/test/ui/proc-macro/nested-macro-rules.stdout b/src/test/ui/proc-macro/nested-macro-rules.stdout
index 337b9863def..7feea56c5d8 100644
--- a/src/test/ui/proc-macro/nested-macro-rules.stdout
+++ b/src/test/ui/proc-macro/nested-macro-rules.stdout
@@ -5,10 +5,10 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         stream: TokenStream [
             Ident {
                 ident: "FirstStruct",
-                span: $DIR/auxiliary/nested-macro-rules.rs:15:14: 15:25 (#CTXT),
+                span: $DIR/auxiliary/nested-macro-rules.rs:15:14: 15:25 (#5),
             },
         ],
-        span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#CTXT),
+        span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#4),
     },
 ]
 PRINT-BANG INPUT (DISPLAY): SecondStruct
@@ -18,9 +18,9 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         stream: TokenStream [
             Ident {
                 ident: "SecondStruct",
-                span: $DIR/nested-macro-rules.rs:19:38: 19:50 (#CTXT),
+                span: $DIR/nested-macro-rules.rs:21:38: 21:50 (#11),
             },
         ],
-        span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#CTXT),
+        span: $DIR/auxiliary/nested-macro-rules.rs:9:27: 9:32 (#10),
     },
 ]
diff --git a/src/test/ui/proc-macro/nodelim-groups.rs b/src/test/ui/proc-macro/nodelim-groups.rs
index 1dc8796de90..db2a879f405 100644
--- a/src/test/ui/proc-macro/nodelim-groups.rs
+++ b/src/test/ui/proc-macro/nodelim-groups.rs
@@ -1,11 +1,13 @@
 // run-pass
 // aux-build:test-macros.rs
 // compile-flags: -Z span-debug
-// normalize-stdout-test "#\d+" -> "#CTXT"
 // edition:2018
 //
 // Tests the pretty-printing behavior of inserting `NoDelim` groups
 
+#![no_std] // Don't load unnecessary hygiene information from std
+extern crate std;
+
 extern crate test_macros;
 use test_macros::print_bang_consume;
 
diff --git a/src/test/ui/proc-macro/nodelim-groups.stdout b/src/test/ui/proc-macro/nodelim-groups.stdout
index 79cdf2b53b5..2fcd41f6da0 100644
--- a/src/test/ui/proc-macro/nodelim-groups.stdout
+++ b/src/test/ui/proc-macro/nodelim-groups.stdout
@@ -4,7 +4,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         kind: Str,
         symbol: "hi",
         suffix: None,
-        span: $DIR/nodelim-groups.rs:14:42: 14:46 (#CTXT),
+        span: $DIR/nodelim-groups.rs:16:42: 16:46 (#3),
     },
     Group {
         delimiter: None,
@@ -13,12 +13,12 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
                 kind: Integer,
                 symbol: "1",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:18:16: 18:17 (#CTXT),
+                span: $DIR/nodelim-groups.rs:20:16: 20:17 (#0),
             },
             Punct {
                 ch: '+',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:18:18: 18:19 (#CTXT),
+                span: $DIR/nodelim-groups.rs:20:18: 20:19 (#0),
             },
             Group {
                 delimiter: Parenthesis,
@@ -27,24 +27,24 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
                         kind: Integer,
                         symbol: "25",
                         suffix: None,
-                        span: $DIR/nodelim-groups.rs:18:21: 18:23 (#CTXT),
+                        span: $DIR/nodelim-groups.rs:20:21: 20:23 (#0),
                     },
                 ],
-                span: $DIR/nodelim-groups.rs:18:20: 18:24 (#CTXT),
+                span: $DIR/nodelim-groups.rs:20:20: 20:24 (#0),
             },
             Punct {
                 ch: '+',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:18:25: 18:26 (#CTXT),
+                span: $DIR/nodelim-groups.rs:20:25: 20:26 (#0),
             },
             Literal {
                 kind: Integer,
                 symbol: "1",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:18:27: 18:28 (#CTXT),
+                span: $DIR/nodelim-groups.rs:20:27: 20:28 (#0),
             },
         ],
-        span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+        span: $DIR/nodelim-groups.rs:16:47: 16:51 (#3),
     },
     Group {
         delimiter: Parenthesis,
@@ -53,21 +53,21 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
                 kind: Integer,
                 symbol: "1",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:14:53: 14:54 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:53: 16:54 (#3),
             },
             Punct {
                 ch: '+',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:14:55: 14:56 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:55: 16:56 (#3),
             },
             Literal {
                 kind: Integer,
                 symbol: "1",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:14:57: 14:58 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:57: 16:58 (#3),
             },
         ],
-        span: $DIR/nodelim-groups.rs:14:52: 14:59 (#CTXT),
+        span: $DIR/nodelim-groups.rs:16:52: 16:59 (#3),
     },
 ]
 PRINT-BANG INPUT (DISPLAY): "hi" "hello".len() + "world".len() (1 + 1)
@@ -77,7 +77,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
         kind: Str,
         symbol: "hi",
         suffix: None,
-        span: $DIR/nodelim-groups.rs:14:42: 14:46 (#CTXT),
+        span: $DIR/nodelim-groups.rs:16:42: 16:46 (#8),
     },
     Group {
         delimiter: None,
@@ -86,49 +86,49 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
                 kind: Str,
                 symbol: "hello",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Punct {
                 ch: '.',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Ident {
                 ident: "len",
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [],
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Punct {
                 ch: '+',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Literal {
                 kind: Str,
                 symbol: "world",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Punct {
                 ch: '.',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Ident {
                 ident: "len",
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
             Group {
                 delimiter: Parenthesis,
                 stream: TokenStream [],
-                span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
             },
         ],
-        span: $DIR/nodelim-groups.rs:14:47: 14:51 (#CTXT),
+        span: $DIR/nodelim-groups.rs:16:47: 16:51 (#8),
     },
     Group {
         delimiter: Parenthesis,
@@ -137,20 +137,20 @@ PRINT-BANG INPUT (DEBUG): TokenStream [
                 kind: Integer,
                 symbol: "1",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:14:53: 14:54 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:53: 16:54 (#8),
             },
             Punct {
                 ch: '+',
                 spacing: Alone,
-                span: $DIR/nodelim-groups.rs:14:55: 14:56 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:55: 16:56 (#8),
             },
             Literal {
                 kind: Integer,
                 symbol: "1",
                 suffix: None,
-                span: $DIR/nodelim-groups.rs:14:57: 14:58 (#CTXT),
+                span: $DIR/nodelim-groups.rs:16:57: 16:58 (#8),
             },
         ],
-        span: $DIR/nodelim-groups.rs:14:52: 14:59 (#CTXT),
+        span: $DIR/nodelim-groups.rs:16:52: 16:59 (#8),
     },
 ]
diff --git a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
index be55890c08c..7f197a238e5 100644
--- a/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
+++ b/src/test/ui/recursion/issue-26548-recursion-via-normalize.stderr
@@ -1,7 +1,7 @@
-error[E0391]: cycle detected when computing layout of `std::option::Option<S>`
+error[E0391]: cycle detected when computing layout of `S`
    |
-   = note: ...which requires computing layout of `S`...
-   = note: ...which again requires computing layout of `std::option::Option<S>`, completing the cycle
+   = note: ...which requires computing layout of `std::option::Option<S>`...
+   = note: ...which again requires computing layout of `S`, completing the cycle
 note: cycle used when optimizing MIR for `main`
   --> $DIR/issue-26548-recursion-via-normalize.rs:15:1
    |
diff --git a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs
index a323bd9e82b..8c436841b44 100644
--- a/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs
+++ b/src/test/ui/simd/simd-intrinsic-generic-bitmask.rs
@@ -2,6 +2,7 @@
 #![allow(non_camel_case_types)]
 
 // ignore-emscripten
+// ignore-endian-big behavior of simd_bitmask is endian-specific
 
 // Test that the simd_bitmask intrinsic produces correct results.
 
diff --git a/src/test/ui/simd/simd-intrinsic-generic-select.rs b/src/test/ui/simd/simd-intrinsic-generic-select.rs
index 22bda4fc9d9..dc9ec5d2760 100644
--- a/src/test/ui/simd/simd-intrinsic-generic-select.rs
+++ b/src/test/ui/simd/simd-intrinsic-generic-select.rs
@@ -2,10 +2,7 @@
 #![allow(non_camel_case_types)]
 
 // ignore-emscripten
-// ignore-mips       behavior of simd_select_bitmask is endian-specific
-// ignore-mips64     behavior of simd_select_bitmask is endian-specific
-// ignore-powerpc    behavior of simd_select_bitmask is endian-specific
-// ignore-powerpc64  behavior of simd_select_bitmask is endian-specific
+// ignore-endian-big behavior of simd_select_bitmask is endian-specific
 
 // Test that the simd_select intrinsics produces correct results.
 
diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs
index e87c33d1b09..6ce36fd2360 100644
--- a/src/tools/clippy/clippy_lints/src/doc.rs
+++ b/src/tools/clippy/clippy_lints/src/doc.rs
@@ -2,6 +2,7 @@ use crate::utils::{implements_trait, is_entrypoint_fn, is_type_diagnostic_item,
 use if_chain::if_chain;
 use itertools::Itertools;
 use rustc_ast::ast::{AttrKind, Attribute};
+use rustc_ast::token::CommentKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -249,7 +250,7 @@ fn lint_for_missing_headers<'tcx>(
     }
 }
 
-/// Cleanup documentation decoration (`///` and such).
+/// Cleanup documentation decoration.
 ///
 /// We can't use `rustc_ast::attr::AttributeMethods::with_desugared_doc` or
 /// `rustc_ast::parse::lexer::comments::strip_doc_comment_decoration` because we
@@ -257,54 +258,45 @@ fn lint_for_missing_headers<'tcx>(
 /// the spans but this function is inspired from the later.
 #[allow(clippy::cast_possible_truncation)]
 #[must_use]
-pub fn strip_doc_comment_decoration(comment: &str, span: Span) -> (String, Vec<(usize, Span)>) {
+pub fn strip_doc_comment_decoration(doc: &str, comment_kind: CommentKind, span: Span) -> (String, Vec<(usize, Span)>) {
     // one-line comments lose their prefix
-    const ONELINERS: &[&str] = &["///!", "///", "//!", "//"];
-    for prefix in ONELINERS {
-        if comment.starts_with(*prefix) {
-            let doc = &comment[prefix.len()..];
-            let mut doc = doc.to_owned();
-            doc.push('\n');
-            return (
-                doc.to_owned(),
-                vec![(doc.len(), span.with_lo(span.lo() + BytePos(prefix.len() as u32)))],
-            );
-        }
+    if comment_kind == CommentKind::Line {
+        let mut doc = doc.to_owned();
+        doc.push('\n');
+        let len = doc.len();
+        // +3 skips the opening delimiter
+        return (doc, vec![(len, span.with_lo(span.lo() + BytePos(3)))]);
     }
 
-    if comment.starts_with("/*") {
-        let doc = &comment[3..comment.len() - 2];
-        let mut sizes = vec![];
-        let mut contains_initial_stars = false;
-        for line in doc.lines() {
-            let offset = line.as_ptr() as usize - comment.as_ptr() as usize;
-            debug_assert_eq!(offset as u32 as usize, offset);
-            contains_initial_stars |= line.trim_start().starts_with('*');
-            // +1 for the newline
-            sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(offset as u32))));
-        }
-        if !contains_initial_stars {
-            return (doc.to_string(), sizes);
-        }
-        // remove the initial '*'s if any
-        let mut no_stars = String::with_capacity(doc.len());
-        for line in doc.lines() {
-            let mut chars = line.chars();
-            while let Some(c) = chars.next() {
-                if c.is_whitespace() {
-                    no_stars.push(c);
-                } else {
-                    no_stars.push(if c == '*' { ' ' } else { c });
-                    break;
-                }
+    let mut sizes = vec![];
+    let mut contains_initial_stars = false;
+    for line in doc.lines() {
+        let offset = line.as_ptr() as usize - doc.as_ptr() as usize;
+        debug_assert_eq!(offset as u32 as usize, offset);
+        contains_initial_stars |= line.trim_start().starts_with('*');
+        // +1 adds the newline, +3 skips the opening delimiter
+        sizes.push((line.len() + 1, span.with_lo(span.lo() + BytePos(3 + offset as u32))));
+    }
+    if !contains_initial_stars {
+        return (doc.to_string(), sizes);
+    }
+    // remove the initial '*'s if any
+    let mut no_stars = String::with_capacity(doc.len());
+    for line in doc.lines() {
+        let mut chars = line.chars();
+        while let Some(c) = chars.next() {
+            if c.is_whitespace() {
+                no_stars.push(c);
+            } else {
+                no_stars.push(if c == '*' { ' ' } else { c });
+                break;
             }
-            no_stars.push_str(chars.as_str());
-            no_stars.push('\n');
         }
-        return (no_stars, sizes);
+        no_stars.push_str(chars.as_str());
+        no_stars.push('\n');
     }
 
-    panic!("not a doc-comment: {}", comment);
+    (no_stars, sizes)
 }
 
 #[derive(Copy, Clone)]
@@ -318,9 +310,8 @@ fn check_attrs<'a>(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs
     let mut spans = vec![];
 
     for attr in attrs {
-        if let AttrKind::DocComment(ref comment) = attr.kind {
-            let comment = comment.to_string();
-            let (comment, current_spans) = strip_doc_comment_decoration(&comment, attr.span);
+        if let AttrKind::DocComment(comment_kind, comment) = attr.kind {
+            let (comment, current_spans) = strip_doc_comment_decoration(&comment.as_str(), comment_kind, attr.span);
             spans.extend_from_slice(&current_spans);
             doc.push_str(&comment);
         } else if attr.has_name(sym!(doc)) {
diff --git a/src/tools/clippy/clippy_lints/src/functions.rs b/src/tools/clippy/clippy_lints/src/functions.rs
index 3ee0b3f74b8..6a141f1fc78 100644
--- a/src/tools/clippy/clippy_lints/src/functions.rs
+++ b/src/tools/clippy/clippy_lints/src/functions.rs
@@ -239,7 +239,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
                 return;
             }
             if cx.access_levels.is_exported(item.hir_id)
-                && !is_proc_macro(&item.attrs)
+                && !is_proc_macro(cx.sess(), &item.attrs)
                 && attr_by_name(&item.attrs, "no_mangle").is_none()
             {
                 check_must_use_candidate(
@@ -262,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
                 let fn_header_span = item.span.with_hi(sig.decl.output.span().hi());
                 check_needless_must_use(cx, &sig.decl, item.hir_id, item.span, fn_header_span, attr);
             } else if cx.access_levels.is_exported(item.hir_id)
-                && !is_proc_macro(&item.attrs)
+                && !is_proc_macro(cx.sess(), &item.attrs)
                 && trait_ref_of_method(cx, item.hir_id).is_none()
             {
                 check_must_use_candidate(
@@ -294,7 +294,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
                 let body = cx.tcx.hir().body(eid);
                 Self::check_raw_ptr(cx, sig.header.unsafety, &sig.decl, body, item.hir_id);
 
-                if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(&item.attrs) {
+                if attr.is_none() && cx.access_levels.is_exported(item.hir_id) && !is_proc_macro(cx.sess(), &item.attrs) {
                     check_must_use_candidate(
                         cx,
                         &sig.decl,
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index ca1381852da..4e49bdbdd21 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -102,7 +102,7 @@ fn check_manual_non_exhaustive_enum(cx: &EarlyContext<'_>, item: &Item, variants
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
                     if_chain! {
-                        if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
+                        if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
                         let header_span = cx.sess.source_map().span_until_char(item.span, '{');
                         if let Some(snippet) = snippet_opt(cx, header_span);
                         then {
@@ -154,7 +154,7 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data:
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
                     if_chain! {
-                        if !attr::contains_name(&item.attrs, sym!(non_exhaustive));
+                        if !item.attrs.iter().any(|attr| attr.has_name(sym!(non_exhaustive)));
                         let header_span = find_header_span(cx, item, data);
                         if let Some(snippet) = snippet_opt(cx, header_span);
                         then {
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index 48ab98418e4..603440c0f83 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -2,7 +2,6 @@ use crate::utils::{span_lint, span_lint_and_then};
 use rustc_ast::ast::{
     Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
 };
-use rustc_ast::attr;
 use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -385,7 +384,7 @@ impl EarlyLintPass for NonExpressiveNames {
 }
 
 fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
-    if !attr::contains_name(attrs, sym!(test)) {
+    if !attrs.iter().any(|attr| attr.has_name(sym!(test))) {
         let mut visitor = SimilarNamesLocalVisitor {
             names: Vec::new(),
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
index 7b673e15b76..74ccd9235de 100644
--- a/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
+++ b/src/tools/clippy/clippy_lints/src/tabs_in_doc_comments.rs
@@ -60,13 +60,14 @@ declare_lint_pass!(TabsInDocComments => [TABS_IN_DOC_COMMENTS]);
 
 impl TabsInDocComments {
     fn warn_if_tabs_in_doc(cx: &EarlyContext<'_>, attr: &ast::Attribute) {
-        if let ast::AttrKind::DocComment(comment) = attr.kind {
+        if let ast::AttrKind::DocComment(_, comment) = attr.kind {
             let comment = comment.as_str();
 
             for (lo, hi) in get_chunks_of_tabs(&comment) {
+                // +3 skips the opening delimiter
                 let new_span = Span::new(
-                    attr.span.lo() + BytePos(lo),
-                    attr.span.lo() + BytePos(hi),
+                    attr.span.lo() + BytePos(3 + lo),
+                    attr.span.lo() + BytePos(3 + hi),
                     attr.span.ctxt(),
                 );
                 span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
index 58c1103da9f..ad02bc5fd8e 100755
--- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs
@@ -506,7 +506,7 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
     use AttrKind::*;
     l.style == r.style
         && match (&l.kind, &r.kind) {
-            (DocComment(l), DocComment(r)) => l == r,
+            (DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
             (Normal(l), Normal(r)) => eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args),
             _ => false,
         }
diff --git a/src/tools/clippy/clippy_lints/src/utils/attrs.rs b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
index 4bb4b087c55..407527251da 100644
--- a/src/tools/clippy/clippy_lints/src/utils/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/attrs.rs
@@ -1,5 +1,4 @@
 use rustc_ast::ast;
-use rustc_ast::expand::is_proc_macro_attr;
 use rustc_errors::Applicability;
 use rustc_session::Session;
 use std::str::FromStr;
@@ -126,6 +125,6 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
 
 /// Return true if the attributes contain any of `proc_macro`,
 /// `proc_macro_derive` or `proc_macro_attribute`, false otherwise
-pub fn is_proc_macro(attrs: &[ast::Attribute]) -> bool {
-    attrs.iter().any(is_proc_macro_attr)
+pub fn is_proc_macro(sess: &Session, attrs: &[ast::Attribute]) -> bool {
+    attrs.iter().any(|attr| sess.is_proc_macro_attr(attr))
 }
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 3f8e15d9029..95a12fe1935 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -932,7 +932,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 /// implementations have.
 pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
-    attr::contains_name(attrs, sym!(automatically_derived))
+    attrs.iter().any(|attr| attr.has_name(sym!(automatically_derived)))
 }
 
 /// Remove blocks around an expression.
diff --git a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
index ac5f0d0a39e..2b3f9be2dfb 100644
--- a/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
+++ b/src/tools/clippy/tests/ui/indexing_slicing_index.stderr
@@ -1,23 +1,3 @@
-error: this operation will panic at runtime
-  --> $DIR/indexing_slicing_index.rs:11:5
-   |
-LL |     x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-   |     ^^^^ index out of bounds: the len is 4 but the index is 4
-   |
-   = note: `#[deny(unconditional_panic)]` on by default
-
-error: this operation will panic at runtime
-  --> $DIR/indexing_slicing_index.rs:12:5
-   |
-LL |     x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-   |     ^^^^^^^^^ index out of bounds: the len is 4 but the index is 8
-
-error: this operation will panic at runtime
-  --> $DIR/indexing_slicing_index.rs:27:5
-   |
-LL |     x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-   |     ^^^^ index out of bounds: the len is 4 but the index is 15
-
 error: indexing may panic.
   --> $DIR/indexing_slicing_index.rs:10:5
    |
@@ -75,5 +55,5 @@ LL |     v[M];
    |
    = help: Consider using `.get(n)` or `.get_mut(n)` instead
 
-error: aborting due to 10 previous errors
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 9269a63b41a..848bd3a43e8 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -169,7 +169,7 @@ impl fmt::Display for Debugger {
 }
 
 /// Configuration for compiletest
-#[derive(Clone)]
+#[derive(Debug, Clone)]
 pub struct Config {
     /// `true` to to overwrite stderr/stdout files instead of complaining about changes in output.
     pub bless: bool,
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 047fbe9da14..edbb8372633 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -827,6 +827,7 @@ impl Config {
             name == util::get_pointer_width(&self.target) ||    // pointer width
             name == self.stage_id.split('-').next().unwrap() || // stage
             (self.target != self.host && name == "cross-compile") ||
+            (name == "endian-big" && util::is_big_endian(&self.target)) ||
             (self.remote_test_client.is_some() && name == "remote") ||
             match self.compare_mode {
                 Some(CompareMode::Nll) => name == "compare-mode-nll",
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 940e16720f6..7f49cb913b1 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1885,7 +1885,8 @@ impl<'test> TestCx<'test> {
         emit_metadata: EmitMetadata,
         allow_unused: AllowUnused,
     ) -> Command {
-        let is_rustdoc = self.is_rustdoc();
+        let is_aux = input_file.components().map(|c| c.as_os_str()).any(|c| c == "auxiliary");
+        let is_rustdoc = self.is_rustdoc() && !is_aux;
         let mut rustc = if !is_rustdoc {
             Command::new(&self.config.rustc_path)
         } else {
@@ -3246,8 +3247,19 @@ impl<'test> TestCx<'test> {
     }
 
     fn diff_mir_files(&self, before: PathBuf, after: PathBuf) -> String {
-        let before = self.get_mir_dump_dir().join(before);
-        let after = self.get_mir_dump_dir().join(after);
+        let to_full_path = |path: PathBuf| {
+            let full = self.get_mir_dump_dir().join(&path);
+            if !full.exists() {
+                panic!(
+                    "the mir dump file for {} does not exist (requested in {})",
+                    path.display(),
+                    self.testpaths.file.display(),
+                );
+            }
+            full
+        };
+        let before = to_full_path(before);
+        let after = to_full_path(after);
         debug!("comparing the contents of: {} with {}", before.display(), after.display());
         let before = fs::read_to_string(before).unwrap();
         let after = fs::read_to_string(after).unwrap();
@@ -3573,6 +3585,7 @@ impl ProcRes {
     }
 }
 
+#[derive(Debug)]
 enum TargetLocation {
     ThisFile(PathBuf),
     ThisDirectory(PathBuf),
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 0437ff8c944..ddd7941b114 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -99,6 +99,20 @@ pub const MSAN_SUPPORTED_TARGETS: &'static [&'static str] =
 pub const TSAN_SUPPORTED_TARGETS: &'static [&'static str] =
     &["aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"];
 
+const BIG_ENDIAN: &'static [&'static str] = &[
+    "armebv7r",
+    "mips",
+    "mips64",
+    "mipsisa32r6",
+    "mipsisa64r6",
+    "powerpc",
+    "powerpc64",
+    "s390x",
+    "sparc",
+    "sparc64",
+    "sparcv9",
+];
+
 pub fn matches_os(triple: &str, name: &str) -> bool {
     // For the wasm32 bare target we ignore anything also ignored on emscripten
     // and then we also recognize `wasm32-bare` as the os for the target
@@ -125,6 +139,12 @@ pub fn get_arch(triple: &str) -> &'static str {
     panic!("Cannot determine Architecture from triple");
 }
 
+/// Determine the endianness from `triple`
+pub fn is_big_endian(triple: &str) -> bool {
+    let triple_arch = triple.split('-').next().unwrap();
+    BIG_ENDIAN.contains(&triple_arch)
+}
+
 pub fn matches_env(triple: &str, name: &str) -> bool {
     if let Some(env) = triple.split('-').nth(3) { env.starts_with(name) } else { false }
 }
diff --git a/src/tools/error_index_generator/main.rs b/src/tools/error_index_generator/main.rs
index bd9f8fb0450..ff6923b3797 100644
--- a/src/tools/error_index_generator/main.rs
+++ b/src/tools/error_index_generator/main.rs
@@ -1,6 +1,5 @@
 #![feature(rustc_private)]
 
-extern crate rustc_ast;
 extern crate rustc_driver;
 extern crate rustc_span;
 
@@ -284,7 +283,7 @@ fn parse_args() -> (OutputFormat, PathBuf) {
 fn main() {
     rustc_driver::init_env_logger("RUST_LOG");
     let (format, dst) = parse_args();
-    let result = rustc_ast::with_default_session_globals(move || main_with_result(format, &dst));
+    let result = rustc_span::with_default_session_globals(move || main_with_result(format, &dst));
     if let Err(e) = result {
         panic!("{}", e.to_string());
     }
diff --git a/src/tools/miri b/src/tools/miri
-Subproject 55bdb3174653039f47362742f8dc941bfc086e8
+Subproject cf633d0e897c065381b7b7d14984830176caf8b
diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py
index c0631fcedd3..55e2d7cf827 100755
--- a/src/tools/publish_toolstate.py
+++ b/src/tools/publish_toolstate.py
@@ -42,7 +42,7 @@ MAINTAINERS = {
 LABELS = {
     'miri': ['A-miri', 'C-bug'],
     'rls': ['A-rls', 'C-bug'],
-    'rustfmt': ['C-bug'],
+    'rustfmt': ['A-rustfmt', 'C-bug'],
     'book': ['C-bug'],
     'nomicon': ['C-bug'],
     'reference': ['C-bug'],
diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs
index 51f135d3761..2fa0f12d7e8 100644
--- a/src/tools/tidy/src/error_codes_check.rs
+++ b/src/tools/tidy/src/error_codes_check.rs
@@ -16,8 +16,7 @@ const EXEMPTED_FROM_TEST: &[&str] = &[
 ];
 
 // Some error codes don't have any tests apparently...
-const IGNORE_EXPLANATION_CHECK: &[&str] =
-    &["E0570", "E0601", "E0602", "E0639", "E0729", "E0749", "E0750"];
+const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0639", "E0729"];
 
 fn check_error_code_explanation(
     f: &str,
diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs
index eabbcee9d73..d2d1807b3bb 100644
--- a/src/tools/unicode-table-generator/src/main.rs
+++ b/src/tools/unicode-table-generator/src/main.rs
@@ -15,7 +15,7 @@
 //! We have two separate encoding schemes: a skiplist-like approach, and a
 //! compressed bitset. The datasets we consider mostly use the skiplist (it's
 //! smaller) but the lowercase and uppercase sets are sufficiently sparse for
-//! the bitset to be worthwhile -- for those sets the biset is a 2x size win.
+//! the bitset to be worthwhile -- for those sets the bitset is a 2x size win.
 //! Since the bitset is also faster, this seems an obvious choice. (As a
 //! historical note, the bitset was also the prior implementation, so its
 //! relative complexity had already been paid).