about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2021-06-21 01:16:15 +0000
committerbors <bors@rust-lang.org>2021-06-21 01:16:15 +0000
commit6a540bd40c6751d6d5efaa7fcc7cd28d473b5946 (patch)
tree1894020967e3fa84cc803574840665b60609d97a
parent03b845a41f1666654ecffcbaa6a582170ec0ed8d (diff)
parent8b9e138ecdb0891a8626f89994eb725e00b79d7e (diff)
downloadrust-6a540bd40c6751d6d5efaa7fcc7cd28d473b5946.tar.gz
rust-6a540bd40c6751d6d5efaa7fcc7cd28d473b5946.zip
Auto merge of #86502 - JohnTitor:rollup-wge0f3x, r=JohnTitor
Rollup of 8 pull requests

Successful merges:

 - #83739 (Account for bad placeholder errors on consts/statics with trait objects)
 - #85637 (document PartialEq, PartialOrd, Ord requirements more explicitly)
 - #86152 (Lazify is_really_default condition in the RustdocGUI bootstrap step)
 - #86156 (Fix a bug in the linkchecker)
 - #86427 (Updated release note)
 - #86452 (fix panic-safety in specialized Zip::next_back)
 - #86484 (Do not set depth to 0 in fully_expand_fragment)
 - #86491 (expand: Move some more derive logic to rustc_builtin_macros)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock1
-rw-r--r--RELEASES.md2
-rw-r--r--compiler/rustc_builtin_macros/src/cfg_eval.rs59
-rw-r--r--compiler/rustc_builtin_macros/src/derive.rs6
-rw-r--r--compiler/rustc_expand/src/base.rs2
-rw-r--r--compiler/rustc_expand/src/expand.rs32
-rw-r--r--compiler/rustc_resolve/src/macros.rs2
-rw-r--r--compiler/rustc_typeck/src/collect.rs8
-rw-r--r--library/core/src/cmp.rs89
-rw-r--r--library/core/src/iter/adapters/zip.rs3
-rw-r--r--library/core/tests/iter/adapters/zip.rs27
-rw-r--r--src/bootstrap/Cargo.toml1
-rw-r--r--src/bootstrap/builder.rs28
-rw-r--r--src/bootstrap/builder/tests.rs5
-rw-r--r--src/bootstrap/test.rs23
-rw-r--r--src/test/ui/error-codes/E0121.rs3
-rw-r--r--src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs16
-rw-r--r--src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr14
-rw-r--r--src/test/ui/typeck/issue-75889.rs6
-rw-r--r--src/test/ui/typeck/issue-75889.stderr15
-rw-r--r--src/tools/linkchecker/main.rs2
-rw-r--r--src/tools/linkchecker/tests/broken_redir/redir-bad.html1
-rw-r--r--src/tools/linkchecker/tests/checks.rs50
-rw-r--r--src/tools/linkchecker/tests/redirect_loop/redir-bad.html1
-rw-r--r--src/tools/linkchecker/tests/valid/inner/redir-bad.html1
-rw-r--r--src/tools/linkchecker/tests/valid/inner/redir.html1
26 files changed, 291 insertions, 107 deletions
diff --git a/Cargo.lock b/Cargo.lock
index bc8b79342b2..5e2987ee826 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -179,6 +179,7 @@ dependencies = [
  "libc",
  "merge",
  "num_cpus",
+ "once_cell",
  "opener",
  "pretty_assertions",
  "serde",
diff --git a/RELEASES.md b/RELEASES.md
index 4b753a2b32f..2b159e7d004 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1749,6 +1749,7 @@ Language
 - [You can now use `#[repr(transparent)]` on univariant `enum`s.][68122] Meaning
   that you can create an enum that has the exact layout and ABI of the type
   it contains.
+- [You can now use outer attribute procedural macros on inline modules.][64273]  
 - [There are some *syntax-only* changes:][67131]
    - `default` is syntactically allowed before items in `trait` definitions.
    - Items in `impl`s (i.e. `const`s, `type`s, and `fn`s) may syntactically
@@ -1810,6 +1811,7 @@ Compatibility Notes
 [67935]: https://github.com/rust-lang/rust/pull/67935/
 [68339]: https://github.com/rust-lang/rust/pull/68339/
 [68122]: https://github.com/rust-lang/rust/pull/68122/
+[64273]: https://github.com/rust-lang/rust/pull/64273/
 [67712]: https://github.com/rust-lang/rust/pull/67712/
 [67887]: https://github.com/rust-lang/rust/pull/67887/
 [67131]: https://github.com/rust-lang/rust/pull/67131/
diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index 79dc857074d..d7b46f28215 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -24,61 +24,60 @@ crate fn expand(
     annotatable: Annotatable,
 ) -> Vec<Annotatable> {
     check_builtin_macro_attribute(ecx, meta_item, sym::cfg_eval);
-    cfg_eval(ecx, annotatable)
+    vec![cfg_eval(ecx, annotatable)]
 }
 
-crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Vec<Annotatable> {
-    let mut visitor = CfgEval {
+crate fn cfg_eval(ecx: &ExtCtxt<'_>, annotatable: Annotatable) -> Annotatable {
+    CfgEval {
         cfg: &mut StripUnconfigured {
             sess: ecx.sess,
             features: ecx.ecfg.features,
             config_tokens: true,
         },
-    };
-    let annotatable = visitor.configure_annotatable(annotatable);
-    vec![annotatable]
+    }
+    .configure_annotatable(annotatable)
+    // Since the item itself has already been configured by the `InvocationCollector`,
+    // we know that fold result vector will contain exactly one element.
+    .unwrap()
 }
 
 struct CfgEval<'a, 'b> {
     cfg: &'a mut StripUnconfigured<'b>,
 }
 
-fn flat_map_annotatable(vis: &mut impl MutVisitor, annotatable: Annotatable) -> Annotatable {
-    // Since the item itself has already been configured by the InvocationCollector,
-    // we know that fold result vector will contain exactly one element
+fn flat_map_annotatable(
+    vis: &mut impl MutVisitor,
+    annotatable: Annotatable,
+) -> Option<Annotatable> {
     match annotatable {
-        Annotatable::Item(item) => Annotatable::Item(vis.flat_map_item(item).pop().unwrap()),
+        Annotatable::Item(item) => vis.flat_map_item(item).pop().map(Annotatable::Item),
         Annotatable::TraitItem(item) => {
-            Annotatable::TraitItem(vis.flat_map_trait_item(item).pop().unwrap())
+            vis.flat_map_trait_item(item).pop().map(Annotatable::TraitItem)
         }
         Annotatable::ImplItem(item) => {
-            Annotatable::ImplItem(vis.flat_map_impl_item(item).pop().unwrap())
+            vis.flat_map_impl_item(item).pop().map(Annotatable::ImplItem)
         }
         Annotatable::ForeignItem(item) => {
-            Annotatable::ForeignItem(vis.flat_map_foreign_item(item).pop().unwrap())
+            vis.flat_map_foreign_item(item).pop().map(Annotatable::ForeignItem)
         }
         Annotatable::Stmt(stmt) => {
-            Annotatable::Stmt(stmt.map(|stmt| vis.flat_map_stmt(stmt).pop().unwrap()))
+            vis.flat_map_stmt(stmt.into_inner()).pop().map(P).map(Annotatable::Stmt)
         }
-        Annotatable::Expr(mut expr) => Annotatable::Expr({
+        Annotatable::Expr(mut expr) => {
             vis.visit_expr(&mut expr);
-            expr
-        }),
-        Annotatable::Arm(arm) => Annotatable::Arm(vis.flat_map_arm(arm).pop().unwrap()),
-        Annotatable::ExprField(field) => {
-            Annotatable::ExprField(vis.flat_map_expr_field(field).pop().unwrap())
+            Some(Annotatable::Expr(expr))
         }
-        Annotatable::PatField(fp) => {
-            Annotatable::PatField(vis.flat_map_pat_field(fp).pop().unwrap())
+        Annotatable::Arm(arm) => vis.flat_map_arm(arm).pop().map(Annotatable::Arm),
+        Annotatable::ExprField(field) => {
+            vis.flat_map_expr_field(field).pop().map(Annotatable::ExprField)
         }
+        Annotatable::PatField(fp) => vis.flat_map_pat_field(fp).pop().map(Annotatable::PatField),
         Annotatable::GenericParam(param) => {
-            Annotatable::GenericParam(vis.flat_map_generic_param(param).pop().unwrap())
-        }
-        Annotatable::Param(param) => Annotatable::Param(vis.flat_map_param(param).pop().unwrap()),
-        Annotatable::FieldDef(sf) => {
-            Annotatable::FieldDef(vis.flat_map_field_def(sf).pop().unwrap())
+            vis.flat_map_generic_param(param).pop().map(Annotatable::GenericParam)
         }
-        Annotatable::Variant(v) => Annotatable::Variant(vis.flat_map_variant(v).pop().unwrap()),
+        Annotatable::Param(param) => vis.flat_map_param(param).pop().map(Annotatable::Param),
+        Annotatable::FieldDef(sf) => vis.flat_map_field_def(sf).pop().map(Annotatable::FieldDef),
+        Annotatable::Variant(v) => vis.flat_map_variant(v).pop().map(Annotatable::Variant),
     }
 }
 
@@ -123,11 +122,11 @@ impl CfgEval<'_, '_> {
         self.cfg.configure(node)
     }
 
-    pub fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Annotatable {
+    fn configure_annotatable(&mut self, mut annotatable: Annotatable) -> Option<Annotatable> {
         // Tokenizing and re-parsing the `Annotatable` can have a significant
         // performance impact, so try to avoid it if possible
         if !CfgFinder::has_cfg_or_cfg_attr(&annotatable) {
-            return annotatable;
+            return Some(annotatable);
         }
 
         // The majority of parsed attribute targets will never need to have early cfg-expansion
diff --git a/compiler/rustc_builtin_macros/src/derive.rs b/compiler/rustc_builtin_macros/src/derive.rs
index 1bb050a40ce..60d6bae38b5 100644
--- a/compiler/rustc_builtin_macros/src/derive.rs
+++ b/compiler/rustc_builtin_macros/src/derive.rs
@@ -26,6 +26,8 @@ impl MultiItemModifier for Expander {
             return ExpandResult::Ready(vec![item]);
         }
 
+        let item = cfg_eval(ecx, item);
+
         let result =
             ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
                 let template =
@@ -54,12 +56,12 @@ impl MultiItemModifier for Expander {
                         report_path_args(sess, &meta);
                         meta.path
                     })
-                    .map(|path| (path, None))
+                    .map(|path| (path, item.clone(), None))
                     .collect()
             });
 
         match result {
-            Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
+            Ok(()) => ExpandResult::Ready(vec![item]),
             Err(Indeterminate) => ExpandResult::Retry(item),
         }
     }
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index aab2741c852..96fd6cb68e8 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -835,7 +835,7 @@ impl SyntaxExtension {
 /// Error type that denotes indeterminacy.
 pub struct Indeterminate;
 
-pub type DeriveResolutions = Vec<(ast::Path, Option<Lrc<SyntaxExtension>>)>;
+pub type DeriveResolutions = Vec<(ast::Path, Annotatable, Option<Lrc<SyntaxExtension>>)>;
 
 pub trait ResolverExpand {
     fn next_node_id(&mut self) -> NodeId;
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 39c0447bd09..f8a12ef8a20 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -427,7 +427,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
     pub fn fully_expand_fragment(&mut self, input_fragment: AstFragment) -> AstFragment {
         let orig_expansion_data = self.cx.current_expansion.clone();
         let orig_force_mode = self.cx.force_mode;
-        self.cx.current_expansion.depth = 0;
 
         // Collect all macro invocations and replace them with placeholders.
         let (mut fragment_with_placeholders, mut invocations) =
@@ -488,6 +487,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
             };
 
             let ExpansionData { depth, id: expn_id, .. } = invoc.expansion_data;
+            let depth = depth - orig_expansion_data.depth;
             self.cx.current_expansion = invoc.expansion_data.clone();
             self.cx.force_mode = force;
 
@@ -500,42 +500,16 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
                         .resolver
                         .take_derive_resolutions(expn_id)
                         .map(|derives| {
-                            enum AnnotatableRef<'a> {
-                                Item(&'a P<ast::Item>),
-                                Stmt(&'a ast::Stmt),
-                            }
-                            let item = match &fragment {
-                                AstFragment::Items(items) => match &items[..] {
-                                    [item] => AnnotatableRef::Item(item),
-                                    _ => unreachable!(),
-                                },
-                                AstFragment::Stmts(stmts) => match &stmts[..] {
-                                    [stmt] => AnnotatableRef::Stmt(stmt),
-                                    _ => unreachable!(),
-                                },
-                                _ => unreachable!(),
-                            };
-
                             derive_invocations.reserve(derives.len());
                             derives
                                 .into_iter()
-                                .map(|(path, _exts)| {
+                                .map(|(path, item, _exts)| {
                                     // FIXME: Consider using the derive resolutions (`_exts`)
                                     // instead of enqueuing the derives to be resolved again later.
                                     let expn_id = ExpnId::fresh(None);
                                     derive_invocations.push((
                                         Invocation {
-                                            kind: InvocationKind::Derive {
-                                                path,
-                                                item: match item {
-                                                    AnnotatableRef::Item(item) => {
-                                                        Annotatable::Item(item.clone())
-                                                    }
-                                                    AnnotatableRef::Stmt(stmt) => {
-                                                        Annotatable::Stmt(P(stmt.clone()))
-                                                    }
-                                                },
-                                            },
+                                            kind: InvocationKind::Derive { path, item },
                                             fragment_kind,
                                             expansion_data: ExpansionData {
                                                 id: expn_id,
diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs
index 3f7db2b6962..38d052f988c 100644
--- a/compiler/rustc_resolve/src/macros.rs
+++ b/compiler/rustc_resolve/src/macros.rs
@@ -380,7 +380,7 @@ impl<'a> ResolverExpand for Resolver<'a> {
             has_derive_copy: false,
         });
         let parent_scope = self.invocation_parent_scopes[&expn_id];
-        for (i, (path, opt_ext)) in entry.resolutions.iter_mut().enumerate() {
+        for (i, (path, _, opt_ext)) in entry.resolutions.iter_mut().enumerate() {
             if opt_ext.is_none() {
                 *opt_ext = Some(
                     match self.resolve_macro_path(
diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs
index 248669a2c0c..ae61dd1cfce 100644
--- a/compiler/rustc_typeck/src/collect.rs
+++ b/compiler/rustc_typeck/src/collect.rs
@@ -813,6 +813,14 @@ fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
             match it.kind {
                 hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id),
                 hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id),
+                hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => {
+                    // (#75889): Account for `const C: dyn Fn() -> _ = "";`
+                    if let hir::TyKind::TraitObject(..) = ty.kind {
+                        let mut visitor = PlaceholderHirTyCollector::default();
+                        visitor.visit_item(it);
+                        placeholder_type_error(tcx, None, &[], visitor.0, false, None);
+                    }
+                }
                 _ => (),
             }
         }
diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs
index ecea898504d..6c2f09514a6 100644
--- a/library/core/src/cmp.rs
+++ b/library/core/src/cmp.rs
@@ -27,12 +27,25 @@ use self::Ordering::*;
 /// Trait for equality comparisons which are [partial equivalence
 /// relations](https://en.wikipedia.org/wiki/Partial_equivalence_relation).
 ///
+/// `x.eq(y)` can also be written `x == y`, and `x.ne(y)` can be written `x != y`.
+/// We use the easier-to-read infix notation in the remainder of this documentation.
+///
 /// This trait allows for partial equality, for types that do not have a full
 /// equivalence relation. For example, in floating point numbers `NaN != NaN`,
 /// so floating point types implement `PartialEq` but not [`trait@Eq`].
 ///
-/// Formally, the equality must be (for all `a`, `b`, `c` of type `A`, `B`,
-/// `C`):
+/// Implementations must ensure that `eq` and `ne` are consistent with each other:
+///
+/// - `a != b` if and only if `!(a == b)`
+///   (ensured by the default implementation).
+///
+/// If [`PartialOrd`] or [`Ord`] are also implemented for `Self` and `Rhs`, their methods must also
+/// be consistent with `PartialEq` (see the documentation of those traits for the exact
+/// requirememts). It's easy to accidentally make them disagree by deriving some of the traits and
+/// manually implementing others.
+///
+/// The equality relation `==` must satisfy the following conditions
+/// (for all `a`, `b`, `c` of type `A`, `B`, `C`):
 ///
 /// - **Symmetric**: if `A: PartialEq<B>` and `B: PartialEq<A>`, then **`a == b`
 ///   implies `b == a`**; and
@@ -53,15 +66,6 @@ use self::Ordering::*;
 ///
 /// ## How can I implement `PartialEq`?
 ///
-/// `PartialEq` only requires the [`eq`] method to be implemented; [`ne`] is defined
-/// in terms of it by default. Any manual implementation of [`ne`] *must* respect
-/// the rule that [`eq`] is a strict inverse of [`ne`]; that is, `!(a == b)` if and
-/// only if `a != b`.
-///
-/// Implementations of `PartialEq`, [`PartialOrd`], and [`Ord`] *must* agree with
-/// each other. It's easy to accidentally make them disagree by deriving some
-/// of the traits and manually implementing others.
-///
 /// An example implementation for a domain in which two books are considered
 /// the same book if their ISBN matches, even if the formats differ:
 ///
@@ -631,10 +635,25 @@ impl<T: Clone> Clone for Reverse<T> {
 
 /// Trait for types that form a [total order](https://en.wikipedia.org/wiki/Total_order).
 ///
-/// An order is a total order if it is (for all `a`, `b` and `c`):
+/// Implementations must be consistent with the [`PartialOrd`] implementation, and ensure
+/// `max`, `min`, and `clamp` are consistent with `cmp`:
+///
+/// - `partial_cmp(a, b) == Some(cmp(a, b))`.
+/// - `max(a, b) == max_by(a, b, cmp)` (ensured by the default implementation).
+/// - `min(a, b) == min_by(a, b, cmp)` (ensured by the default implementation).
+/// - For `a.clamp(min, max)`, see the [method docs](#method.clamp)
+///   (ensured by the default implementation).
+///
+/// It's easy to accidentally make `cmp` and `partial_cmp` disagree by
+/// deriving some of the traits and manually implementing others.
+///
+/// ## Corollaries
+///
+/// From the above and the requirements of `PartialOrd`, it follows that `<` defines a strict total order.
+/// This means that for all `a`, `b` and `c`:
 ///
-/// - total and asymmetric: exactly one of `a < b`, `a == b` or `a > b` is true; and
-/// - transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
+/// - exactly one of `a < b`, `a == b` or `a > b` is true; and
+/// - `<` is transitive: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
 ///
 /// ## Derivable
 ///
@@ -659,12 +678,6 @@ impl<T: Clone> Clone for Reverse<T> {
 /// Then you must define an implementation for [`cmp`]. You may find it useful to use
 /// [`cmp`] on your type's fields.
 ///
-/// Implementations of [`PartialEq`], [`PartialOrd`], and `Ord` *must*
-/// agree with each other. That is, `a.cmp(b) == Ordering::Equal` if
-/// and only if `a == b` and `Some(a.cmp(b)) == a.partial_cmp(b)` for
-/// all `a` and `b`. It's easy to accidentally make them disagree by
-/// deriving some of the traits and manually implementing others.
-///
 /// Here's an example where you want to sort people by height only, disregarding `id`
 /// and `name`:
 ///
@@ -824,15 +837,45 @@ impl PartialOrd for Ordering {
 
 /// Trait for values that can be compared for a sort-order.
 ///
+/// The `lt`, `le`, `gt`, and `ge` methods of this trait can be called using
+/// the `<`, `<=`, `>`, and `>=` operators, respectively.
+///
+/// The methods of this trait must be consistent with each other and with those of `PartialEq` in
+/// the following sense:
+///
+/// - `a == b` if and only if `partial_cmp(a, b) == Some(Equal)`.
+/// - `a < b` if and only if `partial_cmp(a, b) == Some(Less)`
+///   (ensured by the default implementation).
+/// - `a > b` if and only if `partial_cmp(a, b) == Some(Greater)`
+///   (ensured by the default implementation).
+/// - `a <= b` if and only if `a < b || a == b`
+///   (ensured by the default implementation).
+/// - `a >= b` if and only if `a > b || a == b`
+///   (ensured by the default implementation).
+/// - `a != b` if and only if `!(a == b)` (already part of `PartialEq`).
+///
+/// If [`Ord`] is also implemented for `Self` and `Rhs`, it must also be consistent with
+/// `partial_cmp` (see the documentation of that trait for the exact requirements). It's
+/// easy to accidentally make them disagree by deriving some of the traits and manually
+/// implementing others.
+///
 /// The comparison must satisfy, for all `a`, `b` and `c`:
 ///
-/// - asymmetry: if `a < b` then `!(a > b)`, as well as `a > b` implying `!(a < b)`; and
 /// - transitivity: `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
+/// - duality: `a < b` if and only if `b > a`.
 ///
 /// Note that these requirements mean that the trait itself must be implemented symmetrically and
 /// transitively: if `T: PartialOrd<U>` and `U: PartialOrd<V>` then `U: PartialOrd<T>` and `T:
 /// PartialOrd<V>`.
 ///
+/// ## Corollaries
+///
+/// The following corollaries follow from the above requirements:
+///
+/// - irreflexivity of `<` and `>`: `!(a < a)`, `!(a > a)`
+/// - transitivity of `>`: if `a > b` and `b > c` then `a > c`
+/// - duality of `partial_cmp`: `partial_cmp(a, b) == partial_cmp(b, a).map(Ordering::reverse)`
+///
 /// ## Derivable
 ///
 /// This trait can be used with `#[derive]`. When `derive`d on structs, it will produce a
@@ -850,10 +893,6 @@ impl PartialOrd for Ordering {
 ///
 /// `PartialOrd` requires your type to be [`PartialEq`].
 ///
-/// Implementations of [`PartialEq`], `PartialOrd`, and [`Ord`] *must* agree with each other. It's
-/// easy to accidentally make them disagree by deriving some of the traits and manually
-/// implementing others.
-///
 /// If your type is [`Ord`], you can implement [`partial_cmp`] by using [`cmp`]:
 ///
 /// ```
diff --git a/library/core/src/iter/adapters/zip.rs b/library/core/src/iter/adapters/zip.rs
index 8a6955060e8..46bf93dcc01 100644
--- a/library/core/src/iter/adapters/zip.rs
+++ b/library/core/src/iter/adapters/zip.rs
@@ -295,9 +295,10 @@ where
                 let sz_a = self.a.size();
                 if A::MAY_HAVE_SIDE_EFFECT && sz_a > self.len {
                     for _ in 0..sz_a - self.len {
+                        self.a_len -= 1;
                         self.a.next_back();
                     }
-                    self.a_len = self.len;
+                    debug_assert_eq!(self.a_len, self.len);
                 }
                 let sz_b = self.b.size();
                 if B::MAY_HAVE_SIDE_EFFECT && sz_b > self.len {
diff --git a/library/core/tests/iter/adapters/zip.rs b/library/core/tests/iter/adapters/zip.rs
index 797bfd957f9..585cfbb90e4 100644
--- a/library/core/tests/iter/adapters/zip.rs
+++ b/library/core/tests/iter/adapters/zip.rs
@@ -233,6 +233,33 @@ fn test_zip_trusted_random_access_composition() {
 }
 
 #[test]
+#[cfg(panic = "unwind")]
+fn test_zip_trusted_random_access_next_back_drop() {
+    use std::panic::catch_unwind;
+    use std::panic::AssertUnwindSafe;
+
+    let mut counter = 0;
+
+    let it = [42].iter().map(|e| {
+        let c = counter;
+        counter += 1;
+        if c == 0 {
+            panic!("bomb");
+        }
+
+        e
+    });
+    let it2 = [(); 0].iter();
+    let mut zip = it.zip(it2);
+    catch_unwind(AssertUnwindSafe(|| {
+        zip.next_back();
+    }))
+    .unwrap_err();
+    assert!(zip.next().is_none());
+    assert_eq!(counter, 1);
+}
+
+#[test]
 fn test_double_ended_zip() {
     let xs = [1, 2, 3, 4, 5, 6];
     let ys = [1, 2, 3, 7];
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 8445d811e0f..d1e666936f8 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -50,6 +50,7 @@ time = "0.1"
 ignore = "0.4.10"
 opener = "0.4"
 merge = "0.1.0"
+once_cell = "1.7.2"
 
 [target.'cfg(windows)'.dependencies.winapi]
 version = "0.3"
diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs
index bc499fdba59..e2f605257bd 100644
--- a/src/bootstrap/builder.rs
+++ b/src/bootstrap/builder.rs
@@ -29,6 +29,8 @@ use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir};
 use crate::{Build, DocTests, GitRepo, Mode};
 
 pub use crate::Compiler;
+// FIXME: replace with std::lazy after it gets stabilized and reaches beta
+use once_cell::sync::Lazy;
 
 pub struct Builder<'a> {
     pub build: &'a Build,
@@ -195,7 +197,7 @@ impl StepDescription {
 
         if paths.is_empty() || builder.config.include_default_paths {
             for (desc, should_run) in v.iter().zip(&should_runs) {
-                if desc.default && should_run.is_really_default {
+                if desc.default && should_run.is_really_default() {
                     for pathset in &should_run.paths {
                         desc.maybe_run(builder, pathset);
                     }
@@ -228,7 +230,11 @@ impl StepDescription {
     }
 }
 
-#[derive(Clone)]
+enum ReallyDefault<'a> {
+    Bool(bool),
+    Lazy(Lazy<bool, Box<dyn Fn() -> bool + 'a>>),
+}
+
 pub struct ShouldRun<'a> {
     pub builder: &'a Builder<'a>,
     // use a BTreeSet to maintain sort order
@@ -236,7 +242,7 @@ pub struct ShouldRun<'a> {
 
     // If this is a default rule, this is an additional constraint placed on
     // its run. Generally something like compiler docs being enabled.
-    is_really_default: bool,
+    is_really_default: ReallyDefault<'a>,
 }
 
 impl<'a> ShouldRun<'a> {
@@ -244,15 +250,27 @@ impl<'a> ShouldRun<'a> {
         ShouldRun {
             builder,
             paths: BTreeSet::new(),
-            is_really_default: true, // by default no additional conditions
+            is_really_default: ReallyDefault::Bool(true), // by default no additional conditions
         }
     }
 
     pub fn default_condition(mut self, cond: bool) -> Self {
-        self.is_really_default = cond;
+        self.is_really_default = ReallyDefault::Bool(cond);
+        self
+    }
+
+    pub fn lazy_default_condition(mut self, lazy_cond: Box<dyn Fn() -> bool + 'a>) -> Self {
+        self.is_really_default = ReallyDefault::Lazy(Lazy::new(lazy_cond));
         self
     }
 
+    pub fn is_really_default(&self) -> bool {
+        match &self.is_really_default {
+            ReallyDefault::Bool(val) => *val,
+            ReallyDefault::Lazy(lazy) => *lazy.deref(),
+        }
+    }
+
     /// Indicates it should run if the command-line selects the given crate or
     /// any of its (local) dependencies.
     ///
diff --git a/src/bootstrap/builder/tests.rs b/src/bootstrap/builder/tests.rs
index 4d7c207e3ab..e7fb8c0d4d5 100644
--- a/src/bootstrap/builder/tests.rs
+++ b/src/bootstrap/builder/tests.rs
@@ -613,9 +613,14 @@ mod dist {
         // Note that the stages here are +1 than what they actually are because
         // Rustdoc::run swaps out the compiler with stage minus 1 if --stage is
         // not 0.
+        //
+        // The stage 0 copy is the one downloaded for bootstrapping. It is
+        // (currently) needed to run "cargo test" on the linkchecker, and
+        // should be relatively "free".
         assert_eq!(
             first(builder.cache.all::<tool::Rustdoc>()),
             &[
+                tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },
                 tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
                 tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },
             ]
diff --git a/src/bootstrap/test.rs b/src/bootstrap/test.rs
index 0b7a0e25df1..93ba8b07f5b 100644
--- a/src/bootstrap/test.rs
+++ b/src/bootstrap/test.rs
@@ -124,8 +124,25 @@ You can skip linkcheck with --exclude src/tools/linkchecker"
 
         builder.info(&format!("Linkcheck ({})", host));
 
+        // Test the linkchecker itself.
+        let bootstrap_host = builder.config.build;
+        let compiler = builder.compiler(0, bootstrap_host);
+        let cargo = tool::prepare_tool_cargo(
+            builder,
+            compiler,
+            Mode::ToolBootstrap,
+            bootstrap_host,
+            "test",
+            "src/tools/linkchecker",
+            SourceType::InTree,
+            &[],
+        );
+        try_run(builder, &mut cargo.into());
+
+        // Build all the default documentation.
         builder.default_doc(&[]);
 
+        // Run the linkchecker.
         let _time = util::timeit(&builder);
         try_run(
             builder,
@@ -806,15 +823,15 @@ impl Step for RustdocGUI {
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
         let builder = run.builder;
         let run = run.suite_path("src/test/rustdoc-gui");
-        run.default_condition(
+        run.lazy_default_condition(Box::new(move || {
             builder.config.nodejs.is_some()
                 && builder
                     .config
                     .npm
                     .as_ref()
                     .map(|p| check_if_browser_ui_test_is_installed(p))
-                    .unwrap_or(false),
-        )
+                    .unwrap_or(false)
+        }))
     }
 
     fn make_run(run: RunConfig<'_>) {
diff --git a/src/test/ui/error-codes/E0121.rs b/src/test/ui/error-codes/E0121.rs
index f8b4d61b323..98cd6d54c1f 100644
--- a/src/test/ui/error-codes/E0121.rs
+++ b/src/test/ui/error-codes/E0121.rs
@@ -2,5 +2,4 @@ fn foo() -> _ { 5 } //~ ERROR E0121
 
 static BAR: _ = "test"; //~ ERROR E0121
 
-fn main() {
-}
+fn main() {}
diff --git a/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs
new file mode 100644
index 00000000000..9139775c805
--- /dev/null
+++ b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.rs
@@ -0,0 +1,16 @@
+// Regression test for #84632: Recursion limit is ignored
+// for builtin macros that eagerly expands.
+
+#![recursion_limit = "15"]
+macro_rules! a {
+    () => ("");
+    (A) => (concat!("", a!()));
+    (A, $($A:ident),*) => (concat!("", a!($($A),*)))
+    //~^ ERROR recursion limit reached
+    //~| HELP consider adding
+}
+
+fn main() {
+    a!(A, A, A, A, A);
+    a!(A, A, A, A, A, A, A, A, A, A, A);
+}
diff --git a/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr
new file mode 100644
index 00000000000..e6067e33349
--- /dev/null
+++ b/src/test/ui/macros/issue-84632-eager-expansion-recursion-limit.stderr
@@ -0,0 +1,14 @@
+error: recursion limit reached while expanding `concat!`
+  --> $DIR/issue-84632-eager-expansion-recursion-limit.rs:8:28
+   |
+LL |     (A, $($A:ident),*) => (concat!("", a!($($A),*)))
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     a!(A, A, A, A, A, A, A, A, A, A, A);
+   |     ------------------------------------ in this macro invocation
+   |
+   = help: consider adding a `#![recursion_limit="30"]` attribute to your crate (`issue_84632_eager_expansion_recursion_limit`)
+   = note: this error originates in the macro `a` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/typeck/issue-75889.rs b/src/test/ui/typeck/issue-75889.rs
new file mode 100644
index 00000000000..84c067ed0c7
--- /dev/null
+++ b/src/test/ui/typeck/issue-75889.rs
@@ -0,0 +1,6 @@
+// Regression test for #75889.
+
+const FOO: dyn Fn() -> _ = ""; //~ ERROR E0121
+static BOO: dyn Fn() -> _ = ""; //~ ERROR E0121
+
+fn main() {}
diff --git a/src/test/ui/typeck/issue-75889.stderr b/src/test/ui/typeck/issue-75889.stderr
new file mode 100644
index 00000000000..0a8a3c9e743
--- /dev/null
+++ b/src/test/ui/typeck/issue-75889.stderr
@@ -0,0 +1,15 @@
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/issue-75889.rs:3:24
+   |
+LL | const FOO: dyn Fn() -> _ = "";
+   |                        ^ not allowed in type signatures
+
+error[E0121]: the type placeholder `_` is not allowed within types on item signatures
+  --> $DIR/issue-75889.rs:4:25
+   |
+LL | static BOO: dyn Fn() -> _ = "";
+   |                         ^ not allowed in type signatures
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0121`.
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index 47960c3f6cc..15edd628cdf 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -347,7 +347,7 @@ impl Checker {
                 } else {
                     report.errors += 1;
                     print!("{}:{}: broken link fragment ", pretty_path, i + 1);
-                    println!("`#{}` pointing to `{}`", fragment, pretty_path);
+                    println!("`#{}` pointing to `{}`", fragment, target_pretty_path);
                 };
             }
         });
diff --git a/src/tools/linkchecker/tests/broken_redir/redir-bad.html b/src/tools/linkchecker/tests/broken_redir/redir-bad.html
index 3e376629f74..9c580d8e07e 100644
--- a/src/tools/linkchecker/tests/broken_redir/redir-bad.html
+++ b/src/tools/linkchecker/tests/broken_redir/redir-bad.html
@@ -2,6 +2,7 @@
 <html lang="en">
 <head>
     <meta http-equiv="refresh" content="0;URL=sometarget">
+    <title>Redirection</title>
 </head>
 <body>
     <p>Redirecting to <a href="sometarget">sometarget</a>...</p>
diff --git a/src/tools/linkchecker/tests/checks.rs b/src/tools/linkchecker/tests/checks.rs
index c6ec999e5cf..1a0b1b00e0d 100644
--- a/src/tools/linkchecker/tests/checks.rs
+++ b/src/tools/linkchecker/tests/checks.rs
@@ -15,7 +15,7 @@ fn run(dirname: &str) -> (ExitStatus, String, String) {
 fn broken_test(dirname: &str, expected: &str) {
     let (status, stdout, stderr) = run(dirname);
     assert!(!status.success());
-    if !stdout.contains(expected) {
+    if !contains(expected, &stdout) {
         panic!(
             "stdout did not contain expected text: {}\n\
             --- stdout:\n\
@@ -27,6 +27,25 @@ fn broken_test(dirname: &str, expected: &str) {
     }
 }
 
+fn contains(expected: &str, actual: &str) -> bool {
+    // Normalize for Windows paths.
+    let actual = actual.replace('\\', "/");
+    actual.lines().any(|mut line| {
+        for (i, part) in expected.split("[..]").enumerate() {
+            match line.find(part) {
+                Some(j) => {
+                    if i == 0 && j != 0 {
+                        return false;
+                    }
+                    line = &line[j + part.len()..];
+                }
+                None => return false,
+            }
+        }
+        line.is_empty() || expected.ends_with("[..]")
+    })
+}
+
 fn valid_test(dirname: &str) {
     let (status, stdout, stderr) = run(dirname);
     if !status.success() {
@@ -48,30 +67,47 @@ fn valid() {
 
 #[test]
 fn basic_broken() {
-    broken_test("basic_broken", "bar.html");
+    broken_test("basic_broken", "foo.html:3: broken link - `bar.html`");
 }
 
 #[test]
 fn broken_fragment_local() {
-    broken_test("broken_fragment_local", "#somefrag");
+    broken_test(
+        "broken_fragment_local",
+        "foo.html:3: broken link fragment `#somefrag` pointing to `foo.html`",
+    );
 }
 
 #[test]
 fn broken_fragment_remote() {
-    broken_test("broken_fragment_remote/inner", "#somefrag");
+    broken_test(
+        "broken_fragment_remote/inner",
+        "foo.html:3: broken link fragment `#somefrag` pointing to \
+         `[..]/broken_fragment_remote/bar.html`",
+    );
 }
 
 #[test]
 fn broken_redir() {
-    broken_test("broken_redir", "sometarget");
+    broken_test(
+        "broken_redir",
+        "foo.html:3: broken redirect from `redir-bad.html` to `sometarget`",
+    );
 }
 
 #[test]
 fn directory_link() {
-    broken_test("directory_link", "somedir");
+    broken_test(
+        "directory_link",
+        "foo.html:3: directory link to `somedir` (directory links should use index.html instead)",
+    );
 }
 
 #[test]
 fn redirect_loop() {
-    broken_test("redirect_loop", "redir-bad.html");
+    broken_test(
+        "redirect_loop",
+        "foo.html:3: redirect from `redir-bad.html` to `[..]redirect_loop/redir-bad.html` \
+         which is also a redirect (not supported)",
+    );
 }
diff --git a/src/tools/linkchecker/tests/redirect_loop/redir-bad.html b/src/tools/linkchecker/tests/redirect_loop/redir-bad.html
index fe7780e6739..bc567caa78b 100644
--- a/src/tools/linkchecker/tests/redirect_loop/redir-bad.html
+++ b/src/tools/linkchecker/tests/redirect_loop/redir-bad.html
@@ -2,6 +2,7 @@
 <html lang="en">
 <head>
     <meta http-equiv="refresh" content="0;URL=redir-bad.html">
+    <title>Redirection</title>
 </head>
 <body>
     <p>Redirecting to <a href="redir-bad.html">redir-bad.html</a>...</p>
diff --git a/src/tools/linkchecker/tests/valid/inner/redir-bad.html b/src/tools/linkchecker/tests/valid/inner/redir-bad.html
index d21336e7e73..f32683efe67 100644
--- a/src/tools/linkchecker/tests/valid/inner/redir-bad.html
+++ b/src/tools/linkchecker/tests/valid/inner/redir-bad.html
@@ -2,6 +2,7 @@
 <html lang="en">
 <head>
     <meta http-equiv="refresh" content="0;URL=xxx">
+    <title>Redirection</title>
 </head>
 <body>
     <p>Redirecting to <a href="xxx">xxx</a>...</p>
diff --git a/src/tools/linkchecker/tests/valid/inner/redir.html b/src/tools/linkchecker/tests/valid/inner/redir.html
index 1808b23aed8..3a52a897385 100644
--- a/src/tools/linkchecker/tests/valid/inner/redir.html
+++ b/src/tools/linkchecker/tests/valid/inner/redir.html
@@ -2,6 +2,7 @@
 <html lang="en">
 <head>
     <meta http-equiv="refresh" content="0;URL=redir-target.html">
+    <title>Redirection</title>
 </head>
 <body>
     <p>Redirecting to <a href="redir-target.html">redir-target.html</a>...</p>