about summary refs log tree commit diff
path: root/compiler/rustc_passes/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_passes/src')
-rw-r--r--compiler/rustc_passes/src/abi_test.rs1
-rw-r--r--compiler/rustc_passes/src/check_attr.rs180
-rw-r--r--compiler/rustc_passes/src/check_const.rs11
-rw-r--r--compiler/rustc_passes/src/dead.rs71
-rw-r--r--compiler/rustc_passes/src/diagnostic_items.rs2
-rw-r--r--compiler/rustc_passes/src/entry.rs83
-rw-r--r--compiler/rustc_passes/src/errors.rs56
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs7
-rw-r--r--compiler/rustc_passes/src/lang_items.rs15
-rw-r--r--compiler/rustc_passes/src/layout_test.rs1
-rw-r--r--compiler/rustc_passes/src/lib.rs6
-rw-r--r--compiler/rustc_passes/src/liveness.rs11
-rw-r--r--compiler/rustc_passes/src/loops.rs210
-rw-r--r--compiler/rustc_passes/src/naked_functions.rs3
-rw-r--r--compiler/rustc_passes/src/reachable.rs34
-rw-r--r--compiler/rustc_passes/src/stability.rs6
-rw-r--r--compiler/rustc_passes/src/weak_lang_items.rs4
17 files changed, 438 insertions, 263 deletions
diff --git a/compiler/rustc_passes/src/abi_test.rs b/compiler/rustc_passes/src/abi_test.rs
index 986ef69ad88..0c3dd649997 100644
--- a/compiler/rustc_passes/src/abi_test.rs
+++ b/compiler/rustc_passes/src/abi_test.rs
@@ -1,6 +1,7 @@
 use rustc_ast::Attribute;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
+use rustc_middle::span_bug;
 use rustc_middle::ty::layout::{FnAbiError, LayoutError};
 use rustc_middle::ty::{self, GenericArgs, Instance, Ty, TyCtxt};
 use rustc_span::source_map::Spanned;
diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs
index 1254ae8cfc8..4fe84b91b8b 100644
--- a/compiler/rustc_passes/src/check_attr.rs
+++ b/compiler/rustc_passes/src/check_attr.rs
@@ -11,14 +11,15 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::StashKey;
 use rustc_errors::{Applicability, DiagCtxt, IntoDiagArg, MultiSpan};
 use rustc_feature::{AttributeDuplicates, AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
-use rustc_hir as hir;
 use rustc_hir::def_id::LocalModDefId;
 use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{self as hir};
 use rustc_hir::{
     self, FnSig, ForeignItem, HirId, Item, ItemKind, TraitItem, CRATE_HIR_ID, CRATE_OWNER_ID,
 };
-use rustc_hir::{MethodKind, Target, Unsafety};
+use rustc_hir::{MethodKind, Safety, Target};
 use rustc_macros::LintDiagnostic;
+use rustc_middle::bug;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::middle::resolve_bound_vars::ObjectLifetimeDefault;
 use rustc_middle::query::Providers;
@@ -38,15 +39,13 @@ use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
 use rustc_trait_selection::traits::ObligationCtxt;
 use std::cell::Cell;
 use std::collections::hash_map::Entry;
+use tracing::debug;
 
 #[derive(LintDiagnostic)]
 #[diag(passes_diagnostic_diagnostic_on_unimplemented_only_for_traits)]
-pub struct DiagnosticOnUnimplementedOnlyForTraits;
+struct DiagnosticOnUnimplementedOnlyForTraits;
 
-pub(crate) fn target_from_impl_item<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    impl_item: &hir::ImplItem<'_>,
-) -> Target {
+fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target {
     match impl_item.kind {
         hir::ImplItemKind::Const(..) => Target::AssocConst,
         hir::ImplItemKind::Fn(..) => {
@@ -98,7 +97,7 @@ struct CheckAttrVisitor<'tcx> {
 }
 
 impl<'tcx> CheckAttrVisitor<'tcx> {
-    pub fn dcx(&self) -> &'tcx DiagCtxt {
+    fn dcx(&self) -> &'tcx DiagCtxt {
         self.tcx.dcx()
     }
 
@@ -115,92 +114,96 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         let mut seen = FxHashMap::default();
         let attrs = self.tcx.hir().attrs(hir_id);
         for attr in attrs {
-            if attr.path_matches(&[sym::diagnostic, sym::on_unimplemented]) {
-                self.check_diagnostic_on_unimplemented(attr.span, hir_id, target);
-            }
-            match attr.name_or_empty() {
-                sym::do_not_recommend => self.check_do_not_recommend(attr.span, target),
-                sym::inline => self.check_inline(hir_id, attr, span, target),
-                sym::coverage => self.check_coverage(hir_id, attr, span, target),
-                sym::non_exhaustive => self.check_non_exhaustive(hir_id, attr, span, target),
-                sym::marker => self.check_marker(hir_id, attr, span, target),
-                sym::target_feature => self.check_target_feature(hir_id, attr, span, target, attrs),
-                sym::thread_local => self.check_thread_local(attr, span, target),
-                sym::track_caller => {
+            match attr.path().as_slice() {
+                [sym::diagnostic, sym::do_not_recommend] => {
+                    self.check_do_not_recommend(attr.span, hir_id, target)
+                }
+                [sym::diagnostic, sym::on_unimplemented] => {
+                    self.check_diagnostic_on_unimplemented(attr.span, hir_id, target)
+                }
+                [sym::inline] => self.check_inline(hir_id, attr, span, target),
+                [sym::coverage] => self.check_coverage(hir_id, attr, span, target),
+                [sym::non_exhaustive] => self.check_non_exhaustive(hir_id, attr, span, target),
+                [sym::marker] => self.check_marker(hir_id, attr, span, target),
+                [sym::target_feature] => {
+                    self.check_target_feature(hir_id, attr, span, target, attrs)
+                }
+                [sym::thread_local] => self.check_thread_local(attr, span, target),
+                [sym::track_caller] => {
                     self.check_track_caller(hir_id, attr.span, attrs, span, target)
                 }
-                sym::doc => self.check_doc_attrs(
+                [sym::doc] => self.check_doc_attrs(
                     attr,
                     hir_id,
                     target,
                     &mut specified_inline,
                     &mut doc_aliases,
                 ),
-                sym::no_link => self.check_no_link(hir_id, attr, span, target),
-                sym::export_name => self.check_export_name(hir_id, attr, span, target),
-                sym::rustc_layout_scalar_valid_range_start
-                | sym::rustc_layout_scalar_valid_range_end => {
+                [sym::no_link] => self.check_no_link(hir_id, attr, span, target),
+                [sym::export_name] => self.check_export_name(hir_id, attr, span, target),
+                [sym::rustc_layout_scalar_valid_range_start]
+                | [sym::rustc_layout_scalar_valid_range_end] => {
                     self.check_rustc_layout_scalar_valid_range(attr, span, target)
                 }
-                sym::allow_internal_unstable => {
+                [sym::allow_internal_unstable] => {
                     self.check_allow_internal_unstable(hir_id, attr, span, target, attrs)
                 }
-                sym::debugger_visualizer => self.check_debugger_visualizer(attr, target),
-                sym::rustc_allow_const_fn_unstable => {
+                [sym::debugger_visualizer] => self.check_debugger_visualizer(attr, target),
+                [sym::rustc_allow_const_fn_unstable] => {
                     self.check_rustc_allow_const_fn_unstable(hir_id, attr, span, target)
                 }
-                sym::rustc_std_internal_symbol => {
+                [sym::rustc_std_internal_symbol] => {
                     self.check_rustc_std_internal_symbol(attr, span, target)
                 }
-                sym::naked => self.check_naked(hir_id, attr, span, target),
-                sym::rustc_never_returns_null_ptr => {
+                [sym::naked] => self.check_naked(hir_id, attr, span, target),
+                [sym::rustc_never_returns_null_ptr] => {
                     self.check_applied_to_fn_or_method(hir_id, attr, span, target)
                 }
-                sym::rustc_legacy_const_generics => {
+                [sym::rustc_legacy_const_generics] => {
                     self.check_rustc_legacy_const_generics(hir_id, attr, span, target, item)
                 }
-                sym::rustc_lint_query_instability => {
+                [sym::rustc_lint_query_instability] => {
                     self.check_rustc_lint_query_instability(hir_id, attr, span, target)
                 }
-                sym::rustc_lint_diagnostics => {
+                [sym::rustc_lint_diagnostics] => {
                     self.check_rustc_lint_diagnostics(hir_id, attr, span, target)
                 }
-                sym::rustc_lint_opt_ty => self.check_rustc_lint_opt_ty(attr, span, target),
-                sym::rustc_lint_opt_deny_field_access => {
+                [sym::rustc_lint_opt_ty] => self.check_rustc_lint_opt_ty(attr, span, target),
+                [sym::rustc_lint_opt_deny_field_access] => {
                     self.check_rustc_lint_opt_deny_field_access(attr, span, target)
                 }
-                sym::rustc_clean
-                | sym::rustc_dirty
-                | sym::rustc_if_this_changed
-                | sym::rustc_then_this_would_need => self.check_rustc_dirty_clean(attr),
-                sym::rustc_coinductive
-                | sym::rustc_must_implement_one_of
-                | sym::rustc_deny_explicit_impl
-                | sym::const_trait => self.check_must_be_applied_to_trait(attr, span, target),
-                sym::cmse_nonsecure_entry => {
+                [sym::rustc_clean]
+                | [sym::rustc_dirty]
+                | [sym::rustc_if_this_changed]
+                | [sym::rustc_then_this_would_need] => self.check_rustc_dirty_clean(attr),
+                [sym::rustc_coinductive]
+                | [sym::rustc_must_implement_one_of]
+                | [sym::rustc_deny_explicit_impl]
+                | [sym::const_trait] => self.check_must_be_applied_to_trait(attr, span, target),
+                [sym::cmse_nonsecure_entry] => {
                     self.check_cmse_nonsecure_entry(hir_id, attr, span, target)
                 }
-                sym::collapse_debuginfo => self.check_collapse_debuginfo(attr, span, target),
-                sym::must_not_suspend => self.check_must_not_suspend(attr, span, target),
-                sym::must_use => self.check_must_use(hir_id, attr, target),
-                sym::rustc_pass_by_value => self.check_pass_by_value(attr, span, target),
-                sym::rustc_allow_incoherent_impl => {
+                [sym::collapse_debuginfo] => self.check_collapse_debuginfo(attr, span, target),
+                [sym::must_not_suspend] => self.check_must_not_suspend(attr, span, target),
+                [sym::must_use] => self.check_must_use(hir_id, attr, target),
+                [sym::rustc_pass_by_value] => self.check_pass_by_value(attr, span, target),
+                [sym::rustc_allow_incoherent_impl] => {
                     self.check_allow_incoherent_impl(attr, span, target)
                 }
-                sym::rustc_has_incoherent_inherent_impls => {
+                [sym::rustc_has_incoherent_inherent_impls] => {
                     self.check_has_incoherent_inherent_impls(attr, span, target)
                 }
-                sym::ffi_pure => self.check_ffi_pure(attr.span, attrs, target),
-                sym::ffi_const => self.check_ffi_const(attr.span, target),
-                sym::rustc_const_unstable
-                | sym::rustc_const_stable
-                | sym::unstable
-                | sym::stable
-                | sym::rustc_allowed_through_unstable_modules
-                | sym::rustc_promotable => self.check_stability_promotable(attr, target),
-                sym::link_ordinal => self.check_link_ordinal(attr, span, target),
-                sym::rustc_confusables => self.check_confusables(attr, target),
-                sym::rustc_safe_intrinsic => {
+                [sym::ffi_pure] => self.check_ffi_pure(attr.span, attrs, target),
+                [sym::ffi_const] => self.check_ffi_const(attr.span, target),
+                [sym::rustc_const_unstable]
+                | [sym::rustc_const_stable]
+                | [sym::unstable]
+                | [sym::stable]
+                | [sym::rustc_allowed_through_unstable_modules]
+                | [sym::rustc_promotable] => self.check_stability_promotable(attr, target),
+                [sym::link_ordinal] => self.check_link_ordinal(attr, span, target),
+                [sym::rustc_confusables] => self.check_confusables(attr, target),
+                [sym::rustc_safe_intrinsic] => {
                     self.check_rustc_safe_intrinsic(hir_id, attr, span, target)
                 }
                 _ => true,
@@ -292,18 +295,26 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
         );
     }
 
-    /// Checks if `#[do_not_recommend]` is applied on a trait impl.
-    fn check_do_not_recommend(&self, attr_span: Span, target: Target) -> bool {
-        if let Target::Impl = target {
-            true
-        } else {
-            self.dcx().emit_err(errors::IncorrectDoNotRecommendLocation { span: attr_span });
-            false
+    /// Checks if `#[diagnostic::do_not_recommend]` is applied on a trait impl.
+    fn check_do_not_recommend(&self, attr_span: Span, hir_id: HirId, target: Target) -> bool {
+        if !matches!(target, Target::Impl) {
+            self.tcx.emit_node_span_lint(
+                UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
+                hir_id,
+                attr_span,
+                errors::IncorrectDoNotRecommendLocation,
+            );
         }
+        true
     }
 
     /// Checks if `#[diagnostic::on_unimplemented]` is applied to a trait definition
-    fn check_diagnostic_on_unimplemented(&self, attr_span: Span, hir_id: HirId, target: Target) {
+    fn check_diagnostic_on_unimplemented(
+        &self,
+        attr_span: Span,
+        hir_id: HirId,
+        target: Target,
+    ) -> bool {
         if !matches!(target, Target::Trait) {
             self.tcx.emit_node_span_lint(
                 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
@@ -312,6 +323,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 DiagnosticOnUnimplementedOnlyForTraits,
             );
         }
+        true
     }
 
     /// Checks if an `#[inline]` is applied to a function or a closure. Returns `true` if valid.
@@ -519,7 +531,26 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                 self.dcx().emit_err(errors::NakedTrackedCaller { attr_span });
                 false
             }
-            Target::Fn | Target::Method(..) | Target::ForeignFn | Target::Closure => true,
+            Target::Fn => {
+                // `#[track_caller]` is not valid on weak lang items because they are called via
+                // `extern` declarations and `#[track_caller]` would alter their ABI.
+                if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
+                    && let Some(item) = hir::LangItem::from_name(lang_item)
+                    && item.is_weak()
+                {
+                    let sig = self.tcx.hir_node(hir_id).fn_sig().unwrap();
+
+                    self.dcx().emit_err(errors::LangItemWithTrackCaller {
+                        attr_span,
+                        name: lang_item,
+                        sig_span: sig.span,
+                    });
+                    false
+                } else {
+                    true
+                }
+            }
+            Target::Method(..) | Target::ForeignFn | Target::Closure => true,
             // FIXME(#80564): We permit struct fields, match arms and macro defs to have an
             // `#[track_caller]` attribute with just a lint, because we previously
             // erroneously allowed it and some crates used it accidentally, to be compatible
@@ -602,7 +633,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
     ) -> bool {
         match target {
             Target::Fn => {
-                // `#[target_feature]` is not allowed in language items.
+                // `#[target_feature]` is not allowed in lang items.
                 if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
                     // Calling functions with `#[target_feature]` is
                     // not unsafe on WASM, see #84988
@@ -2315,7 +2346,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
             }),
             token_stream,
             false,
-            Unsafety::Normal,
+            Safety::Safe,
             Abi::Rust,
         );
 
@@ -2342,7 +2373,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
                             cause.span = ty.span;
                         }
                     }
-                    TypeError::UnsafetyMismatch(_) => {
+                    TypeError::SafetyMismatch(_) => {
                         // FIXME: Would be nice if we had a span here..
                     }
                     TypeError::AbiMismatch(_) => {
@@ -2503,7 +2534,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
         sym::automatically_derived,
         sym::start,
         sym::rustc_main,
-        sym::unix_sigpipe,
         sym::derive,
         sym::test,
         sym::test_case,
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index 4a1a2049083..fa2c8f69c71 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -13,6 +13,7 @@ use rustc_hir::def_id::{LocalDefId, LocalModDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::query::Providers;
+use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
 use rustc_span::{sym, Span, Symbol};
@@ -195,11 +196,6 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
         self.recurse_into(kind, None, |this| intravisit::walk_anon_const(this, anon));
     }
 
-    fn visit_inline_const(&mut self, block: &'tcx hir::ConstBlock) {
-        let kind = Some(hir::ConstContext::Const { inline: true });
-        self.recurse_into(kind, None, |this| intravisit::walk_inline_const(this, block));
-    }
-
     fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) {
         let owner = self.tcx.hir().body_owner_def_id(body.id());
         let kind = self.tcx.hir().body_const_context(owner);
@@ -227,6 +223,11 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
                     self.const_check_violated(expr, e.span);
                 }
             }
+            hir::ExprKind::ConstBlock(expr) => {
+                let kind = Some(hir::ConstContext::Const { inline: true });
+                self.recurse_into(kind, None, |this| intravisit::walk_expr(this, expr));
+                return;
+            }
 
             _ => {}
         }
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 51f288b3c95..0049afff528 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -16,6 +16,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::privacy::Level;
 use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, TyCtxt};
+use rustc_middle::{bug, span_bug};
 use rustc_session::lint;
 use rustc_session::lint::builtin::DEAD_CODE;
 use rustc_span::symbol::{sym, Symbol};
@@ -424,10 +425,11 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
                             && let ItemKind::Impl(impl_ref) =
                                 self.tcx.hir().expect_item(local_impl_id).kind
                         {
-                            if self.tcx.visibility(trait_id).is_public()
-                                && matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
+                            if matches!(trait_item.kind, hir::TraitItemKind::Fn(..))
                                 && !ty_ref_to_pub_struct(self.tcx, impl_ref.self_ty)
                             {
+                                // skip methods of private ty,
+                                // they would be solved in `solve_rest_impl_items`
                                 continue;
                             }
 
@@ -484,32 +486,46 @@ impl<'tcx> MarkSymbolVisitor<'tcx> {
 
     fn solve_rest_impl_items(&mut self, mut unsolved_impl_items: Vec<(hir::ItemId, LocalDefId)>) {
         let mut ready;
-        (ready, unsolved_impl_items) = unsolved_impl_items
-            .into_iter()
-            .partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
+        (ready, unsolved_impl_items) =
+            unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
+                self.impl_item_with_used_self(impl_id, impl_item_id)
+            });
 
         while !ready.is_empty() {
             self.worklist =
                 ready.into_iter().map(|(_, id)| (id, ComesFromAllowExpect::No)).collect();
             self.mark_live_symbols();
 
-            (ready, unsolved_impl_items) = unsolved_impl_items
-                .into_iter()
-                .partition(|&(impl_id, _)| self.impl_item_with_used_self(impl_id));
+            (ready, unsolved_impl_items) =
+                unsolved_impl_items.into_iter().partition(|&(impl_id, impl_item_id)| {
+                    self.impl_item_with_used_self(impl_id, impl_item_id)
+                });
         }
     }
 
-    fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId) -> bool {
+    fn impl_item_with_used_self(&mut self, impl_id: hir::ItemId, impl_item_id: LocalDefId) -> bool {
         if let TyKind::Path(hir::QPath::Resolved(_, path)) =
             self.tcx.hir().item(impl_id).expect_impl().self_ty.kind
             && let Res::Def(def_kind, def_id) = path.res
             && let Some(local_def_id) = def_id.as_local()
             && matches!(def_kind, DefKind::Struct | DefKind::Enum | DefKind::Union)
         {
-            self.live_symbols.contains(&local_def_id)
-        } else {
-            false
+            if self.tcx.visibility(impl_item_id).is_public() {
+                // for the public method, we don't know the trait item is used or not,
+                // so we mark the method live if the self is used
+                return self.live_symbols.contains(&local_def_id);
+            }
+
+            if let Some(trait_item_id) = self.tcx.associated_item(impl_item_id).trait_item_def_id
+                && let Some(local_id) = trait_item_id.as_local()
+            {
+                // for the private method, we can know the trait item is used or not,
+                // so we mark the method live if the self is used and the trait item is used
+                return self.live_symbols.contains(&local_id)
+                    && self.live_symbols.contains(&local_def_id);
+            }
         }
+        false
     }
 }
 
@@ -571,6 +587,16 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
             hir::ExprKind::OffsetOf(..) => {
                 self.handle_offset_of(expr);
             }
+            hir::ExprKind::ConstBlock(expr) => {
+                // When inline const blocks are used in pattern position, paths
+                // referenced by it should be considered as used.
+                let in_pat = mem::replace(&mut self.in_pat, false);
+
+                intravisit::walk_expr(self, expr);
+
+                self.in_pat = in_pat;
+                return;
+            }
             _ => (),
         }
 
@@ -632,17 +658,6 @@ impl<'tcx> Visitor<'tcx> for MarkSymbolVisitor<'tcx> {
 
         self.in_pat = in_pat;
     }
-
-    fn visit_inline_const(&mut self, c: &'tcx hir::ConstBlock) {
-        // When inline const blocks are used in pattern position, paths
-        // referenced by it should be considered as used.
-        let in_pat = mem::replace(&mut self.in_pat, false);
-
-        self.live_symbols.insert(c.def_id);
-        intravisit::walk_inline_const(self, c);
-
-        self.in_pat = in_pat;
-    }
 }
 
 fn has_allow_dead_code_or_lang_attr(
@@ -744,20 +759,22 @@ fn check_item<'tcx>(
                         matches!(fn_sig.decl.implicit_self, hir::ImplicitSelfKind::None);
                 }
 
-                // for impl trait blocks, mark associate functions live if the trait is public
+                // for trait impl blocks,
+                // mark the method live if the self_ty is public,
+                // or the method is public and may construct self
                 if of_trait
                     && (!matches!(tcx.def_kind(local_def_id), DefKind::AssocFn)
                         || tcx.visibility(local_def_id).is_public()
                             && (ty_is_pub || may_construct_self))
                 {
                     worklist.push((local_def_id, ComesFromAllowExpect::No));
-                } else if of_trait && tcx.visibility(local_def_id).is_public() {
-                    // pub method && private ty & methods not construct self
-                    unsolved_impl_items.push((id, local_def_id));
                 } else if let Some(comes_from_allow) =
                     has_allow_dead_code_or_lang_attr(tcx, local_def_id)
                 {
                     worklist.push((local_def_id, comes_from_allow));
+                } else if of_trait {
+                    // private method || public method not constructs self
+                    unsolved_impl_items.push((id, local_def_id));
                 }
             }
         }
diff --git a/compiler/rustc_passes/src/diagnostic_items.rs b/compiler/rustc_passes/src/diagnostic_items.rs
index 906ecdfe5ab..78653e5f95a 100644
--- a/compiler/rustc_passes/src/diagnostic_items.rs
+++ b/compiler/rustc_passes/src/diagnostic_items.rs
@@ -82,7 +82,7 @@ fn all_diagnostic_items(tcx: TyCtxt<'_>, (): ()) -> DiagnosticItems {
     let mut items = DiagnosticItems::default();
 
     // Collect diagnostic items in other crates.
-    for &cnum in tcx.crates(()).iter().chain(std::iter::once(&LOCAL_CRATE)) {
+    for &cnum in tcx.crates_including_speculative(()).iter().chain(std::iter::once(&LOCAL_CRATE)) {
         for (&name, &def_id) in &tcx.diagnostic_items(cnum).name_to_id {
             collect_item(tcx, &mut items, name, def_id);
         }
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index 438c583db49..b43c8282db1 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -12,17 +12,16 @@ use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
 
 use crate::errors::{
-    AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain,
-    MultipleStartFunctions, NoMainErr, UnixSigpipeValues,
+    AttrOnlyInFunctions, ExternMain, MultipleRustcMain, MultipleStartFunctions, NoMainErr,
 };
 
 struct EntryContext<'tcx> {
     tcx: TyCtxt<'tcx>,
 
-    /// The function that has attribute named `main`.
-    attr_main_fn: Option<(LocalDefId, Span)>,
+    /// The function has the `#[rustc_main]` attribute.
+    rustc_main_fn: Option<(LocalDefId, Span)>,
 
-    /// The function that has the attribute 'start' on it.
+    /// The function that has the attribute `#[start]` on it.
     start_fn: Option<(LocalDefId, Span)>,
 
     /// The functions that one might think are `main` but aren't, e.g.
@@ -43,10 +42,10 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
     }
 
     let mut ctxt =
-        EntryContext { tcx, attr_main_fn: None, start_fn: None, non_main_fns: Vec::new() };
+        EntryContext { tcx, rustc_main_fn: None, start_fn: None, non_main_fns: Vec::new() };
 
     for id in tcx.hir().items() {
-        find_item(id, &mut ctxt);
+        check_and_search_item(id, &mut ctxt);
     }
 
     configure_main(tcx, &ctxt)
@@ -57,7 +56,16 @@ fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Opti
     attr::find_by_name(attrs, sym).map(|attr| attr.span)
 }
 
-fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
+fn check_and_search_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
+    if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) {
+        for attr in [sym::start, sym::rustc_main] {
+            if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
+                ctxt.tcx.dcx().emit_err(AttrOnlyInFunctions { span, attr });
+            }
+        }
+        return;
+    }
+
     let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID);
 
     let attrs = ctxt.tcx.hir().attrs(id.hir_id());
@@ -66,41 +74,25 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
         at_root,
         ctxt.tcx.opt_item_name(id.owner_id.to_def_id()),
     );
+
     match entry_point_type {
-        EntryPointType::None => {
-            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
-                ctxt.tcx.dcx().emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
-            }
-        }
-        _ if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) => {
-            for attr in [sym::start, sym::rustc_main] {
-                if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
-                    ctxt.tcx.dcx().emit_err(AttrOnlyInFunctions { span, attr });
-                }
-            }
-        }
-        EntryPointType::MainNamed => (),
+        EntryPointType::None => {}
+        EntryPointType::MainNamed => {}
         EntryPointType::OtherMain => {
-            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
-                ctxt.tcx.dcx().emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe });
-            }
             ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id));
         }
         EntryPointType::RustcMainAttr => {
-            if ctxt.attr_main_fn.is_none() {
-                ctxt.attr_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
+            if ctxt.rustc_main_fn.is_none() {
+                ctxt.rustc_main_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
             } else {
                 ctxt.tcx.dcx().emit_err(MultipleRustcMain {
                     span: ctxt.tcx.def_span(id.owner_id.to_def_id()),
-                    first: ctxt.attr_main_fn.unwrap().1,
+                    first: ctxt.rustc_main_fn.unwrap().1,
                     additional: ctxt.tcx.def_span(id.owner_id.to_def_id()),
                 });
             }
         }
         EntryPointType::Start => {
-            if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
-                ctxt.tcx.dcx().emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
-            }
             if ctxt.start_fn.is_none() {
                 ctxt.start_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
             } else {
@@ -118,10 +110,11 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
 fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
     if let Some((def_id, _)) = visitor.start_fn {
         Some((def_id.to_def_id(), EntryFnType::Start))
-    } else if let Some((local_def_id, _)) = visitor.attr_main_fn {
+    } else if let Some((local_def_id, _)) = visitor.rustc_main_fn {
         let def_id = local_def_id.to_def_id();
-        Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }))
+        Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }))
     } else {
+        // The actual resolution of main happens in the resolver, this here
         if let Some(main_def) = tcx.resolutions(()).main_def
             && let Some(def_id) = main_def.opt_fn_def_id()
         {
@@ -133,31 +126,19 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
                 return None;
             }
 
-            return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }));
+            return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }));
         }
         no_main_err(tcx, visitor);
         None
     }
 }
 
-fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
-    if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) {
-        match (attr.value_str(), attr.meta_item_list()) {
-            (Some(sym::inherit), None) => sigpipe::INHERIT,
-            (Some(sym::sig_ign), None) => sigpipe::SIG_IGN,
-            (Some(sym::sig_dfl), None) => sigpipe::SIG_DFL,
-            (Some(_), None) => {
-                tcx.dcx().emit_err(UnixSigpipeValues { span: attr.span });
-                sigpipe::DEFAULT
-            }
-            _ => {
-                // Keep going so that `fn emit_malformed_attribute()` can print
-                // an excellent error message
-                sigpipe::DEFAULT
-            }
-        }
-    } else {
-        sigpipe::DEFAULT
+fn sigpipe(tcx: TyCtxt<'_>) -> u8 {
+    match tcx.sess.opts.unstable_opts.on_broken_pipe {
+        rustc_target::spec::OnBrokenPipe::Default => sigpipe::DEFAULT,
+        rustc_target::spec::OnBrokenPipe::Kill => sigpipe::SIG_DFL,
+        rustc_target::spec::OnBrokenPipe::Error => sigpipe::SIG_IGN,
+        rustc_target::spec::OnBrokenPipe::Inherit => sigpipe::INHERIT,
     }
 }
 
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 3f26ea4507d..7fdd9924b51 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -17,12 +17,9 @@ use rustc_span::{Span, Symbol, DUMMY_SP};
 use crate::check_attr::ProcMacroKind;
 use crate::lang_items::Duplicate;
 
-#[derive(Diagnostic)]
+#[derive(LintDiagnostic)]
 #[diag(passes_incorrect_do_not_recommend_location)]
-pub struct IncorrectDoNotRecommendLocation {
-    #[primary_span]
-    pub span: Span,
-}
+pub struct IncorrectDoNotRecommendLocation;
 
 #[derive(LintDiagnostic)]
 #[diag(passes_outer_crate_level_attr)]
@@ -820,6 +817,16 @@ pub struct MissingLangItem {
 }
 
 #[derive(Diagnostic)]
+#[diag(passes_lang_item_fn_with_track_caller)]
+pub struct LangItemWithTrackCaller {
+    #[primary_span]
+    pub attr_span: Span,
+    pub name: Symbol,
+    #[label]
+    pub sig_span: Span,
+}
+
+#[derive(Diagnostic)]
 #[diag(passes_lang_item_fn_with_target_feature)]
 pub struct LangItemWithTargetFeature {
     #[primary_span]
@@ -1076,14 +1083,16 @@ pub struct BreakInsideClosure<'a> {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_break_inside_async_block, code = E0267)]
-pub struct BreakInsideAsyncBlock<'a> {
+#[diag(passes_break_inside_coroutine, code = E0267)]
+pub struct BreakInsideCoroutine<'a> {
     #[primary_span]
     #[label]
     pub span: Span,
-    #[label(passes_async_block_label)]
-    pub closure_span: Span,
+    #[label(passes_coroutine_label)]
+    pub coroutine_span: Span,
     pub name: &'a str,
+    pub kind: &'a str,
+    pub source: &'a str,
 }
 
 #[derive(Diagnostic)]
@@ -1091,7 +1100,7 @@ pub struct BreakInsideAsyncBlock<'a> {
 pub struct OutsideLoop<'a> {
     #[primary_span]
     #[label]
-    pub span: Span,
+    pub spans: Vec<Span>,
     pub name: &'a str,
     pub is_break: bool,
     #[subdiagnostic]
@@ -1103,7 +1112,7 @@ pub struct OutsideLoopSuggestion {
     #[suggestion_part(code = "'block: ")]
     pub block_span: Span,
     #[suggestion_part(code = " 'block")]
-    pub break_span: Span,
+    pub break_spans: Vec<Span>,
 }
 
 #[derive(Diagnostic)]
@@ -1197,22 +1206,6 @@ pub struct NakedFunctionsMustUseNoreturn {
 }
 
 #[derive(Diagnostic)]
-#[diag(passes_attr_only_on_main)]
-pub struct AttrOnlyOnMain {
-    #[primary_span]
-    pub span: Span,
-    pub attr: Symbol,
-}
-
-#[derive(Diagnostic)]
-#[diag(passes_attr_only_on_root_main)]
-pub struct AttrOnlyOnRootMain {
-    #[primary_span]
-    pub span: Span,
-    pub attr: Symbol,
-}
-
-#[derive(Diagnostic)]
 #[diag(passes_attr_only_in_functions)]
 pub struct AttrOnlyInFunctions {
     #[primary_span]
@@ -1249,13 +1242,6 @@ pub struct ExternMain {
     pub span: Span,
 }
 
-#[derive(Diagnostic)]
-#[diag(passes_unix_sigpipe_values)]
-pub struct UnixSigpipeValues {
-    #[primary_span]
-    pub span: Span,
-}
-
 pub struct NoMainErr {
     pub sp: Span,
     pub crate_name: Symbol,
@@ -1755,7 +1741,7 @@ impl Subdiagnostic for UnusedVariableStringInterp {
     fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
         self,
         diag: &mut Diag<'_, G>,
-        _f: F,
+        _f: &F,
     ) {
         diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation);
         diag.multipart_suggestion(
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 49408c5618b..a980d5dcaba 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -498,7 +498,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
             (self, i, i.kind, Id::None, ast, ForeignItem, ForeignItemKind),
             [Static, Fn, TyAlias, MacCall]
         );
-        ast_visit::walk_foreign_item(self, i)
+        ast_visit::walk_item(self, i)
     }
 
     fn visit_item(&mut self, i: &'v ast::Item) {
@@ -522,7 +522,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
                 Impl,
                 MacCall,
                 MacroDef,
-                Delegation
+                Delegation,
+                DelegationMac
             ]
         );
         ast_visit::walk_item(self, i)
@@ -650,7 +651,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
     fn visit_assoc_item(&mut self, i: &'v ast::AssocItem, ctxt: ast_visit::AssocCtxt) {
         record_variants!(
             (self, i, i.kind, Id::None, ast, AssocItem, AssocItemKind),
-            [Const, Fn, Type, MacCall, Delegation]
+            [Const, Fn, Type, MacCall, Delegation, DelegationMac]
         );
         ast_visit::walk_assoc_item(self, i, ctxt);
     }
diff --git a/compiler/rustc_passes/src/lang_items.rs b/compiler/rustc_passes/src/lang_items.rs
index d1368267224..b3722e99e16 100644
--- a/compiler/rustc_passes/src/lang_items.rs
+++ b/compiler/rustc_passes/src/lang_items.rs
@@ -1,4 +1,4 @@
-//! Detecting language items.
+//! Detecting lang items.
 //!
 //! Language items are items that represent concepts intrinsic to the language
 //! itself. Examples are:
@@ -149,8 +149,9 @@ impl<'ast, 'tcx> LanguageItemCollector<'ast, 'tcx> {
                 }
             };
 
-            // When there's a duplicate lang item, something went very wrong and there's no value in recovering or doing anything.
-            // Give the user the one message to let them debug the mess they created and then wish them farewell.
+            // When there's a duplicate lang item, something went very wrong and there's no value
+            // in recovering or doing anything. Give the user the one message to let them debug the
+            // mess they created and then wish them farewell.
             self.tcx.dcx().emit_fatal(DuplicateLangItem {
                 local_span: item_span,
                 lang_item_name,
@@ -285,7 +286,9 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
             ast::ItemKind::TraitAlias(_, _) => Target::TraitAlias,
             ast::ItemKind::Impl(_) => Target::Impl,
             ast::ItemKind::MacroDef(_) => Target::MacroDef,
-            ast::ItemKind::MacCall(_) => unreachable!("macros should have been expanded"),
+            ast::ItemKind::MacCall(_) | ast::ItemKind::DelegationMac(_) => {
+                unreachable!("macros should have been expanded")
+            }
         };
 
         self.check_for_lang(
@@ -340,7 +343,9 @@ impl<'ast, 'tcx> visit::Visitor<'ast> for LanguageItemCollector<'ast, 'tcx> {
             }
             ast::AssocItemKind::Const(ct) => (Target::AssocConst, Some(&ct.generics)),
             ast::AssocItemKind::Type(ty) => (Target::AssocTy, Some(&ty.generics)),
-            ast::AssocItemKind::MacCall(_) => unreachable!("macros should have been expanded"),
+            ast::AssocItemKind::MacCall(_) | ast::AssocItemKind::DelegationMac(_) => {
+                unreachable!("macros should have been expanded")
+            }
         };
 
         self.check_for_lang(
diff --git a/compiler/rustc_passes/src/layout_test.rs b/compiler/rustc_passes/src/layout_test.rs
index 8d223c23363..82d43f078ee 100644
--- a/compiler/rustc_passes/src/layout_test.rs
+++ b/compiler/rustc_passes/src/layout_test.rs
@@ -1,6 +1,7 @@
 use rustc_ast::Attribute;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
+use rustc_middle::span_bug;
 use rustc_middle::ty::layout::{HasParamEnv, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout};
 use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
 use rustc_span::source_map::Spanned;
diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs
index e03052bcfed..045a0a1525b 100644
--- a/compiler/rustc_passes/src/lib.rs
+++ b/compiler/rustc_passes/src/lib.rs
@@ -8,16 +8,10 @@
 #![doc(rust_logo)]
 #![feature(rustdoc_internals)]
 #![allow(internal_features)]
-#![feature(generic_nonzero)]
 #![feature(let_chains)]
 #![feature(map_try_insert)]
 #![feature(try_blocks)]
 
-#[macro_use]
-extern crate rustc_middle;
-#[macro_use]
-extern crate tracing;
-
 use rustc_middle::query::Providers;
 
 pub mod abi_test;
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index c7729302783..1f966be26ff 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -94,14 +94,15 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Expr, HirId, HirIdMap, HirIdSet};
 use rustc_index::IndexVec;
 use rustc_middle::query::Providers;
+use rustc_middle::span_bug;
 use rustc_middle::ty::{self, RootVariableMinCaptureList, Ty, TyCtxt};
 use rustc_session::lint;
 use rustc_span::symbol::{kw, sym, Symbol};
 use rustc_span::{BytePos, Span};
-
 use std::io;
 use std::io::prelude::*;
 use std::rc::Rc;
+use tracing::{debug, instrument};
 
 mod rwu_table;
 
@@ -146,6 +147,11 @@ fn check_liveness(tcx: TyCtxt<'_>, def_id: LocalDefId) {
         return;
     }
 
+    // Don't run for inline consts, they are collected together with their parent
+    if let DefKind::InlineConst = tcx.def_kind(def_id) {
+        return;
+    }
+
     // Don't run unused pass for #[naked]
     if tcx.has_attr(def_id.to_def_id(), sym::naked) {
         return;
@@ -1143,12 +1149,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
             }
 
             hir::ExprKind::Lit(..)
-            | hir::ExprKind::ConstBlock(..)
             | hir::ExprKind::Err(_)
             | hir::ExprKind::Path(hir::QPath::TypeRelative(..))
             | hir::ExprKind::Path(hir::QPath::LangItem(..))
             | hir::ExprKind::OffsetOf(..) => succ,
 
+            hir::ExprKind::ConstBlock(expr) => self.propagate_through_expr(expr, succ),
+
             // Note that labels have been resolved, so we don't need to look
             // at the label ident
             hir::ExprKind::Block(ref blk, _) => self.propagate_through_block(blk, succ),
diff --git a/compiler/rustc_passes/src/loops.rs b/compiler/rustc_passes/src/loops.rs
index e10a22cdf31..737310e5c04 100644
--- a/compiler/rustc_passes/src/loops.rs
+++ b/compiler/rustc_passes/src/loops.rs
@@ -1,3 +1,5 @@
+use std::collections::BTreeMap;
+use std::fmt;
 use Context::*;
 
 use rustc_hir as hir;
@@ -6,13 +8,14 @@ use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{Destination, Node};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::query::Providers;
+use rustc_middle::span_bug;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::{BytePos, Span};
 
 use crate::errors::{
-    BreakInsideAsyncBlock, BreakInsideClosure, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
+    BreakInsideClosure, BreakInsideCoroutine, BreakNonLoop, ContinueLabeledBlock, OutsideLoop,
     OutsideLoopSuggestion, UnlabeledCfInWhileCondition, UnlabeledInLabeledBlock,
 };
 
@@ -22,24 +25,57 @@ enum Context {
     Fn,
     Loop(hir::LoopSource),
     Closure(Span),
-    AsyncClosure(Span),
+    Coroutine { coroutine_span: Span, kind: hir::CoroutineDesugaring, source: hir::CoroutineSource },
     UnlabeledBlock(Span),
+    UnlabeledIfBlock(Span),
     LabeledBlock,
     Constant,
 }
 
-#[derive(Copy, Clone)]
+#[derive(Clone)]
+struct BlockInfo {
+    name: String,
+    spans: Vec<Span>,
+    suggs: Vec<Span>,
+}
+
+#[derive(PartialEq)]
+enum BreakContextKind {
+    Break,
+    Continue,
+}
+
+impl fmt::Display for BreakContextKind {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            BreakContextKind::Break => "break",
+            BreakContextKind::Continue => "continue",
+        }
+        .fmt(f)
+    }
+}
+
+#[derive(Clone)]
 struct CheckLoopVisitor<'a, 'tcx> {
     sess: &'a Session,
     tcx: TyCtxt<'tcx>,
-    cx: Context,
+    // Keep track of a stack of contexts, so that suggestions
+    // are not made for contexts where it would be incorrect,
+    // such as adding a label for an `if`.
+    // e.g. `if 'foo: {}` would be incorrect.
+    cx_stack: Vec<Context>,
+    block_breaks: BTreeMap<Span, BlockInfo>,
 }
 
 fn check_mod_loops(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) {
-    tcx.hir().visit_item_likes_in_module(
-        module_def_id,
-        &mut CheckLoopVisitor { sess: tcx.sess, tcx, cx: Normal },
-    );
+    let mut check = CheckLoopVisitor {
+        sess: tcx.sess,
+        tcx,
+        cx_stack: vec![Normal],
+        block_breaks: Default::default(),
+    };
+    tcx.hir().visit_item_likes_in_module(module_def_id, &mut check);
+    check.report_outside_loop_error();
 }
 
 pub(crate) fn provide(providers: &mut Providers) {
@@ -57,10 +93,6 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
         self.with_context(Constant, |v| intravisit::walk_anon_const(v, c));
     }
 
-    fn visit_inline_const(&mut self, c: &'hir hir::ConstBlock) {
-        self.with_context(Constant, |v| intravisit::walk_inline_const(v, c));
-    }
-
     fn visit_fn(
         &mut self,
         fk: hir::intravisit::FnKind<'hir>,
@@ -82,18 +114,55 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
 
     fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
         match e.kind {
+            hir::ExprKind::If(cond, then, else_opt) => {
+                self.visit_expr(cond);
+
+                let get_block = |ck_loop: &CheckLoopVisitor<'a, 'hir>,
+                                 expr: &hir::Expr<'hir>|
+                 -> Option<&hir::Block<'hir>> {
+                    if let hir::ExprKind::Block(b, None) = expr.kind
+                        && matches!(
+                            ck_loop.cx_stack.last(),
+                            Some(&Normal)
+                                | Some(&Constant)
+                                | Some(&UnlabeledBlock(_))
+                                | Some(&UnlabeledIfBlock(_))
+                        )
+                    {
+                        Some(b)
+                    } else {
+                        None
+                    }
+                };
+
+                if let Some(b) = get_block(self, then) {
+                    self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| {
+                        v.visit_block(b)
+                    });
+                } else {
+                    self.visit_expr(then);
+                }
+
+                if let Some(else_expr) = else_opt {
+                    if let Some(b) = get_block(self, else_expr) {
+                        self.with_context(UnlabeledIfBlock(b.span.shrink_to_lo()), |v| {
+                            v.visit_block(b)
+                        });
+                    } else {
+                        self.visit_expr(else_expr);
+                    }
+                }
+            }
             hir::ExprKind::Loop(ref b, _, source, _) => {
                 self.with_context(Loop(source), |v| v.visit_block(b));
             }
             hir::ExprKind::Closure(&hir::Closure {
                 ref fn_decl, body, fn_decl_span, kind, ..
             }) => {
-                // FIXME(coroutines): This doesn't handle coroutines correctly
                 let cx = match kind {
-                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
-                        hir::CoroutineDesugaring::Async,
-                        hir::CoroutineSource::Block,
-                    )) => AsyncClosure(fn_decl_span),
+                    hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(kind, source)) => {
+                        Coroutine { coroutine_span: fn_decl_span, kind, source }
+                    }
                     _ => Closure(fn_decl_span),
                 };
                 self.visit_fn_decl(fn_decl);
@@ -102,11 +171,14 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
             hir::ExprKind::Block(ref b, Some(_label)) => {
                 self.with_context(LabeledBlock, |v| v.visit_block(b));
             }
-            hir::ExprKind::Block(ref b, None) if matches!(self.cx, Fn) => {
+            hir::ExprKind::Block(ref b, None) if matches!(self.cx_stack.last(), Some(&Fn)) => {
                 self.with_context(Normal, |v| v.visit_block(b));
             }
             hir::ExprKind::Block(ref b, None)
-                if matches!(self.cx, Normal | Constant | UnlabeledBlock(_)) =>
+                if matches!(
+                    self.cx_stack.last(),
+                    Some(&Normal) | Some(&Constant) | Some(&UnlabeledBlock(_))
+                ) =>
             {
                 self.with_context(UnlabeledBlock(b.span.shrink_to_lo()), |v| v.visit_block(b));
             }
@@ -179,7 +251,12 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     Some(label) => sp_lo.with_hi(label.ident.span.hi()),
                     None => sp_lo.shrink_to_lo(),
                 };
-                self.require_break_cx("break", e.span, label_sp);
+                self.require_break_cx(
+                    BreakContextKind::Break,
+                    e.span,
+                    label_sp,
+                    self.cx_stack.len() - 1,
+                );
             }
             hir::ExprKind::Continue(destination) => {
                 self.require_label_in_labeled_block(e.span, &destination, "continue");
@@ -201,7 +278,15 @@ impl<'a, 'hir> Visitor<'hir> for CheckLoopVisitor<'a, 'hir> {
                     }
                     Err(_) => {}
                 }
-                self.require_break_cx("continue", e.span, e.span)
+                self.require_break_cx(
+                    BreakContextKind::Continue,
+                    e.span,
+                    e.span,
+                    self.cx_stack.len() - 1,
+                )
+            }
+            hir::ExprKind::ConstBlock(expr) => {
+                self.with_context(Constant, |v| intravisit::walk_expr(v, expr));
             }
             _ => intravisit::walk_expr(self, e),
         }
@@ -213,28 +298,67 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
     where
         F: FnOnce(&mut CheckLoopVisitor<'a, 'hir>),
     {
-        let old_cx = self.cx;
-        self.cx = cx;
+        self.cx_stack.push(cx);
         f(self);
-        self.cx = old_cx;
+        self.cx_stack.pop();
     }
 
-    fn require_break_cx(&self, name: &str, span: Span, break_span: Span) {
-        let is_break = name == "break";
-        match self.cx {
+    fn require_break_cx(
+        &mut self,
+        br_cx_kind: BreakContextKind,
+        span: Span,
+        break_span: Span,
+        cx_pos: usize,
+    ) {
+        match self.cx_stack[cx_pos] {
             LabeledBlock | Loop(_) => {}
             Closure(closure_span) => {
-                self.sess.dcx().emit_err(BreakInsideClosure { span, closure_span, name });
+                self.sess.dcx().emit_err(BreakInsideClosure {
+                    span,
+                    closure_span,
+                    name: &br_cx_kind.to_string(),
+                });
+            }
+            Coroutine { coroutine_span, kind, source } => {
+                let kind = match kind {
+                    hir::CoroutineDesugaring::Async => "async",
+                    hir::CoroutineDesugaring::Gen => "gen",
+                    hir::CoroutineDesugaring::AsyncGen => "async gen",
+                };
+                let source = match source {
+                    hir::CoroutineSource::Block => "block",
+                    hir::CoroutineSource::Closure => "closure",
+                    hir::CoroutineSource::Fn => "function",
+                };
+                self.sess.dcx().emit_err(BreakInsideCoroutine {
+                    span,
+                    coroutine_span,
+                    name: &br_cx_kind.to_string(),
+                    kind,
+                    source,
+                });
             }
-            AsyncClosure(closure_span) => {
-                self.sess.dcx().emit_err(BreakInsideAsyncBlock { span, closure_span, name });
+            UnlabeledBlock(block_span)
+                if br_cx_kind == BreakContextKind::Break && block_span.eq_ctxt(break_span) =>
+            {
+                let block = self.block_breaks.entry(block_span).or_insert_with(|| BlockInfo {
+                    name: br_cx_kind.to_string(),
+                    spans: vec![],
+                    suggs: vec![],
+                });
+                block.spans.push(span);
+                block.suggs.push(break_span);
             }
-            UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
-                let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
-                self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion });
+            UnlabeledIfBlock(_) if br_cx_kind == BreakContextKind::Break => {
+                self.require_break_cx(br_cx_kind, span, break_span, cx_pos - 1);
             }
-            Normal | Constant | Fn | UnlabeledBlock(_) => {
-                self.sess.dcx().emit_err(OutsideLoop { span, name, is_break, suggestion: None });
+            Normal | Constant | Fn | UnlabeledBlock(_) | UnlabeledIfBlock(_) => {
+                self.sess.dcx().emit_err(OutsideLoop {
+                    spans: vec![span],
+                    name: &br_cx_kind.to_string(),
+                    is_break: br_cx_kind == BreakContextKind::Break,
+                    suggestion: None,
+                });
             }
         }
     }
@@ -246,7 +370,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         cf_type: &str,
     ) -> bool {
         if !span.is_desugaring(DesugaringKind::QuestionMark)
-            && self.cx == LabeledBlock
+            && self.cx_stack.last() == Some(&LabeledBlock)
             && label.label.is_none()
         {
             self.sess.dcx().emit_err(UnlabeledInLabeledBlock { span, cf_type });
@@ -254,4 +378,18 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
         }
         false
     }
+
+    fn report_outside_loop_error(&mut self) {
+        for (s, block) in &self.block_breaks {
+            self.sess.dcx().emit_err(OutsideLoop {
+                spans: block.spans.clone(),
+                name: &block.name,
+                is_break: true,
+                suggestion: Some(OutsideLoopSuggestion {
+                    block_span: *s,
+                    break_spans: block.suggs.clone(),
+                }),
+            });
+        }
+    }
 }
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index a1f37ee3b83..d45ee32a624 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -83,8 +83,7 @@ fn check_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, abi: Abi) {
 fn check_no_patterns(tcx: TyCtxt<'_>, params: &[hir::Param<'_>]) {
     for param in params {
         match param.pat.kind {
-            hir::PatKind::Wild
-            | hir::PatKind::Binding(hir::BindingAnnotation::NONE, _, _, None) => {}
+            hir::PatKind::Wild | hir::PatKind::Binding(hir::BindingMode::NONE, _, _, None) => {}
             _ => {
                 tcx.dcx().emit_err(NoPatterns { span: param.pat.span });
             }
diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs
index 869cbebbc0d..ab1dd248556 100644
--- a/compiler/rustc_passes/src/reachable.rs
+++ b/compiler/rustc_passes/src/reachable.rs
@@ -1,14 +1,26 @@
-//! Finds local items that are externally reachable, which means that other crates need access to
-//! their compiled machine code or their MIR.
+//! Finds local items that are "reachable", which means that other crates need access to their
+//! compiled code or their *runtime* MIR. (Compile-time MIR is always encoded anyway, so we don't
+//! worry about that here.)
 //!
-//! An item is "externally reachable" if it is relevant for other crates. This obviously includes
-//! all public items. However, some of these items cannot be compiled to machine code (because they
-//! are generic), and for some the machine code is not sufficient (because we want to cross-crate
-//! inline them). These items "need cross-crate MIR". When a reachable function `f` needs
-//! cross-crate MIR, then all the functions it calls also become reachable, as they will be
-//! necessary to use the MIR of `f` from another crate. Furthermore, an item can become "externally
-//! reachable" by having a `const`/`const fn` return a pointer to that item, so we also need to
-//! recurse into reachable `const`/`const fn`.
+//! An item is "reachable" if codegen that happens in downstream crates can end up referencing this
+//! item. This obviously includes all public items. However, some of these items cannot be codegen'd
+//! (because they are generic), and for some the compiled code is not sufficient (because we want to
+//! cross-crate inline them). These items "need cross-crate MIR". When a reachable function `f`
+//! needs cross-crate MIR, then its MIR may be codegen'd in a downstream crate, and hence items it
+//! mentions need to be considered reachable.
+//!
+//! Furthermore, if a `const`/`const fn` is reachable, then it can return pointers to other items,
+//! making those reachable as well. For instance, consider a `const fn` returning a pointer to an
+//! otherwise entirely private function: if a downstream crate calls that `const fn` to compute the
+//! initial value of a `static`, then it needs to generate a direct reference to this function --
+//! i.e., the function is directly reachable from that downstream crate! Hence we have to recurse
+//! into `const` and `const fn`.
+//!
+//! Conversely, reachability *stops* when it hits a monomorphic non-`const` function that we do not
+//! want to cross-crate inline. That function will just be codegen'd in this crate, which means the
+//! monomorphization collector will consider it a root and then do another graph traversal to
+//! codegen everything called by this function -- but that's a very different graph from what we are
+//! considering here as at that point, everything is monomorphic.
 
 use hir::def_id::LocalDefIdSet;
 use rustc_data_structures::stack::ensure_sufficient_stack;
@@ -17,6 +29,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::Node;
+use rustc_middle::bug;
 use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
 use rustc_middle::middle::privacy::{self, Level};
 use rustc_middle::mir::interpret::{ConstAllocation, GlobalAlloc};
@@ -24,6 +37,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::{self, ExistentialTraitRef, TyCtxt};
 use rustc_privacy::DefIdVisitor;
 use rustc_session::config::CrateType;
+use tracing::debug;
 
 /// Determines whether this item is recursive for reachability. See `is_recursively_reachable_local`
 /// below for details.
diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs
index 05c833cdfb6..31c709f2eb6 100644
--- a/compiler/rustc_passes/src/stability.rs
+++ b/compiler/rustc_passes/src/stability.rs
@@ -25,9 +25,9 @@ use rustc_session::lint::builtin::{INEFFECTIVE_UNSTABLE_TRAIT_IMPL, USELESS_DEPR
 use rustc_span::symbol::{sym, Symbol};
 use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
-
 use std::mem::replace;
 use std::num::NonZero;
+use tracing::{debug, info};
 
 #[derive(PartialEq)]
 enum AnnotationKind {
@@ -1020,7 +1020,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
         // stabilization diagnostic, but it can be avoided when there are no
         // `remaining_lib_features`.
         let mut all_implications = remaining_implications.clone();
-        for &cnum in tcx.crates(()) {
+        for &cnum in tcx.used_crates(()) {
             all_implications
                 .extend_unord(tcx.stability_implications(cnum).items().map(|(k, v)| (*k, *v)));
         }
@@ -1033,7 +1033,7 @@ pub fn check_unused_or_stable_features(tcx: TyCtxt<'_>) {
             &all_implications,
         );
 
-        for &cnum in tcx.crates(()) {
+        for &cnum in tcx.used_crates(()) {
             if remaining_lib_features.is_empty() && remaining_implications.is_empty() {
                 break;
             }
diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs
index 4eb0c6c275e..d80addf1236 100644
--- a/compiler/rustc_passes/src/weak_lang_items.rs
+++ b/compiler/rustc_passes/src/weak_lang_items.rs
@@ -14,7 +14,7 @@ use crate::errors::{
 };
 
 /// Checks the crate for usage of weak lang items, returning a vector of all the
-/// language items required by this crate, but not defined yet.
+/// lang items required by this crate, but not defined yet.
 pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems, krate: &ast::Crate) {
     // These are never called by user code, they're generated by the compiler.
     // They will never implicitly be added to the `missing` array unless we do
@@ -68,7 +68,7 @@ fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) {
     }
 
     let mut missing = FxHashSet::default();
-    for &cnum in tcx.crates(()).iter() {
+    for &cnum in tcx.used_crates(()).iter() {
         for &item in tcx.missing_lang_items(cnum).iter() {
             missing.insert(item);
         }