about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock28
-rw-r--r--compiler/rustc_driver/src/lib.rs8
-rw-r--r--compiler/rustc_error_codes/src/error_codes.rs9
-rw-r--r--compiler/rustc_middle/src/thir.rs144
-rw-r--r--compiler/rustc_mir/src/util/pretty.rs46
-rw-r--r--compiler/rustc_typeck/src/check/upvar.rs14
-rw-r--r--library/core/src/iter/traits/double_ended.rs9
-rw-r--r--library/core/src/iter/traits/iterator.rs22
-rw-r--r--src/bootstrap/Cargo.toml2
-rw-r--r--src/librustdoc/html/render/mod.rs22
-rw-r--r--src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff3
-rw-r--r--src/test/mir-opt/const_prop/indirect.main.ConstProp.diff3
-rw-r--r--src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff3
-rw-r--r--src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff3
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff3
-rw-r--r--src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff3
-rw-r--r--src/test/mir-opt/const_prop/return_place.add.ConstProp.diff3
-rw-r--r--src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff3
-rw-r--r--src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff3
-rw-r--r--src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff3
-rw-r--r--src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff3
-rw-r--r--src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs23
m---------src/tools/cargo0
-rw-r--r--src/tools/rustc-workspace-hack/Cargo.toml1
-rw-r--r--src/tools/tidy/src/error_codes_check.rs114
25 files changed, 318 insertions, 157 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 86dd4695aa4..17378cb4a2d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -180,7 +180,7 @@ dependencies = [
  "merge",
  "num_cpus",
  "once_cell",
- "opener",
+ "opener 0.5.0",
  "pretty_assertions",
  "serde",
  "serde_json",
@@ -195,7 +195,9 @@ version = "0.2.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "31accafdb70df7871592c058eca3985b71104e15ac32f64706022c58867da931"
 dependencies = [
+ "lazy_static",
  "memchr",
+ "regex-automata",
 ]
 
 [[package]]
@@ -253,7 +255,7 @@ checksum = "81a18687293a1546b67c246452202bbbf143d239cb43494cc163da14979082da"
 
 [[package]]
 name = "cargo"
-version = "0.55.0"
+version = "0.56.0"
 dependencies = [
  "anyhow",
  "atty",
@@ -288,11 +290,10 @@ dependencies = [
  "log",
  "memchr",
  "num_cpus",
- "opener",
+ "opener 0.5.0",
  "openssl",
  "percent-encoding 2.1.0",
  "pretty_env_logger",
- "rand 0.8.3",
  "rustc-workspace-hack",
  "rustfix 0.6.0",
  "semver 1.0.3",
@@ -375,10 +376,12 @@ dependencies = [
  "flate2",
  "git2",
  "glob",
+ "itertools 0.10.0",
  "lazy_static",
  "remove_dir_all",
  "serde_json",
  "tar",
+ "termcolor",
  "toml",
  "url 2.2.2",
 ]
@@ -575,7 +578,7 @@ dependencies = [
  "bytecount",
  "clap",
  "itertools 0.9.0",
- "opener",
+ "opener 0.4.1",
  "regex",
  "shell-escape",
  "walkdir",
@@ -2419,6 +2422,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "opener"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ea3ebcd72a54701f56345f16785a6d3ac2df7e986d273eb4395c0b01db17952"
+dependencies = [
+ "bstr",
+ "winapi 0.3.9",
+]
+
+[[package]]
 name = "openssl"
 version = "0.10.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3558,6 +3571,7 @@ dependencies = [
 name = "rustc-workspace-hack"
 version = "1.0.0"
 dependencies = [
+ "bstr",
  "byteorder",
  "crossbeam-utils 0.8.3",
  "libc",
@@ -5158,9 +5172,9 @@ dependencies = [
 
 [[package]]
 name = "termcolor"
-version = "1.1.0"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
+checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
 dependencies = [
  "winapi-util",
 ]
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index b943977e4c2..87bc829b488 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -528,8 +528,12 @@ fn stderr_isatty() -> bool {
 }
 
 fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
-    let normalised =
-        if code.starts_with('E') { code.to_string() } else { format!("E{0:0>4}", code) };
+    let upper_cased_code = code.to_ascii_uppercase();
+    let normalised = if upper_cased_code.starts_with('E') {
+        upper_cased_code
+    } else {
+        format!("E{0:0>4}", code)
+    };
     match registry.try_find_description(&normalised) {
         Ok(Some(description)) => {
             let mut is_in_code_block = false;
diff --git a/compiler/rustc_error_codes/src/error_codes.rs b/compiler/rustc_error_codes/src/error_codes.rs
index f10efd83236..ff7a2344e69 100644
--- a/compiler/rustc_error_codes/src/error_codes.rs
+++ b/compiler/rustc_error_codes/src/error_codes.rs
@@ -609,7 +609,7 @@ E0783: include_str!("./error_codes/E0783.md"),
 //  E0540, // multiple rustc_deprecated attributes
     E0544, // multiple stability levels
 //  E0548, // replaced with a generic attribute input check
-    E0553, // multiple rustc_const_unstable attributes
+//  E0553, // multiple rustc_const_unstable attributes
 //  E0555, // replaced with a generic attribute input check
 //  E0558, // replaced with a generic attribute input check
 //  E0563, // cannot determine a type for this `impl Trait` removed in 6383de15
@@ -620,10 +620,9 @@ E0783: include_str!("./error_codes/E0783.md"),
 //  E0612, // merged into E0609
 //  E0613, // Removed (merged with E0609)
     E0625, // thread-local statics cannot be accessed at compile-time
-    E0629, // missing 'feature' (rustc_const_unstable)
-    // rustc_const_unstable attribute must be paired with stable/unstable
-    // attribute
-    E0630,
+//  E0629, // missing 'feature' (rustc_const_unstable)
+//  E0630, // rustc_const_unstable attribute must be paired with stable/unstable
+           // attribute
     E0632, // cannot provide explicit generic arguments when `impl Trait` is
            // used in argument position
     E0640, // infer outlives requirements
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index 6b2e542ee70..cdefc9effa1 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -1,3 +1,13 @@
+//! THIR datatypes and definitions. See the [rustc dev guide] for more info.
+//!
+//! If you compare the THIR [`ExprKind`] to [`hir::ExprKind`], you will see it is
+//! a good bit simpler. In fact, a number of the more straight-forward
+//! MIR simplifications are already done in the lowering to THIR. For
+//! example, method calls and overloaded operators are absent: they are
+//! expected to be converted into [`ExprKind::Call`] instances.
+//!
+//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/thir.html
+
 use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
@@ -24,6 +34,7 @@ use std::fmt;
 use std::ops::Index;
 
 newtype_index! {
+    /// An index to an [`Arm`] stored in [`Thir::arms`]
     #[derive(HashStable)]
     pub struct ArmId {
         DEBUG_FORMAT = "a{}"
@@ -31,6 +42,7 @@ newtype_index! {
 }
 
 newtype_index! {
+    /// An index to an [`Expr`] stored in [`Thir::exprs`]
     #[derive(HashStable)]
     pub struct ExprId {
         DEBUG_FORMAT = "e{}"
@@ -39,6 +51,7 @@ newtype_index! {
 
 newtype_index! {
     #[derive(HashStable)]
+    /// An index to a [`Stmt`] stored in [`Thir::stmts`]
     pub struct StmtId {
         DEBUG_FORMAT = "s{}"
     }
@@ -46,6 +59,9 @@ newtype_index! {
 
 macro_rules! thir_with_elements {
     ($($name:ident: $id:ty => $value:ty,)*) => {
+        /// A container for a THIR body.
+        ///
+        /// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
         #[derive(Debug, HashStable)]
         pub struct Thir<'tcx> {
             $(
@@ -88,18 +104,28 @@ pub enum LintLevel {
 
 #[derive(Debug, HashStable)]
 pub struct Block {
+    /// Whether the block itself has a label. Used by `label: {}`
+    /// and `try` blocks.
+    ///
+    /// This does *not* include labels on loops, e.g. `'label: loop {}`.
     pub targeted_by_break: bool,
     pub region_scope: region::Scope,
     pub opt_destruction_scope: Option<region::Scope>,
+    /// The span of the block, including the opening braces,
+    /// the label, and the `unsafe` keyword, if present.
     pub span: Span,
+    /// The statements in the blocK.
     pub stmts: Box<[StmtId]>,
+    /// The trailing expression of the block, if any.
     pub expr: Option<ExprId>,
     pub safety_mode: BlockSafety,
 }
 
 #[derive(Debug, HashStable)]
 pub struct Adt<'tcx> {
+    /// The ADT we're constructing.
     pub adt_def: &'tcx AdtDef,
+    /// The variant of the ADT.
     pub variant_index: VariantIdx,
     pub substs: SubstsRef<'tcx>,
 
@@ -108,13 +134,16 @@ pub struct Adt<'tcx> {
     pub user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
 
     pub fields: Box<[FieldExpr]>,
+    /// The base, e.g. `Foo {x: 1, .. base}`.
     pub base: Option<FruInfo<'tcx>>,
 }
 
 #[derive(Copy, Clone, Debug, HashStable)]
 pub enum BlockSafety {
     Safe,
+    /// A compiler-generated unsafe block
     BuiltinUnsafe,
+    /// An `unsafe` block. The `HirId` is the ID of the block.
     ExplicitUnsafe(hir::HirId),
 }
 
@@ -126,32 +155,34 @@ pub struct Stmt<'tcx> {
 
 #[derive(Debug, HashStable)]
 pub enum StmtKind<'tcx> {
+    /// An expression with a trailing semicolon.
     Expr {
-        /// scope for this statement; may be used as lifetime of temporaries
+        /// The scope for this statement; may be used as lifetime of temporaries.
         scope: region::Scope,
 
-        /// expression being evaluated in this statement
+        /// The expression being evaluated in this statement.
         expr: ExprId,
     },
 
+    /// A `let` binding.
     Let {
-        /// scope for variables bound in this let; covers this and
-        /// remaining statements in block
+        /// The scope for variables bound in this `let`; it covers this and
+        /// all the remaining statements in the block.
         remainder_scope: region::Scope,
 
-        /// scope for the initialization itself; might be used as
-        /// lifetime of temporaries
+        /// The scope for the initialization itself; might be used as
+        /// lifetime of temporaries.
         init_scope: region::Scope,
 
         /// `let <PAT> = ...`
         ///
-        /// if a type is included, it is added as an ascription pattern
+        /// If a type annotation is included, it is added as an ascription pattern.
         pattern: Pat<'tcx>,
 
-        /// let pat: ty = <INIT> ...
+        /// `let pat: ty = <INIT>`
         initializer: Option<ExprId>,
 
-        /// the lint level for this let-statement
+        /// The lint level for this `let` statement.
         lint_level: LintLevel,
     },
 }
@@ -160,27 +191,14 @@ pub enum StmtKind<'tcx> {
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
 rustc_data_structures::static_assert_size!(Expr<'_>, 104);
 
-/// The Thir trait implementor lowers their expressions (`&'tcx H::Expr`)
-/// into instances of this `Expr` enum. This lowering can be done
-/// basically as lazily or as eagerly as desired: every recursive
-/// reference to an expression in this enum is an `ExprId`, which
-/// may in turn be another instance of this enum (boxed), or else an
-/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very
-/// short-lived. They are created by `Thir::to_expr`, analyzed and
-/// converted into MIR, and then discarded.
-///
-/// If you compare `Expr` to the full compiler AST, you will see it is
-/// a good bit simpler. In fact, a number of the more straight-forward
-/// MIR simplifications are already done in the impl of `Thir`. For
-/// example, method calls and overloaded operators are absent: they are
-/// expected to be converted into `Expr::Call` instances.
+/// A THIR expression.
 #[derive(Debug, HashStable)]
 pub struct Expr<'tcx> {
-    /// type of this expression
+    /// The type of this expression
     pub ty: Ty<'tcx>,
 
-    /// lifetime of this expression if it should be spilled into a
-    /// temporary; should be None only if in a constant context
+    /// The lifetime of this expression if it should be spilled into a
+    /// temporary; should be `None` only if in a constant context
     pub temp_lifetime: Option<region::Scope>,
 
     /// span of the expression in the source
@@ -192,88 +210,120 @@ pub struct Expr<'tcx> {
 
 #[derive(Debug, HashStable)]
 pub enum ExprKind<'tcx> {
+    /// `Scope`s are used to explicitely mark destruction scopes,
+    /// and to track the `HirId` of the expressions within the scope.
     Scope {
         region_scope: region::Scope,
         lint_level: LintLevel,
         value: ExprId,
     },
+    /// A `box <value>` expression.
     Box {
         value: ExprId,
     },
+    /// An `if` expression.
     If {
         cond: ExprId,
         then: ExprId,
         else_opt: Option<ExprId>,
     },
+    /// A function call. Method calls and overloaded operators are converted to plain function calls.
     Call {
+        /// The type of the function. This is often a [`FnDef`] or a [`FnPtr`].
+        ///
+        /// [`FnDef`]: ty::TyKind::FnDef
+        /// [`FnPtr`]: ty::TyKind::FnPtr
         ty: Ty<'tcx>,
+        /// The function itself.
         fun: ExprId,
+        /// The arguments passed to the function.
+        ///
+        /// Note: in some cases (like calling a closure), the function call `f(...args)` gets
+        /// rewritten as a call to a function trait method (e.g. `FnOnce::call_once(f, (...args))`).
         args: Box<[ExprId]>,
-        /// Whether this is from a call in HIR, rather than from an overloaded
-        /// operator. `true` for overloaded function call.
+        /// Whether this is from an overloaded operator rather than a
+        /// function call from HIR. `true` for overloaded function call.
         from_hir_call: bool,
-        /// This `Span` is the span of the function, without the dot and receiver
-        /// (e.g. `foo(a, b)` in `x.foo(a, b)`
+        /// The span of the function, without the dot and receiver
+        /// (e.g. `foo(a, b)` in `x.foo(a, b)`).
         fn_span: Span,
     },
+    /// A *non-overloaded* dereference.
     Deref {
         arg: ExprId,
-    }, // NOT overloaded!
+    },
+    /// A *non-overloaded* binary operation.
     Binary {
         op: BinOp,
         lhs: ExprId,
         rhs: ExprId,
-    }, // NOT overloaded!
+    },
+    /// A logical operation. This is distinct from `BinaryOp` because
+    /// the operands need to be lazily evaluated.
     LogicalOp {
         op: LogicalOp,
         lhs: ExprId,
         rhs: ExprId,
-    }, // NOT overloaded!
-    // LogicalOp is distinct from BinaryOp because of lazy evaluation of the operands.
+    },
+    /// A *non-overloaded* unary operation. Note that here the deref (`*`)
+    /// operator is represented by `ExprKind::Deref`.
     Unary {
         op: UnOp,
         arg: ExprId,
-    }, // NOT overloaded!
+    },
+    /// A cast: `<source> as <type>`. The type we cast to is the type of
+    /// the parent expression.
     Cast {
         source: ExprId,
     },
     Use {
         source: ExprId,
     }, // Use a lexpr to get a vexpr.
+    /// A coercion from `!` to any type.
     NeverToAny {
         source: ExprId,
     },
+    /// A pointer cast. More information can be found in [`PointerCast`].
     Pointer {
         cast: PointerCast,
         source: ExprId,
     },
+    /// A `loop` expression.
     Loop {
         body: ExprId,
     },
+    /// A `match` expression.
     Match {
         scrutinee: ExprId,
         arms: Box<[ArmId]>,
     },
+    /// A block.
     Block {
         body: Block,
     },
+    /// An assignment: `lhs = rhs`.
     Assign {
         lhs: ExprId,
         rhs: ExprId,
     },
+    /// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
     AssignOp {
         op: BinOp,
         lhs: ExprId,
         rhs: ExprId,
     },
+    /// Access to a struct or tuple field.
     Field {
         lhs: ExprId,
+        /// This can be a named (`.foo`) or unnamed (`.0`) field.
         name: Field,
     },
+    /// A *non-overloaded* indexing operation.
     Index {
         lhs: ExprId,
         index: ExprId,
     },
+    /// A local variable.
     VarRef {
         id: hir::HirId,
     },
@@ -285,6 +335,7 @@ pub enum ExprKind<'tcx> {
         /// HirId of the root variable
         var_hir_id: hir::HirId,
     },
+    /// A borrow, e.g. `&arg`.
     Borrow {
         borrow_kind: BorrowKind,
         arg: ExprId,
@@ -294,40 +345,51 @@ pub enum ExprKind<'tcx> {
         mutability: hir::Mutability,
         arg: ExprId,
     },
+    /// A `break` expression.
     Break {
         label: region::Scope,
         value: Option<ExprId>,
     },
+    /// A `continue` expression.
     Continue {
         label: region::Scope,
     },
+    /// A `return` expression.
     Return {
         value: Option<ExprId>,
     },
+    /// An inline `const` block, e.g. `const {}`.
     ConstBlock {
         value: &'tcx Const<'tcx>,
     },
+    /// An array literal constructed from one repeated element, e.g. `[1; 5]`.
     Repeat {
         value: ExprId,
         count: &'tcx Const<'tcx>,
     },
+    /// An array, e.g. `[a, b, c, d]`.
     Array {
         fields: Box<[ExprId]>,
     },
+    /// A tuple, e.g. `(a, b, c, d)`.
     Tuple {
         fields: Box<[ExprId]>,
     },
+    /// An ADT constructor, e.g. `Foo {x: 1, y: 2}`.
     Adt(Box<Adt<'tcx>>),
+    /// A type ascription on a place.
     PlaceTypeAscription {
         source: ExprId,
         /// Type that the user gave to this expression
         user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
     },
+    /// A type ascription on a value, e.g. `42: i32`.
     ValueTypeAscription {
         source: ExprId,
         /// Type that the user gave to this expression
         user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
     },
+    /// A closure definition.
     Closure {
         closure_id: DefId,
         substs: UpvarSubsts<'tcx>,
@@ -335,6 +397,7 @@ pub enum ExprKind<'tcx> {
         movability: Option<hir::Movability>,
         fake_reads: Vec<(ExprId, FakeReadCause, hir::HirId)>,
     },
+    /// A literal.
     Literal {
         literal: &'tcx Const<'tcx>,
         user_ty: Option<Canonical<'tcx, UserType<'tcx>>>,
@@ -351,6 +414,7 @@ pub enum ExprKind<'tcx> {
         literal: &'tcx Const<'tcx>,
         def_id: DefId,
     },
+    /// Inline assembly, i.e. `asm!()`.
     InlineAsm {
         template: &'tcx [InlineAsmTemplatePiece],
         operands: Box<[InlineAsmOperand<'tcx>]>,
@@ -359,16 +423,21 @@ pub enum ExprKind<'tcx> {
     },
     /// An expression taking a reference to a thread local.
     ThreadLocalRef(DefId),
+    /// Inline LLVM assembly, i.e. `llvm_asm!()`.
     LlvmInlineAsm {
         asm: &'tcx hir::LlvmInlineAsmInner,
         outputs: Box<[ExprId]>,
         inputs: Box<[ExprId]>,
     },
+    /// A `yield` expression.
     Yield {
         value: ExprId,
     },
 }
 
+/// Represents the association of a field identifier and an expression.
+///
+/// This is used in struct constructors.
 #[derive(Debug, HashStable)]
 pub struct FieldExpr {
     pub name: Field,
@@ -381,6 +450,7 @@ pub struct FruInfo<'tcx> {
     pub field_types: Box<[Ty<'tcx>]>,
 }
 
+/// A `match` arm.
 #[derive(Debug, HashStable)]
 pub struct Arm<'tcx> {
     pub pattern: Pat<'tcx>,
@@ -391,6 +461,7 @@ pub struct Arm<'tcx> {
     pub span: Span,
 }
 
+/// A `match` guard.
 #[derive(Debug, HashStable)]
 pub enum Guard<'tcx> {
     If(ExprId),
@@ -399,7 +470,9 @@ pub enum Guard<'tcx> {
 
 #[derive(Copy, Clone, Debug, HashStable)]
 pub enum LogicalOp {
+    /// The `&&` operator.
     And,
+    /// The `||` operator.
     Or,
 }
 
@@ -516,6 +589,7 @@ pub struct Ascription<'tcx> {
 
 #[derive(Clone, Debug, PartialEq, HashStable)]
 pub enum PatKind<'tcx> {
+    /// A wildward pattern: `_`.
     Wild,
 
     AscribeUserType {
diff --git a/compiler/rustc_mir/src/util/pretty.rs b/compiler/rustc_mir/src/util/pretty.rs
index f8325a3646f..cb1203393b3 100644
--- a/compiler/rustc_mir/src/util/pretty.rs
+++ b/compiler/rustc_mir/src/util/pretty.rs
@@ -426,14 +426,14 @@ impl ExtraComments<'tcx> {
     }
 }
 
-fn use_verbose(ty: &&TyS<'tcx>) -> bool {
+fn use_verbose(ty: &&TyS<'tcx>, fn_def: bool) -> bool {
     match ty.kind() {
         ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char | ty::Float(_) => false,
         // Unit type
         ty::Tuple(g_args) if g_args.is_empty() => false,
-        ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty())),
-        ty::Array(ty, _) => use_verbose(ty),
-        ty::FnDef(..) => false,
+        ty::Tuple(g_args) => g_args.iter().any(|g_arg| use_verbose(&g_arg.expect_ty(), fn_def)),
+        ty::Array(ty, _) => use_verbose(ty, fn_def),
+        ty::FnDef(..) => fn_def,
         _ => true,
     }
 }
@@ -442,28 +442,20 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
     fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
         self.super_constant(constant, location);
         let Constant { span, user_ty, literal } = constant;
-        match literal.ty().kind() {
-            ty::Int(_) | ty::Uint(_) | ty::Bool | ty::Char => {}
-            // Unit type
-            ty::Tuple(tys) if tys.is_empty() => {}
-            _ => {
-                self.push("mir::Constant");
-                self.push(&format!(
-                    "+ span: {}",
-                    self.tcx.sess.source_map().span_to_embeddable_string(*span)
-                ));
-                if let Some(user_ty) = user_ty {
-                    self.push(&format!("+ user_ty: {:?}", user_ty));
-                }
-                match literal {
-                    ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
-                    ConstantKind::Val(val, ty) => {
-                        // To keep the diffs small, we render this almost like we render ty::Const
-                        self.push(&format!(
-                            "+ literal: Const {{ ty: {}, val: Value({:?}) }}",
-                            ty, val
-                        ))
-                    }
+        if use_verbose(&literal.ty(), true) {
+            self.push("mir::Constant");
+            self.push(&format!(
+                "+ span: {}",
+                self.tcx.sess.source_map().span_to_embeddable_string(*span)
+            ));
+            if let Some(user_ty) = user_ty {
+                self.push(&format!("+ user_ty: {:?}", user_ty));
+            }
+            match literal {
+                ConstantKind::Ty(literal) => self.push(&format!("+ literal: {:?}", literal)),
+                ConstantKind::Val(val, ty) => {
+                    // To keep the diffs small, we render this almost like we render ty::Const
+                    self.push(&format!("+ literal: Const {{ ty: {}, val: Value({:?}) }}", ty, val))
                 }
             }
         }
@@ -472,7 +464,7 @@ impl Visitor<'tcx> for ExtraComments<'tcx> {
     fn visit_const(&mut self, constant: &&'tcx ty::Const<'tcx>, _: Location) {
         self.super_const(constant);
         let ty::Const { ty, val, .. } = constant;
-        if use_verbose(ty) {
+        if use_verbose(ty, false) {
             self.push("ty::Const");
             self.push(&format!("+ ty: {:?}", ty));
             let val = match val {
diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs
index 6baa185406e..4c9c3954624 100644
--- a/compiler/rustc_typeck/src/check/upvar.rs
+++ b/compiler/rustc_typeck/src/check/upvar.rs
@@ -177,7 +177,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // We now fake capture information for all variables that are mentioned within the closure
         // We do this after handling migrations so that min_captures computes before
-        if !self.tcx.features().capture_disjoint_fields {
+        if !enable_precise_capture(self.tcx, span) {
             let mut capture_information: InferredCaptureInformation<'tcx> = Default::default();
 
             if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) {
@@ -212,7 +212,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             // If we have an origin, store it.
             if let Some(origin) = delegate.current_origin.clone() {
-                let origin = if self.tcx.features().capture_disjoint_fields {
+                let origin = if enable_precise_capture(self.tcx, span) {
                     (origin.0, restrict_capture_precision(origin.1))
                 } else {
                     (origin.0, Place { projections: vec![], ..origin.1 })
@@ -1924,3 +1924,13 @@ fn determine_place_ancestry_relation(
         PlaceAncestryRelation::Divergent
     }
 }
+
+/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if
+/// user is using Rust Edition 2021 or higher.
+///
+/// `span` is the span of the closure.
+fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool {
+    // We use span here to ensure that if the closure was generated by a macro with a different
+    // edition.
+    tcx.features().capture_disjoint_fields || span.rust_2021()
+}
diff --git a/library/core/src/iter/traits/double_ended.rs b/library/core/src/iter/traits/double_ended.rs
index c302502b3b7..6d3ab788e5f 100644
--- a/library/core/src/iter/traits/double_ended.rs
+++ b/library/core/src/iter/traits/double_ended.rs
@@ -248,6 +248,11 @@ pub trait DoubleEndedIterator: Iterator {
     /// Folding is useful whenever you have a collection of something, and want
     /// to produce a single value from it.
     ///
+    /// Note: `rfold()` combines elements in a *right-associative* fashion. For associative
+    /// operators like `+`, the order the elements are combined in is not important, but for non-associative
+    /// operators like `-` the order will affect the final result.
+    /// For a *left-associative* version of `rfold()`, see [`Iterator::fold()`].
+    ///
     /// # Examples
     ///
     /// Basic usage:
@@ -262,7 +267,8 @@ pub trait DoubleEndedIterator: Iterator {
     /// assert_eq!(sum, 6);
     /// ```
     ///
-    /// This example builds a string, starting with an initial value
+    /// This example demonstrates the right-associative nature of `rfold()`:
+    /// it builds a string, starting with an initial value
     /// and continuing with each element from the back until the front:
     ///
     /// ```
@@ -276,6 +282,7 @@ pub trait DoubleEndedIterator: Iterator {
     ///
     /// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))");
     /// ```
+    #[doc(alias = "foldr")]
     #[inline]
     #[stable(feature = "iter_rfold", since = "1.27.0")]
     fn rfold<B, F>(mut self, init: B, mut f: F) -> B
diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs
index 78d317096b4..41d9993abaa 100644
--- a/library/core/src/iter/traits/iterator.rs
+++ b/library/core/src/iter/traits/iterator.rs
@@ -2083,6 +2083,11 @@ pub trait Iterator {
     /// Note: [`reduce()`] can be used to use the first element as the initial
     /// value, if the accumulator type and item type is the same.
     ///
+    /// Note: `fold()` combines elements in a *left-associative* fashion. For associative
+    /// operators like `+`, the order the elements are combined in is not important, but for non-associative
+    /// operators like `-` the order will affect the final result.
+    /// For a *right-associative* version of `fold()`, see [`DoubleEndedIterator::rfold()`].
+    ///
     /// # Note to Implementors
     ///
     /// Several of the other (forward) methods have default implementations in
@@ -2116,6 +2121,21 @@ pub trait Iterator {
     ///
     /// And so, our final result, `6`.
     ///
+    /// This example demonstrates the left-associative nature of `fold()`:
+    /// it builds a string, starting with an initial value
+    /// and continuing with each element from the front until the back:
+    ///
+    /// ```
+    /// let numbers = [1, 2, 3, 4, 5];
+    ///
+    /// let zero = "0".to_string();
+    ///
+    /// let result = numbers.iter().fold(zero, |acc, &x| {
+    ///     format!("({} + {})", acc, x)
+    /// });
+    ///
+    /// assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)");
+    /// ```
     /// It's common for people who haven't used iterators a lot to
     /// use a `for` loop with a list of things to build up a result. Those
     /// can be turned into `fold()`s:
@@ -2140,7 +2160,7 @@ pub trait Iterator {
     /// ```
     ///
     /// [`reduce()`]: Iterator::reduce
-    #[doc(alias = "inject")]
+    #[doc(alias = "inject", alias = "foldl")]
     #[inline]
     #[stable(feature = "rust1", since = "1.0.0")]
     fn fold<B, F>(mut self, init: B, mut f: F) -> B
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index d1e666936f8..6fb7a1c088f 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -48,7 +48,7 @@ toml = "0.5"
 lazy_static = "1.3.0"
 time = "0.1"
 ignore = "0.4.10"
-opener = "0.4"
+opener = "0.5"
 merge = "0.1.0"
 once_cell = "1.7.2"
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 8c922240c44..a43a5424f04 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -101,28 +101,6 @@ crate struct RenderType {
     generics: Option<Vec<Generic>>,
 }
 
-impl Serialize for RenderType {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        if let Some(name) = &self.name {
-            let mut seq = serializer.serialize_seq(None)?;
-            if let Some(id) = self.idx {
-                seq.serialize_element(&id)?;
-            } else {
-                seq.serialize_element(&name)?;
-            }
-            if let Some(generics) = &self.generics {
-                seq.serialize_element(&generics)?;
-            }
-            seq.end()
-        } else {
-            serializer.serialize_none()
-        }
-    }
-}
-
 /// A type used for the search index.
 #[derive(Debug)]
 crate struct Generic {
diff --git a/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff b/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff
index bccfa9da615..77ff8ef4e49 100644
--- a/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/checked_add.main.ConstProp.diff
@@ -14,9 +14,6 @@
 -         _2 = CheckedAdd(const 1_u32, const 1_u32); // scope 0 at $DIR/checked_add.rs:5:18: 5:23
 -         assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23
 +         _2 = const (2_u32, false);       // scope 0 at $DIR/checked_add.rs:5:18: 5:23
-+                                          // mir::Constant
-+                                          // + span: $DIR/checked_add.rs:5:18: 5:23
-+                                          // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [2, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
 +         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 1_u32, const 1_u32) -> bb1; // scope 0 at $DIR/checked_add.rs:5:18: 5:23
       }
   
diff --git a/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff b/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff
index 57a00ba12b0..8dd55235ef3 100644
--- a/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/indirect.main.ConstProp.diff
@@ -18,9 +18,6 @@
 -         assert(!move (_3.1: bool), "attempt to compute `{} + {}`, which would overflow", move _2, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29
 +         _2 = const 2_u8;                 // scope 0 at $DIR/indirect.rs:5:13: 5:25
 +         _3 = const (3_u8, false);        // scope 0 at $DIR/indirect.rs:5:13: 5:29
-+                                          // mir::Constant
-+                                          // + span: $DIR/indirect.rs:5:13: 5:29
-+                                          // + literal: Const { ty: (u8, bool), val: Value(ByRef { alloc: Allocation { bytes: [3, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
 +         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u8, const 1_u8) -> bb1; // scope 0 at $DIR/indirect.rs:5:13: 5:29
       }
   
diff --git a/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff b/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff
index 518974e24b3..2d3289f7ce5 100644
--- a/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/issue_67019.main.ConstProp.diff
@@ -15,9 +15,6 @@
           (_3.1: u8) = const 2_u8;         // scope 0 at $DIR/issue-67019.rs:11:11: 11:17
 -         (_2.0: (u8, u8)) = move _3;      // scope 0 at $DIR/issue-67019.rs:11:10: 11:19
 +         (_2.0: (u8, u8)) = const (1_u8, 2_u8); // scope 0 at $DIR/issue-67019.rs:11:10: 11:19
-+                                          // mir::Constant
-+                                          // + span: $DIR/issue-67019.rs:11:10: 11:19
-+                                          // + literal: Const { ty: (u8, u8), val: Value(ByRef { alloc: Allocation { bytes: [1, 2], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [3], len: Size { raw: 2 } }, align: Align { pow2: 0 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
           StorageDead(_3);                 // scope 0 at $DIR/issue-67019.rs:11:18: 11:19
           _1 = test(move _2) -> bb1;       // scope 0 at $DIR/issue-67019.rs:11:5: 11:20
                                            // mir::Constant
diff --git a/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff b/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff
index d94c4f6fb26..a044d1dcfe1 100644
--- a/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/mutable_variable_aggregate.main.ConstProp.diff
@@ -20,9 +20,6 @@
           StorageLive(_2);                 // scope 1 at $DIR/mutable_variable_aggregate.rs:7:9: 7:10
 -         _2 = _1;                         // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14
 +         _2 = const (42_i32, 99_i32);     // scope 1 at $DIR/mutable_variable_aggregate.rs:7:13: 7:14
-+                                          // mir::Constant
-+                                          // + span: $DIR/mutable_variable_aggregate.rs:7:13: 7:14
-+                                          // + literal: Const { ty: (i32, i32), val: Value(ByRef { alloc: Allocation { bytes: [42, 0, 0, 0, 99, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
           nop;                             // scope 0 at $DIR/mutable_variable_aggregate.rs:4:11: 8:2
           StorageDead(_2);                 // scope 1 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2
           StorageDead(_1);                 // scope 0 at $DIR/mutable_variable_aggregate.rs:8:1: 8:2
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
index 537f1b6253c..4c3f66cd090 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.32bit.diff
@@ -27,9 +27,6 @@
 -         _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
 -         assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
 +         _2 = const (4_i32, false);       // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
-+                                          // mir::Constant
-+                                          // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18
-+                                          // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
 +         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
       }
   
diff --git a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
index 537f1b6253c..4c3f66cd090 100644
--- a/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
+++ b/src/test/mir-opt/const_prop/optimizes_into_variable.main.ConstProp.64bit.diff
@@ -27,9 +27,6 @@
 -         _2 = CheckedAdd(const 2_i32, const 2_i32); // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
 -         assert(!move (_2.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
 +         _2 = const (4_i32, false);       // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
-+                                          // mir::Constant
-+                                          // + span: $DIR/optimizes_into_variable.rs:12:13: 12:18
-+                                          // + literal: Const { ty: (i32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
 +         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_i32, const 2_i32) -> bb1; // scope 0 at $DIR/optimizes_into_variable.rs:12:13: 12:18
       }
   
diff --git a/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff b/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff
index 97808246dd4..c6ac8d6fb13 100644
--- a/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/return_place.add.ConstProp.diff
@@ -9,9 +9,6 @@
 -         _1 = CheckedAdd(const 2_u32, const 2_u32); // scope 0 at $DIR/return_place.rs:6:5: 6:10
 -         assert(!move (_1.1: bool), "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10
 +         _1 = const (4_u32, false);       // scope 0 at $DIR/return_place.rs:6:5: 6:10
-+                                          // mir::Constant
-+                                          // + span: $DIR/return_place.rs:6:5: 6:10
-+                                          // + literal: Const { ty: (u32, bool), val: Value(ByRef { alloc: Allocation { bytes: [4, 0, 0, 0, 0, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [31], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
 +         assert(!const false, "attempt to compute `{} + {}`, which would overflow", const 2_u32, const 2_u32) -> bb1; // scope 0 at $DIR/return_place.rs:6:5: 6:10
       }
   
diff --git a/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff b/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff
index b8f9cd34c99..15253a364e9 100644
--- a/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff
+++ b/src/test/mir-opt/const_prop/tuple_literal_propagation.main.ConstProp.diff
@@ -18,9 +18,6 @@
           StorageLive(_3);                 // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14
 -         _3 = _1;                         // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14
 +         _3 = const (1_u32, 2_u32);       // scope 1 at $DIR/tuple_literal_propagation.rs:5:13: 5:14
-+                                          // mir::Constant
-+                                          // + span: $DIR/tuple_literal_propagation.rs:5:13: 5:14
-+                                          // + literal: Const { ty: (u32, u32), val: Value(ByRef { alloc: Allocation { bytes: [1, 0, 0, 0, 2, 0, 0, 0], relocations: Relocations(SortedMap { data: [] }), init_mask: InitMask { blocks: [255], len: Size { raw: 8 } }, align: Align { pow2: 2 }, mutability: Not, extra: () }, offset: Size { raw: 0 } }) }
           _2 = consume(move _3) -> bb1;    // scope 1 at $DIR/tuple_literal_propagation.rs:5:5: 5:15
                                            // mir::Constant
                                            // + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12
diff --git a/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff b/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff
index e60a1f3e75f..d3c7136c647 100644
--- a/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff
+++ b/src/test/mir-opt/deaggregator_test.bar.Deaggregator.diff
@@ -12,9 +12,6 @@
 -         _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
 +         (_0.0: usize) = move _2;         // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
 +         (_0.1: f32) = const 0f32;        // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
-                                           // mir::Constant
-                                           // + span: $DIR/deaggregator_test.rs:9:20: 9:23
-                                           // + literal: Const { ty: f32, val: Value(Scalar(0x00000000)) }
 +         (_0.2: bool) = const false;      // scope 0 at $DIR/deaggregator_test.rs:9:5: 9:35
           StorageDead(_2);                 // scope 0 at $DIR/deaggregator_test.rs:9:34: 9:35
           return;                          // scope 0 at $DIR/deaggregator_test.rs:10:2: 10:2
diff --git a/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff
index 2c4952402a4..64a3f52f3a0 100644
--- a/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff
+++ b/src/test/mir-opt/if_condition_int.dont_opt_floats.SimplifyComparisonIntegral.diff
@@ -12,9 +12,6 @@
           StorageLive(_3);                 // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
           _3 = _1;                         // scope 0 at $DIR/if-condition-int.rs:53:8: 53:9
           _2 = Eq(move _3, const -42f32);  // scope 0 at $DIR/if-condition-int.rs:53:8: 53:18
-                                           // mir::Constant
-                                           // + span: $DIR/if-condition-int.rs:53:13: 53:18
-                                           // + literal: Const { ty: f32, val: Value(Scalar(0xc2280000)) }
           StorageDead(_3);                 // scope 0 at $DIR/if-condition-int.rs:53:17: 53:18
           switchInt(move _2) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/if-condition-int.rs:53:5: 53:35
       }
diff --git a/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff
index a698d8abcdb..598e8247efc 100644
--- a/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff
+++ b/src/test/mir-opt/simplify_locals_removes_unused_consts.main.SimplifyLocals.diff
@@ -38,9 +38,6 @@
                                            // mir::Constant
                                            // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:12
                                            // + literal: Const { ty: fn(((), ())) {use_zst}, val: Value(Scalar(<ZST>)) }
-                                           // mir::Constant
-                                           // + span: $DIR/simplify-locals-removes-unused-consts.rs:14:5: 14:22
-                                           // + literal: Const { ty: ((), ()), val: Value(Scalar(<ZST>)) }
       }
   
       bb1: {
diff --git a/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs
new file mode 100644
index 00000000000..20bbe1d89e4
--- /dev/null
+++ b/src/test/ui/closures/2229_closure_analysis/run_pass/edition.rs
@@ -0,0 +1,23 @@
+// edition:2021
+// run-pass
+
+// Test that edition 2021 enables disjoint capture by default.
+
+struct Point {
+    x: i32,
+    y: i32,
+}
+
+fn main() {
+    let mut p = Point { x: 10, y: 10 };
+
+    let c = || {
+        println!("{}", p.x);
+    };
+
+    // `c` should only capture `p.x`, therefore mutating `p.y` is allowed.
+    let py = &mut p.y;
+
+    c();
+    *py = 20;
+}
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 44456677b5d1d82fe981c955dc5c67734b31f34
+Subproject 9233aa06c801801cff75df65df718d70905a235
diff --git a/src/tools/rustc-workspace-hack/Cargo.toml b/src/tools/rustc-workspace-hack/Cargo.toml
index d39cb597b21..4a4b26da54c 100644
--- a/src/tools/rustc-workspace-hack/Cargo.toml
+++ b/src/tools/rustc-workspace-hack/Cargo.toml
@@ -61,6 +61,7 @@ features = [
 ]
 
 [dependencies]
+bstr = { version = "0.2.13", features = ["default"] }
 byteorder = { version = "1", features = ['default', 'std'] }
 curl-sys = { version = "0.4.13", features = ["http2", "libnghttp2-sys"], optional = true }
 crossbeam-utils = { version = "0.8.0", features = ["nightly"] }
diff --git a/src/tools/tidy/src/error_codes_check.rs b/src/tools/tidy/src/error_codes_check.rs
index d6e0ebaa541..63fbee34bd6 100644
--- a/src/tools/tidy/src/error_codes_check.rs
+++ b/src/tools/tidy/src/error_codes_check.rs
@@ -6,20 +6,33 @@ use std::ffi::OsStr;
 use std::fs::read_to_string;
 use std::path::Path;
 
+use regex::Regex;
+
 // A few of those error codes can't be tested but all the others can and *should* be tested!
 const EXEMPTED_FROM_TEST: &[&str] = &[
-    "E0227", "E0279", "E0280", "E0313", "E0314", "E0315", "E0377", "E0461", "E0462", "E0464",
-    "E0465", "E0473", "E0474", "E0475", "E0476", "E0479", "E0480", "E0481", "E0482", "E0483",
-    "E0484", "E0485", "E0486", "E0487", "E0488", "E0489", "E0514", "E0519", "E0523", "E0553",
-    "E0554", "E0570", "E0629", "E0630", "E0640", "E0717", "E0729",
+    "E0227", "E0279", "E0280", "E0313", "E0377", "E0461", "E0462", "E0464", "E0465", "E0476",
+    "E0482", "E0514", "E0519", "E0523", "E0554", "E0570", "E0640", "E0717", "E0729",
 ];
 
 // Some error codes don't have any tests apparently...
 const IGNORE_EXPLANATION_CHECK: &[&str] = &["E0570", "E0601", "E0602", "E0729"];
 
+// If the file path contains any of these, we don't want to try to extract error codes from it.
+//
+// We need to declare each path in the windows version (with backslash).
+const PATHS_TO_IGNORE_FOR_EXTRACTION: &[&str] =
+    &["src/test/", "src\\test\\", "src/doc/", "src\\doc\\", "src/tools/", "src\\tools\\"];
+
+#[derive(Default, Debug)]
+struct ErrorCodeStatus {
+    has_test: bool,
+    has_explanation: bool,
+    is_used: bool,
+}
+
 fn check_error_code_explanation(
     f: &str,
-    error_codes: &mut HashMap<String, bool>,
+    error_codes: &mut HashMap<String, ErrorCodeStatus>,
     err_code: String,
 ) -> bool {
     let mut invalid_compile_fail_format = false;
@@ -30,7 +43,7 @@ fn check_error_code_explanation(
         if s.starts_with("```") {
             if s.contains("compile_fail") && s.contains('E') {
                 if !found_error_code {
-                    error_codes.insert(err_code.clone(), true);
+                    error_codes.get_mut(&err_code).map(|x| x.has_test = true);
                     found_error_code = true;
                 }
             } else if s.contains("compile-fail") {
@@ -38,7 +51,7 @@ fn check_error_code_explanation(
             }
         } else if s.starts_with("#### Note: this error code is no longer emitted by the compiler") {
             if !found_error_code {
-                error_codes.get_mut(&err_code).map(|x| *x = true);
+                error_codes.get_mut(&err_code).map(|x| x.has_test = true);
                 found_error_code = true;
             }
         }
@@ -77,7 +90,7 @@ macro_rules! some_or_continue {
 
 fn extract_error_codes(
     f: &str,
-    error_codes: &mut HashMap<String, bool>,
+    error_codes: &mut HashMap<String, ErrorCodeStatus>,
     path: &Path,
     errors: &mut Vec<String>,
 ) {
@@ -90,15 +103,16 @@ fn extract_error_codes(
                 .split_once(':')
                 .expect(
                     format!(
-                        "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} without a `:` delimiter",
+                        "Expected a line with the format `E0xxx: include_str!(\"..\")`, but got {} \
+                         without a `:` delimiter",
                         s,
-                    ).as_str()
+                    )
+                    .as_str(),
                 )
                 .0
                 .to_owned();
-            if !error_codes.contains_key(&err_code) {
-                error_codes.insert(err_code.clone(), false);
-            }
+            error_codes.entry(err_code.clone()).or_default().has_explanation = true;
+
             // Now we extract the tests from the markdown file!
             let md_file_name = match s.split_once("include_str!(\"") {
                 None => continue,
@@ -145,7 +159,7 @@ fn extract_error_codes(
             .to_string();
             if !error_codes.contains_key(&err_code) {
                 // this check should *never* fail!
-                error_codes.insert(err_code, false);
+                error_codes.insert(err_code, ErrorCodeStatus::default());
             }
         } else if s == ";" {
             reached_no_explanation = true;
@@ -153,7 +167,7 @@ fn extract_error_codes(
     }
 }
 
-fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, bool>) {
+fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, ErrorCodeStatus>) {
     for line in f.lines() {
         let s = line.trim();
         if s.starts_with("error[E") || s.starts_with("warning[E") {
@@ -164,8 +178,24 @@ fn extract_error_codes_from_tests(f: &str, error_codes: &mut HashMap<String, boo
                     Some((_, err_code)) => err_code,
                 },
             };
-            let nb = error_codes.entry(err_code.to_owned()).or_insert(false);
-            *nb = true;
+            error_codes.entry(err_code.to_owned()).or_default().has_test = true;
+        }
+    }
+}
+
+fn extract_error_codes_from_source(
+    f: &str,
+    error_codes: &mut HashMap<String, ErrorCodeStatus>,
+    regex: &Regex,
+) {
+    for line in f.lines() {
+        if line.trim_start().starts_with("//") {
+            continue;
+        }
+        for cap in regex.captures_iter(line) {
+            if let Some(error_code) = cap.get(1) {
+                error_codes.entry(error_code.as_str().to_owned()).or_default().is_used = true;
+            }
         }
     }
 }
@@ -174,8 +204,17 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
     let mut errors = Vec::new();
     let mut found_explanations = 0;
     let mut found_tests = 0;
+    let mut error_codes: HashMap<String, ErrorCodeStatus> = HashMap::new();
+    // We want error codes which match the following cases:
+    //
+    // * foo(a, E0111, a)
+    // * foo(a, E0111)
+    // * foo(E0111, a)
+    // * #[error = "E0111"]
+    let regex = Regex::new(r#"[(,"\s](E\d{4})[,)"]"#).unwrap();
+
     println!("Checking which error codes lack tests...");
-    let mut error_codes: HashMap<String, bool> = HashMap::new();
+
     for path in paths {
         super::walk(path, &mut |path| super::filter_dirs(path), &mut |entry, contents| {
             let file_name = entry.file_name();
@@ -185,6 +224,11 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
             } else if entry.path().extension() == Some(OsStr::new("stderr")) {
                 extract_error_codes_from_tests(contents, &mut error_codes);
                 found_tests += 1;
+            } else if entry.path().extension() == Some(OsStr::new("rs")) {
+                let path = entry.path().to_string_lossy();
+                if PATHS_TO_IGNORE_FOR_EXTRACTION.iter().all(|c| !path.contains(c)) {
+                    extract_error_codes_from_source(contents, &mut error_codes, &regex);
+                }
             }
         });
     }
@@ -199,15 +243,43 @@ pub fn check(paths: &[&Path], bad: &mut bool) {
     if errors.is_empty() {
         println!("Found {} error codes", error_codes.len());
 
-        for (err_code, nb) in &error_codes {
-            if !*nb && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
+        for (err_code, error_status) in &error_codes {
+            if !error_status.has_test && !EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
                 errors.push(format!("Error code {} needs to have at least one UI test!", err_code));
-            } else if *nb && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
+            } else if error_status.has_test && EXEMPTED_FROM_TEST.contains(&err_code.as_str()) {
                 errors.push(format!(
                     "Error code {} has a UI test, it shouldn't be listed into EXEMPTED_FROM_TEST!",
                     err_code
                 ));
             }
+            if !error_status.is_used && !error_status.has_explanation {
+                errors.push(format!(
+                    "Error code {} isn't used and doesn't have an error explanation, it should be \
+                     commented in error_codes.rs file",
+                    err_code
+                ));
+            }
+        }
+    }
+    if errors.is_empty() {
+        // Checking if local constants need to be cleaned.
+        for err_code in EXEMPTED_FROM_TEST {
+            match error_codes.get(err_code.to_owned()) {
+                Some(status) => {
+                    if status.has_test {
+                        errors.push(format!(
+                            "{} error code has a test and therefore should be \
+                            removed from the `EXEMPTED_FROM_TEST` constant",
+                            err_code
+                        ));
+                    }
+                }
+                None => errors.push(format!(
+                    "{} error code isn't used anymore and therefore should be removed \
+                        from `EXEMPTED_FROM_TEST` constant",
+                    err_code
+                )),
+            }
         }
     }
     errors.sort();