about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_driver/src/lib.rs8
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs64
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs71
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs48
-rw-r--r--library/core/src/ptr/mod.rs4
-rw-r--r--src/etc/htmldocck.py47
-rw-r--r--src/librustdoc/html/render/mod.rs7
-rw-r--r--src/librustdoc/html/static/css/themes/ayu.css6
-rw-r--r--src/librustdoc/html/static/css/themes/dark.css1
-rw-r--r--src/librustdoc/html/static/css/themes/light.css1
-rw-r--r--src/librustdoc/passes/stripper.rs5
-rw-r--r--src/test/rustdoc/empty-impl-block.rs20
-rw-r--r--src/test/ui/cast/casts-issue-46365.rs3
-rw-r--r--src/test/ui/impl-trait/universal_wrong_hrtb.rs8
-rw-r--r--src/test/ui/impl-trait/universal_wrong_hrtb.stderr14
-rw-r--r--src/test/ui/parser/labeled-no-colon-expr.stderr6
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.fixed27
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.rs26
-rw-r--r--src/test/ui/parser/recover-labeled-non-block-expr.stderr80
-rw-r--r--src/test/ui/suggestions/issue-97704.fixed19
-rw-r--r--src/test/ui/suggestions/issue-97704.rs19
-rw-r--r--src/test/ui/suggestions/issue-97704.stderr15
23 files changed, 419 insertions, 82 deletions
diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs
index 179a184536e..1a7972716d3 100644
--- a/compiler/rustc_driver/src/lib.rs
+++ b/compiler/rustc_driver/src/lib.rs
@@ -1055,13 +1055,7 @@ pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
     }
 
     if cg_flags.iter().any(|x| *x == "passes=list") {
-        let backend_name = debug_flags.iter().find_map(|x| {
-            if x.starts_with("codegen-backend=") {
-                Some(&x["codegen-backends=".len()..])
-            } else {
-                None
-            }
-        });
+        let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
         get_codegen_backend(&None, backend_name).print_passes();
         return None;
     }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index cb2e66090e7..83821b5b536 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -734,7 +734,7 @@ impl<'hir> WherePredicate<'hir> {
     }
 }
 
-#[derive(Debug, HashStable_Generic, PartialEq, Eq)]
+#[derive(Copy, Clone, Debug, HashStable_Generic, PartialEq, Eq)]
 pub enum PredicateOrigin {
     WhereClause,
     GenericParam,
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 63c7decbb2f..b786c52e688 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -13,10 +13,13 @@ use rustc_ast::tokenstream::Spacing;
 use rustc_ast::util::classify;
 use rustc_ast::util::literal::LitError;
 use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
+use rustc_ast::visit::Visitor;
+use rustc_ast::StmtKind;
 use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, Lit, UnOp, DUMMY_NODE_ID};
 use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
 use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
 use rustc_ast_pretty::pprust;
+use rustc_data_structures::thin_vec::ThinVec;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, PResult};
 use rustc_session::lint::builtin::BREAK_WITH_LABEL_AND_LOOP;
 use rustc_session::lint::BuiltinLintDiagnostics;
@@ -1548,9 +1551,66 @@ impl<'a> Parser<'a> {
             Ok(self.mk_expr_err(lo))
         } else {
             let msg = "expected `while`, `for`, `loop` or `{` after a label";
-            self.struct_span_err(self.token.span, msg).span_label(self.token.span, msg).emit();
+
+            let mut err = self.struct_span_err(self.token.span, msg);
+            err.span_label(self.token.span, msg);
+
             // Continue as an expression in an effort to recover on `'label: non_block_expr`.
-            self.parse_expr()
+            let expr = self.parse_expr().map(|expr| {
+                let span = expr.span;
+
+                let found_labeled_breaks = {
+                    struct FindLabeledBreaksVisitor(bool);
+
+                    impl<'ast> Visitor<'ast> for FindLabeledBreaksVisitor {
+                        fn visit_expr_post(&mut self, ex: &'ast Expr) {
+                            if let ExprKind::Break(Some(_label), _) = ex.kind {
+                                self.0 = true;
+                            }
+                        }
+                    }
+
+                    let mut vis = FindLabeledBreaksVisitor(false);
+                    vis.visit_expr(&expr);
+                    vis.0
+                };
+
+                // Suggestion involves adding a (as of time of writing this, unstable) labeled block.
+                //
+                // If there are no breaks that may use this label, suggest removing the label and
+                // recover to the unmodified expression.
+                if !found_labeled_breaks {
+                    let msg = "consider removing the label";
+                    err.span_suggestion_verbose(
+                        lo.until(span),
+                        msg,
+                        "",
+                        Applicability::MachineApplicable,
+                    );
+
+                    return expr;
+                }
+
+                let sugg_msg = "consider enclosing expression in a block";
+                let suggestions = vec![
+                    (span.shrink_to_lo(), "{ ".to_owned()),
+                    (span.shrink_to_hi(), " }".to_owned()),
+                ];
+
+                err.multipart_suggestion_verbose(
+                    sugg_msg,
+                    suggestions,
+                    Applicability::MachineApplicable,
+                );
+
+                // Replace `'label: non_block_expr` with `'label: {non_block_expr}` in order to supress future errors about `break 'label`.
+                let stmt = self.mk_stmt(span, StmtKind::Expr(expr));
+                let blk = self.mk_block(vec![stmt], BlockCheckMode::Default, span);
+                self.mk_expr(span, ExprKind::Block(blk, label), ThinVec::new())
+            });
+
+            err.emit();
+            expr
         }?;
 
         if !ate_colon && consume_colon {
diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs
index 11f80b314d7..bfc42902be4 100644
--- a/compiler/rustc_resolve/src/late/lifetimes.rs
+++ b/compiler/rustc_resolve/src/late/lifetimes.rs
@@ -207,6 +207,13 @@ enum Scope<'a> {
         /// In some cases not allowing late bounds allows us to avoid ICEs.
         /// This is almost ways set to true.
         allow_late_bound: bool,
+
+        /// If this binder comes from a where clause, specify how it was created.
+        /// This is used to diagnose inaccessible lifetimes in APIT:
+        /// ```ignore (illustrative)
+        /// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+        /// ```
+        where_bound_origin: Option<hir::PredicateOrigin>,
     },
 
     /// Lifetimes introduced by a fn are scoped to the call-site for that fn,
@@ -277,8 +284,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 opaque_type_parent,
                 scope_type,
                 hir_id,
-                s: _,
                 allow_late_bound,
+                where_bound_origin,
+                s: _,
             } => f
                 .debug_struct("Binder")
                 .field("lifetimes", lifetimes)
@@ -286,8 +294,9 @@ impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
                 .field("opaque_type_parent", opaque_type_parent)
                 .field("scope_type", scope_type)
                 .field("hir_id", hir_id)
-                .field("s", &"..")
                 .field("allow_late_bound", allow_late_bound)
+                .field("where_bound_origin", where_bound_origin)
+                .field("s", &"..")
                 .finish(),
             Scope::Body { id, s: _ } => {
                 f.debug_struct("Body").field("id", id).field("s", &"..").finish()
@@ -638,6 +647,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: false,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, move |this| intravisit::walk_fn(this, fk, fd, b, s, hir_id));
             }
@@ -753,6 +763,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     scope_type: BinderScopeType::Normal,
                     s: ROOT_SCOPE,
                     allow_late_bound: false,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |this| {
                     let scope = Scope::TraitRefBoundary { s: this.scope };
@@ -818,6 +829,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: false,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |this| {
                     // a bare fn has no bounds, so everything
@@ -1006,6 +1018,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                             opaque_type_parent: false,
                             scope_type: BinderScopeType::Normal,
                             allow_late_bound: false,
+                            where_bound_origin: None,
                         };
                         this.with(scope, |this| {
                             this.visit_generics(generics);
@@ -1026,6 +1039,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         opaque_type_parent: false,
                         scope_type: BinderScopeType::Normal,
                         allow_late_bound: false,
+                        where_bound_origin: None,
                     };
                     self.with(scope, |this| {
                         let scope = Scope::TraitRefBoundary { s: this.scope };
@@ -1084,6 +1098,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: false,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |this| {
                     let scope = Scope::TraitRefBoundary { s: this.scope };
@@ -1151,6 +1166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: true,
                     scope_type: BinderScopeType::Normal,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |this| {
                     let scope = Scope::TraitRefBoundary { s: this.scope };
@@ -1266,6 +1282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                         ref bounded_ty,
                         bounds,
                         ref bound_generic_params,
+                        origin,
                         ..
                     }) => {
                         let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
@@ -1296,6 +1313,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                             opaque_type_parent: false,
                             scope_type: BinderScopeType::Normal,
                             allow_late_bound: true,
+                            where_bound_origin: Some(origin),
                         };
                         this.with(scope, |this| {
                             this.visit_ty(&bounded_ty);
@@ -1368,6 +1386,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
                     opaque_type_parent: false,
                     scope_type,
                     allow_late_bound: true,
+                    where_bound_origin: None,
                 };
                 self.with(scope, |this| {
                     intravisit::walk_param_bound(this, bound);
@@ -1420,6 +1439,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
             opaque_type_parent: false,
             scope_type,
             allow_late_bound: true,
+            where_bound_origin: None,
         };
         self.with(scope, |this| {
             walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
@@ -1680,6 +1700,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             opaque_type_parent: true,
             scope_type: BinderScopeType::Normal,
             allow_late_bound: true,
+            where_bound_origin: None,
         };
         self.with(scope, walk);
     }
@@ -1783,12 +1804,48 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
             }
 
             self.insert_lifetime(lifetime_ref, def);
-        } else {
-            self.tcx.sess.delay_span_bug(
-                lifetime_ref.span,
-                &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
-            );
+            return;
         }
+
+        // We may fail to resolve higher-ranked lifetimes that are mentionned by APIT.
+        // AST-based resolution does not care for impl-trait desugaring, which are the
+        // responibility of lowering.  This may create a mismatch between the resolution
+        // AST found (`region_def_id`) which points to HRTB, and what HIR allows.
+        // ```
+        // fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+        // ```
+        //
+        // In such case, walk back the binders to diagnose it properly.
+        let mut scope = self.scope;
+        loop {
+            match *scope {
+                Scope::Binder {
+                    where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
+                } => {
+                    let mut err = self.tcx.sess.struct_span_err(
+                        lifetime_ref.span,
+                        "`impl Trait` can only mention lifetimes bound at the fn or impl level",
+                    );
+                    err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
+                    err.emit();
+                    return;
+                }
+                Scope::Root => break,
+                Scope::Binder { s, .. }
+                | Scope::Body { s, .. }
+                | Scope::Elision { s, .. }
+                | Scope::ObjectLifetimeDefault { s, .. }
+                | Scope::Supertrait { s, .. }
+                | Scope::TraitRefBoundary { s, .. } => {
+                    scope = s;
+                }
+            }
+        }
+
+        self.tcx.sess.delay_span_bug(
+            lifetime_ref.span,
+            &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
+        );
     }
 
     fn visit_segment_args(
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index fd0732f51d5..185f500808f 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -5,7 +5,7 @@ use super::{
 
 use crate::autoderef::Autoderef;
 use crate::infer::InferCtxt;
-use crate::traits::normalize_projection_type;
+use crate::traits::normalize_to;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -2706,55 +2706,43 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
                 let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
 
                 let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
-
-                // Do not check on infer_types to avoid panic in evaluate_obligation.
-                if self_ty.has_infer_types() {
-                    return;
-                }
-                let self_ty = self.tcx.erase_regions(self_ty);
-
                 let impls_future = self.type_implements_trait(
                     future_trait,
-                    self_ty.skip_binder(),
+                    self.tcx.erase_late_bound_regions(self_ty),
                     ty::List::empty(),
                     obligation.param_env,
                 );
+                if !impls_future.must_apply_modulo_regions() {
+                    return;
+                }
 
                 let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
                 // `<T as Future>::Output`
-                let projection_ty = ty::ProjectionTy {
-                    // `T`
-                    substs: self.tcx.mk_substs_trait(
-                        trait_pred.self_ty().skip_binder(),
-                        &self.fresh_substs_for_item(span, item_def_id)[1..],
-                    ),
-                    // `Future::Output`
-                    item_def_id,
-                };
-
-                let mut selcx = SelectionContext::new(self);
-
-                let mut obligations = vec![];
-                let normalized_ty = normalize_projection_type(
-                    &mut selcx,
+                let projection_ty = trait_pred.map_bound(|trait_pred| {
+                    self.tcx.mk_projection(
+                        item_def_id,
+                        // Future::Output has no substs
+                        self.tcx.mk_substs_trait(trait_pred.self_ty(), &[]),
+                    )
+                });
+                let projection_ty = normalize_to(
+                    &mut SelectionContext::new(self),
                     obligation.param_env,
-                    projection_ty,
                     obligation.cause.clone(),
-                    0,
-                    &mut obligations,
+                    projection_ty,
+                    &mut vec![],
                 );
 
                 debug!(
                     "suggest_await_before_try: normalized_projection_type {:?}",
-                    self.resolve_vars_if_possible(normalized_ty)
+                    self.resolve_vars_if_possible(projection_ty)
                 );
                 let try_obligation = self.mk_trait_obligation_with_new_self_ty(
                     obligation.param_env,
-                    trait_pred.map_bound(|trait_pred| (trait_pred, normalized_ty.ty().unwrap())),
+                    trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())),
                 );
                 debug!("suggest_await_before_try: try_trait_obligation {:?}", try_obligation);
                 if self.predicate_may_hold(&try_obligation)
-                    && impls_future.must_apply_modulo_regions()
                     && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
                     && snippet.ends_with('?')
                 {
diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs
index 5b04ae7b07e..a3b4e5886ef 100644
--- a/library/core/src/ptr/mod.rs
+++ b/library/core/src/ptr/mod.rs
@@ -532,7 +532,7 @@ pub const fn null<T>() -> *const T {
 #[rustc_diagnostic_item = "ptr_null"]
 #[cfg(not(bootstrap))]
 pub const fn null<T: ?Sized + Thin>() -> *const T {
-    from_raw_parts(0 as *const (), ())
+    from_raw_parts(invalid(0), ())
 }
 
 /// Creates a null mutable raw pointer.
@@ -709,7 +709,7 @@ where
 #[rustc_diagnostic_item = "ptr_null_mut"]
 #[cfg(not(bootstrap))]
 pub const fn null_mut<T: ?Sized + Thin>() -> *mut T {
-    from_raw_parts_mut(0 as *mut (), ())
+    from_raw_parts_mut(invalid_mut(0), ())
 }
 
 /// Forms a raw slice from a pointer and a length.
diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index 5eb70ab13db..f762e389005 100644
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -94,6 +94,10 @@ There are a number of supported commands:
   in the specified file. The number of occurrences must match the given
   count.
 
+* `@count PATH XPATH TEXT COUNT` checks for the occurrence of the given XPath
+  with the given text in the specified file. The number of occurrences must
+  match the given count.
+
 * `@snapshot NAME PATH XPATH` creates a snapshot test named NAME.
   A snapshot test captures a subtree of the DOM, at the location
   determined by the XPath, and compares it to a pre-recorded value
@@ -382,9 +386,10 @@ def check_tree_attr(tree, path, attr, pat, regexp):
     return ret
 
 
-def check_tree_text(tree, path, pat, regexp):
+# Returns the number of occurences matching the regex (`regexp`) and the text (`pat`).
+def check_tree_text(tree, path, pat, regexp, stop_at_first):
     path = normalize_xpath(path)
-    ret = False
+    match_count = 0
     try:
         for e in tree.findall(path):
             try:
@@ -392,13 +397,14 @@ def check_tree_text(tree, path, pat, regexp):
             except KeyError:
                 continue
             else:
-                ret = check_string(value, pat, regexp)
-                if ret:
-                    break
+                if check_string(value, pat, regexp):
+                    match_count += 1
+                    if stop_at_first:
+                        break
     except Exception:
         print('Failed to get path "{}"'.format(path))
         raise
-    return ret
+    return match_count
 
 
 def get_tree_count(tree, path):
@@ -518,6 +524,19 @@ def print_err(lineno, context, err, message=None):
         stderr("\t{}".format(context))
 
 
+def get_nb_matching_elements(cache, c, regexp, stop_at_first):
+    tree = cache.get_tree(c.args[0])
+    pat, sep, attr = c.args[1].partition('/@')
+    if sep:  # attribute
+        tree = cache.get_tree(c.args[0])
+        return check_tree_attr(tree, pat, attr, c.args[2], False)
+    else:  # normalized text
+        pat = c.args[1]
+        if pat.endswith('/text()'):
+            pat = pat[:-7]
+        return check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp, stop_at_first)
+
+
 ERR_COUNT = 0
 
 
@@ -538,16 +557,7 @@ def check_command(c, cache):
                 ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
             elif len(c.args) == 3:  # @has/matches <path> <pat> <match> = XML tree test
                 cerr = "`XPATH PATTERN` did not match"
-                tree = cache.get_tree(c.args[0])
-                pat, sep, attr = c.args[1].partition('/@')
-                if sep:  # attribute
-                    tree = cache.get_tree(c.args[0])
-                    ret = check_tree_attr(tree, pat, attr, c.args[2], regexp)
-                else:  # normalized text
-                    pat = c.args[1]
-                    if pat.endswith('/text()'):
-                        pat = pat[:-7]
-                    ret = check_tree_text(cache.get_tree(c.args[0]), pat, c.args[2], regexp)
+                ret = get_nb_matching_elements(cache, c, regexp, True) != 0
             else:
                 raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
 
@@ -557,6 +567,11 @@ def check_command(c, cache):
                 found = get_tree_count(cache.get_tree(c.args[0]), c.args[1])
                 cerr = "Expected {} occurrences but found {}".format(expected, found)
                 ret = expected == found
+            elif len(c.args) == 4:  # @count <path> <pat> <text> <count> = count test
+                expected = int(c.args[3])
+                found = get_nb_matching_elements(cache, c, False, False)
+                cerr = "Expected {} occurrences but found {}".format(expected, found)
+                ret = found == expected
             else:
                 raise InvalidCheck('Invalid number of @{} arguments'.format(c.cmd))
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 23ce634cf28..cb887d16906 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1600,6 +1600,13 @@ fn render_impl(
         }
 
         if let Some(ref dox) = i.impl_item.collapsed_doc_value() {
+            if trait_.is_none() && i.inner_impl().items.is_empty() {
+                w.write_str(
+                    "<div class=\"item-info\">\
+                    <div class=\"stab empty-impl\">This impl block contains no items.</div>
+                </div>",
+                );
+            }
             write!(
                 w,
                 "<div class=\"docblock\">{}</div>",
diff --git a/src/librustdoc/html/static/css/themes/ayu.css b/src/librustdoc/html/static/css/themes/ayu.css
index 7303cecc0d6..8e0521d9ad6 100644
--- a/src/librustdoc/html/static/css/themes/ayu.css
+++ b/src/librustdoc/html/static/css/themes/ayu.css
@@ -281,9 +281,13 @@ details.undocumented > summary::before {
 	color: #000;
 }
 
+/* Created this empty rule to satisfy the theme checks. */
+.stab.empty-impl {}
+
 .stab.unstable,
 .stab.deprecated,
-.stab.portability {
+.stab.portability,
+.stab.empty-impl {
 	color: #c5c5c5;
 	background: #314559 !important;
 	border-style: none !important;
diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css
index 34a4f446b56..071ad006ed3 100644
--- a/src/librustdoc/html/static/css/themes/dark.css
+++ b/src/librustdoc/html/static/css/themes/dark.css
@@ -266,6 +266,7 @@ details.undocumented > summary::before {
 	color: #ddd;
 }
 
+.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
 .stab.unstable { background: #FFF5D6; border-color: #FFC600; color: #2f2f2f; }
 .stab.deprecated { background: #ffc4c4; border-color: #db7b7b; color: #2f2f2f; }
 .stab.portability { background: #F3DFFF; border-color: #b07bdb; color: #2f2f2f; }
diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css
index aa6ad2f5473..5c3789bf463 100644
--- a/src/librustdoc/html/static/css/themes/light.css
+++ b/src/librustdoc/html/static/css/themes/light.css
@@ -255,6 +255,7 @@ details.undocumented > summary::before {
 	color: #000;
 }
 
+.stab.empty-impl { background: #FFF5D6; border-color: #FFC600; }
 .stab.unstable { background: #FFF5D6; border-color: #FFC600; }
 .stab.deprecated { background: #ffc4c4; border-color: #db7b7b; }
 .stab.portability { background: #F3DFFF; border-color: #b07bdb; }
diff --git a/src/librustdoc/passes/stripper.rs b/src/librustdoc/passes/stripper.rs
index d5db919dc4b..0fd124e6154 100644
--- a/src/librustdoc/passes/stripper.rs
+++ b/src/librustdoc/passes/stripper.rs
@@ -124,8 +124,9 @@ pub(crate) struct ImplStripper<'a> {
 impl<'a> DocFolder for ImplStripper<'a> {
     fn fold_item(&mut self, i: Item) -> Option<Item> {
         if let clean::ImplItem(ref imp) = *i.kind {
-            // emptied none trait impls can be stripped
-            if imp.trait_.is_none() && imp.items.is_empty() {
+            // Impl blocks can be skipped if they are: empty; not a trait impl; and have no
+            // documentation.
+            if imp.trait_.is_none() && imp.items.is_empty() && i.doc_value().is_none() {
                 return None;
             }
             if let Some(did) = imp.for_.def_id(self.cache) {
diff --git a/src/test/rustdoc/empty-impl-block.rs b/src/test/rustdoc/empty-impl-block.rs
new file mode 100644
index 00000000000..6a2a254f63a
--- /dev/null
+++ b/src/test/rustdoc/empty-impl-block.rs
@@ -0,0 +1,20 @@
+#![crate_name = "foo"]
+
+// @has 'foo/struct.Foo.html'
+pub struct Foo;
+
+// @has - '//*[@class="docblock"]' 'Hello empty impl block!'
+// @has - '//*[@class="item-info"]' 'This impl block contains no items.'
+/// Hello empty impl block!
+impl Foo {}
+// We ensure that this empty impl block without doc isn't rendered.
+// @count - '//*[@class="impl has-srclink"]' 'impl Foo' 1
+impl Foo {}
+
+// Just to ensure that empty trait impl blocks are rendered.
+pub struct Another;
+pub trait Bar {}
+
+// @has 'foo/struct.Another.html'
+// @has - '//h3[@class="code-header in-band"]' 'impl Bar for Another'
+impl Bar for Another {}
diff --git a/src/test/ui/cast/casts-issue-46365.rs b/src/test/ui/cast/casts-issue-46365.rs
index a2205b718c1..50aa1a856a7 100644
--- a/src/test/ui/cast/casts-issue-46365.rs
+++ b/src/test/ui/cast/casts-issue-46365.rs
@@ -3,5 +3,6 @@ struct Lorem {
 }
 
 fn main() {
-    let _foo: *mut Lorem = core::ptr::NonNull::dangling().as_ptr(); // no error here
+    // Testing `as` casts, so deliberately not using `ptr::null`.
+    let _foo: *mut Lorem = 0 as *mut _; // no error here
 }
diff --git a/src/test/ui/impl-trait/universal_wrong_hrtb.rs b/src/test/ui/impl-trait/universal_wrong_hrtb.rs
new file mode 100644
index 00000000000..b9551c2ceb0
--- /dev/null
+++ b/src/test/ui/impl-trait/universal_wrong_hrtb.rs
@@ -0,0 +1,8 @@
+trait Trait<'a> {
+    type Assoc;
+}
+
+fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+//~^ ERROR `impl Trait` can only mention lifetimes bound at the fn or impl level
+
+fn main() {}
diff --git a/src/test/ui/impl-trait/universal_wrong_hrtb.stderr b/src/test/ui/impl-trait/universal_wrong_hrtb.stderr
new file mode 100644
index 00000000000..37eb8dfa1a1
--- /dev/null
+++ b/src/test/ui/impl-trait/universal_wrong_hrtb.stderr
@@ -0,0 +1,14 @@
+error: `impl Trait` can only mention lifetimes bound at the fn or impl level
+  --> $DIR/universal_wrong_hrtb.rs:5:73
+   |
+LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+   |                                                                         ^^
+   |
+note: lifetime declared here
+  --> $DIR/universal_wrong_hrtb.rs:5:39
+   |
+LL | fn test_argument_position(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+   |                                       ^^
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/parser/labeled-no-colon-expr.stderr b/src/test/ui/parser/labeled-no-colon-expr.stderr
index 26884dc5d74..5c9597c440c 100644
--- a/src/test/ui/parser/labeled-no-colon-expr.stderr
+++ b/src/test/ui/parser/labeled-no-colon-expr.stderr
@@ -47,6 +47,12 @@ error: expected `while`, `for`, `loop` or `{` after a label
    |
 LL |     'l4 0;
    |         ^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider removing the label
+   |
+LL -     'l4 0;
+LL +     0;
+   | 
 
 error: labeled expression must be followed by `:`
   --> $DIR/labeled-no-colon-expr.rs:8:9
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.fixed b/src/test/ui/parser/recover-labeled-non-block-expr.fixed
new file mode 100644
index 00000000000..fe546a71971
--- /dev/null
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.fixed
@@ -0,0 +1,27 @@
+// run-rustfix
+#![feature(label_break_value)]
+fn main() {
+    let _ = 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    'label: { match () { () => break 'label, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    #[allow(unused_labels)]
+    'label: { match () { () => 'lp: loop { break 'lp 0 }, } }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    let x = 1;
+    let _i = 'label: { match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+        0 => 42,
+        1 if false => break 'label 17,
+        1 => {
+            if true {
+                break 'label 13
+            } else {
+                break 'label 0;
+            }
+        }
+        _ => 1,
+    } };
+
+    let other = 3;
+    let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+}
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs
index be92170acf0..35862e2eef9 100644
--- a/src/test/ui/parser/recover-labeled-non-block-expr.rs
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs
@@ -1,5 +1,27 @@
+// run-rustfix
+#![feature(label_break_value)]
 fn main() {
-    'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    let _ = 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
 
-    let _recovery_witness: () = 0; //~ ERROR mismatched types
+    'label: match () { () => {}, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    'label: match () { () => break 'label, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+    #[allow(unused_labels)]
+    'label: match () { () => 'lp: loop { break 'lp 0 }, }; //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+
+    let x = 1;
+    let _i = 'label: match x { //~ ERROR expected `while`, `for`, `loop` or `{` after a label
+        0 => 42,
+        1 if false => break 'label 17,
+        1 => {
+            if true {
+                break 'label 13
+            } else {
+                break 'label 0;
+            }
+        }
+        _ => 1,
+    };
+
+    let other = 3;
+    let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other }); //~ ERROR expected `while`, `for`, `loop` or `{` after a label
 }
diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr
index 771a915288c..767389c4808 100644
--- a/src/test/ui/parser/recover-labeled-non-block-expr.stderr
+++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr
@@ -1,17 +1,75 @@
 error: expected `while`, `for`, `loop` or `{` after a label
-  --> $DIR/recover-labeled-non-block-expr.rs:2:13
+  --> $DIR/recover-labeled-non-block-expr.rs:4:21
    |
-LL |     'label: 1 + 1;
-   |             ^ expected `while`, `for`, `loop` or `{` after a label
+LL |     let _ = 'label: 1 + 1;
+   |                     ^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider removing the label
+   |
+LL -     let _ = 'label: 1 + 1;
+LL +     let _ = 1 + 1;
+   | 
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:6:13
+   |
+LL |     'label: match () { () => {}, };
+   |             ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider removing the label
+   |
+LL -     'label: match () { () => {}, };
+LL +     match () { () => {}, };
+   | 
 
-error[E0308]: mismatched types
-  --> $DIR/recover-labeled-non-block-expr.rs:4:33
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:7:13
+   |
+LL |     'label: match () { () => break 'label, };
+   |             ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
+   |
+LL |     'label: { match () { () => break 'label, } };
+   |             +                                  +
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:9:13
+   |
+LL |     'label: match () { () => 'lp: loop { break 'lp 0 }, };
+   |             ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
+   |
+LL |     'label: { match () { () => 'lp: loop { break 'lp 0 }, } };
+   |             +                                               +
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:12:22
+   |
+LL |     let _i = 'label: match x {
+   |                      ^^^^^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
+   |
+LL ~     let _i = 'label: { match x {
+LL |         0 => 42,
+LL |         1 if false => break 'label 17,
+LL |         1 => {
+LL |             if true {
+LL |                 break 'label 13
+ ...
+
+error: expected `while`, `for`, `loop` or `{` after a label
+  --> $DIR/recover-labeled-non-block-expr.rs:26:24
+   |
+LL |     let _val = 'label: (1, if other == 3 { break 'label (2, 3) } else { other });
+   |                        ^ expected `while`, `for`, `loop` or `{` after a label
+   |
+help: consider enclosing expression in a block
    |
-LL |     let _recovery_witness: () = 0;
-   |                            --   ^ expected `()`, found integer
-   |                            |
-   |                            expected due to this
+LL |     let _val = 'label: { (1, if other == 3 { break 'label (2, 3) } else { other }) };
+   |                        +                                                           +
 
-error: aborting due to 2 previous errors
+error: aborting due to 6 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/suggestions/issue-97704.fixed b/src/test/ui/suggestions/issue-97704.fixed
new file mode 100644
index 00000000000..c42bdfff5f9
--- /dev/null
+++ b/src/test/ui/suggestions/issue-97704.fixed
@@ -0,0 +1,19 @@
+// edition:2021
+// run-rustfix
+
+#![allow(unused)]
+
+use std::future::Future;
+
+async fn foo() -> Result<(), i32> {
+    func(async { Ok::<_, i32>(()) }).await?;
+    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+
+    Ok(())
+}
+
+async fn func<T>(fut: impl Future<Output = T>) -> T {
+    fut.await
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/issue-97704.rs b/src/test/ui/suggestions/issue-97704.rs
new file mode 100644
index 00000000000..5dfee6cac60
--- /dev/null
+++ b/src/test/ui/suggestions/issue-97704.rs
@@ -0,0 +1,19 @@
+// edition:2021
+// run-rustfix
+
+#![allow(unused)]
+
+use std::future::Future;
+
+async fn foo() -> Result<(), i32> {
+    func(async { Ok::<_, i32>(()) })?;
+    //~^ ERROR the `?` operator can only be applied to values that implement `Try`
+
+    Ok(())
+}
+
+async fn func<T>(fut: impl Future<Output = T>) -> T {
+    fut.await
+}
+
+fn main() {}
diff --git a/src/test/ui/suggestions/issue-97704.stderr b/src/test/ui/suggestions/issue-97704.stderr
new file mode 100644
index 00000000000..ca017be45ac
--- /dev/null
+++ b/src/test/ui/suggestions/issue-97704.stderr
@@ -0,0 +1,15 @@
+error[E0277]: the `?` operator can only be applied to values that implement `Try`
+  --> $DIR/issue-97704.rs:9:5
+   |
+LL |     func(async { Ok::<_, i32>(()) })?;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `impl Future<Output = Result<(), i32>>`
+   |
+   = help: the trait `Try` is not implemented for `impl Future<Output = Result<(), i32>>`
+help: consider `await`ing on the `Future`
+   |
+LL |     func(async { Ok::<_, i32>(()) }).await?;
+   |                                     ++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.