about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-11-15 13:19:05 +0000
committerbors <bors@rust-lang.org>2020-11-15 13:19:05 +0000
commit5fab31e5ddf5f2613bf57a0a7286dc6f5887e1cb (patch)
treebf2540f955bf3e3adb49330b1fd06ee628d9fc35
parent04688459242356c0f6b9fdad3ba76c9ec4dcc354 (diff)
parent568354f01f22148709e51fe1130826addb455e18 (diff)
downloadrust-5fab31e5ddf5f2613bf57a0a7286dc6f5887e1cb.tar.gz
rust-5fab31e5ddf5f2613bf57a0a7286dc6f5887e1cb.zip
Auto merge of #79070 - jonas-schievink:rollup-wacn2b8, r=jonas-schievink
Rollup of 13 pull requests

Successful merges:

 - #77802 (Allow making `RUSTC_BOOTSTRAP` conditional on the crate name)
 - #79004 (Add `--color` support to bootstrap)
 - #79005 (cleanup: Remove `ParseSess::injected_crate_name`)
 - #79016 (Make `_` an expression, to discard values in destructuring assignments)
 - #79019 (astconv: extract closures into a separate trait)
 - #79026 (Implement BTreeMap::retain and BTreeSet::retain)
 - #79031 (Validate that locals have a corresponding `LocalDecl`)
 - #79034 (rustc_resolve: Make `macro_rules` scope chain compression lazy)
 - #79036 (Move Steal to rustc_data_structures.)
 - #79041 (Rename clean::{ItemEnum -> ItemKind}, clean::Item::{inner -> kind})
 - #79058 (Move likely/unlikely argument outside of invisible unsafe block)
 - #79059 (Print 'checking cranelift artifacts' to easily separate it from other artifacts)
 - #79063 (Update rustfmt to v1.4.26)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock2
-rw-r--r--compiler/rustc_ast/src/ast.rs3
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs1
-rw-r--r--compiler/rustc_ast/src/visit.rs1
-rw-r--r--compiler/rustc_ast_lowering/src/expr.rs19
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs4
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs6
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs4
-rw-r--r--compiler/rustc_builtin_macros/src/standard_library_imports.rs6
-rw-r--r--compiler/rustc_codegen_llvm/src/llvm_util.rs13
-rw-r--r--compiler/rustc_data_structures/src/lib.rs13
-rw-r--r--compiler/rustc_data_structures/src/steal.rs (renamed from compiler/rustc_middle/src/ty/steal.rs)11
-rw-r--r--compiler/rustc_driver/src/lib.rs21
-rw-r--r--compiler/rustc_driver/src/pretty.rs2
-rw-r--r--compiler/rustc_feature/src/lib.rs42
-rw-r--r--compiler/rustc_incremental/src/persist/file_format.rs12
-rw-r--r--compiler/rustc_incremental/src/persist/load.rs19
-rw-r--r--compiler/rustc_incremental/src/persist/save.rs2
-rw-r--r--compiler/rustc_interface/src/passes.rs10
-rw-r--r--compiler/rustc_interface/src/queries.rs2
-rw-r--r--compiler/rustc_middle/src/arena.rs4
-rw-r--r--compiler/rustc_middle/src/ich/impls_ty.rs9
-rw-r--r--compiler/rustc_middle/src/ty/context.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs1
-rw-r--r--compiler/rustc_middle/src/ty/query/mod.rs2
-rw-r--r--compiler/rustc_mir/src/transform/check_consts/ops.rs3
-rw-r--r--compiler/rustc_mir/src/transform/mod.rs2
-rw-r--r--compiler/rustc_mir/src/transform/validate.rs7
-rw-r--r--compiler/rustc_mir_build/src/build/mod.rs2
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs3
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs3
-rw-r--r--compiler/rustc_passes/src/check_const.rs3
-rw-r--r--compiler/rustc_resolve/src/build_reduced_graph.rs4
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs5
-rw-r--r--compiler/rustc_resolve/src/lib.rs27
-rw-r--r--compiler/rustc_resolve/src/macros.rs26
-rw-r--r--compiler/rustc_session/src/config.rs22
-rw-r--r--compiler/rustc_session/src/parse.rs6
-rw-r--r--compiler/rustc_session/src/session.rs3
-rw-r--r--compiler/rustc_trait_selection/src/opaque_types.rs3
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs25
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs224
-rw-r--r--compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs147
-rw-r--r--compiler/rustc_typeck/src/check/method/confirm.rs74
-rw-r--r--compiler/rustc_typeck/src/check/method/probe.rs3
-rw-r--r--library/alloc/src/collections/btree/map.rs24
-rw-r--r--library/alloc/src/collections/btree/map/tests.rs11
-rw-r--r--library/alloc/src/collections/btree/set.rs24
-rw-r--r--library/alloc/src/collections/btree/set/tests.rs11
-rw-r--r--src/bootstrap/builder.rs12
-rw-r--r--src/bootstrap/check.rs5
-rw-r--r--src/bootstrap/config.rs4
-rw-r--r--src/bootstrap/flags.rs30
-rw-r--r--src/librustdoc/clean/auto_trait.rs2
-rw-r--r--src/librustdoc/clean/blanket_impl.rs2
-rw-r--r--src/librustdoc/clean/inline.rs10
-rw-r--r--src/librustdoc/clean/mod.rs62
-rw-r--r--src/librustdoc/clean/types.rs37
-rw-r--r--src/librustdoc/clean/utils.rs18
-rw-r--r--src/librustdoc/config.rs16
-rw-r--r--src/librustdoc/core.rs2
-rw-r--r--src/librustdoc/doctest.rs3
-rw-r--r--src/librustdoc/externalfiles.rs4
-rw-r--r--src/librustdoc/fold.rs16
-rw-r--r--src/librustdoc/formats/cache.rs18
-rw-r--r--src/librustdoc/formats/item_type.rs6
-rw-r--r--src/librustdoc/formats/mod.rs2
-rw-r--r--src/librustdoc/formats/renderer.rs2
-rw-r--r--src/librustdoc/html/render/cache.rs2
-rw-r--r--src/librustdoc/html/render/mod.rs61
-rw-r--r--src/librustdoc/markdown.rs5
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs2
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs8
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs8
-rw-r--r--src/librustdoc/passes/doc_test_lints.rs6
-rw-r--r--src/librustdoc/passes/html_tags.rs3
-rw-r--r--src/librustdoc/passes/non_autolinks.rs3
-rw-r--r--src/librustdoc/passes/strip_hidden.rs2
-rw-r--r--src/librustdoc/passes/stripper.rs8
-rw-r--r--src/test/ui/cross/cross-file-errors/main.rs3
-rw-r--r--src/test/ui/cross/cross-file-errors/main.stderr22
-rw-r--r--src/test/ui/destructuring-assignment/nested_destructure.rs3
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure.rs2
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure_fail.rs1
-rw-r--r--src/test/ui/destructuring-assignment/slice_destructure_fail.stderr8
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure.rs6
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure_fail.rs2
-rw-r--r--src/test/ui/destructuring-assignment/struct_destructure_fail.stderr32
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure.rs2
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure_fail.rs1
-rw-r--r--src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr13
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure.rs4
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs4
-rw-r--r--src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr28
-rw-r--r--src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs2
-rw-r--r--src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr13
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs18
-rw-r--r--src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr89
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/sugg.rs1
m---------src/tools/rustfmt10
100 files changed, 976 insertions, 520 deletions
diff --git a/Cargo.lock b/Cargo.lock
index f02ae7ef6cf..c7178ba255a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4337,7 +4337,7 @@ dependencies = [
 
 [[package]]
 name = "rustfmt-nightly"
-version = "1.4.25"
+version = "1.4.26"
 dependencies = [
  "annotate-snippets 0.6.1",
  "anyhow",
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 3e953729aab..328086af183 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1192,6 +1192,7 @@ impl Expr {
             ExprKind::Field(..) => ExprPrecedence::Field,
             ExprKind::Index(..) => ExprPrecedence::Index,
             ExprKind::Range(..) => ExprPrecedence::Range,
+            ExprKind::Underscore => ExprPrecedence::Path,
             ExprKind::Path(..) => ExprPrecedence::Path,
             ExprKind::AddrOf(..) => ExprPrecedence::AddrOf,
             ExprKind::Break(..) => ExprPrecedence::Break,
@@ -1324,6 +1325,8 @@ pub enum ExprKind {
     Index(P<Expr>, P<Expr>),
     /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment).
     Range(Option<P<Expr>>, Option<P<Expr>>, RangeLimits),
+    /// An underscore, used in destructuring assignment to ignore a value.
+    Underscore,
 
     /// Variable reference, possibly containing `::` and/or type
     /// parameters (e.g., `foo::bar::<baz>`).
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 26097980e8b..ddae0ab03e4 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1232,6 +1232,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
             visit_opt(e1, |e1| vis.visit_expr(e1));
             visit_opt(e2, |e2| vis.visit_expr(e2));
         }
+        ExprKind::Underscore => {}
         ExprKind::Path(qself, path) => {
             vis.visit_qself(qself);
             vis.visit_path(path);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 49b521afcdc..560064182e1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -806,6 +806,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
             walk_list!(visitor, visit_expr, start);
             walk_list!(visitor, visit_expr, end);
         }
+        ExprKind::Underscore => {}
         ExprKind::Path(ref maybe_qself, ref path) => {
             if let Some(ref qself) = *maybe_qself {
                 visitor.visit_ty(&qself.ty);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index b94fb1d8437..f83fc29577b 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -164,6 +164,16 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 ExprKind::Range(ref e1, ref e2, lims) => {
                     self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims)
                 }
+                ExprKind::Underscore => {
+                    self.sess
+                        .struct_span_err(
+                            e.span,
+                            "in expressions, `_` can only be used on the left-hand side of an assignment",
+                        )
+                        .span_label(e.span, "`_` not allowed here")
+                        .emit();
+                    hir::ExprKind::Err
+                }
                 ExprKind::Path(ref qself, ref path) => {
                     let qpath = self.lower_qpath(
                         e.id,
@@ -863,7 +873,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         // Return early in case of an ordinary assignment.
         fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
             match &lhs.kind {
-                ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false,
+                ExprKind::Array(..)
+                | ExprKind::Struct(..)
+                | ExprKind::Tup(..)
+                | ExprKind::Underscore => false,
                 // Check for tuple struct constructor.
                 ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
                 ExprKind::Paren(e) => {
@@ -943,6 +956,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         assignments: &mut Vec<hir::Stmt<'hir>>,
     ) -> &'hir hir::Pat<'hir> {
         match &lhs.kind {
+            // Underscore pattern.
+            ExprKind::Underscore => {
+                return self.pat_without_dbm(lhs.span, hir::PatKind::Wild);
+            }
             // Slice patterns.
             ExprKind::Array(elements) => {
                 let (pats, rest) =
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 88ad2706eac..d93655e5905 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -53,7 +53,6 @@ use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
 use rustc_hir::intravisit;
 use rustc_hir::{ConstArg, GenericArg, ParamName};
 use rustc_index::vec::{Idx, IndexVec};
-use rustc_session::config::nightly_options;
 use rustc_session::lint::{builtin::BARE_TRAIT_OBJECTS, BuiltinLintDiagnostics, LintBuffer};
 use rustc_session::parse::ParseSess;
 use rustc_session::Session;
@@ -1398,8 +1397,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                             "`impl Trait` not allowed outside of {}",
                             allowed_in,
                         );
-                        if pos == ImplTraitPosition::Binding && nightly_options::is_nightly_build()
-                        {
+                        if pos == ImplTraitPosition::Binding && self.sess.is_nightly_build() {
                             err.help(
                                 "add `#![feature(impl_trait_in_bindings)]` to the crate \
                                    attributes to enable",
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 2831675cb36..181783441f3 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -630,7 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
     gate_all!(const_trait_impl, "const trait impls are experimental");
     gate_all!(half_open_range_patterns, "half-open range patterns are unstable");
     gate_all!(inline_const, "inline-const is experimental");
-    gate_all!(destructuring_assignment, "destructuring assignments are unstable");
+    if sess.parse_sess.span_diagnostic.err_count() == 0 {
+        // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is
+        // involved, so we only emit errors where there are no other parsing errors.
+        gate_all!(destructuring_assignment, "destructuring assignments are unstable");
+    }
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 958219cbebf..fdb129d9e2a 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -109,7 +109,6 @@ pub fn print_crate<'a>(
     ann: &'a dyn PpAnn,
     is_expanded: bool,
     edition: Edition,
-    has_injected_crate: bool,
 ) -> String {
     let mut s = State {
         s: pp::mk_printer(),
@@ -119,7 +118,7 @@ pub fn print_crate<'a>(
         insert_extra_parens: true,
     };
 
-    if is_expanded && has_injected_crate {
+    if is_expanded && !krate.attrs.iter().any(|attr| attr.has_name(sym::no_core)) {
         // We need to print `#![no_std]` (and its feature gate) so that
         // compiling pretty-printed source won't inject libstd again.
         // However, we don't want these attributes in the AST because
@@ -2068,6 +2067,7 @@ impl<'a> State<'a> {
                     self.print_expr_maybe_paren(e, fake_prec);
                 }
             }
+            ast::ExprKind::Underscore => self.s.word("_"),
             ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0),
             ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true),
             ast::ExprKind::Break(opt_label, ref opt_expr) => {
diff --git a/compiler/rustc_builtin_macros/src/standard_library_imports.rs b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
index e801b5c7b0c..91566ec1ef2 100644
--- a/compiler/rustc_builtin_macros/src/standard_library_imports.rs
+++ b/compiler/rustc_builtin_macros/src/standard_library_imports.rs
@@ -13,12 +13,12 @@ pub fn inject(
     resolver: &mut dyn ResolverExpand,
     sess: &Session,
     alt_std_name: Option<Symbol>,
-) -> (ast::Crate, Option<Symbol>) {
+) -> ast::Crate {
     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 sess.contains_name(&krate.attrs, sym::no_core) {
-        return (krate, None);
+        return krate;
     } else if sess.contains_name(&krate.attrs, sym::no_std) {
         if sess.contains_name(&krate.attrs, sym::compiler_builtins) {
             &[sym::core]
@@ -81,5 +81,5 @@ pub fn inject(
 
     krate.module.items.insert(0, use_item);
 
-    (krate, Some(name))
+    krate
 }
diff --git a/compiler/rustc_codegen_llvm/src/llvm_util.rs b/compiler/rustc_codegen_llvm/src/llvm_util.rs
index cc71b6289fa..a8a1646183c 100644
--- a/compiler/rustc_codegen_llvm/src/llvm_util.rs
+++ b/compiler/rustc_codegen_llvm/src/llvm_util.rs
@@ -3,7 +3,6 @@ use crate::llvm;
 use libc::c_int;
 use rustc_codegen_ssa::target_features::supported_target_features;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_feature::UnstableFeatures;
 use rustc_middle::bug;
 use rustc_session::config::PrintRequest;
 use rustc_session::Session;
@@ -147,13 +146,11 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
     let target_machine = create_informational_target_machine(sess);
     supported_target_features(sess)
         .iter()
-        .filter_map(|&(feature, gate)| {
-            if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() {
-                Some(feature)
-            } else {
-                None
-            }
-        })
+        .filter_map(
+            |&(feature, gate)| {
+                if sess.is_nightly_build() || gate.is_none() { Some(feature) } else { None }
+            },
+        )
         .filter(|feature| {
             let llvm_feature = to_llvm_feature(sess, feature);
             let cstr = CString::new(llvm_feature).unwrap();
diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs
index 7669b78834c..322c7a71160 100644
--- a/compiler/rustc_data_structures/src/lib.rs
+++ b/compiler/rustc_data_structures/src/lib.rs
@@ -47,9 +47,9 @@ pub fn cold_path<F: FnOnce() -> R, R>(f: F) -> R {
 #[macro_export]
 macro_rules! likely {
     ($e:expr) => {
-        #[allow(unused_unsafe)]
-        {
-            unsafe { std::intrinsics::likely($e) }
+        match $e {
+            #[allow(unused_unsafe)]
+            e => unsafe { std::intrinsics::likely(e) },
         }
     };
 }
@@ -57,9 +57,9 @@ macro_rules! likely {
 #[macro_export]
 macro_rules! unlikely {
     ($e:expr) => {
-        #[allow(unused_unsafe)]
-        {
-            unsafe { std::intrinsics::unlikely($e) }
+        match $e {
+            #[allow(unused_unsafe)]
+            e => unsafe { std::intrinsics::unlikely(e) },
         }
     };
 }
@@ -102,6 +102,7 @@ pub mod work_queue;
 pub use atomic_ref::AtomicRef;
 pub mod frozen;
 pub mod sso;
+pub mod steal;
 pub mod tagged_ptr;
 pub mod temp_dir;
 pub mod unhash;
diff --git a/compiler/rustc_middle/src/ty/steal.rs b/compiler/rustc_data_structures/src/steal.rs
index 224e76845d7..e532a84cea3 100644
--- a/compiler/rustc_middle/src/ty/steal.rs
+++ b/compiler/rustc_data_structures/src/steal.rs
@@ -1,4 +1,5 @@
-use rustc_data_structures::sync::{MappedReadGuard, ReadGuard, RwLock};
+use crate::stable_hasher::{HashStable, StableHasher};
+use crate::sync::{MappedReadGuard, ReadGuard, RwLock};
 
 /// The `Steal` struct is intended to used as the value for a query.
 /// Specifically, we sometimes have queries (*cough* MIR *cough*)
@@ -31,7 +32,7 @@ impl<T> Steal<T> {
 
     pub fn borrow(&self) -> MappedReadGuard<'_, T> {
         ReadGuard::map(self.value.borrow(), |opt| match *opt {
-            None => bug!("attempted to read from stolen value"),
+            None => panic!("attempted to read from stolen value"),
             Some(ref v) => v,
         })
     }
@@ -42,3 +43,9 @@ impl<T> Steal<T> {
         value.expect("attempt to read from stolen value")
     }
 }
+
+impl<CTX, T: HashStable<CTX>> HashStable<CTX> for Steal<T> {
+    fn hash_stable(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
+        self.borrow().hash_stable(hcx, hasher);
+    }
+}
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index a192c2eb94e..c5447612555 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -20,7 +20,7 @@ use rustc_data_structures::profiling::print_time_passes_entry;
 use rustc_data_structures::sync::SeqCst;
 use rustc_errors::registry::{InvalidErrorCode, Registry};
 use rustc_errors::{ErrorReported, PResult};
-use rustc_feature::{find_gated_cfg, UnstableFeatures};
+use rustc_feature::find_gated_cfg;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_interface::util::{self, collect_crate_types, get_builtin_codegen_backend};
 use rustc_interface::{interface, Queries};
@@ -746,9 +746,6 @@ impl RustcDefaultCalls {
                     }
                 }
                 Cfg => {
-                    let allow_unstable_cfg =
-                        UnstableFeatures::from_environment().is_nightly_build();
-
                     let mut cfgs = sess
                         .parse_sess
                         .config
@@ -763,7 +760,7 @@ impl RustcDefaultCalls {
                             // it, this is intended to get into Cargo and then go
                             // through to build scripts.
                             if (name != sym::target_feature || value != Some(sym::crt_dash_static))
-                                && !allow_unstable_cfg
+                                && !sess.is_nightly_build()
                                 && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
                             {
                                 return None;
@@ -814,14 +811,14 @@ pub fn version(binary: &str, matches: &getopts::Matches) {
     }
 }
 
-fn usage(verbose: bool, include_unstable_options: bool) {
+fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
     let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
     let mut options = getopts::Options::new();
     for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
         (option.apply)(&mut options);
     }
     let message = "Usage: rustc [OPTIONS] INPUT";
-    let nightly_help = if nightly_options::is_nightly_build() {
+    let nightly_help = if nightly_build {
         "\n    -Z help             Print unstable compiler options"
     } else {
         ""
@@ -831,7 +828,7 @@ fn usage(verbose: bool, include_unstable_options: bool) {
     } else {
         "\n    --help -v           Print the full set of options rustc accepts"
     };
-    let at_path = if verbose && nightly_options::is_nightly_build() {
+    let at_path = if verbose && nightly_build {
         "    @path               Read newline separated options from `path`\n"
     } else {
         ""
@@ -1034,7 +1031,9 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
     if args.is_empty() {
         // user did not write `-v` nor `-Z unstable-options`, so do not
         // include that extra information.
-        usage(false, false);
+        let nightly_build =
+            rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
+        usage(false, false, nightly_build);
         return None;
     }
 
@@ -1063,7 +1062,9 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
 
     if matches.opt_present("h") || matches.opt_present("help") {
         // Only show unstable options in --help if we accept unstable options.
-        usage(matches.opt_present("verbose"), nightly_options::is_unstable_enabled(&matches));
+        let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
+        let nightly_build = nightly_options::match_is_nightly_build(&matches);
+        usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
         return None;
     }
 
diff --git a/compiler/rustc_driver/src/pretty.rs b/compiler/rustc_driver/src/pretty.rs
index f74e1fe61e7..305fa838afa 100644
--- a/compiler/rustc_driver/src/pretty.rs
+++ b/compiler/rustc_driver/src/pretty.rs
@@ -404,7 +404,6 @@ pub fn print_after_parsing(
                 annotation.pp_ann(),
                 false,
                 parse.edition,
-                parse.injected_crate_name.get().is_some(),
             )
         })
     } else {
@@ -446,7 +445,6 @@ pub fn print_after_hir_lowering<'tcx>(
                     annotation.pp_ann(),
                     true,
                     parse.edition,
-                    parse.injected_crate_name.get().is_some(),
                 )
             })
         }
diff --git a/compiler/rustc_feature/src/lib.rs b/compiler/rustc_feature/src/lib.rs
index 68ac2841fed..f965f7fdefe 100644
--- a/compiler/rustc_feature/src/lib.rs
+++ b/compiler/rustc_feature/src/lib.rs
@@ -59,7 +59,7 @@ pub enum Stability {
     Deprecated(&'static str, Option<&'static str>),
 }
 
-#[derive(Clone, Copy, Hash)]
+#[derive(Clone, Copy, Debug, Hash)]
 pub enum UnstableFeatures {
     /// Hard errors for unstable features are active, as on beta/stable channels.
     Disallow,
@@ -73,11 +73,20 @@ pub enum UnstableFeatures {
 }
 
 impl UnstableFeatures {
-    pub fn from_environment() -> UnstableFeatures {
+    /// This takes into account `RUSTC_BOOTSTRAP`.
+    ///
+    /// If `krate` is [`Some`], then setting `RUSTC_BOOTSTRAP=krate` will enable the nightly features.
+    /// Otherwise, only `RUSTC_BOOTSTRAP=1` will work.
+    pub fn from_environment(krate: Option<&str>) -> Self {
         // `true` if this is a feature-staged build, i.e., on the beta or stable channel.
         let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
+        // Returns whether `krate` should be counted as unstable
+        let is_unstable_crate = |var: &str| {
+            krate.map_or(false, |name| var.split(',').any(|new_krate| new_krate == name))
+        };
         // `true` if we should enable unstable features for bootstrapping.
-        let bootstrap = std::env::var("RUSTC_BOOTSTRAP").is_ok();
+        let bootstrap = std::env::var("RUSTC_BOOTSTRAP")
+            .map_or(false, |var| var == "1" || is_unstable_crate(&var));
         match (disable_unstable_features, bootstrap) {
             (_, true) => UnstableFeatures::Cheat,
             (true, _) => UnstableFeatures::Disallow,
@@ -140,3 +149,30 @@ pub use builtin_attrs::{
     AttributeType, BuiltinAttribute, GatedCfg, BUILTIN_ATTRIBUTES, BUILTIN_ATTRIBUTE_MAP,
 };
 pub use removed::{REMOVED_FEATURES, STABLE_REMOVED_FEATURES};
+
+#[cfg(test)]
+mod test {
+    use super::UnstableFeatures;
+
+    #[test]
+    fn rustc_bootstrap_parsing() {
+        let is_bootstrap = |env, krate| {
+            std::env::set_var("RUSTC_BOOTSTRAP", env);
+            matches!(UnstableFeatures::from_environment(krate), UnstableFeatures::Cheat)
+        };
+        assert!(is_bootstrap("1", None));
+        assert!(is_bootstrap("1", Some("x")));
+        // RUSTC_BOOTSTRAP allows specifying a specific crate
+        assert!(is_bootstrap("x", Some("x")));
+        // RUSTC_BOOTSTRAP allows multiple comma-delimited crates
+        assert!(is_bootstrap("x,y,z", Some("x")));
+        assert!(is_bootstrap("x,y,z", Some("y")));
+        // Crate that aren't specified do not get unstable features
+        assert!(!is_bootstrap("x", Some("a")));
+        assert!(!is_bootstrap("x,y,z", Some("a")));
+        assert!(!is_bootstrap("x,y,z", None));
+
+        // this is technically a breaking change, but there are no stability guarantees for RUSTC_BOOTSTRAP
+        assert!(!is_bootstrap("0", None));
+    }
+}
diff --git a/compiler/rustc_incremental/src/persist/file_format.rs b/compiler/rustc_incremental/src/persist/file_format.rs
index 048a81b81ba..e185ee24d17 100644
--- a/compiler/rustc_incremental/src/persist/file_format.rs
+++ b/compiler/rustc_incremental/src/persist/file_format.rs
@@ -15,7 +15,6 @@ use std::io::{self, Read};
 use std::path::Path;
 
 use rustc_serialize::opaque::Encoder;
-use rustc_session::config::nightly_options;
 
 /// The first few bytes of files generated by incremental compilation.
 const FILE_MAGIC: &[u8] = b"RSIC";
@@ -28,12 +27,12 @@ const HEADER_FORMAT_VERSION: u16 = 0;
 /// the Git commit hash.
 const RUSTC_VERSION: Option<&str> = option_env!("CFG_VERSION");
 
-pub fn write_file_header(stream: &mut Encoder) {
+pub fn write_file_header(stream: &mut Encoder, nightly_build: bool) {
     stream.emit_raw_bytes(FILE_MAGIC);
     stream
         .emit_raw_bytes(&[(HEADER_FORMAT_VERSION >> 0) as u8, (HEADER_FORMAT_VERSION >> 8) as u8]);
 
-    let rustc_version = rustc_version();
+    let rustc_version = rustc_version(nightly_build);
     assert_eq!(rustc_version.len(), (rustc_version.len() as u8) as usize);
     stream.emit_raw_bytes(&[rustc_version.len() as u8]);
     stream.emit_raw_bytes(rustc_version.as_bytes());
@@ -51,6 +50,7 @@ pub fn write_file_header(stream: &mut Encoder) {
 pub fn read_file(
     report_incremental_info: bool,
     path: &Path,
+    nightly_build: bool,
 ) -> io::Result<Option<(Vec<u8>, usize)>> {
     if !path.exists() {
         return Ok(None);
@@ -93,7 +93,7 @@ pub fn read_file(
         let mut buffer = vec![0; rustc_version_str_len];
         file.read_exact(&mut buffer)?;
 
-        if buffer != rustc_version().as_bytes() {
+        if buffer != rustc_version(nightly_build).as_bytes() {
             report_format_mismatch(report_incremental_info, path, "Different compiler version");
             return Ok(None);
         }
@@ -115,8 +115,8 @@ fn report_format_mismatch(report_incremental_info: bool, file: &Path, message: &
     }
 }
 
-fn rustc_version() -> String {
-    if nightly_options::is_nightly_build() {
+fn rustc_version(nightly_build: bool) -> String {
+    if nightly_build {
         if let Some(val) = env::var_os("RUSTC_FORCE_INCR_COMP_ARTIFACT_HEADER") {
             return val.to_string_lossy().into_owned();
         }
diff --git a/compiler/rustc_incremental/src/persist/load.rs b/compiler/rustc_incremental/src/persist/load.rs
index 966faa9639d..578c045a2b4 100644
--- a/compiler/rustc_incremental/src/persist/load.rs
+++ b/compiler/rustc_incremental/src/persist/load.rs
@@ -53,8 +53,12 @@ impl LoadResult<(PreviousDepGraph, WorkProductMap)> {
     }
 }
 
-fn load_data(report_incremental_info: bool, path: &Path) -> LoadResult<(Vec<u8>, usize)> {
-    match file_format::read_file(report_incremental_info, path) {
+fn load_data(
+    report_incremental_info: bool,
+    path: &Path,
+    nightly_build: bool,
+) -> LoadResult<(Vec<u8>, usize)> {
+    match file_format::read_file(report_incremental_info, path, nightly_build) {
         Ok(Some(data_and_pos)) => LoadResult::Ok { data: data_and_pos },
         Ok(None) => {
             // The file either didn't exist or was produced by an incompatible
@@ -111,13 +115,14 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
     let expected_hash = sess.opts.dep_tracking_hash();
 
     let mut prev_work_products = FxHashMap::default();
+    let nightly_build = sess.is_nightly_build();
 
     // If we are only building with -Zquery-dep-graph but without an actual
     // incr. comp. session directory, we skip this. Otherwise we'd fail
     // when trying to load work products.
     if sess.incr_comp_session_dir_opt().is_some() {
         let work_products_path = work_products_path(sess);
-        let load_result = load_data(report_incremental_info, &work_products_path);
+        let load_result = load_data(report_incremental_info, &work_products_path, nightly_build);
 
         if let LoadResult::Ok { data: (work_products_data, start_pos) } = load_result {
             // Decode the list of work_products
@@ -163,7 +168,7 @@ pub fn load_dep_graph(sess: &Session) -> DepGraphFuture {
     MaybeAsync::Async(std::thread::spawn(move || {
         let _prof_timer = prof.generic_activity("incr_comp_load_dep_graph");
 
-        match load_data(report_incremental_info, &path) {
+        match load_data(report_incremental_info, &path, nightly_build) {
             LoadResult::DataOutOfDate => LoadResult::DataOutOfDate,
             LoadResult::Error { message } => LoadResult::Error { message },
             LoadResult::Ok { data: (bytes, start_pos) } => {
@@ -201,7 +206,11 @@ pub fn load_query_result_cache(sess: &Session) -> OnDiskCache<'_> {
 
     let _prof_timer = sess.prof.generic_activity("incr_comp_load_query_result_cache");
 
-    match load_data(sess.opts.debugging_opts.incremental_info, &query_cache_path(sess)) {
+    match load_data(
+        sess.opts.debugging_opts.incremental_info,
+        &query_cache_path(sess),
+        sess.is_nightly_build(),
+    ) {
         LoadResult::Ok { data: (bytes, start_pos) } => OnDiskCache::new(sess, bytes, start_pos),
         _ => OnDiskCache::new_empty(sess.source_map()),
     }
diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs
index 45cef479a4f..102a77e8e79 100644
--- a/compiler/rustc_incremental/src/persist/save.rs
+++ b/compiler/rustc_incremental/src/persist/save.rs
@@ -119,7 +119,7 @@ where
 
     // generate the data in a memory buffer
     let mut encoder = Encoder::new(Vec::new());
-    file_format::write_file_header(&mut encoder);
+    file_format::write_file_header(&mut encoder, sess.is_nightly_build());
     encode(&mut encoder);
 
     // write the data out
diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs
index 548b6c03daa..82cf4ab7f5c 100644
--- a/compiler/rustc_interface/src/passes.rs
+++ b/compiler/rustc_interface/src/passes.rs
@@ -6,6 +6,7 @@ use rustc_ast::mut_visit::MutVisitor;
 use rustc_ast::{self as ast, visit};
 use rustc_codegen_ssa::back::link::emit_metadata;
 use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{par_iter, Lrc, OnceCell, ParallelIterator, WorkerLocal};
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_data_structures::{box_region_allow_access, declare_box_region_type, parallel};
@@ -20,7 +21,6 @@ use rustc_middle::dep_graph::DepGraph;
 use rustc_middle::middle;
 use rustc_middle::middle::cstore::{CrateStore, MetadataLoader, MetadataLoaderDyn};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::steal::Steal;
 use rustc_middle::ty::{self, GlobalCtxt, ResolverOutputs, TyCtxt};
 use rustc_mir as mir;
 use rustc_mir_build as mir_build;
@@ -239,16 +239,12 @@ fn configure_and_expand_inner<'a>(
 
     krate = sess.time("crate_injection", || {
         let alt_std_name = sess.opts.alt_std_name.as_ref().map(|s| Symbol::intern(s));
-        let (krate, name) = rustc_builtin_macros::standard_library_imports::inject(
+        rustc_builtin_macros::standard_library_imports::inject(
             krate,
             &mut resolver,
             &sess,
             alt_std_name,
-        );
-        if let Some(name) = name {
-            sess.parse_sess.injected_crate_name.set(name).expect("not yet initialized");
-        }
-        krate
+        )
     });
 
     util::check_attr_crate_type(&sess, &krate.attrs, &mut resolver.lint_buffer());
diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index 1de7350a3e2..a2704c3adbf 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -3,6 +3,7 @@ use crate::passes::{self, BoxedResolver, QueryContext};
 
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal};
 use rustc_errors::ErrorReported;
@@ -12,7 +13,6 @@ use rustc_incremental::DepGraphFuture;
 use rustc_lint::LintStore;
 use rustc_middle::arena::Arena;
 use rustc_middle::dep_graph::DepGraph;
-use rustc_middle::ty::steal::Steal;
 use rustc_middle::ty::{GlobalCtxt, ResolverOutputs, TyCtxt};
 use rustc_serialize::json;
 use rustc_session::config::{self, OutputFilenames, OutputType};
diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs
index f6570cc95d2..672073b1d34 100644
--- a/compiler/rustc_middle/src/arena.rs
+++ b/compiler/rustc_middle/src/arena.rs
@@ -14,10 +14,10 @@ macro_rules! arena_types {
             [] layouts: rustc_target::abi::Layout,
             // AdtDef are interned and compared by address
             [] adt_def: rustc_middle::ty::AdtDef,
-            [] steal_mir: rustc_middle::ty::steal::Steal<rustc_middle::mir::Body<$tcx>>,
+            [] steal_mir: rustc_data_structures::steal::Steal<rustc_middle::mir::Body<$tcx>>,
             [decode] mir: rustc_middle::mir::Body<$tcx>,
             [] steal_promoted:
-                rustc_middle::ty::steal::Steal<
+                rustc_data_structures::steal::Steal<
                     rustc_index::vec::IndexVec<
                         rustc_middle::mir::Promoted,
                         rustc_middle::mir::Body<$tcx>
diff --git a/compiler/rustc_middle/src/ich/impls_ty.rs b/compiler/rustc_middle/src/ich/impls_ty.rs
index 8f15c99f951..69bb4e23c4c 100644
--- a/compiler/rustc_middle/src/ich/impls_ty.rs
+++ b/compiler/rustc_middle/src/ich/impls_ty.rs
@@ -184,15 +184,6 @@ impl<'a> HashStable<StableHashingContext<'a>> for ty::FloatVid {
     }
 }
 
-impl<'a, T> HashStable<StableHashingContext<'a>> for ty::steal::Steal<T>
-where
-    T: HashStable<StableHashingContext<'a>>,
-{
-    fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
-        self.borrow().hash_stable(hcx, hasher);
-    }
-}
-
 impl<'a> HashStable<StableHashingContext<'a>> for crate::middle::privacy::AccessLevels {
     fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
         hcx.with_node_id_hashing_mode(NodeIdHashingMode::HashDefPath, |hcx| {
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index 3838e1b006f..b29e6d4a967 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -14,7 +14,6 @@ use crate::mir::interpret::{self, Allocation, ConstValue, Scalar};
 use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted};
 use crate::traits;
 use crate::ty::query::{self, TyCtxtAt};
-use crate::ty::steal::Steal;
 use crate::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSubsts};
 use crate::ty::TyKind::*;
 use crate::ty::{
@@ -33,6 +32,7 @@ use rustc_data_structures::sharded::{IntoPointer, ShardedHashMap};
 use rustc_data_structures::stable_hasher::{
     hash_stable_hashmap, HashStable, StableHasher, StableVec,
 };
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::sync::{self, Lock, Lrc, WorkerLocal};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::ErrorReported;
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index db11746971d..5e8a4a56db3 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -106,7 +106,6 @@ pub mod outlives;
 pub mod print;
 pub mod query;
 pub mod relate;
-pub mod steal;
 pub mod subst;
 pub mod trait_def;
 pub mod util;
diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs
index 7ba4d5a14df..187f86a52f4 100644
--- a/compiler/rustc_middle/src/ty/query/mod.rs
+++ b/compiler/rustc_middle/src/ty/query/mod.rs
@@ -28,13 +28,13 @@ use crate::traits::query::{
 };
 use crate::traits::specialization_graph;
 use crate::traits::{self, ImplSource};
-use crate::ty::steal::Steal;
 use crate::ty::subst::{GenericArg, SubstsRef};
 use crate::ty::util::AlwaysRequiresDrop;
 use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
 use rustc_data_structures::fingerprint::Fingerprint;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::stable_hasher::StableVec;
+use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::ErrorReported;
diff --git a/compiler/rustc_mir/src/transform/check_consts/ops.rs b/compiler/rustc_mir/src/transform/check_consts/ops.rs
index bd51136b8db..d2e65abfbc7 100644
--- a/compiler/rustc_mir/src/transform/check_consts/ops.rs
+++ b/compiler/rustc_mir/src/transform/check_consts/ops.rs
@@ -4,7 +4,6 @@ use rustc_errors::{struct_span_err, DiagnosticBuilder};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_middle::mir;
-use rustc_session::config::nightly_options;
 use rustc_session::parse::feature_err;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
@@ -104,7 +103,7 @@ impl NonConstOp for FnCallUnstable {
 
         if ccx.is_const_stable_const_fn() {
             err.help("Const-stable functions can only call other const-stable functions");
-        } else if nightly_options::is_nightly_build() {
+        } else if ccx.tcx.sess.is_nightly_build() {
             if let Some(feature) = feature {
                 err.help(&format!(
                     "add `#![feature({})]` to the crate attributes to enable",
diff --git a/compiler/rustc_mir/src/transform/mod.rs b/compiler/rustc_mir/src/transform/mod.rs
index 2f81db8af2f..e86d11e248f 100644
--- a/compiler/rustc_mir/src/transform/mod.rs
+++ b/compiler/rustc_mir/src/transform/mod.rs
@@ -1,6 +1,7 @@
 use crate::{shim, util};
 use required_consts::RequiredConstsVisitor;
 use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::steal::Steal;
 use rustc_hir as hir;
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
@@ -8,7 +9,6 @@ use rustc_index::vec::IndexVec;
 use rustc_middle::mir::visit::Visitor as _;
 use rustc_middle::mir::{traversal, Body, ConstQualifs, MirPhase, Promoted};
 use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::steal::Steal;
 use rustc_middle::ty::{self, TyCtxt, TypeFoldable};
 use rustc_span::{Span, Symbol};
 use std::borrow::Cow;
diff --git a/compiler/rustc_mir/src/transform/validate.rs b/compiler/rustc_mir/src/transform/validate.rs
index 876ecee80c6..75399e9c32c 100644
--- a/compiler/rustc_mir/src/transform/validate.rs
+++ b/compiler/rustc_mir/src/transform/validate.rs
@@ -181,6 +181,13 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
 
 impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
     fn visit_local(&mut self, local: &Local, context: PlaceContext, location: Location) {
+        if self.body.local_decls.get(*local).is_none() {
+            self.fail(
+                location,
+                format!("local {:?} has no corresponding declaration in `body.local_decls`", local),
+            );
+        }
+
         if self.reachable_blocks.contains(location.block) && context.is_use() {
             // Uses of locals must occur while the local's storage is allocated.
             self.storage_liveness.seek_after_primary_effect(location);
diff --git a/compiler/rustc_mir_build/src/build/mod.rs b/compiler/rustc_mir_build/src/build/mod.rs
index f9995f43f5a..6982cdccd52 100644
--- a/compiler/rustc_mir_build/src/build/mod.rs
+++ b/compiler/rustc_mir_build/src/build/mod.rs
@@ -24,7 +24,7 @@ use super::lints;
 crate fn mir_built<'tcx>(
     tcx: TyCtxt<'tcx>,
     def: ty::WithOptConstParam<LocalDefId>,
-) -> &'tcx ty::steal::Steal<Body<'tcx>> {
+) -> &'tcx rustc_data_structures::steal::Steal<Body<'tcx>> {
     if let Some(def) = def.try_upgrade(tcx) {
         return tcx.mir_built(def);
     }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 14ed93f1127..d395ae4ca2a 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -12,7 +12,6 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_hir::{HirId, Pat};
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_session::config::nightly_options;
 use rustc_session::lint::builtin::BINDINGS_WITH_VARIANT_NAME;
 use rustc_session::lint::builtin::{IRREFUTABLE_LET_PATTERNS, UNREACHABLE_PATTERNS};
 use rustc_session::parse::feature_err;
@@ -502,7 +501,7 @@ fn check_exhaustive<'p, 'tcx>(
                 so a wildcard `_` is necessary to match exhaustively",
             scrut_ty,
         ));
-        if nightly_options::is_nightly_build() {
+        if cx.tcx.sess.is_nightly_build() {
             err.help(&format!(
                 "add `#![feature(precise_pointer_size_matching)]` \
                     to the crate attributes to enable precise `{}` matching",
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 188bf227c42..ffbf786491d 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -1089,6 +1089,9 @@ impl<'a> Parser<'a> {
             self.parse_yield_expr(attrs)
         } else if self.eat_keyword(kw::Let) {
             self.parse_let_expr(attrs)
+        } else if self.eat_keyword(kw::Underscore) {
+            self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
+            Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs))
         } else if !self.unclosed_delims.is_empty() && self.check(&token::Semi) {
             // Don't complain about bare semicolons after unclosed braces
             // recovery in order to keep the error count down. Fixing the
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index b24c62b971a..e37c6418eb8 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -15,7 +15,6 @@ use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
-use rustc_session::config::nightly_options;
 use rustc_session::parse::feature_err;
 use rustc_span::{sym, Span, Symbol};
 
@@ -145,7 +144,7 @@ impl<'tcx> CheckConstVisitor<'tcx> {
                 //
                 // FIXME(ecstaticmorse): Maybe this could be incorporated into `feature_err`? This
                 // is a pretty narrow case, however.
-                if nightly_options::is_nightly_build() {
+                if tcx.sess.is_nightly_build() {
                     for gate in missing_secondary {
                         let note = format!(
                             "add `#![feature({})]` to the crate attributes to enable",
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 34145c3c138..493b9f15271 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -1163,9 +1163,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope);
         assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation");
 
-        let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id));
-        self.r.invocation_macro_rules_scopes.entry(invoc_id).or_default().insert(scope);
-        scope
+        self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id))
     }
 
     fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 00e6d5ca381..2473436a916 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -16,7 +16,6 @@ use rustc_hir::def::Namespace::{self, *};
 use rustc_hir::def::{self, CtorKind, CtorOf, DefKind};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
 use rustc_hir::PrimTy;
-use rustc_session::config::nightly_options;
 use rustc_session::parse::feature_err;
 use rustc_span::hygiene::MacroKind;
 use rustc_span::symbol::{kw, sym, Ident, Symbol};
@@ -890,7 +889,7 @@ impl<'a: 'ast, 'ast> LateResolutionVisitor<'a, '_, 'ast> {
             }
             (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => {
                 err.span_label(span, "type aliases cannot be used as traits");
-                if nightly_options::is_nightly_build() {
+                if self.r.session.is_nightly_build() {
                     let msg = "you might have meant to use `#![feature(trait_alias)]` instead of a \
                                `type` alias";
                     if let Some(span) = self.def_span(def_id) {
@@ -1675,7 +1674,7 @@ impl<'tcx> LifetimeContext<'_, 'tcx> {
                 _ => {}
             }
         }
-        if nightly_options::is_nightly_build()
+        if self.tcx.sess.is_nightly_build()
             && !self.tcx.features().in_band_lifetimes
             && suggests_in_band
         {
diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs
index 4e85c88c0e5..d18335ef2e6 100644
--- a/compiler/rustc_resolve/src/lib.rs
+++ b/compiler/rustc_resolve/src/lib.rs
@@ -976,9 +976,6 @@ pub struct Resolver<'a> {
     /// `macro_rules` scopes *produced* by expanding the macro invocations,
     /// include all the `macro_rules` items and other invocations generated by them.
     output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScopeRef<'a>>,
-    /// References to all `MacroRulesScope::Invocation(invoc_id)`s, used to update such scopes
-    /// when their corresponding `invoc_id`s get expanded.
-    invocation_macro_rules_scopes: FxHashMap<ExpnId, FxHashSet<MacroRulesScopeRef<'a>>>,
     /// Helper attributes that are in scope for the given expansion.
     helper_attrs: FxHashMap<ExpnId, Vec<Ident>>,
 
@@ -1310,7 +1307,6 @@ impl<'a> Resolver<'a> {
             non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)],
             invocation_parent_scopes: Default::default(),
             output_macro_rules_scopes: Default::default(),
-            invocation_macro_rules_scopes: Default::default(),
             helper_attrs: Default::default(),
             local_macro_def_scopes: FxHashMap::default(),
             name_already_seen: FxHashMap::default(),
@@ -1680,7 +1676,20 @@ impl<'a> Resolver<'a> {
                     !(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive))
                 }
                 Scope::DeriveHelpersCompat => true,
-                Scope::MacroRules(..) => true,
+                Scope::MacroRules(macro_rules_scope) => {
+                    // Use "path compression" on `macro_rules` scope chains. This is an optimization
+                    // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`.
+                    // As another consequence of this optimization visitors never observe invocation
+                    // scopes for macros that were already expanded.
+                    while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() {
+                        if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) {
+                            macro_rules_scope.set(next_scope.get());
+                        } else {
+                            break;
+                        }
+                    }
+                    true
+                }
                 Scope::CrateRoot => true,
                 Scope::Module(..) => true,
                 Scope::RegisteredAttrs => use_prelude,
@@ -1716,11 +1725,9 @@ impl<'a> Resolver<'a> {
                     MacroRulesScope::Binding(binding) => {
                         Scope::MacroRules(binding.parent_macro_rules_scope)
                     }
-                    MacroRulesScope::Invocation(invoc_id) => Scope::MacroRules(
-                        self.output_macro_rules_scopes.get(&invoc_id).cloned().unwrap_or_else(
-                            || self.invocation_parent_scopes[&invoc_id].macro_rules,
-                        ),
-                    ),
+                    MacroRulesScope::Invocation(invoc_id) => {
+                        Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
+                    }
                     MacroRulesScope::Empty => Scope::Module(module),
                 },
                 Scope::CrateRoot => match ns {
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 6bc9419ea84..e052b6b3345 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -62,8 +62,8 @@ pub enum MacroRulesScope<'a> {
 }
 
 /// `macro_rules!` scopes are always kept by reference and inside a cell.
-/// The reason is that we update all scopes with value `MacroRulesScope::Invocation(invoc_id)`
-/// in-place immediately after `invoc_id` gets expanded.
+/// The reason is that we update scopes with value `MacroRulesScope::Invocation(invoc_id)`
+/// in-place after `invoc_id` gets expanded.
 /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains,
 /// which usually grow lineraly with the number of macro invocations
 /// in a module (including derives) and hurt performance.
@@ -173,22 +173,6 @@ impl<'a> ResolverExpand for Resolver<'a> {
         let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope);
         self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
 
-        // Update all `macro_rules` scopes referring to this invocation. This is an optimization
-        // used to avoid long scope chains, see the comments on `MacroRulesScopeRef`.
-        if let Some(invocation_scopes) = self.invocation_macro_rules_scopes.remove(&expansion) {
-            for invocation_scope in &invocation_scopes {
-                invocation_scope.set(output_macro_rules_scope.get());
-            }
-            // All `macro_rules` scopes that previously referred to `expansion`
-            // are now rerouted to its output scope, if it's also an invocation.
-            if let MacroRulesScope::Invocation(invoc_id) = output_macro_rules_scope.get() {
-                self.invocation_macro_rules_scopes
-                    .entry(invoc_id)
-                    .or_default()
-                    .extend(invocation_scopes);
-            }
-        }
-
         parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
     }
 
@@ -687,11 +671,7 @@ impl<'a> Resolver<'a> {
                         {
                             Ok((macro_rules_binding.binding, Flags::MACRO_RULES))
                         }
-                        MacroRulesScope::Invocation(invoc_id)
-                            if !this.output_macro_rules_scopes.contains_key(&invoc_id) =>
-                        {
-                            Err(Determinacy::Undetermined)
-                        }
+                        MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined),
                         _ => Err(Determinacy::Determined),
                     },
                     Scope::CrateRoot => {
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 87687339370..db16a90cc60 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1250,7 +1250,7 @@ fn parse_crate_edition(matches: &getopts::Matches) -> Edition {
         None => DEFAULT_EDITION,
     };
 
-    if !edition.is_stable() && !nightly_options::is_nightly_build() {
+    if !edition.is_stable() && !nightly_options::match_is_nightly_build(matches) {
         early_error(
             ErrorOutputType::default(),
             &format!(
@@ -1547,7 +1547,9 @@ fn parse_libs(
                     );
                 }
             };
-            if kind == NativeLibKind::StaticNoBundle && !nightly_options::is_nightly_build() {
+            if kind == NativeLibKind::StaticNoBundle
+                && !nightly_options::match_is_nightly_build(matches)
+            {
                 early_error(
                     error_format,
                     "the library kind 'static-nobundle' is only \
@@ -1836,10 +1838,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
         cg,
         error_format,
         externs,
+        unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
         crate_name,
         alt_std_name: None,
         libs,
-        unstable_features: UnstableFeatures::from_environment(),
         debug_assertions,
         actually_rustdoc: false,
         trimmed_def_paths: TrimmedDefPaths::default(),
@@ -1960,17 +1962,21 @@ pub mod nightly_options {
     use rustc_feature::UnstableFeatures;
 
     pub fn is_unstable_enabled(matches: &getopts::Matches) -> bool {
-        is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
+        match_is_nightly_build(matches)
+            && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
     }
 
-    pub fn is_nightly_build() -> bool {
-        UnstableFeatures::from_environment().is_nightly_build()
+    pub fn match_is_nightly_build(matches: &getopts::Matches) -> bool {
+        is_nightly_build(matches.opt_str("crate-name").as_deref())
+    }
+
+    pub fn is_nightly_build(krate: Option<&str>) -> bool {
+        UnstableFeatures::from_environment(krate).is_nightly_build()
     }
 
     pub fn check_nightly_options(matches: &getopts::Matches, flags: &[RustcOptGroup]) {
         let has_z_unstable_option = matches.opt_strs("Z").iter().any(|x| *x == "unstable-options");
-        let really_allows_unstable_options =
-            UnstableFeatures::from_environment().is_nightly_build();
+        let really_allows_unstable_options = match_is_nightly_build(matches);
 
         for opt in flags.iter() {
             if opt.stability == OptionStability::Stable {
diff --git a/compiler/rustc_session/src/parse.rs b/compiler/rustc_session/src/parse.rs
index 6f10d0c4b89..66c3738fb5b 100644
--- a/compiler/rustc_session/src/parse.rs
+++ b/compiler/rustc_session/src/parse.rs
@@ -4,7 +4,7 @@
 use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
 use rustc_ast::node_id::NodeId;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_data_structures::sync::{Lock, Lrc, OnceCell};
+use rustc_data_structures::sync::{Lock, Lrc};
 use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
 use rustc_errors::{error_code, Applicability, DiagnosticBuilder};
 use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
@@ -129,7 +129,6 @@ pub struct ParseSess {
     /// operation token that followed it, but that the parser cannot identify without further
     /// analysis.
     pub ambiguous_block_expr_parse: Lock<FxHashMap<Span, Span>>,
-    pub injected_crate_name: OnceCell<Symbol>,
     pub gated_spans: GatedSpans,
     pub symbol_gallery: SymbolGallery,
     /// The parser has reached `Eof` due to an unclosed brace. Used to silence unnecessary errors.
@@ -150,7 +149,7 @@ impl ParseSess {
     pub fn with_span_handler(handler: Handler, source_map: Lrc<SourceMap>) -> Self {
         Self {
             span_diagnostic: handler,
-            unstable_features: UnstableFeatures::from_environment(),
+            unstable_features: UnstableFeatures::from_environment(None),
             config: FxHashSet::default(),
             edition: ExpnId::root().expn_data().edition,
             raw_identifier_spans: Lock::new(Vec::new()),
@@ -158,7 +157,6 @@ impl ParseSess {
             source_map,
             buffered_lints: Lock::new(vec![]),
             ambiguous_block_expr_parse: Lock::new(FxHashMap::default()),
-            injected_crate_name: OnceCell::new(),
             gated_spans: GatedSpans::default(),
             symbol_gallery: SymbolGallery::default(),
             reached_eof: Lock::new(false),
diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs
index 98b7f03df38..419d1447764 100644
--- a/compiler/rustc_session/src/session.rs
+++ b/compiler/rustc_session/src/session.rs
@@ -745,6 +745,9 @@ impl Session {
     pub fn unstable_options(&self) -> bool {
         self.opts.debugging_opts.unstable_options
     }
+    pub fn is_nightly_build(&self) -> bool {
+        self.opts.unstable_features.is_nightly_build()
+    }
     pub fn overflow_checks(&self) -> bool {
         self.opts
             .cg
diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs
index 914fa1e52c2..37538e7aac4 100644
--- a/compiler/rustc_trait_selection/src/opaque_types.rs
+++ b/compiler/rustc_trait_selection/src/opaque_types.rs
@@ -12,7 +12,6 @@ use rustc_infer::infer::{self, InferCtxt, InferOk};
 use rustc_middle::ty::fold::{BottomUpFolder, TypeFoldable, TypeFolder, TypeVisitor};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, Subst, SubstsRef};
 use rustc_middle::ty::{self, Ty, TyCtxt};
-use rustc_session::config::nightly_options;
 use rustc_span::Span;
 
 use std::ops::ControlFlow;
@@ -602,7 +601,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
         };
         err.span_label(span, label);
 
-        if nightly_options::is_nightly_build() {
+        if self.tcx.sess.is_nightly_build() {
             err.help("add #![feature(member_constraints)] to the crate attributes to enable");
         }
 
diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs
index 3bfb2d3f1b0..301ede66006 100644
--- a/compiler/rustc_typeck/src/astconv/generics.rs
+++ b/compiler/rustc_typeck/src/astconv/generics.rs
@@ -1,12 +1,13 @@
 use crate::astconv::{
-    AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition,
+    AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
+    GenericArgCountResult, GenericArgPosition,
 };
 use crate::errors::AssocTypeBindingNotAllowed;
 use rustc_ast::ast::ParamKindOrd;
 use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorReported};
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{GenericArg, GenericArgs};
+use rustc_hir::GenericArg;
 use rustc_middle::ty::{
     self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt,
 };
@@ -90,20 +91,14 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     ///   instantiate a `GenericArg`.
     /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then
     ///   creates a suitable inference variable.
-    pub fn create_substs_for_generic_args<'b>(
+    pub fn create_substs_for_generic_args<'a>(
         tcx: TyCtxt<'tcx>,
         def_id: DefId,
         parent_substs: &[subst::GenericArg<'tcx>],
         has_self: bool,
         self_ty: Option<Ty<'tcx>>,
         arg_count: GenericArgCountResult,
-        args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool),
-        mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>,
-        mut inferred_kind: impl FnMut(
-            Option<&[subst::GenericArg<'tcx>]>,
-            &GenericParamDef,
-            bool,
-        ) -> subst::GenericArg<'tcx>,
+        ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>,
     ) -> SubstsRef<'tcx> {
         // Collect the segments of the path; we need to substitute arguments
         // for parameters throughout the entire path (wherever there are
@@ -142,7 +137,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             substs.push(
                                 self_ty
                                     .map(|ty| ty.into())
-                                    .unwrap_or_else(|| inferred_kind(None, param, true)),
+                                    .unwrap_or_else(|| ctx.inferred_kind(None, param, true)),
                             );
                             params.next();
                         }
@@ -151,7 +146,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             }
 
             // Check whether this segment takes generic arguments and the user has provided any.
-            let (generic_args, infer_args) = args_for_def_id(def_id);
+            let (generic_args, infer_args) = ctx.args_for_def_id(def_id);
 
             let mut args =
                 generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).peekable();
@@ -173,7 +168,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _)
                             | (GenericArg::Type(_), GenericParamDefKind::Type { .. }, _)
                             | (GenericArg::Const(_), GenericParamDefKind::Const, _) => {
-                                substs.push(provided_kind(param, arg));
+                                substs.push(ctx.provided_kind(param, arg));
                                 args.next();
                                 params.next();
                             }
@@ -184,7 +179,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             ) => {
                                 // We expected a lifetime argument, but got a type or const
                                 // argument. That means we're inferring the lifetimes.
-                                substs.push(inferred_kind(None, param, infer_args));
+                                substs.push(ctx.inferred_kind(None, param, infer_args));
                                 force_infer_lt = Some(arg);
                                 params.next();
                             }
@@ -302,7 +297,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                     (None, Some(&param)) => {
                         // If there are fewer arguments than parameters, it means
                         // we're inferring the remaining arguments.
-                        substs.push(inferred_kind(Some(&substs), param, infer_args));
+                        substs.push(ctx.inferred_kind(Some(&substs), param, infer_args));
                         params.next();
                     }
 
diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs
index 07e523af3eb..89c5adfa14c 100644
--- a/compiler/rustc_typeck/src/astconv/mod.rs
+++ b/compiler/rustc_typeck/src/astconv/mod.rs
@@ -165,6 +165,23 @@ pub struct GenericArgCountResult {
     pub correct: Result<(), GenericArgCountMismatch>,
 }
 
+pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> {
+    fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool);
+
+    fn provided_kind(
+        &mut self,
+        param: &ty::GenericParamDef,
+        arg: &GenericArg<'_>,
+    ) -> subst::GenericArg<'tcx>;
+
+    fn inferred_kind(
+        &mut self,
+        substs: Option<&[subst::GenericArg<'tcx>]>,
+        param: &ty::GenericParamDef,
+        infer_args: bool,
+    ) -> subst::GenericArg<'tcx>;
+}
+
 impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
     pub fn ast_region_to_region(
         &self,
@@ -321,81 +338,102 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
         );
 
         let is_object = self_ty.map_or(false, |ty| ty == self.tcx().types.trait_object_dummy_self);
-        let default_needs_object_self = |param: &ty::GenericParamDef| {
-            if let GenericParamDefKind::Type { has_default, .. } = param.kind {
-                if is_object && has_default {
-                    let default_ty = tcx.at(span).type_of(param.def_id);
-                    let self_param = tcx.types.self_param;
-                    if default_ty.walk().any(|arg| arg == self_param.into()) {
-                        // There is no suitable inference default for a type parameter
-                        // that references self, in an object type.
-                        return true;
+
+        struct SubstsForAstPathCtxt<'a, 'tcx> {
+            astconv: &'a (dyn AstConv<'tcx> + 'a),
+            def_id: DefId,
+            generic_args: &'a GenericArgs<'a>,
+            span: Span,
+            missing_type_params: Vec<String>,
+            inferred_params: Vec<Span>,
+            infer_args: bool,
+            is_object: bool,
+        }
+
+        impl<'tcx, 'a> SubstsForAstPathCtxt<'tcx, 'a> {
+            fn default_needs_object_self(&mut self, param: &ty::GenericParamDef) -> bool {
+                let tcx = self.astconv.tcx();
+                if let GenericParamDefKind::Type { has_default, .. } = param.kind {
+                    if self.is_object && has_default {
+                        let default_ty = tcx.at(self.span).type_of(param.def_id);
+                        let self_param = tcx.types.self_param;
+                        if default_ty.walk().any(|arg| arg == self_param.into()) {
+                            // There is no suitable inference default for a type parameter
+                            // that references self, in an object type.
+                            return true;
+                        }
                     }
                 }
-            }
 
-            false
-        };
+                false
+            }
+        }
 
-        let mut missing_type_params = vec![];
-        let mut inferred_params = vec![];
-        let substs = Self::create_substs_for_generic_args(
-            tcx,
-            def_id,
-            parent_substs,
-            self_ty.is_some(),
-            self_ty,
-            arg_count.clone(),
-            // Provide the generic args, and whether types should be inferred.
-            |did| {
-                if did == def_id {
-                    (Some(generic_args), infer_args)
+        impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> {
+            fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) {
+                if did == self.def_id {
+                    (Some(self.generic_args), self.infer_args)
                 } else {
                     // The last component of this tuple is unimportant.
                     (None, false)
                 }
-            },
-            // Provide substitutions for parameters for which (valid) arguments have been provided.
-            |param, arg| match (&param.kind, arg) {
-                (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
-                    self.ast_region_to_region(&lt, Some(param)).into()
-                }
-                (GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
-                    if *has_default {
-                        tcx.check_optional_stability(
-                            param.def_id,
-                            Some(arg.id()),
-                            arg.span(),
-                            |_, _| {
-                                // Default generic parameters may not be marked
-                                // with stability attributes, i.e. when the
-                                // default parameter was defined at the same time
-                                // as the rest of the type. As such, we ignore missing
-                                // stability attributes.
+            }
+
+            fn provided_kind(
+                &mut self,
+                param: &ty::GenericParamDef,
+                arg: &GenericArg<'_>,
+            ) -> subst::GenericArg<'tcx> {
+                let tcx = self.astconv.tcx();
+                match (&param.kind, arg) {
+                    (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+                        self.astconv.ast_region_to_region(&lt, Some(param)).into()
+                    }
+                    (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
+                        if has_default {
+                            tcx.check_optional_stability(
+                                param.def_id,
+                                Some(arg.id()),
+                                arg.span(),
+                                |_, _| {
+                                    // Default generic parameters may not be marked
+                                    // with stability attributes, i.e. when the
+                                    // default parameter was defined at the same time
+                                    // as the rest of the type. As such, we ignore missing
+                                    // stability attributes.
+                                },
+                            )
+                        }
+                        if let (hir::TyKind::Infer, false) =
+                            (&ty.kind, self.astconv.allow_ty_infer())
+                        {
+                            self.inferred_params.push(ty.span);
+                            tcx.ty_error().into()
+                        } else {
+                            self.astconv.ast_ty_to_ty(&ty).into()
+                        }
+                    }
+                    (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
+                        ty::Const::from_opt_const_arg_anon_const(
+                            tcx,
+                            ty::WithOptConstParam {
+                                did: tcx.hir().local_def_id(ct.value.hir_id),
+                                const_param_did: Some(param.def_id),
                             },
                         )
+                        .into()
                     }
-                    if let (hir::TyKind::Infer, false) = (&ty.kind, self.allow_ty_infer()) {
-                        inferred_params.push(ty.span);
-                        tcx.ty_error().into()
-                    } else {
-                        self.ast_ty_to_ty(&ty).into()
-                    }
-                }
-                (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
-                    ty::Const::from_opt_const_arg_anon_const(
-                        tcx,
-                        ty::WithOptConstParam {
-                            did: tcx.hir().local_def_id(ct.value.hir_id),
-                            const_param_did: Some(param.def_id),
-                        },
-                    )
-                    .into()
+                    _ => unreachable!(),
                 }
-                _ => unreachable!(),
-            },
-            // Provide substitutions for parameters for which arguments are inferred.
-            |substs, param, infer_args| {
+            }
+
+            fn inferred_kind(
+                &mut self,
+                substs: Option<&[subst::GenericArg<'tcx>]>,
+                param: &ty::GenericParamDef,
+                infer_args: bool,
+            ) -> subst::GenericArg<'tcx> {
+                let tcx = self.astconv.tcx();
                 match param.kind {
                     GenericParamDefKind::Lifetime => tcx.lifetimes.re_static.into(),
                     GenericParamDefKind::Type { has_default, .. } => {
@@ -407,48 +445,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
                             // other type parameters may reference `Self` in their
                             // defaults. This will lead to an ICE if we are not
                             // careful!
-                            if default_needs_object_self(param) {
-                                missing_type_params.push(param.name.to_string());
+                            if self.default_needs_object_self(param) {
+                                self.missing_type_params.push(param.name.to_string());
                                 tcx.ty_error().into()
                             } else {
                                 // This is a default type parameter.
-                                self.normalize_ty(
-                                    span,
-                                    tcx.at(span).type_of(param.def_id).subst_spanned(
-                                        tcx,
-                                        substs.unwrap(),
-                                        Some(span),
-                                    ),
-                                )
-                                .into()
+                                self.astconv
+                                    .normalize_ty(
+                                        self.span,
+                                        tcx.at(self.span).type_of(param.def_id).subst_spanned(
+                                            tcx,
+                                            substs.unwrap(),
+                                            Some(self.span),
+                                        ),
+                                    )
+                                    .into()
                             }
                         } else if infer_args {
                             // No type parameters were provided, we can infer all.
-                            let param =
-                                if !default_needs_object_self(param) { Some(param) } else { None };
-                            self.ty_infer(param, span).into()
+                            let param = if !self.default_needs_object_self(param) {
+                                Some(param)
+                            } else {
+                                None
+                            };
+                            self.astconv.ty_infer(param, self.span).into()
                         } else {
                             // We've already errored above about the mismatch.
                             tcx.ty_error().into()
                         }
                     }
                     GenericParamDefKind::Const => {
-                        let ty = tcx.at(span).type_of(param.def_id);
+                        let ty = tcx.at(self.span).type_of(param.def_id);
                         // FIXME(const_generics:defaults)
                         if infer_args {
                             // No const parameters were provided, we can infer all.
-                            self.ct_infer(ty, Some(param), span).into()
+                            self.astconv.ct_infer(ty, Some(param), self.span).into()
                         } else {
                             // We've already errored above about the mismatch.
                             tcx.const_error(ty).into()
                         }
                     }
                 }
-            },
+            }
+        }
+
+        let mut substs_ctx = SubstsForAstPathCtxt {
+            astconv: self,
+            def_id,
+            span,
+            generic_args,
+            missing_type_params: vec![],
+            inferred_params: vec![],
+            infer_args,
+            is_object,
+        };
+        let substs = Self::create_substs_for_generic_args(
+            tcx,
+            def_id,
+            parent_substs,
+            self_ty.is_some(),
+            self_ty,
+            arg_count.clone(),
+            &mut substs_ctx,
         );
 
         self.complain_about_missing_type_params(
-            missing_type_params,
+            substs_ctx.missing_type_params,
             def_id,
             span,
             generic_args.args.is_empty(),
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
index 0bb7b464f16..300ee8f85f8 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs
@@ -1,5 +1,6 @@
 use crate::astconv::{
-    AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, PathSeg,
+    AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
+    GenericArgCountResult, PathSeg,
 };
 use crate::check::callee::{self, DeferredCallResolution};
 use crate::check::method::{self, MethodCallee, SelfSource};
@@ -1298,76 +1299,108 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             },
         };
 
-        let substs = self_ctor_substs.unwrap_or_else(|| {
-            AstConv::create_substs_for_generic_args(
-                tcx,
-                def_id,
-                &[][..],
-                has_self,
-                self_ty,
-                arg_count,
-                // Provide the generic args, and whether types should be inferred.
-                |def_id| {
-                    if let Some(&PathSeg(_, index)) =
-                        path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
-                    {
-                        // If we've encountered an `impl Trait`-related error, we're just
-                        // going to infer the arguments for better error messages.
-                        if !infer_args_for_err.contains(&index) {
-                            // Check whether the user has provided generic arguments.
-                            if let Some(ref data) = segments[index].args {
-                                return (Some(data), segments[index].infer_args);
-                            }
+        struct CreateCtorSubstsContext<'a, 'tcx> {
+            fcx: &'a FnCtxt<'a, 'tcx>,
+            span: Span,
+            path_segs: &'a [PathSeg],
+            infer_args_for_err: &'a FxHashSet<usize>,
+            segments: &'a [hir::PathSegment<'a>],
+        }
+        impl<'tcx, 'a> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for CreateCtorSubstsContext<'a, 'tcx> {
+            fn args_for_def_id(
+                &mut self,
+                def_id: DefId,
+            ) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
+                if let Some(&PathSeg(_, index)) =
+                    self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id)
+                {
+                    // If we've encountered an `impl Trait`-related error, we're just
+                    // going to infer the arguments for better error messages.
+                    if !self.infer_args_for_err.contains(&index) {
+                        // Check whether the user has provided generic arguments.
+                        if let Some(ref data) = self.segments[index].args {
+                            return (Some(data), self.segments[index].infer_args);
                         }
-                        return (None, segments[index].infer_args);
                     }
+                    return (None, self.segments[index].infer_args);
+                }
 
-                    (None, true)
-                },
-                // Provide substitutions for parameters for which (valid) arguments have been provided.
-                |param, arg| match (&param.kind, arg) {
+                (None, true)
+            }
+
+            fn provided_kind(
+                &mut self,
+                param: &ty::GenericParamDef,
+                arg: &GenericArg<'_>,
+            ) -> subst::GenericArg<'tcx> {
+                match (&param.kind, arg) {
                     (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
-                        AstConv::ast_region_to_region(self, lt, Some(param)).into()
+                        AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into()
                     }
                     (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
-                        self.to_ty(ty).into()
+                        self.fcx.to_ty(ty).into()
                     }
                     (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
-                        self.const_arg_to_const(&ct.value, param.def_id).into()
+                        self.fcx.const_arg_to_const(&ct.value, param.def_id).into()
                     }
                     _ => unreachable!(),
-                },
-                // Provide substitutions for parameters for which arguments are inferred.
-                |substs, param, infer_args| {
-                    match param.kind {
-                        GenericParamDefKind::Lifetime => {
-                            self.re_infer(Some(param), span).unwrap().into()
-                        }
-                        GenericParamDefKind::Type { has_default, .. } => {
-                            if !infer_args && has_default {
-                                // If we have a default, then we it doesn't matter that we're not
-                                // inferring the type arguments: we provide the default where any
-                                // is missing.
-                                let default = tcx.type_of(param.def_id);
-                                self.normalize_ty(
-                                    span,
-                                    default.subst_spanned(tcx, substs.unwrap(), Some(span)),
+                }
+            }
+
+            fn inferred_kind(
+                &mut self,
+                substs: Option<&[subst::GenericArg<'tcx>]>,
+                param: &ty::GenericParamDef,
+                infer_args: bool,
+            ) -> subst::GenericArg<'tcx> {
+                let tcx = self.fcx.tcx();
+                match param.kind {
+                    GenericParamDefKind::Lifetime => {
+                        self.fcx.re_infer(Some(param), self.span).unwrap().into()
+                    }
+                    GenericParamDefKind::Type { has_default, .. } => {
+                        if !infer_args && has_default {
+                            // If we have a default, then we it doesn't matter that we're not
+                            // inferring the type arguments: we provide the default where any
+                            // is missing.
+                            let default = tcx.type_of(param.def_id);
+                            self.fcx
+                                .normalize_ty(
+                                    self.span,
+                                    default.subst_spanned(tcx, substs.unwrap(), Some(self.span)),
                                 )
                                 .into()
-                            } else {
-                                // If no type arguments were provided, we have to infer them.
-                                // This case also occurs as a result of some malformed input, e.g.
-                                // a lifetime argument being given instead of a type parameter.
-                                // Using inference instead of `Error` gives better error messages.
-                                self.var_for_def(span, param)
-                            }
-                        }
-                        GenericParamDefKind::Const => {
-                            // FIXME(const_generics:defaults)
-                            // No const parameters were provided, we have to infer them.
-                            self.var_for_def(span, param)
+                        } else {
+                            // If no type arguments were provided, we have to infer them.
+                            // This case also occurs as a result of some malformed input, e.g.
+                            // a lifetime argument being given instead of a type parameter.
+                            // Using inference instead of `Error` gives better error messages.
+                            self.fcx.var_for_def(self.span, param)
                         }
                     }
+                    GenericParamDefKind::Const => {
+                        // FIXME(const_generics:defaults)
+                        // No const parameters were provided, we have to infer them.
+                        self.fcx.var_for_def(self.span, param)
+                    }
+                }
+            }
+        }
+
+        let substs = self_ctor_substs.unwrap_or_else(|| {
+            AstConv::create_substs_for_generic_args(
+                tcx,
+                def_id,
+                &[][..],
+                has_self,
+                self_ty,
+                arg_count,
+                &mut CreateCtorSubstsContext {
+                    fcx: self,
+                    span,
+                    path_segs: &path_segs,
+                    infer_args_for_err: &infer_args_for_err,
+                    segments,
                 },
             )
         });
diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs
index fd2700b85e2..fb048abc0e9 100644
--- a/compiler/rustc_typeck/src/check/method/confirm.rs
+++ b/compiler/rustc_typeck/src/check/method/confirm.rs
@@ -1,6 +1,6 @@
 use super::{probe, MethodCallee};
 
-use crate::astconv::AstConv;
+use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt};
 use crate::check::{callee, FnCtxt};
 use crate::hir::def_id::DefId;
 use crate::hir::GenericArg;
@@ -10,7 +10,7 @@ use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast};
 use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::subst::{Subst, SubstsRef};
+use rustc_middle::ty::subst::{self, Subst, SubstsRef};
 use rustc_middle::ty::{self, GenericParamDefKind, Ty};
 use rustc_span::Span;
 use rustc_trait_selection::traits;
@@ -307,6 +307,52 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
         // parameters from the type and those from the method.
         assert_eq!(generics.parent_count, parent_substs.len());
 
+        struct MethodSubstsCtxt<'a, 'tcx> {
+            cfcx: &'a ConfirmContext<'a, 'tcx>,
+            pick: &'a probe::Pick<'tcx>,
+            seg: &'a hir::PathSegment<'a>,
+        }
+        impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> {
+            fn args_for_def_id(
+                &mut self,
+                def_id: DefId,
+            ) -> (Option<&'a hir::GenericArgs<'a>>, bool) {
+                if def_id == self.pick.item.def_id {
+                    if let Some(ref data) = self.seg.args {
+                        return (Some(data), false);
+                    }
+                }
+                (None, false)
+            }
+
+            fn provided_kind(
+                &mut self,
+                param: &ty::GenericParamDef,
+                arg: &GenericArg<'_>,
+            ) -> subst::GenericArg<'tcx> {
+                match (&param.kind, arg) {
+                    (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+                        AstConv::ast_region_to_region(self.cfcx.fcx, lt, Some(param)).into()
+                    }
+                    (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => {
+                        self.cfcx.to_ty(ty).into()
+                    }
+                    (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
+                        self.cfcx.const_arg_to_const(&ct.value, param.def_id).into()
+                    }
+                    _ => unreachable!(),
+                }
+            }
+
+            fn inferred_kind(
+                &mut self,
+                _substs: Option<&[subst::GenericArg<'tcx>]>,
+                param: &ty::GenericParamDef,
+                _infer_args: bool,
+            ) -> subst::GenericArg<'tcx> {
+                self.cfcx.var_for_def(self.cfcx.span, param)
+            }
+        }
         AstConv::create_substs_for_generic_args(
             self.tcx,
             pick.item.def_id,
@@ -314,29 +360,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> {
             false,
             None,
             arg_count_correct,
-            // Provide the generic args, and whether types should be inferred.
-            |def_id| {
-                // The last component of the returned tuple here is unimportant.
-                if def_id == pick.item.def_id {
-                    if let Some(ref data) = seg.args {
-                        return (Some(data), false);
-                    }
-                }
-                (None, false)
-            },
-            // Provide substitutions for parameters for which (valid) arguments have been provided.
-            |param, arg| match (&param.kind, arg) {
-                (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
-                    AstConv::ast_region_to_region(self.fcx, lt, Some(param)).into()
-                }
-                (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => self.to_ty(ty).into(),
-                (GenericParamDefKind::Const, GenericArg::Const(ct)) => {
-                    self.const_arg_to_const(&ct.value, param.def_id).into()
-                }
-                _ => unreachable!(),
-            },
-            // Provide substitutions for parameters for which arguments are inferred.
-            |_, param, _| self.var_for_def(self.span, param),
+            &mut MethodSubstsCtxt { cfcx: self, pick, seg },
         )
     }
 
diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs
index 713b24e583a..84efae694db 100644
--- a/compiler/rustc_typeck/src/check/method/probe.rs
+++ b/compiler/rustc_typeck/src/check/method/probe.rs
@@ -25,7 +25,6 @@ use rustc_middle::ty::GenericParamDefKind;
 use rustc_middle::ty::{
     self, ParamEnvAnd, ToPolyTraitRef, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness,
 };
-use rustc_session::config::nightly_options;
 use rustc_session::lint;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP};
@@ -1272,7 +1271,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     self.tcx.def_path_str(stable_pick.item.def_id),
                 ));
 
-                if nightly_options::is_nightly_build() {
+                if self.tcx.sess.is_nightly_build() {
                     for (candidate, feature) in unstable_candidates {
                         diag.help(&format!(
                             "add `#![feature({})]` to the crate attributes to enable `{}`",
diff --git a/library/alloc/src/collections/btree/map.rs b/library/alloc/src/collections/btree/map.rs
index 49122f53d33..7151d3763f0 100644
--- a/library/alloc/src/collections/btree/map.rs
+++ b/library/alloc/src/collections/btree/map.rs
@@ -863,6 +863,30 @@ impl<K: Ord, V> BTreeMap<K, V> {
         }
     }
 
+    /// Retains only the elements specified by the predicate.
+    ///
+    /// In other words, remove all pairs `(k, v)` such that `f(&k, &mut v)` returns `false`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(btree_retain)]
+    /// use std::collections::BTreeMap;
+    ///
+    /// let mut map: BTreeMap<i32, i32> = (0..8).map(|x| (x, x*10)).collect();
+    /// // Keep only the elements with even-numbered keys.
+    /// map.retain(|&k, _| k % 2 == 0);
+    /// assert!(map.into_iter().eq(vec![(0, 0), (2, 20), (4, 40), (6, 60)]));
+    /// ```
+    #[inline]
+    #[unstable(feature = "btree_retain", issue = "79025")]
+    pub fn retain<F>(&mut self, mut f: F)
+    where
+        F: FnMut(&K, &mut V) -> bool,
+    {
+        self.drain_filter(|k, v| !f(k, v));
+    }
+
     /// Moves all elements from `other` into `Self`, leaving `other` empty.
     ///
     /// # Examples
diff --git a/library/alloc/src/collections/btree/map/tests.rs b/library/alloc/src/collections/btree/map/tests.rs
index dd3ebcccf76..11dbb584abd 100644
--- a/library/alloc/src/collections/btree/map/tests.rs
+++ b/library/alloc/src/collections/btree/map/tests.rs
@@ -808,6 +808,17 @@ fn test_range_mut() {
     map.check();
 }
 
+#[test]
+fn test_retain() {
+    let mut map: BTreeMap<i32, i32> = (0..100).map(|x| (x, x * 10)).collect();
+
+    map.retain(|&k, _| k % 2 == 0);
+    assert_eq!(map.len(), 50);
+    assert_eq!(map[&2], 20);
+    assert_eq!(map[&4], 40);
+    assert_eq!(map[&6], 60);
+}
+
 mod test_drain_filter {
     use super::*;
 
diff --git a/library/alloc/src/collections/btree/set.rs b/library/alloc/src/collections/btree/set.rs
index 684019f8f5f..1a807100653 100644
--- a/library/alloc/src/collections/btree/set.rs
+++ b/library/alloc/src/collections/btree/set.rs
@@ -798,6 +798,30 @@ impl<T: Ord> BTreeSet<T> {
         Recover::take(&mut self.map, value)
     }
 
+    /// Retains only the elements specified by the predicate.
+    ///
+    /// In other words, remove all elements `e` such that `f(&e)` returns `false`.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(btree_retain)]
+    /// use std::collections::BTreeSet;
+    ///
+    /// let xs = [1, 2, 3, 4, 5, 6];
+    /// let mut set: BTreeSet<i32> = xs.iter().cloned().collect();
+    /// // Keep only the even numbers.
+    /// set.retain(|&k| k % 2 == 0);
+    /// assert!(set.iter().eq([2, 4, 6].iter()));
+    /// ```
+    #[unstable(feature = "btree_retain", issue = "79025")]
+    pub fn retain<F>(&mut self, mut f: F)
+    where
+        F: FnMut(&T) -> bool,
+    {
+        self.drain_filter(|v| !f(v));
+    }
+
     /// Moves all elements from `other` into `Self`, leaving `other` empty.
     ///
     /// # Examples
diff --git a/library/alloc/src/collections/btree/set/tests.rs b/library/alloc/src/collections/btree/set/tests.rs
index 52cde8299e4..ef40a048a38 100644
--- a/library/alloc/src/collections/btree/set/tests.rs
+++ b/library/alloc/src/collections/btree/set/tests.rs
@@ -325,6 +325,17 @@ fn test_is_subset() {
 }
 
 #[test]
+fn test_retain() {
+    let xs = [1, 2, 3, 4, 5, 6];
+    let mut set: BTreeSet<i32> = xs.iter().cloned().collect();
+    set.retain(|&k| k % 2 == 0);
+    assert_eq!(set.len(), 3);
+    assert!(set.contains(&2));
+    assert!(set.contains(&4));
+    assert!(set.contains(&6));
+}
+
+#[test]
 fn test_drain_filter() {
     let mut x: BTreeSet<_> = [1].iter().copied().collect();
     let mut y: BTreeSet<_> = [1].iter().copied().collect();
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index 3d724c14842..508d785834f 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -19,7 +19,7 @@ use crate::compile;
 use crate::config::TargetSelection;
 use crate::dist;
 use crate::doc;
-use crate::flags::Subcommand;
+use crate::flags::{Color, Subcommand};
 use crate::install;
 use crate::native;
 use crate::run;
@@ -811,6 +811,16 @@ impl<'a> Builder<'a> {
             cargo.env("REAL_LIBRARY_PATH", e);
         }
 
+        match self.build.config.color {
+            Color::Always => {
+                cargo.arg("--color=always");
+            }
+            Color::Never => {
+                cargo.arg("--color=never");
+            }
+            Color::Auto => {} // nothing to do
+        }
+
         if cmd != "install" {
             cargo.arg("--target").arg(target.rustc_target_arg());
         } else {
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index ecca12108b6..f65b2b2c79f 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -232,6 +232,11 @@ impl Step for CodegenBackend {
             .arg(builder.src.join(format!("compiler/rustc_codegen_{}/Cargo.toml", backend)));
         rustc_cargo_env(builder, &mut cargo, target);
 
+        builder.info(&format!(
+            "Checking {} artifacts ({} -> {})",
+            backend, &compiler.host.triple, target.triple
+        ));
+
         run_cargo(
             builder,
             cargo,
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index c0753d88504..94319a6d1e9 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -13,8 +13,8 @@ use std::path::{Path, PathBuf};
 use std::str::FromStr;
 
 use crate::cache::{Interned, INTERNER};
-use crate::flags::Flags;
 pub use crate::flags::Subcommand;
+use crate::flags::{Color, Flags};
 use crate::util::exe;
 use build_helper::t;
 use merge::Merge;
@@ -67,6 +67,7 @@ pub struct Config {
     pub json_output: bool,
     pub test_compare_mode: bool,
     pub llvm_libunwind: Option<LlvmLibunwind>,
+    pub color: Color,
 
     pub on_fail: Option<String>,
     pub stage: u32,
@@ -577,6 +578,7 @@ impl Config {
         config.keep_stage = flags.keep_stage;
         config.keep_stage_std = flags.keep_stage_std;
         config.bindir = "bin".into(); // default
+        config.color = flags.color;
         if let Some(value) = flags.deny_warnings {
             config.deny_warnings = value;
         }
diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs
index dbfcf4df9b4..5a8096674c6 100644
--- a/src/bootstrap/flags.rs
+++ b/src/bootstrap/flags.rs
@@ -15,6 +15,31 @@ use crate::config::{Config, TargetSelection};
 use crate::setup::Profile;
 use crate::{Build, DocTests};
 
+pub enum Color {
+    Always,
+    Never,
+    Auto,
+}
+
+impl Default for Color {
+    fn default() -> Self {
+        Self::Auto
+    }
+}
+
+impl std::str::FromStr for Color {
+    type Err = ();
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        match s.to_lowercase().as_str() {
+            "always" => Ok(Self::Always),
+            "never" => Ok(Self::Never),
+            "auto" => Ok(Self::Auto),
+            _ => Err(()),
+        }
+    }
+}
+
 /// Deserialized version of all flags for this compile.
 pub struct Flags {
     pub verbose: usize, // number of -v args; each extra -v after the first is passed to Cargo
@@ -34,6 +59,7 @@ pub struct Flags {
     pub rustc_error_format: Option<String>,
     pub json_output: bool,
     pub dry_run: bool,
+    pub color: Color,
 
     // This overrides the deny-warnings configuration option,
     // which passes -Dwarnings to the compiler invocations.
@@ -184,6 +210,7 @@ To learn more about a subcommand, run `./x.py <subcommand> -h`",
         );
         opts.optopt("", "error-format", "rustc error format", "FORMAT");
         opts.optflag("", "json-output", "use message-format=json");
+        opts.optopt("", "color", "whether to use color in cargo and rustc output", "STYLE");
         opts.optopt(
             "",
             "llvm-skip-rebuild",
@@ -644,6 +671,9 @@ Arguments:
             llvm_skip_rebuild: matches.opt_str("llvm-skip-rebuild").map(|s| s.to_lowercase()).map(
                 |s| s.parse::<bool>().expect("`llvm-skip-rebuild` should be either true or false"),
             ),
+            color: matches
+                .opt_get_default("color", Color::Auto)
+                .expect("`color` should be `always`, `never`, or `auto`"),
         }
     }
 }
diff --git a/src/librustdoc/clean/auto_trait.rs b/src/librustdoc/clean/auto_trait.rs
index f39b53f3c82..a07d6b73f06 100644
--- a/src/librustdoc/clean/auto_trait.rs
+++ b/src/librustdoc/clean/auto_trait.rs
@@ -125,7 +125,7 @@ impl<'a, 'tcx> AutoTraitFinder<'a, 'tcx> {
                     def_id: self.cx.next_def_id(param_env_def_id.krate),
                     stability: None,
                     deprecation: None,
-                    inner: ImplItem(Impl {
+                    kind: ImplItem(Impl {
                         unsafety: hir::Unsafety::Normal,
                         generics: new_generics,
                         provided_trait_methods: Default::default(),
diff --git a/src/librustdoc/clean/blanket_impl.rs b/src/librustdoc/clean/blanket_impl.rs
index 7030fd9b7f2..f15142f4983 100644
--- a/src/librustdoc/clean/blanket_impl.rs
+++ b/src/librustdoc/clean/blanket_impl.rs
@@ -114,7 +114,7 @@ impl<'a, 'tcx> BlanketImplFinder<'a, 'tcx> {
                     def_id: self.cx.next_def_id(impl_def_id.krate),
                     stability: None,
                     deprecation: None,
-                    inner: ImplItem(Impl {
+                    kind: ImplItem(Impl {
                         unsafety: hir::Unsafety::Normal,
                         generics: (
                             self.cx.tcx.generics_of(impl_def_id),
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index b3de70e5905..d6f8870c859 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -54,7 +54,7 @@ crate fn try_inline(
     debug!("attrs={:?}", attrs);
     let attrs_clone = attrs;
 
-    let inner = match res {
+    let kind = match res {
         Res::Def(DefKind::Trait, did) => {
             record_extern_fqn(cx, did, clean::TypeKind::Trait);
             ret.extend(build_impls(cx, Some(parent_module), did, attrs));
@@ -128,7 +128,7 @@ crate fn try_inline(
         source: cx.tcx.def_span(did).clean(cx),
         name: Some(name.clean(cx)),
         attrs,
-        inner,
+        kind,
         visibility: clean::Public,
         stability: cx.tcx.lookup_stability(did).cloned(),
         deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
@@ -446,7 +446,7 @@ crate fn build_impl(
     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
 
     ret.push(clean::Item {
-        inner: clean::ImplItem(clean::Impl {
+        kind: clean::ImplItem(clean::Impl {
             unsafety: hir::Unsafety::Normal,
             generics,
             provided_trait_methods: provided,
@@ -498,7 +498,7 @@ fn build_module(cx: &DocContext<'_>, did: DefId, visited: &mut FxHashSet<DefId>)
                         visibility: clean::Public,
                         stability: None,
                         deprecation: None,
-                        inner: clean::ImportItem(clean::Import::new_simple(
+                        kind: clean::ImportItem(clean::Import::new_simple(
                             item.ident.to_string(),
                             clean::ImportSource {
                                 path: clean::Path {
@@ -555,7 +555,7 @@ fn build_static(cx: &DocContext<'_>, did: DefId, mutable: bool) -> clean::Static
     }
 }
 
-fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemEnum {
+fn build_macro(cx: &DocContext<'_>, did: DefId, name: Symbol) -> clean::ItemKind {
     let imported_from = cx.tcx.original_crate_name(did.krate);
     match cx.enter_resolver(|r| r.cstore().load_macro_untracked(did, cx.sess())) {
         LoadedMacro::MacroDef(def, _) => {
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 366548d5b5f..56ce0bae8bb 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -42,7 +42,7 @@ use utils::*;
 pub use utils::{get_auto_trait_and_blanket_impls, krate, register_res};
 
 pub use self::types::FnRetTy::*;
-pub use self::types::ItemEnum::*;
+pub use self::types::ItemKind::*;
 pub use self::types::SelfTy::*;
 pub use self::types::Type::*;
 pub use self::types::Visibility::{Inherited, Public};
@@ -276,7 +276,7 @@ impl Clean<Item> for doctree::Module<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
-            inner: ModuleItem(Module { is_crate: self.is_crate, items }),
+            kind: ModuleItem(Module { is_crate: self.is_crate, items }),
         }
     }
 }
@@ -916,7 +916,7 @@ impl Clean<Item> for doctree::Function<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: did.to_def_id(),
-            inner: FunctionItem(Function {
+            kind: FunctionItem(Function {
                 decl,
                 generics,
                 header: hir::FnHeader { constness, ..self.header },
@@ -1023,7 +1023,7 @@ impl Clean<Item> for doctree::Trait<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: TraitItem(Trait {
+            kind: TraitItem(Trait {
                 auto: self.is_auto.clean(cx),
                 unsafety: self.unsafety,
                 items: self.items.iter().map(|ti| ti.clean(cx)).collect(),
@@ -1047,7 +1047,7 @@ impl Clean<Item> for doctree::TraitAlias<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: TraitAliasItem(TraitAlias {
+            kind: TraitAliasItem(TraitAlias {
                 generics: self.generics.clean(cx),
                 bounds: self.bounds.clean(cx),
             }),
@@ -1102,7 +1102,7 @@ impl Clean<TypeKind> for hir::def::DefKind {
 impl Clean<Item> for hir::TraitItem<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
         let local_did = cx.tcx.hir().local_def_id(self.hir_id);
-        let inner = match self.kind {
+        let kind = match self.kind {
             hir::TraitItemKind::Const(ref ty, default) => {
                 AssocConstItem(ty.clean(cx), default.map(|e| print_const_expr(cx, e)))
             }
@@ -1140,7 +1140,7 @@ impl Clean<Item> for hir::TraitItem<'_> {
             visibility: Visibility::Inherited,
             stability: get_stability(cx, local_did.to_def_id()),
             deprecation: get_deprecation(cx, local_did.to_def_id()),
-            inner,
+            kind,
         }
     }
 }
@@ -1148,7 +1148,7 @@ impl Clean<Item> for hir::TraitItem<'_> {
 impl Clean<Item> for hir::ImplItem<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
         let local_did = cx.tcx.hir().local_def_id(self.hir_id);
-        let inner = match self.kind {
+        let kind = match self.kind {
             hir::ImplItemKind::Const(ref ty, expr) => {
                 AssocConstItem(ty.clean(cx), Some(print_const_expr(cx, expr)))
             }
@@ -1175,14 +1175,14 @@ impl Clean<Item> for hir::ImplItem<'_> {
             visibility: self.vis.clean(cx),
             stability: get_stability(cx, local_did.to_def_id()),
             deprecation: get_deprecation(cx, local_did.to_def_id()),
-            inner,
+            kind,
         }
     }
 }
 
 impl Clean<Item> for ty::AssocItem {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
-        let inner = match self.kind {
+        let kind = match self.kind {
             ty::AssocKind::Const => {
                 let ty = cx.tcx.type_of(self.def_id);
                 let default = if self.defaultness.has_value() {
@@ -1343,7 +1343,7 @@ impl Clean<Item> for ty::AssocItem {
             def_id: self.def_id,
             attrs: inline::load_attrs(cx, self.def_id).clean(cx),
             source: cx.tcx.def_span(self.def_id).clean(cx),
-            inner,
+            kind,
         }
     }
 }
@@ -1784,7 +1784,7 @@ impl Clean<Item> for hir::StructField<'_> {
             stability: get_stability(cx, local_did.to_def_id()),
             deprecation: get_deprecation(cx, local_did.to_def_id()),
             def_id: local_did.to_def_id(),
-            inner: StructFieldItem(self.ty.clean(cx)),
+            kind: StructFieldItem(self.ty.clean(cx)),
         }
     }
 }
@@ -1799,7 +1799,7 @@ impl Clean<Item> for ty::FieldDef {
             stability: get_stability(cx, self.did),
             deprecation: get_deprecation(cx, self.did),
             def_id: self.did,
-            inner: StructFieldItem(cx.tcx.type_of(self.did).clean(cx)),
+            kind: StructFieldItem(cx.tcx.type_of(self.did).clean(cx)),
         }
     }
 }
@@ -1835,7 +1835,7 @@ impl Clean<Item> for doctree::Struct<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: StructItem(Struct {
+            kind: StructItem(Struct {
                 struct_type: self.struct_type,
                 generics: self.generics.clean(cx),
                 fields: self.fields.clean(cx),
@@ -1855,7 +1855,7 @@ impl Clean<Item> for doctree::Union<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: UnionItem(Union {
+            kind: UnionItem(Union {
                 struct_type: self.struct_type,
                 generics: self.generics.clean(cx),
                 fields: self.fields.clean(cx),
@@ -1885,7 +1885,7 @@ impl Clean<Item> for doctree::Enum<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: EnumItem(Enum {
+            kind: EnumItem(Enum {
                 variants: self.variants.iter().map(|v| v.clean(cx)).collect(),
                 generics: self.generics.clean(cx),
                 variants_stripped: false,
@@ -1904,7 +1904,7 @@ impl Clean<Item> for doctree::Variant<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
-            inner: VariantItem(Variant { kind: self.def.clean(cx) }),
+            kind: VariantItem(Variant { kind: self.def.clean(cx) }),
         }
     }
 }
@@ -1930,7 +1930,7 @@ impl Clean<Item> for ty::VariantDef {
                         def_id: field.did,
                         stability: get_stability(cx, field.did),
                         deprecation: get_deprecation(cx, field.did),
-                        inner: StructFieldItem(cx.tcx.type_of(field.did).clean(cx)),
+                        kind: StructFieldItem(cx.tcx.type_of(field.did).clean(cx)),
                     })
                     .collect(),
             }),
@@ -1941,7 +1941,7 @@ impl Clean<Item> for ty::VariantDef {
             source: cx.tcx.def_span(self.def_id).clean(cx),
             visibility: Inherited,
             def_id: self.def_id,
-            inner: VariantItem(Variant { kind }),
+            kind: VariantItem(Variant { kind }),
             stability: get_stability(cx, self.def_id),
             deprecation: get_deprecation(cx, self.def_id),
         }
@@ -2057,7 +2057,7 @@ impl Clean<Item> for doctree::Typedef<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
+            kind: TypedefItem(Typedef { type_, generics: self.gen.clean(cx), item_type }, false),
         }
     }
 }
@@ -2072,7 +2072,7 @@ impl Clean<Item> for doctree::OpaqueTy<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: OpaqueTyItem(OpaqueTy {
+            kind: OpaqueTyItem(OpaqueTy {
                 bounds: self.opaque_ty.bounds.clean(cx),
                 generics: self.opaque_ty.generics.clean(cx),
             }),
@@ -2100,7 +2100,7 @@ impl Clean<Item> for doctree::Static<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: StaticItem(Static {
+            kind: StaticItem(Static {
                 type_: self.type_.clean(cx),
                 mutability: self.mutability,
                 expr: print_const_expr(cx, self.expr),
@@ -2121,7 +2121,7 @@ impl Clean<Item> for doctree::Constant<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: ConstantItem(Constant {
+            kind: ConstantItem(Constant {
                 type_: self.type_.clean(cx),
                 expr: print_const_expr(cx, self.expr),
                 value: print_evaluated_const(cx, def_id.to_def_id()),
@@ -2175,7 +2175,7 @@ impl Clean<Vec<Item>> for doctree::Impl<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner: ImplItem(Impl {
+            kind: ImplItem(Impl {
                 unsafety: self.unsafety,
                 generics: self.generics.clean(cx),
                 provided_trait_methods: provided.clone(),
@@ -2231,7 +2231,7 @@ impl Clean<Vec<Item>> for doctree::ExternCrate<'_> {
             visibility: self.vis.clean(cx),
             stability: None,
             deprecation: None,
-            inner: ExternCrateItem(self.name.clean(cx), self.path.clone()),
+            kind: ExternCrateItem(self.name.clean(cx), self.path.clone()),
         }]
     }
 }
@@ -2302,7 +2302,7 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
                         visibility: self.vis.clean(cx),
                         stability: None,
                         deprecation: None,
-                        inner: ImportItem(Import::new_simple(
+                        kind: ImportItem(Import::new_simple(
                             self.name.clean(cx),
                             resolve_use_source(cx, path),
                             false,
@@ -2322,14 +2322,14 @@ impl Clean<Vec<Item>> for doctree::Import<'_> {
             visibility: self.vis.clean(cx),
             stability: None,
             deprecation: None,
-            inner: ImportItem(inner),
+            kind: ImportItem(inner),
         }]
     }
 }
 
 impl Clean<Item> for doctree::ForeignItem<'_> {
     fn clean(&self, cx: &DocContext<'_>) -> Item {
-        let inner = match self.kind {
+        let kind = match self.kind {
             hir::ForeignItemKind::Fn(ref decl, ref names, ref generics) => {
                 let abi = cx.tcx.hir().get_foreign_abi(self.id);
                 let (generics, decl) =
@@ -2364,7 +2364,7 @@ impl Clean<Item> for doctree::ForeignItem<'_> {
             visibility: self.vis.clean(cx),
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
-            inner,
+            kind,
         }
     }
 }
@@ -2380,7 +2380,7 @@ impl Clean<Item> for doctree::Macro<'_> {
             stability: cx.stability(self.hid),
             deprecation: cx.deprecation(self.hid).clean(cx),
             def_id: self.def_id,
-            inner: MacroItem(Macro {
+            kind: MacroItem(Macro {
                 source: format!(
                     "macro_rules! {} {{\n{}}}",
                     name,
@@ -2405,7 +2405,7 @@ impl Clean<Item> for doctree::ProcMacro<'_> {
             stability: cx.stability(self.id),
             deprecation: cx.deprecation(self.id).clean(cx),
             def_id: cx.tcx.hir().local_def_id(self.id).to_def_id(),
-            inner: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }),
+            kind: ProcMacroItem(ProcMacro { kind: self.kind, helpers: self.helpers.clean(cx) }),
         }
     }
 }
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 32b3f69ecd4..3060cf79cd5 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -41,7 +41,7 @@ use crate::formats::item_type::ItemType;
 use crate::html::render::cache::ExternalLocation;
 
 use self::FnRetTy::*;
-use self::ItemEnum::*;
+use self::ItemKind::*;
 use self::SelfTy::*;
 use self::Type::*;
 
@@ -81,7 +81,7 @@ pub struct Item {
     /// Not everything has a name. E.g., impls
     pub name: Option<String>,
     pub attrs: Attributes,
-    pub inner: ItemEnum,
+    pub kind: ItemKind,
     pub visibility: Visibility,
     pub def_id: DefId,
     pub stability: Option<Stability>,
@@ -90,14 +90,13 @@ pub struct Item {
 
 impl fmt::Debug for Item {
     fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let fake = self.is_fake();
-        let def_id: &dyn fmt::Debug = if fake { &"**FAKE**" } else { &self.def_id };
+        let def_id: &dyn fmt::Debug = if self.is_fake() { &"**FAKE**" } else { &self.def_id };
 
         fmt.debug_struct("Item")
             .field("source", &self.source)
             .field("name", &self.name)
             .field("attrs", &self.attrs)
-            .field("inner", &self.inner)
+            .field("kind", &self.kind)
             .field("visibility", &self.visibility)
             .field("def_id", def_id)
             .field("stability", &self.stability)
@@ -124,7 +123,7 @@ impl Item {
     }
 
     pub fn is_crate(&self) -> bool {
-        match self.inner {
+        match self.kind {
             StrippedItem(box ModuleItem(Module { is_crate: true, .. }))
             | ModuleItem(Module { is_crate: true, .. }) => true,
             _ => false,
@@ -176,14 +175,14 @@ impl Item {
         self.type_() == ItemType::Keyword
     }
     pub fn is_stripped(&self) -> bool {
-        match self.inner {
+        match self.kind {
             StrippedItem(..) => true,
             ImportItem(ref i) => !i.should_be_displayed,
             _ => false,
         }
     }
     pub fn has_stripped_fields(&self) -> Option<bool> {
-        match self.inner {
+        match self.kind {
             StructItem(ref _struct) => Some(_struct.fields_stripped),
             UnionItem(ref union) => Some(union.fields_stripped),
             VariantItem(Variant { kind: VariantKind::Struct(ref vstruct) }) => {
@@ -227,8 +226,8 @@ impl Item {
     }
 
     pub fn is_default(&self) -> bool {
-        match self.inner {
-            ItemEnum::MethodItem(ref meth) => {
+        match self.kind {
+            ItemKind::MethodItem(ref meth) => {
                 if let Some(defaultness) = meth.defaultness {
                     defaultness.has_value() && !defaultness.is_final()
                 } else {
@@ -248,7 +247,7 @@ impl Item {
 }
 
 #[derive(Clone, Debug)]
-pub enum ItemEnum {
+pub enum ItemKind {
     ExternCrateItem(String, Option<String>),
     ImportItem(Import),
     StructItem(Struct),
@@ -282,23 +281,23 @@ pub enum ItemEnum {
     AssocConstItem(Type, Option<String>),
     AssocTypeItem(Vec<GenericBound>, Option<Type>),
     /// An item that has been stripped by a rustdoc pass
-    StrippedItem(Box<ItemEnum>),
+    StrippedItem(Box<ItemKind>),
     KeywordItem(String),
 }
 
-impl ItemEnum {
+impl ItemKind {
     pub fn is_type_alias(&self) -> bool {
         match *self {
-            ItemEnum::TypedefItem(_, _) | ItemEnum::AssocTypeItem(_, _) => true,
+            ItemKind::TypedefItem(_, _) | ItemKind::AssocTypeItem(_, _) => true,
             _ => false,
         }
     }
 
     pub fn as_assoc_kind(&self) -> Option<AssocKind> {
         match *self {
-            ItemEnum::AssocConstItem(..) => Some(AssocKind::Const),
-            ItemEnum::AssocTypeItem(..) => Some(AssocKind::Type),
-            ItemEnum::TyMethodItem(..) | ItemEnum::MethodItem(..) => Some(AssocKind::Fn),
+            ItemKind::AssocConstItem(..) => Some(AssocKind::Const),
+            ItemKind::AssocTypeItem(..) => Some(AssocKind::Type),
+            ItemKind::TyMethodItem(..) | ItemKind::MethodItem(..) => Some(AssocKind::Fn),
             _ => None,
         }
     }
@@ -681,7 +680,9 @@ impl Attributes {
                                 }
                                 Some(&(_, _, ExternalLocation::Remote(ref s))) => s.to_string(),
                                 Some(&(_, _, ExternalLocation::Unknown)) | None => String::from(
-                                    if UnstableFeatures::from_environment().is_nightly_build() {
+                                    // NOTE: intentionally doesn't pass crate name to avoid having
+                                    // different primitive links between crates
+                                    if UnstableFeatures::from_environment(None).is_nightly_build() {
                                         "https://doc.rust-lang.org/nightly"
                                     } else {
                                         "https://doc.rust-lang.org"
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index f6258221e32..e5fb656cbb9 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -2,7 +2,7 @@ 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, Lifetime,
+    GenericArgs, GenericBound, Generics, GetDefId, ImportSource, Item, ItemKind, Lifetime,
     MacroKind, Path, PathSegment, Primitive, PrimitiveType, ResolvedPath, Span, Type, TypeBinding,
     TypeKind, Visibility, WherePredicate,
 };
@@ -44,8 +44,8 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
     let mut module = module.clean(cx);
     let mut masked_crates = FxHashSet::default();
 
-    match module.inner {
-        ItemEnum::ModuleItem(ref module) => {
+    match module.kind {
+        ItemKind::ModuleItem(ref module) => {
             for it in &module.items {
                 // `compiler_builtins` should be masked too, but we can't apply
                 // `#[doc(masked)]` to the injected `extern crate` because it's unstable.
@@ -62,8 +62,8 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
 
     let ExternalCrate { name, src, primitives, keywords, .. } = LOCAL_CRATE.clean(cx);
     {
-        let m = match module.inner {
-            ItemEnum::ModuleItem(ref mut m) => m,
+        let m = match module.kind {
+            ItemKind::ModuleItem(ref mut m) => m,
             _ => unreachable!(),
         };
         m.items.extend(primitives.iter().map(|&(def_id, prim, ref attrs)| Item {
@@ -74,7 +74,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
             stability: get_stability(cx, def_id),
             deprecation: get_deprecation(cx, def_id),
             def_id,
-            inner: ItemEnum::PrimitiveItem(prim),
+            kind: ItemKind::PrimitiveItem(prim),
         }));
         m.items.extend(keywords.into_iter().map(|(def_id, kw, attrs)| Item {
             source: Span::empty(),
@@ -84,7 +84,7 @@ pub fn krate(mut cx: &mut DocContext<'_>) -> Crate {
             stability: get_stability(cx, def_id),
             deprecation: get_deprecation(cx, def_id),
             def_id,
-            inner: ItemEnum::KeywordItem(kw),
+            kind: ItemKind::KeywordItem(kw),
         }));
     }
 
@@ -355,8 +355,8 @@ pub fn build_deref_target_impls(cx: &DocContext<'_>, items: &[Item], ret: &mut V
     let tcx = cx.tcx;
 
     for item in items {
-        let target = match item.inner {
-            ItemEnum::TypedefItem(ref t, true) => &t.type_,
+        let target = match item.kind {
+            ItemKind::TypedefItem(ref t, true) => &t.type_,
             _ => continue,
         };
         let primitive = match *target {
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index c248d57a9dd..f0fc0dc6514 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -257,6 +257,7 @@ pub struct RenderOptions {
     pub document_private: bool,
     /// Document items that have `doc(hidden)`.
     pub document_hidden: bool,
+    pub unstable_features: rustc_feature::UnstableFeatures,
 }
 
 /// Temporary storage for data obtained during `RustdocVisitor::clean()`.
@@ -299,7 +300,7 @@ impl Options {
                 println_condition(p.condition);
             }
 
-            if nightly_options::is_nightly_build() {
+            if nightly_options::match_is_nightly_build(matches) {
                 println!("\nPasses run with `--show-coverage`:");
                 for p in passes::COVERAGE_PASSES {
                     print!("{:>20}", p.pass.name);
@@ -483,6 +484,7 @@ impl Options {
             &matches.opt_strs("html-after-content"),
             &matches.opt_strs("markdown-before-content"),
             &matches.opt_strs("markdown-after-content"),
+            nightly_options::match_is_nightly_build(&matches),
             &diag,
             &mut id_map,
             edition,
@@ -539,7 +541,9 @@ impl Options {
         let output_format = match matches.opt_str("output-format") {
             Some(s) => match OutputFormat::try_from(s.as_str()) {
                 Ok(o) => {
-                    if o.is_json() && !(show_coverage || nightly_options::is_nightly_build()) {
+                    if o.is_json()
+                        && !(show_coverage || nightly_options::match_is_nightly_build(matches))
+                    {
                         diag.struct_err("json output format isn't supported for doc generation")
                             .emit();
                         return Err(1);
@@ -591,7 +595,6 @@ impl Options {
 
         Ok(Options {
             input,
-            crate_name,
             proc_macro_crate,
             error_format,
             libs,
@@ -643,7 +646,11 @@ impl Options {
                 generate_search_filter,
                 document_private,
                 document_hidden,
+                unstable_features: rustc_feature::UnstableFeatures::from_environment(
+                    crate_name.as_deref(),
+                ),
             },
+            crate_name,
             output_format,
         })
     }
@@ -661,7 +668,8 @@ fn check_deprecated_options(matches: &getopts::Matches, diag: &rustc_errors::Han
     for flag in deprecated_flags.iter() {
         if matches.opt_present(flag) {
             if *flag == "output-format"
-                && (matches.opt_present("show-coverage") || nightly_options::is_nightly_build())
+                && (matches.opt_present("show-coverage")
+                    || nightly_options::match_is_nightly_build(matches))
             {
                 continue;
             }
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 4cad6418d6a..14a2def1383 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -371,7 +371,7 @@ pub fn run_core(
         cg: codegen_options,
         externs,
         target_triple: target,
-        unstable_features: UnstableFeatures::from_environment(),
+        unstable_features: UnstableFeatures::from_environment(crate_name.as_deref()),
         actually_rustdoc: true,
         debugging_opts,
         error_format,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index eb33890fb5f..5e40e6b151d 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -1,7 +1,6 @@
 use rustc_ast as ast;
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::ErrorReported;
-use rustc_feature::UnstableFeatures;
 use rustc_hir as hir;
 use rustc_hir::intravisit;
 use rustc_hir::{HirId, CRATE_HIR_ID};
@@ -70,7 +69,7 @@ pub fn run(options: Options) -> Result<(), ErrorReported> {
         lint_cap: Some(options.lint_cap.clone().unwrap_or_else(|| lint::Forbid)),
         cg: options.codegen_options.clone(),
         externs: options.externs.clone(),
-        unstable_features: UnstableFeatures::from_environment(),
+        unstable_features: options.render_options.unstable_features,
         actually_rustdoc: true,
         debugging_opts: config::DebuggingOptions { ..config::basic_debugging_options() },
         edition: options.edition,
diff --git a/src/librustdoc/externalfiles.rs b/src/librustdoc/externalfiles.rs
index c8121d39d0f..900821dbf4a 100644
--- a/src/librustdoc/externalfiles.rs
+++ b/src/librustdoc/externalfiles.rs
@@ -1,6 +1,5 @@
 use crate::html::markdown::{ErrorCodes, IdMap, Markdown, Playground};
 use crate::rustc_span::edition::Edition;
-use rustc_feature::UnstableFeatures;
 use std::fs;
 use std::path::Path;
 use std::str;
@@ -25,12 +24,13 @@ impl ExternalHtml {
         after_content: &[String],
         md_before_content: &[String],
         md_after_content: &[String],
+        nightly_build: bool,
         diag: &rustc_errors::Handler,
         id_map: &mut IdMap,
         edition: Edition,
         playground: &Option<Playground>,
     ) -> Option<ExternalHtml> {
-        let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+        let codes = ErrorCodes::from(nightly_build);
         let ih = load_external_files(in_header, diag)?;
         let bc = load_external_files(before_content, diag)?;
         let m_bc = load_external_files(md_before_content, diag)?;
diff --git a/src/librustdoc/fold.rs b/src/librustdoc/fold.rs
index d4ada3278e6..694051aa54f 100644
--- a/src/librustdoc/fold.rs
+++ b/src/librustdoc/fold.rs
@@ -5,9 +5,9 @@ pub struct StripItem(pub Item);
 impl StripItem {
     pub fn strip(self) -> Option<Item> {
         match self.0 {
-            Item { inner: StrippedItem(..), .. } => Some(self.0),
+            Item { kind: StrippedItem(..), .. } => Some(self.0),
             mut i => {
-                i.inner = StrippedItem(box i.inner);
+                i.kind = StrippedItem(box i.kind);
                 Some(i)
             }
         }
@@ -20,8 +20,8 @@ pub trait DocFolder: Sized {
     }
 
     /// don't override!
-    fn fold_inner_recur(&mut self, inner: ItemEnum) -> ItemEnum {
-        match inner {
+    fn fold_inner_recur(&mut self, kind: ItemKind) -> ItemKind {
+        match kind {
             StrippedItem(..) => unreachable!(),
             ModuleItem(i) => ModuleItem(self.fold_mod(i)),
             StructItem(mut i) => {
@@ -72,14 +72,14 @@ pub trait DocFolder: Sized {
 
     /// don't override!
     fn fold_item_recur(&mut self, item: Item) -> Option<Item> {
-        let Item { attrs, name, source, visibility, def_id, inner, stability, deprecation } = item;
+        let Item { attrs, name, source, visibility, def_id, kind, stability, deprecation } = item;
 
-        let inner = match inner {
+        let kind = match kind {
             StrippedItem(box i) => StrippedItem(box self.fold_inner_recur(i)),
-            _ => self.fold_inner_recur(inner),
+            _ => self.fold_inner_recur(kind),
         };
 
-        Some(Item { attrs, name, source, inner, visibility, stability, deprecation, def_id })
+        Some(Item { attrs, name, source, kind, visibility, stability, deprecation, def_id })
     }
 
     fn fold_mod(&mut self, m: Module) -> Module {
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index b99321e8484..277571b11f5 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -218,7 +218,7 @@ impl DocFolder for Cache {
 
         // If this is a stripped module,
         // we don't want it or its children in the search index.
-        let orig_stripped_mod = match item.inner {
+        let orig_stripped_mod = match item.kind {
             clean::StrippedItem(box clean::ModuleItem(..)) => {
                 mem::replace(&mut self.stripped_mod, true)
             }
@@ -227,7 +227,7 @@ impl DocFolder for Cache {
 
         // If the impl is from a masked crate or references something from a
         // masked crate then remove it completely.
-        if let clean::ImplItem(ref i) = item.inner {
+        if let clean::ImplItem(ref i) = item.kind {
             if self.masked_crates.contains(&item.def_id.krate)
                 || i.trait_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate))
                 || i.for_.def_id().map_or(false, |d| self.masked_crates.contains(&d.krate))
@@ -238,12 +238,12 @@ impl DocFolder for Cache {
 
         // Propagate a trait method's documentation to all implementors of the
         // trait.
-        if let clean::TraitItem(ref t) = item.inner {
+        if let clean::TraitItem(ref t) = item.kind {
             self.traits.entry(item.def_id).or_insert_with(|| t.clone());
         }
 
         // Collect all the implementors of traits.
-        if let clean::ImplItem(ref i) = item.inner {
+        if let clean::ImplItem(ref i) = item.kind {
             if let Some(did) = i.trait_.def_id() {
                 if i.blanket_impl.is_none() {
                     self.implementors
@@ -256,7 +256,7 @@ impl DocFolder for Cache {
 
         // Index this method for searching later on.
         if let Some(ref s) = item.name {
-            let (parent, is_inherent_impl_item) = match item.inner {
+            let (parent, is_inherent_impl_item) = match item.kind {
                 clean::StrippedItem(..) => ((None, None), false),
                 clean::AssocConstItem(..) | clean::TypedefItem(_, true)
                     if self.parent_is_trait_impl =>
@@ -345,7 +345,7 @@ impl DocFolder for Cache {
             _ => false,
         };
 
-        match item.inner {
+        match item.kind {
             clean::StructItem(..)
             | clean::EnumItem(..)
             | clean::TypedefItem(..)
@@ -384,7 +384,7 @@ impl DocFolder for Cache {
 
         // Maintain the parent stack
         let orig_parent_is_trait_impl = self.parent_is_trait_impl;
-        let parent_pushed = match item.inner {
+        let parent_pushed = match item.kind {
             clean::TraitItem(..)
             | clean::EnumItem(..)
             | clean::ForeignTypeItem
@@ -422,12 +422,12 @@ impl DocFolder for Cache {
         // Once we've recursively found all the generics, hoard off all the
         // implementations elsewhere.
         let ret = self.fold_item_recur(item).and_then(|item| {
-            if let clean::Item { inner: clean::ImplItem(_), .. } = item {
+            if let clean::Item { kind: clean::ImplItem(_), .. } = item {
                 // Figure out the id of this impl. This may map to a
                 // primitive rather than always to a struct/enum.
                 // Note: matching twice to restrict the lifetime of the `i` borrow.
                 let mut dids = FxHashSet::default();
-                if let clean::Item { inner: clean::ImplItem(ref i), .. } = item {
+                if let clean::Item { kind: clean::ImplItem(ref i), .. } = item {
                     match i.for_ {
                         clean::ResolvedPath { did, .. }
                         | clean::BorrowedRef {
diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs
index 696bdae94fc..a0f4502f750 100644
--- a/src/librustdoc/formats/item_type.rs
+++ b/src/librustdoc/formats/item_type.rs
@@ -60,12 +60,12 @@ impl Serialize for ItemType {
 
 impl<'a> From<&'a clean::Item> for ItemType {
     fn from(item: &'a clean::Item) -> ItemType {
-        let inner = match item.inner {
+        let kind = match item.kind {
             clean::StrippedItem(box ref item) => item,
-            ref inner => inner,
+            ref kind => kind,
         };
 
-        match *inner {
+        match *kind {
             clean::ModuleItem(..) => ItemType::Module,
             clean::ExternCrateItem(..) => ItemType::ExternCrate,
             clean::ImportItem(..) => ItemType::Import,
diff --git a/src/librustdoc/formats/mod.rs b/src/librustdoc/formats/mod.rs
index dcb0184c58c..b893d6c64ec 100644
--- a/src/librustdoc/formats/mod.rs
+++ b/src/librustdoc/formats/mod.rs
@@ -32,7 +32,7 @@ pub struct Impl {
 
 impl Impl {
     pub fn inner_impl(&self) -> &clean::Impl {
-        match self.impl_item.inner {
+        match self.impl_item.kind {
             clean::ImplItem(ref impl_) => impl_,
             _ => panic!("non-impl item found in impl"),
         }
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index 90ace4d44c4..273e2819257 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -86,7 +86,7 @@ pub fn run_format<T: FormatRenderer>(
             }
 
             cx.mod_item_in(&item, &name, &cache)?;
-            let module = match item.inner {
+            let module = match item.kind {
                 clean::StrippedItem(box clean::ModuleItem(m)) | clean::ModuleItem(m) => m,
                 _ => unreachable!(),
             };
diff --git a/src/librustdoc/html/render/cache.rs b/src/librustdoc/html/render/cache.rs
index add28de17ed..0541bf118e1 100644
--- a/src/librustdoc/html/render/cache.rs
+++ b/src/librustdoc/html/render/cache.rs
@@ -165,7 +165,7 @@ pub fn build_index(krate: &clean::Crate, cache: &mut Cache) -> String {
 }
 
 crate fn get_index_search_type(item: &clean::Item) -> Option<IndexItemFunctionType> {
-    let (all_types, ret_types) = match item.inner {
+    let (all_types, ret_types) = match item.kind {
         clean::FunctionItem(ref f) => (&f.all_types, &f.ret_types),
         clean::MethodItem(ref m) => (&m.all_types, &m.ret_types),
         clean::TyMethodItem(ref m) => (&m.all_types, &m.ret_types),
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 5ac0ffcfbf1..eebb07f0476 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -52,7 +52,6 @@ use rustc_ast_pretty::pprust;
 use rustc_attr::StabilityLevel;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_feature::UnstableFeatures;
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::Mutability;
@@ -397,6 +396,7 @@ impl FormatRenderer for Context {
             resource_suffix,
             static_root_path,
             generate_search_filter,
+            unstable_features,
             ..
         } = options;
 
@@ -466,7 +466,7 @@ impl FormatRenderer for Context {
             static_root_path,
             fs: DocFS::new(sender),
             edition,
-            codes: ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build()),
+            codes: ErrorCodes::from(unstable_features.is_nightly_build()),
             playground,
         };
 
@@ -618,7 +618,7 @@ impl FormatRenderer for Context {
 
         // Render sidebar-items.js used throughout this module.
         if !self.render_redirect_pages {
-            let module = match item.inner {
+            let module = match item.kind {
                 clean::StrippedItem(box clean::ModuleItem(ref m)) | clean::ModuleItem(ref m) => m,
                 _ => unreachable!(),
             };
@@ -1717,7 +1717,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache)
 
     write!(buf, "</span>"); // out-of-band
     write!(buf, "<span class=\"in-band\">");
-    let name = match item.inner {
+    let name = match item.kind {
         clean::ModuleItem(ref m) => {
             if m.is_crate {
                 "Crate "
@@ -1766,7 +1766,7 @@ fn print_item(cx: &Context, item: &clean::Item, buf: &mut Buffer, cache: &Cache)
 
     write!(buf, "</span></h1>"); // in-band
 
-    match item.inner {
+    match item.kind {
         clean::ModuleItem(ref m) => item_module(buf, cx, item, &m.items),
         clean::FunctionItem(ref f) | clean::ForeignFunctionItem(ref f) => {
             item_function(buf, cx, item, f)
@@ -2133,7 +2133,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
             );
         }
 
-        match myitem.inner {
+        match myitem.kind {
             clean::ExternCrateItem(ref name, ref src) => {
                 use crate::html::format::anchor;
 
@@ -2169,7 +2169,7 @@ fn item_module(w: &mut Buffer, cx: &Context, item: &clean::Item, items: &[clean:
                     continue;
                 }
 
-                let unsafety_flag = match myitem.inner {
+                let unsafety_flag = match myitem.kind {
                     clean::FunctionItem(ref func) | clean::ForeignFunctionItem(ref func)
                         if func.header.unsafety == hir::Unsafety::Unsafe =>
                     {
@@ -2582,7 +2582,7 @@ fn item_trait(w: &mut Buffer, cx: &Context, it: &clean::Item, t: &clean::Trait,
             }
             for (pos, m) in provided.iter().enumerate() {
                 render_assoc_item(w, m, AssocItemLink::Anchor(None), ItemType::Trait);
-                match m.inner {
+                match m.kind {
                     clean::MethodItem(ref inner) if !inner.generics.where_predicates.is_empty() => {
                         write!(w, ",\n    {{ ... }}\n");
                     }
@@ -2958,7 +2958,7 @@ fn render_assoc_item(
             where_clause = WhereClause { gens: g, indent, end_newline }
         )
     }
-    match item.inner {
+    match item.kind {
         clean::StrippedItem(..) => {}
         clean::TyMethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent),
         clean::MethodItem(ref m) => method(w, item, m.header, &m.generics, &m.decl, link, parent),
@@ -2994,7 +2994,7 @@ fn item_struct(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Struct
     let mut fields = s
         .fields
         .iter()
-        .filter_map(|f| match f.inner {
+        .filter_map(|f| match f.kind {
             clean::StructFieldItem(ref ty) => Some((f, ty)),
             _ => None,
         })
@@ -3044,7 +3044,7 @@ fn item_union(w: &mut Buffer, cx: &Context, it: &clean::Item, s: &clean::Union,
     let mut fields = s
         .fields
         .iter()
-        .filter_map(|f| match f.inner {
+        .filter_map(|f| match f.kind {
             clean::StructFieldItem(ref ty) => Some((f, ty)),
             _ => None,
         })
@@ -3097,7 +3097,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
             for v in &e.variants {
                 write!(w, "    ");
                 let name = v.name.as_ref().unwrap();
-                match v.inner {
+                match v.kind {
                     clean::VariantItem(ref var) => match var.kind {
                         clean::VariantKind::CLike => write!(w, "{}", name),
                         clean::VariantKind::Tuple(ref tys) => {
@@ -3147,7 +3147,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
                 id = id,
                 name = variant.name.as_ref().unwrap()
             );
-            if let clean::VariantItem(ref var) = variant.inner {
+            if let clean::VariantItem(ref var) = variant.kind {
                 if let clean::VariantKind::Tuple(ref tys) = var.kind {
                     write!(w, "(");
                     for (i, ty) in tys.iter().enumerate() {
@@ -3164,8 +3164,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
             document_non_exhaustive(w, variant);
 
             use crate::clean::{Variant, VariantKind};
-            if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.inner
-            {
+            if let clean::VariantItem(Variant { kind: VariantKind::Struct(ref s) }) = variant.kind {
                 let variant_id = cx.derive_id(format!(
                     "{}.{}.fields",
                     ItemType::Variant,
@@ -3179,7 +3178,7 @@ fn item_enum(w: &mut Buffer, cx: &Context, it: &clean::Item, e: &clean::Enum, ca
                 );
                 for field in &s.fields {
                     use crate::clean::StructFieldItem;
-                    if let StructFieldItem(ref ty) = field.inner {
+                    if let StructFieldItem(ref ty) = field.kind {
                         let id = cx.derive_id(format!(
                             "variant.{}.field.{}",
                             variant.name.as_ref().unwrap(),
@@ -3275,7 +3274,7 @@ fn render_struct(
             let mut has_visible_fields = false;
             write!(w, " {{");
             for field in fields {
-                if let clean::StructFieldItem(ref ty) = field.inner {
+                if let clean::StructFieldItem(ref ty) = field.kind {
                     write!(
                         w,
                         "\n{}    {}{}: {},",
@@ -3306,7 +3305,7 @@ fn render_struct(
                 if i > 0 {
                     write!(w, ", ");
                 }
-                match field.inner {
+                match field.kind {
                     clean::StrippedItem(box clean::StructFieldItem(..)) => write!(w, "_"),
                     clean::StructFieldItem(ref ty) => {
                         write!(w, "{}{}", field.visibility.print_with_space(), ty.print())
@@ -3352,7 +3351,7 @@ fn render_union(
 
     write!(w, " {{\n{}", tab);
     for field in fields {
-        if let clean::StructFieldItem(ref ty) = field.inner {
+        if let clean::StructFieldItem(ref ty) = field.kind {
             write!(
                 w,
                 "    {}{}: {},\n{}",
@@ -3516,7 +3515,7 @@ fn render_deref_methods(
         .inner_impl()
         .items
         .iter()
-        .find_map(|item| match item.inner {
+        .find_map(|item| match item.kind {
             clean::TypedefItem(ref t, true) => Some(match *t {
                 clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
                 _ => (&t.type_, &t.type_),
@@ -3538,7 +3537,7 @@ fn render_deref_methods(
 }
 
 fn should_render_item(item: &clean::Item, deref_mut_: bool) -> bool {
-    let self_type_opt = match item.inner {
+    let self_type_opt = match item.kind {
         clean::MethodItem(ref method) => method.decl.self_type(),
         clean::TyMethodItem(ref method) => method.decl.self_type(),
         _ => None,
@@ -3589,7 +3588,7 @@ fn spotlight_decl(decl: &clean::FnDecl) -> String {
                     ));
                     let t_did = impl_.trait_.def_id().unwrap();
                     for it in &impl_.items {
-                        if let clean::TypedefItem(ref tydef, _) = it.inner {
+                        if let clean::TypedefItem(ref tydef, _) = it.kind {
                             out.push_str("<span class=\"where fmt-newline\">    ");
                             assoc_type(
                                 &mut out,
@@ -3657,7 +3656,7 @@ fn render_impl(
             fmt_impl_for_trait_page(&i.inner_impl(), w, use_absolute);
             if show_def_docs {
                 for it in &i.inner_impl().items {
-                    if let clean::TypedefItem(ref tydef, _) = it.inner {
+                    if let clean::TypedefItem(ref tydef, _) = it.kind {
                         write!(w, "<span class=\"where fmt-newline\">  ");
                         assoc_type(w, it, &[], Some(&tydef.type_), AssocItemLink::Anchor(None), "");
                         write!(w, ";</span>");
@@ -3728,14 +3727,14 @@ fn render_impl(
         };
 
         let (is_hidden, extra_class) =
-            if (trait_.is_none() || item.doc_value().is_some() || item.inner.is_type_alias())
+            if (trait_.is_none() || item.doc_value().is_some() || item.kind.is_type_alias())
                 && !is_default_item
             {
                 (false, "")
             } else {
                 (true, " hidden")
             };
-        match item.inner {
+        match item.kind {
             clean::MethodItem(clean::Method { .. })
             | clean::TyMethodItem(clean::TyMethod { .. }) => {
                 // Only render when the method is not static or we allow static methods
@@ -4000,7 +3999,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca
         write!(
             buffer,
             "<p class=\"location\">{}{}</p>",
-            match it.inner {
+            match it.kind {
                 clean::StructItem(..) => "Struct ",
                 clean::TraitItem(..) => "Trait ",
                 clean::PrimitiveItem(..) => "Primitive Type ",
@@ -4040,7 +4039,7 @@ fn print_sidebar(cx: &Context, it: &clean::Item, buffer: &mut Buffer, cache: &Ca
             it.name.as_ref().expect("crates always have a name")
         );
     }
-    match it.inner {
+    match it.kind {
         clean::StructItem(ref s) => sidebar_struct(buffer, it, s),
         clean::TraitItem(ref t) => sidebar_trait(buffer, it, t),
         clean::PrimitiveItem(_) => sidebar_primitive(buffer, it),
@@ -4180,7 +4179,7 @@ fn sidebar_assoc_items(it: &clean::Item) -> String {
                 .find(|i| i.inner_impl().trait_.def_id() == c.deref_trait_did)
             {
                 if let Some((target, real_target)) =
-                    impl_.inner_impl().items.iter().find_map(|item| match item.inner {
+                    impl_.inner_impl().items.iter().find_map(|item| match item.kind {
                         clean::TypedefItem(ref t, true) => Some(match *t {
                             clean::Typedef { item_type: Some(ref type_), .. } => (type_, &t.type_),
                             _ => (&t.type_, &t.type_),
@@ -4319,8 +4318,8 @@ fn get_id_for_impl_on_foreign_type(for_: &clean::Type, trait_: &clean::Type) ->
 }
 
 fn extract_for_impl_name(item: &clean::Item) -> Option<(String, String)> {
-    match item.inner {
-        clean::ItemEnum::ImplItem(ref i) => {
+    match item.kind {
+        clean::ItemKind::ImplItem(ref i) => {
             if let Some(ref trait_) = i.trait_ {
                 Some((
                     format!("{:#}", i.for_.print()),
@@ -4470,7 +4469,7 @@ fn sidebar_typedef(buf: &mut Buffer, it: &clean::Item) {
 fn get_struct_fields_name(fields: &[clean::Item]) -> String {
     let mut fields = fields
         .iter()
-        .filter(|f| if let clean::StructFieldItem(..) = f.inner { true } else { false })
+        .filter(|f| if let clean::StructFieldItem(..) = f.kind { true } else { false })
         .filter_map(|f| match f.name {
             Some(ref name) => {
                 Some(format!("<a href=\"#structfield.{name}\">{name}</a>", name = name))
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 3a87e1c46a6..33bd57223b8 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -2,7 +2,6 @@ use std::fs::{create_dir_all, read_to_string, File};
 use std::io::prelude::*;
 use std::path::Path;
 
-use rustc_feature::UnstableFeatures;
 use rustc_span::edition::Edition;
 use rustc_span::source_map::DUMMY_SP;
 
@@ -66,7 +65,7 @@ pub fn render<P: AsRef<Path>>(
     let title = metadata[0];
 
     let mut ids = IdMap::new();
-    let error_codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+    let error_codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
     let text = if !options.markdown_no_toc {
         MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).into_string()
     } else {
@@ -131,7 +130,7 @@ pub fn test(mut options: Options) -> Result<(), String> {
         options.enable_per_target_ignores,
     );
     collector.set_position(DUMMY_SP);
-    let codes = ErrorCodes::from(UnstableFeatures::from_environment().is_nightly_build());
+    let codes = ErrorCodes::from(options.render_options.unstable_features.is_nightly_build());
 
     find_testable_code(&input_str, &mut collector, codes, options.enable_per_target_ignores, None);
 
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index ced26fcf5b0..ef68bae1078 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -187,7 +187,7 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
 
 impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
     fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
-        match i.inner {
+        match i.kind {
             _ if !i.def_id.is_local() => {
                 // non-local items are skipped because they can be out of the users control,
                 // especially in the case of trait impls, which rustdoc eagerly inlines
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index e0cb5bf1a4e..fd0dd339abd 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -671,7 +671,7 @@ fn resolve_associated_trait_item(
     let implicit_impls = crate::clean::get_auto_trait_and_blanket_impls(cx, ty, did);
     let mut candidates: Vec<_> = implicit_impls
         .flat_map(|impl_outer| {
-            match impl_outer.inner {
+            match impl_outer.kind {
                 clean::ImplItem(impl_) => {
                     debug!("considering auto or blanket impl for trait {:?}", impl_.trait_);
                     // Give precedence to methods that were overridden
@@ -681,14 +681,14 @@ fn resolve_associated_trait_item(
                                 return None;
                             }
                             let kind = assoc
-                                .inner
+                                .kind
                                 .as_assoc_kind()
                                 .expect("inner items for a trait should be associated items");
                             if kind.namespace() != ns {
                                 return None;
                             }
 
-                            trace!("considering associated item {:?}", assoc.inner);
+                            trace!("considering associated item {:?}", assoc.kind);
                             // We have a slight issue: normal methods come from `clean` types,
                             // but provided methods come directly from `tcx`.
                             // Fortunately, we don't need the whole method, we just need to know
@@ -832,7 +832,7 @@ impl<'a, 'tcx> DocFolder for LinkCollector<'a, 'tcx> {
             trace!("got parent node for {:?} {:?}, id {:?}", item.type_(), item.name, item.def_id);
         }
 
-        let current_item = match item.inner {
+        let current_item = match item.kind {
             clean::ModuleItem(..) => {
                 if item.attrs.inner_docs {
                     if item.def_id.is_top_level_module() { item.name.clone() } else { None }
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index 5eb3f98b123..81de0730247 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -55,11 +55,11 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
 
     // scan through included items ahead of time to splice in Deref targets to the "valid" sets
     for it in &new_items {
-        if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.inner {
+        if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = it.kind {
             if cleaner.keep_item(for_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() {
                 let target = items
                     .iter()
-                    .find_map(|item| match item.inner {
+                    .find_map(|item| match item.kind {
                         TypedefItem(ref t, true) => Some(&t.type_),
                         _ => None,
                     })
@@ -75,7 +75,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     }
 
     new_items.retain(|it| {
-        if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.inner {
+        if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = it.kind {
             cleaner.keep_item(for_)
                 || trait_.as_ref().map_or(false, |t| cleaner.keep_item(t))
                 || blanket_impl.is_some()
@@ -96,7 +96,7 @@ pub fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
     }
 
     if let Some(ref mut it) = krate.module {
-        if let ModuleItem(Module { ref mut items, .. }) = it.inner {
+        if let ModuleItem(Module { ref mut items, .. }) = it.kind {
             items.extend(synth.impls);
             items.extend(new_items);
         } else {
diff --git a/src/librustdoc/passes/doc_test_lints.rs b/src/librustdoc/passes/doc_test_lints.rs
index 686ec51fb06..094f85f2ccb 100644
--- a/src/librustdoc/passes/doc_test_lints.rs
+++ b/src/librustdoc/passes/doc_test_lints.rs
@@ -58,7 +58,7 @@ impl crate::doctest::Tester for Tests {
 }
 
 pub fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -> bool {
-    if matches!(item.inner,
+    if matches!(item.kind,
         clean::StructFieldItem(_)
         | clean::VariantItem(_)
         | clean::AssocConstItem(_, _)
@@ -92,9 +92,7 @@ pub fn look_for_tests<'tcx>(cx: &DocContext<'tcx>, dox: &str, item: &Item) {
 
     find_testable_code(&dox, &mut tests, ErrorCodes::No, false, None);
 
-    if tests.found_tests == 0
-        && rustc_feature::UnstableFeatures::from_environment().is_nightly_build()
-    {
+    if tests.found_tests == 0 && cx.tcx.sess.is_nightly_build() {
         if should_have_doc_example(cx, &item) {
             debug!("reporting error for {:?} (hir_id={:?})", item, hir_id);
             let sp = span_of_attrs(&item.attrs).unwrap_or(item.source.span());
diff --git a/src/librustdoc/passes/html_tags.rs b/src/librustdoc/passes/html_tags.rs
index 1d9be619ec9..26b64b4905e 100644
--- a/src/librustdoc/passes/html_tags.rs
+++ b/src/librustdoc/passes/html_tags.rs
@@ -5,7 +5,6 @@ use crate::fold::DocFolder;
 use crate::html::markdown::opts;
 use core::ops::Range;
 use pulldown_cmark::{Event, Parser};
-use rustc_feature::UnstableFeatures;
 use rustc_session::lint;
 use std::iter::Peekable;
 use std::str::CharIndices;
@@ -27,7 +26,7 @@ impl<'a, 'tcx> InvalidHtmlTagsLinter<'a, 'tcx> {
 }
 
 pub fn check_invalid_html_tags(krate: Crate, cx: &DocContext<'_>) -> Crate {
-    if !UnstableFeatures::from_environment().is_nightly_build() {
+    if !cx.tcx.sess.is_nightly_build() {
         krate
     } else {
         let mut coll = InvalidHtmlTagsLinter::new(cx);
diff --git a/src/librustdoc/passes/non_autolinks.rs b/src/librustdoc/passes/non_autolinks.rs
index 4a8fc7fc618..964773dc055 100644
--- a/src/librustdoc/passes/non_autolinks.rs
+++ b/src/librustdoc/passes/non_autolinks.rs
@@ -7,7 +7,6 @@ use core::ops::Range;
 use pulldown_cmark::{Event, LinkType, Parser, Tag};
 use regex::Regex;
 use rustc_errors::Applicability;
-use rustc_feature::UnstableFeatures;
 use rustc_session::lint;
 
 pub const CHECK_NON_AUTOLINKS: Pass = Pass {
@@ -54,7 +53,7 @@ impl<'a, 'tcx> NonAutolinksLinter<'a, 'tcx> {
 }
 
 pub fn check_non_autolinks(krate: Crate, cx: &DocContext<'_>) -> Crate {
-    if !UnstableFeatures::from_environment().is_nightly_build() {
+    if !cx.tcx.sess.is_nightly_build() {
         krate
     } else {
         let mut coll = NonAutolinksLinter::new(cx);
diff --git a/src/librustdoc/passes/strip_hidden.rs b/src/librustdoc/passes/strip_hidden.rs
index f82e72b488b..4b9e150eb1e 100644
--- a/src/librustdoc/passes/strip_hidden.rs
+++ b/src/librustdoc/passes/strip_hidden.rs
@@ -41,7 +41,7 @@ impl<'a> DocFolder for Stripper<'a> {
         if i.attrs.lists(sym::doc).has_word(sym::hidden) {
             debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
             // use a dedicated hidden item for given item type if any
-            match i.inner {
+            match i.kind {
                 clean::StructFieldItem(..) | clean::ModuleItem(..) => {
                     // We need to recurse into stripped modules to
                     // strip things like impl methods but when doing so
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index 9b4f62235f5..4250c2b48fc 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -13,7 +13,7 @@ pub struct Stripper<'a> {
 
 impl<'a> DocFolder for Stripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        match i.inner {
+        match i.kind {
             clean::StrippedItem(..) => {
                 // We need to recurse into stripped modules to strip things
                 // like impl methods but when doing so we must not add any
@@ -86,7 +86,7 @@ impl<'a> DocFolder for Stripper<'a> {
             clean::KeywordItem(..) => {}
         }
 
-        let fastreturn = match i.inner {
+        let fastreturn = match i.kind {
             // nothing left to do for traits (don't want to filter their
             // methods out, visibility controlled by the trait)
             clean::TraitItem(..) => true,
@@ -123,7 +123,7 @@ pub struct ImplStripper<'a> {
 
 impl<'a> DocFolder for ImplStripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        if let clean::ImplItem(ref imp) = i.inner {
+        if let clean::ImplItem(ref imp) = i.kind {
             // emptied none trait impls can be stripped
             if imp.trait_.is_none() && imp.items.is_empty() {
                 return None;
@@ -162,7 +162,7 @@ pub struct ImportStripper;
 
 impl DocFolder for ImportStripper {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
-        match i.inner {
+        match i.kind {
             clean::ExternCrateItem(..) | clean::ImportItem(..) if i.visibility != clean::Public => {
                 None
             }
diff --git a/src/test/ui/cross/cross-file-errors/main.rs b/src/test/ui/cross/cross-file-errors/main.rs
index 74e9461803c..1902ab94d4c 100644
--- a/src/test/ui/cross/cross-file-errors/main.rs
+++ b/src/test/ui/cross/cross-file-errors/main.rs
@@ -3,5 +3,6 @@ mod underscore;
 
 fn main() {
     underscore!();
-    //~^ ERROR expected expression, found reserved identifier `_`
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/cross/cross-file-errors/main.stderr b/src/test/ui/cross/cross-file-errors/main.stderr
index f9101d8a583..b8658745060 100644
--- a/src/test/ui/cross/cross-file-errors/main.stderr
+++ b/src/test/ui/cross/cross-file-errors/main.stderr
@@ -1,15 +1,31 @@
-error: expected expression, found reserved identifier `_`
+error[E0658]: destructuring assignments are unstable
   --> $DIR/underscore.rs:8:9
    |
 LL |         _
-   |         ^ expected expression
+   |         ^
    | 
   ::: $DIR/main.rs:5:5
    |
 LL |     underscore!();
    |     -------------- in this macro invocation
    |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
    = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to previous error
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/underscore.rs:8:9
+   |
+LL |         _
+   |         ^ `_` not allowed here
+   | 
+  ::: $DIR/main.rs:5:5
+   |
+LL |     underscore!();
+   |     -------------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs
index 393dfc16c0a..0d45ff7da72 100644
--- a/src/test/ui/destructuring-assignment/nested_destructure.rs
+++ b/src/test/ui/destructuring-assignment/nested_destructure.rs
@@ -14,4 +14,7 @@ fn main() {
     Struct { a: TupleStruct((a, b), c), b: [d] } =
         Struct { a: TupleStruct((0, 1), 2), b: [3] };
     assert_eq!((a, b, c, d), (0, 1, 2, 3));
+
+    // unnested underscore: just discard
+    _ = 1;
 }
diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs
index 3dd10aff19c..76cdc1260fc 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure.rs
+++ b/src/test/ui/destructuring-assignment/slice_destructure.rs
@@ -9,6 +9,8 @@ fn main() {
   let mut c;
   [a, .., b, c] = [1, 2, 3, 4, 5];
   assert_eq!((a, b, c), (1, 4, 5));
+  [_, a, _] = [1, 2, 3];
+  assert_eq!((a, b), (2, 4));
   [..] = [1, 2, 3];
   [c, ..] = [5, 6, 6];
   assert_eq!(c, 5);
diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
index f636ea3511c..90d93892f7f 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs
@@ -4,4 +4,5 @@ fn main() {
   let (mut a, mut b);
   [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern
   [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2
+  [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2
 }
diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
index 728687deb8b..cc412c72df5 100644
--- a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr
@@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2
 LL |   [a, a, b] = [1, 2];
    |   ^^^^^^^^^ expected 2 elements
 
-error: aborting due to 2 previous errors
+error[E0527]: pattern requires 1 element but array has 2
+  --> $DIR/slice_destructure_fail.rs:7:3
+   |
+LL |   [_] = [1, 2];
+   |   ^^^ expected 2 elements
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0527`.
diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs
index b3a96ee1573..2bcbd9d0d74 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure.rs
+++ b/src/test/ui/destructuring-assignment/struct_destructure.rs
@@ -12,8 +12,10 @@ fn main() {
     assert_eq!((a, b), (0, 1));
     Struct { a: b, b: a }  = Struct { a: 1, b: 2 };
     assert_eq!((a,b), (2, 1));
+    Struct { a: _, b } = Struct { a: 1, b: 2 };
+    assert_eq!((a, b), (2, 2));
     Struct { a, .. } = Struct { a: 1, b: 3 };
-    assert_eq!((a, b), (1, 1));
+    assert_eq!((a, b), (1, 2));
     Struct { .. } = Struct { a: 1, b: 4 };
-    assert_eq!((a, b), (1, 1));
+    assert_eq!((a, b), (1, 2));
 }
diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
index c22695ed388..4aa327b61f4 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
@@ -9,6 +9,8 @@ fn main() {
     let mut c;
     let d = Struct { a: 0, b: 1 };
     Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c`
+    Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b`
+    //~| ERROR expected identifier, found reserved identifier `_`
     Struct { a, ..d } = Struct { a: 1, b: 2 };
     //~^ ERROR functional record updates are not allowed in destructuring assignments
     Struct { a, .. }; //~ ERROR base expression required after `..`
diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
index 4da4698804f..81661a357e7 100644
--- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
@@ -1,11 +1,19 @@
+error: expected identifier, found reserved identifier `_`
+  --> $DIR/struct_destructure_fail.rs:12:17
+   |
+LL |     Struct { a, _ } = Struct { a: 1, b: 2 };
+   |     ------      ^ expected identifier, found reserved identifier
+   |     |
+   |     while parsing this struct
+
 error: functional record updates are not allowed in destructuring assignments
-  --> $DIR/struct_destructure_fail.rs:12:19
+  --> $DIR/struct_destructure_fail.rs:14:19
    |
 LL |     Struct { a, ..d } = Struct { a: 1, b: 2 };
    |                   ^ help: consider removing the trailing pattern
 
 error: base expression required after `..`
-  --> $DIR/struct_destructure_fail.rs:14:19
+  --> $DIR/struct_destructure_fail.rs:16:19
    |
 LL |     Struct { a, .. };
    |                   ^ add a base expression here
@@ -16,6 +24,22 @@ error[E0026]: struct `Struct` does not have a field named `c`
 LL |     Struct { a, b, c } = Struct { a: 0, b: 1 };
    |                    ^ struct `Struct` does not have this field
 
-error: aborting due to 3 previous errors
+error[E0027]: pattern does not mention field `b`
+  --> $DIR/struct_destructure_fail.rs:12:5
+   |
+LL |     Struct { a, _ } = Struct { a: 1, b: 2 };
+   |     ^^^^^^^^^^^^^^^ missing field `b`
+   |
+help: include the missing field in the pattern
+   |
+LL |     Struct { a, b, _ } = Struct { a: 1, b: 2 };
+   |               ^^^
+help: if you don't care about this missing field, you can explicitly ignore it
+   |
+LL |     Struct { a, .., _ } = Struct { a: 1, b: 2 };
+   |               ^^^^
+
+error: aborting due to 5 previous errors
 
-For more information about this error, try `rustc --explain E0026`.
+Some errors have detailed explanations: E0026, E0027.
+For more information about an error, try `rustc --explain E0026`.
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs
index 16aafc4693f..2096182d421 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure.rs
+++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs
@@ -16,6 +16,8 @@ fn main() {
     assert_eq!((a, b), (2, 2));
     (b, ..) = (5, 6, 7);
     assert_eq!(b, 5);
+    (a, _) = (8, 9);
+    assert_eq!(a, 8);
 
     // Test for a non-Copy type (String):
     let (mut c, mut d);
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
index b76f4968e62..5524e91dc40 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs
@@ -7,4 +7,5 @@ fn main() {
     (a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
     (a, a, b) = (1, 2); //~ ERROR mismatched types
     (C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
+    (_,) = (1, 2); //~ ERROR mismatched types
 }
diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
index a60e1cb1eec..1146b88278d 100644
--- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr
@@ -25,7 +25,18 @@ LL |     (C, ..) = (0,1);
    |      |
    |      cannot assign to this expression
 
-error: aborting due to 3 previous errors
+error[E0308]: mismatched types
+  --> $DIR/tuple_destructure_fail.rs:10:5
+   |
+LL |     (_,) = (1, 2);
+   |     ^^^^   ------ this expression has type `({integer}, {integer})`
+   |     |
+   |     expected a tuple with 2 elements, found one with 1 element
+   |
+   = note: expected type `({integer}, {integer})`
+             found tuple `(_,)`
+
+error: aborting due to 4 previous errors
 
 Some errors have detailed explanations: E0070, E0308.
 For more information about an error, try `rustc --explain E0070`.
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
index 106a9b16db4..7b5c5ad2bae 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
@@ -23,8 +23,10 @@ fn main() {
     assert_eq!((a, b), (0, 1));
     TupleStruct(a, .., b) = TupleStruct(1, 2);
     assert_eq!((a, b), (1, 2));
+    TupleStruct(_, a) = TupleStruct(2, 2);
+    assert_eq!((a, b), (2, 2));
     TupleStruct(..) = TupleStruct(3, 4);
-    assert_eq!((a, b), (1, 2));
+    assert_eq!((a, b), (2, 2));
     TupleStruct(5,6).assign(&mut a, &mut b);
     assert_eq!((a, b), (5, 6));
     Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8);
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
index 61ae42a5175..c39db061177 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
@@ -29,8 +29,12 @@ fn main() {
 
     TupleStruct(a, a, b) = TupleStruct(1, 2);
     //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields
+    TupleStruct(_) = TupleStruct(1, 2);
+    //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields
     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
     //~^ ERROR this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+    Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
+    //~^ ERROR this pattern has 1 field, but the corresponding tuple variant has 2 fields
 
     // Check if `test` is recognized as not a tuple struct but a function call:
     test() = TupleStruct(0, 0);
diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
index 863eedecf76..0e7174e5b19 100644
--- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
@@ -23,17 +23,35 @@ LL | struct TupleStruct<S, T>(S, T);
 LL |     TupleStruct(a, a, b) = TupleStruct(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
-error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields
   --> $DIR/tuple_struct_destructure_fail.rs:32:5
    |
+LL | struct TupleStruct<S, T>(S, T);
+   | ------------------------------- tuple struct defined here
+...
+LL |     TupleStruct(_) = TupleStruct(1, 2);
+   |     ^^^^^^^^^^^^^^ expected 2 fields, found 1
+
+error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:34:5
+   |
 LL |     SingleVariant(S, T)
    |     ------------------- tuple variant defined here
 ...
 LL |     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
 
+error[E0023]: this pattern has 1 field, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:36:5
+   |
+LL |     SingleVariant(S, T)
+   |     ------------------- tuple variant defined here
+...
+LL |     Enum::SingleVariant(_) = Enum::SingleVariant(1, 2);
+   |     ^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 1
+
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:36:12
+  --> $DIR/tuple_struct_destructure_fail.rs:40:12
    |
 LL |     test() = TupleStruct(0, 0);
    |     ------ ^
@@ -41,7 +59,7 @@ LL |     test() = TupleStruct(0, 0);
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:38:14
+  --> $DIR/tuple_struct_destructure_fail.rs:42:14
    |
 LL |     (test)() = TupleStruct(0, 0);
    |     -------- ^
@@ -49,14 +67,14 @@ LL |     (test)() = TupleStruct(0, 0);
    |     cannot assign to this expression
 
 error[E0070]: invalid left-hand side of assignment
-  --> $DIR/tuple_struct_destructure_fail.rs:40:38
+  --> $DIR/tuple_struct_destructure_fail.rs:44:38
    |
 LL |     <Alias::<isize> as Test>::test() = TupleStruct(0, 0);
    |     -------------------------------- ^
    |     |
    |     cannot assign to this expression
 
-error: aborting due to 7 previous errors
+error: aborting due to 9 previous errors
 
 Some errors have detailed explanations: E0023, E0070.
 For more information about an error, try `rustc --explain E0023`.
diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
index b41f2f52a3d..4ed4f56702c 100644
--- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
@@ -4,5 +4,7 @@ struct S { x : u32 }
 
 #[cfg(FALSE)]
 fn foo() {
+    _; //~ ERROR destructuring assignments are unstable
+
     S { x: 5, .. }; //~ ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
index 442e36cd306..a5ed761a01c 100644
--- a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
@@ -1,5 +1,14 @@
 error[E0658]: destructuring assignments are unstable
-  --> $DIR/underscore-range-expr-gating.rs:7:15
+  --> $DIR/underscore-range-expr-gating.rs:7:5
+   |
+LL |     _;
+   |     ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/underscore-range-expr-gating.rs:9:15
    |
 LL |     S { x: 5, .. };
    |               ^^
@@ -7,6 +16,6 @@ LL |     S { x: 5, .. };
    = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
    = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
 
-error: aborting due to previous error
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
index a8ea3faefe8..00638e04f5d 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs
@@ -8,12 +8,18 @@ trait T {
 
 fn main() {
     let _: usize = foo(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
     let _: S = S(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
     let _: usize = T::baz(_, _);
-    //~^ ERROR expected expression
-    //~| ERROR expected expression
+    //~^ ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR `_` can only be used on the left-hand side of an assignment
+    //~| ERROR destructuring assignments are unstable
+    //~| ERROR destructuring assignments are unstable
 }
diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
index a6d1c4b859f..248fa6b9c9c 100644
--- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
+++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr
@@ -1,38 +1,93 @@
-error: expected expression, found reserved identifier `_`
+error[E0658]: destructuring assignments are unstable
   --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24
    |
 LL |     let _: usize = foo(_, _);
-   |                        ^ expected expression
+   |                        ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27
+   |
+LL |     let _: usize = foo(_, _);
+   |                           ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18
+   |
+LL |     let _: S = S(_, _);
+   |                  ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21
+   |
+LL |     let _: S = S(_, _);
+   |                     ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27
+   |
+LL |     let _: usize = T::baz(_, _);
+   |                           ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30
+   |
+LL |     let _: usize = T::baz(_, _);
+   |                              ^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24
+   |
+LL |     let _: usize = foo(_, _);
+   |                        ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
+error: in expressions, `_` can only be used on the left-hand side of an assignment
   --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:27
    |
 LL |     let _: usize = foo(_, _);
-   |                           ^ expected expression
+   |                           ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:18
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18
    |
 LL |     let _: S = S(_, _);
-   |                  ^ expected expression
+   |                  ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:21
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21
    |
 LL |     let _: S = S(_, _);
-   |                     ^ expected expression
+   |                     ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:27
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27
    |
 LL |     let _: usize = T::baz(_, _);
-   |                           ^ expected expression
+   |                           ^ `_` not allowed here
 
-error: expected expression, found reserved identifier `_`
-  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:30
+error: in expressions, `_` can only be used on the left-hand side of an assignment
+  --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30
    |
 LL |     let _: usize = T::baz(_, _);
-   |                              ^ expected expression
+   |                              ^ `_` not allowed here
 
-error: aborting due to 6 previous errors
+error: aborting due to 12 previous errors
 
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
index 625120b880e..1fcd41e4dbf 100644
--- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs
@@ -170,6 +170,7 @@ impl<'a> Sugg<'a> {
             | ast::ExprKind::MacCall(..)
             | ast::ExprKind::MethodCall(..)
             | ast::ExprKind::Paren(..)
+            | ast::ExprKind::Underscore
             | ast::ExprKind::Path(..)
             | ast::ExprKind::Repeat(..)
             | ast::ExprKind::Ret(..)
diff --git a/src/tools/rustfmt b/src/tools/rustfmt
-Subproject 0f29ff6da0c5ff622e739beb8fc3bbe77119b3c
+Subproject 293d7d01118c9fb5479649399e1dae60322b8e0