about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-04-22 23:31:22 +0000
committerMichael Goulet <michael@errs.io>2025-04-23 15:09:25 +0000
commitf943f73db4791d64ff83d72986da8d6250c42933 (patch)
tree4474dcc7ba139e0d9f70a7754c31694fcf7186ee
parent7c1661f9457825df6e6bbf4869be3cad59b608a9 (diff)
downloadrust-f943f73db4791d64ff83d72986da8d6250c42933.tar.gz
rust-f943f73db4791d64ff83d72986da8d6250c42933.zip
More
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs6
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/query/plumbing.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs17
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs5
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs4
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/trait_goals.rs58
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs47
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs42
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs4
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs2
-rw-r--r--compiler/rustc_ty_utils/src/opaque_types.rs50
-rw-r--r--compiler/rustc_ty_utils/src/stalled_generators.rs54
-rw-r--r--compiler/rustc_type_ir/src/infer_ctxt.rs17
-rw-r--r--compiler/rustc_type_ir/src/interner.rs5
-rw-r--r--tests/ui/async-await/async-closures/is-not-fn.next.stderr6
-rw-r--r--tests/ui/async-await/async-closures/is-not-fn.rs3
-rw-r--r--tests/ui/coroutine/clone-rpit.next.stderr47
-rw-r--r--tests/ui/coroutine/clone-rpit.rs3
19 files changed, 189 insertions, 187 deletions
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs
index 3b0e8560159..c4698e5cbb4 100644
--- a/compiler/rustc_infer/src/infer/mod.rs
+++ b/compiler/rustc_infer/src/infer/mod.rs
@@ -967,7 +967,9 @@ impl<'tcx> InferCtxt<'tcx> {
     pub fn can_define_opaque_ty(&self, id: impl Into<DefId>) -> bool {
         debug_assert!(!self.next_trait_solver());
         match self.typing_mode() {
-            TypingMode::Analysis { defining_opaque_types, stalled_generators: _ }
+            TypingMode::Analysis {
+                defining_opaque_types_and_generators: defining_opaque_types,
+            }
             | TypingMode::Borrowck { defining_opaque_types } => {
                 id.into().as_local().is_some_and(|def_id| defining_opaque_types.contains(&def_id))
             }
@@ -1262,7 +1264,7 @@ impl<'tcx> InferCtxt<'tcx> {
             // to handle them without proper canonicalization. This means we may cause cycle
             // errors and fail to reveal opaques while inside of bodies. We should rename this
             // function and require explicit comments on all use-sites in the future.
-            ty::TypingMode::Analysis { defining_opaque_types: _, stalled_generators: _ }
+            ty::TypingMode::Analysis { defining_opaque_types_and_generators: _ }
             | ty::TypingMode::Borrowck { defining_opaque_types: _ } => {
                 TypingMode::non_body_analysis()
             }
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 4d4096b0225..3432648feab 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -391,7 +391,7 @@ rustc_queries! {
         key: LocalDefId
     ) -> &'tcx ty::List<LocalDefId> {
         desc {
-            |tcx| "computing the opaque types defined by `{}`",
+            |tcx| "computing the coroutines defined within `{}`",
             tcx.def_path_str(key.to_def_id())
         }
     }
diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs
index c16adef63ea..69b6f88d72b 100644
--- a/compiler/rustc_middle/src/query/plumbing.rs
+++ b/compiler/rustc_middle/src/query/plumbing.rs
@@ -366,11 +366,11 @@ macro_rules! define_callbacks {
 
                 pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache<Erase<$V>>;
 
-                // Ensure that keys grow no larger than 96 bytes by accident.
+                // Ensure that keys grow no larger than 88 bytes by accident.
                 // Increase this limit if necessary, but do try to keep the size low if possible
                 #[cfg(target_pointer_width = "64")]
                 const _: () = {
-                    if size_of::<Key<'static>>() > 96 {
+                    if size_of::<Key<'static>>() > 88 {
                         panic!("{}", concat!(
                             "the query `",
                             stringify!($name),
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index f5872f0269e..98057a25f04 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -678,11 +678,18 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
         self.opaque_types_defined_by(defining_anchor)
     }
 
-    fn stalled_generators_within(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds {
+    fn opaque_types_and_generators_defined_by(
+        self,
+        defining_anchor: Self::LocalDefId,
+    ) -> Self::LocalDefIds {
         if self.next_trait_solver_globally() {
-            self.stalled_generators_within(defining_anchor)
+            self.mk_local_def_ids_from_iter(
+                self.opaque_types_defined_by(defining_anchor)
+                    .iter()
+                    .chain(self.stalled_generators_within(defining_anchor)),
+            )
         } else {
-            ty::List::empty()
+            self.opaque_types_defined_by(defining_anchor)
         }
     }
 }
@@ -2914,11 +2921,11 @@ impl<'tcx> TyCtxt<'tcx> {
         self.interners.intern_clauses(clauses)
     }
 
-    pub fn mk_local_def_ids(self, clauses: &[LocalDefId]) -> &'tcx List<LocalDefId> {
+    pub fn mk_local_def_ids(self, def_ids: &[LocalDefId]) -> &'tcx List<LocalDefId> {
         // FIXME consider asking the input slice to be sorted to avoid
         // re-interning permutations, in which case that would be asserted
         // here.
-        self.intern_local_def_ids(clauses)
+        self.intern_local_def_ids(def_ids)
     }
 
     pub fn mk_local_def_ids_from_iter<I, T>(self, iter: I) -> T::Output
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index 8868769906d..0695c5acdca 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -329,10 +329,7 @@ where
             TypingMode::Coherence | TypingMode::PostAnalysis => false,
             // During analysis, opaques are rigid unless they may be defined by
             // the current body.
-            TypingMode::Analysis {
-                defining_opaque_types: non_rigid_opaques,
-                stalled_generators: _,
-            }
+            TypingMode::Analysis { defining_opaque_types_and_generators: non_rigid_opaques }
             | TypingMode::Borrowck { defining_opaque_types: non_rigid_opaques }
             | TypingMode::PostBorrowckAnalysis { defined_opaque_types: non_rigid_opaques } => {
                 !def_id.as_local().is_some_and(|def_id| non_rigid_opaques.contains(&def_id))
diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
index dcfc3b3a701..ee439f1b3d0 100644
--- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs
@@ -33,11 +33,11 @@ where
                 );
                 self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
             }
-            TypingMode::Analysis { defining_opaque_types, stalled_generators: _ } => {
+            TypingMode::Analysis { defining_opaque_types_and_generators } => {
                 let Some(def_id) = opaque_ty
                     .def_id
                     .as_local()
-                    .filter(|&def_id| defining_opaque_types.contains(&def_id))
+                    .filter(|&def_id| defining_opaque_types_and_generators.contains(&def_id))
                 else {
                     self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias);
                     return self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
index 13132d60dfa..827853be280 100644
--- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs
@@ -208,20 +208,9 @@ where
             }
         }
 
-        // TODO:
-        if let ty::CoroutineWitness(def_id, _) = goal.predicate.self_ty().kind() {
-            match ecx.typing_mode() {
-                TypingMode::Analysis { stalled_generators, defining_opaque_types: _ } => {
-                    if def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
-                    {
-                        return ecx.forced_ambiguity(MaybeCause::Ambiguity);
-                    }
-                }
-                TypingMode::Coherence
-                | TypingMode::PostAnalysis
-                | TypingMode::Borrowck { defining_opaque_types: _ }
-                | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
-            }
+        // We need to make sure to stall any coroutines we are inferring to avoid query cycles.
+        if let Some(cand) = ecx.try_stall_coroutine_witness(goal.predicate.self_ty()) {
+            return cand;
         }
 
         ecx.probe_and_evaluate_goal_for_constituent_tys(
@@ -275,20 +264,9 @@ where
             return Err(NoSolution);
         }
 
-        // TODO:
-        if let ty::CoroutineWitness(def_id, _) = goal.predicate.self_ty().kind() {
-            match ecx.typing_mode() {
-                TypingMode::Analysis { stalled_generators, defining_opaque_types: _ } => {
-                    if def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
-                    {
-                        return ecx.forced_ambiguity(MaybeCause::Ambiguity);
-                    }
-                }
-                TypingMode::Coherence
-                | TypingMode::PostAnalysis
-                | TypingMode::Borrowck { defining_opaque_types: _ }
-                | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
-            }
+        // We need to make sure to stall any coroutines we are inferring to avoid query cycles.
+        if let Some(cand) = ecx.try_stall_coroutine_witness(goal.predicate.self_ty()) {
+            return cand;
         }
 
         ecx.probe_and_evaluate_goal_for_constituent_tys(
@@ -1400,4 +1378,28 @@ where
         let candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All);
         self.merge_trait_candidates(goal, candidates)
     }
+
+    fn try_stall_coroutine_witness(
+        &mut self,
+        self_ty: I::Ty,
+    ) -> Option<Result<Candidate<I>, NoSolution>> {
+        if let ty::CoroutineWitness(def_id, _) = self_ty.kind() {
+            match self.typing_mode() {
+                TypingMode::Analysis {
+                    defining_opaque_types_and_generators: stalled_generators,
+                } => {
+                    if def_id.as_local().is_some_and(|def_id| stalled_generators.contains(&def_id))
+                    {
+                        return Some(self.forced_ambiguity(MaybeCause::Ambiguity));
+                    }
+                }
+                TypingMode::Coherence
+                | TypingMode::PostAnalysis
+                | TypingMode::Borrowck { defining_opaque_types: _ }
+                | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ } => {}
+            }
+        }
+
+        None
+    }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index abee5ac52c1..848d0646d00 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -14,6 +14,7 @@ use rustc_middle::ty::{
 };
 use rustc_next_trait_solver::solve::{GenerateProofTree, HasChanged, SolverDelegateEvalExt as _};
 use rustc_span::Span;
+use rustc_type_ir::data_structures::DelayedSet;
 use tracing::instrument;
 
 use self::derive_errors::*;
@@ -217,26 +218,30 @@ where
         &mut self,
         infcx: &InferCtxt<'tcx>,
     ) -> PredicateObligations<'tcx> {
-        self.obligations.drain_pending(|obl| {
-            let stalled_generators = match infcx.typing_mode() {
-                TypingMode::Analysis { defining_opaque_types: _, stalled_generators } => {
-                    stalled_generators
-                }
-                TypingMode::Coherence
-                | TypingMode::Borrowck { defining_opaque_types: _ }
-                | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
-                | TypingMode::PostAnalysis => return false,
-            };
-
-            if stalled_generators.is_empty() {
-                return false;
+        let stalled_generators = match infcx.typing_mode() {
+            TypingMode::Analysis { defining_opaque_types_and_generators } => {
+                defining_opaque_types_and_generators
             }
+            TypingMode::Coherence
+            | TypingMode::Borrowck { defining_opaque_types: _ }
+            | TypingMode::PostBorrowckAnalysis { defined_opaque_types: _ }
+            | TypingMode::PostAnalysis => return Default::default(),
+        };
+
+        if stalled_generators.is_empty() {
+            return Default::default();
+        }
 
+        self.obligations.drain_pending(|obl| {
             infcx.probe(|_| {
                 infcx
                     .visit_proof_tree(
                         obl.as_goal(),
-                        &mut StalledOnCoroutines { stalled_generators, span: obl.cause.span },
+                        &mut StalledOnCoroutines {
+                            stalled_generators,
+                            span: obl.cause.span,
+                            cache: Default::default(),
+                        },
                     )
                     .is_break()
             })
@@ -244,10 +249,18 @@ where
     }
 }
 
+/// Detect if a goal is stalled on a coroutine that is owned by the current typeck root.
+///
+/// This function can (erroneously) fail to detect a predicate, i.e. it doesn't need to
+/// be complete. However, this will lead to ambiguity errors, so we want to make it
+/// accurate.
+///
+/// This function can be also return false positives, which will lead to poor diagnostics
+/// so we want to keep this visitor *precise* too.
 struct StalledOnCoroutines<'tcx> {
     stalled_generators: &'tcx ty::List<LocalDefId>,
     span: Span,
-    // TODO: Cache
+    cache: DelayedSet<Ty<'tcx>>,
 }
 
 impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
@@ -272,6 +285,10 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for StalledOnCoroutines<'tcx> {
     type Result = ControlFlow<()>;
 
     fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        if !self.cache.insert(ty) {
+            return ControlFlow::Continue(());
+        }
+
         if let ty::CoroutineWitness(def_id, _) = *ty.kind()
             && def_id.as_local().is_some_and(|def_id| self.stalled_generators.contains(&def_id))
         {
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index 65ab14ae07c..5f1e63ab225 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,6 +1,5 @@
 use std::assert_matches::assert_matches;
 use std::fmt::Debug;
-use std::marker::PhantomData;
 
 use rustc_data_structures::stack::ensure_sufficient_stack;
 use rustc_infer::infer::InferCtxt;
@@ -60,7 +59,8 @@ where
 /// entered before passing `value` to the function. This is currently needed for
 /// `normalize_erasing_regions`, which skips binders as it walks through a type.
 ///
-/// TODO: doc
+/// This returns a set of stalled obligations if the typing mode of the underlying infcx
+/// has any stalled coroutine def ids.
 pub fn deeply_normalize_with_skipped_universes_and_ambiguous_goals<'tcx, T, E>(
     at: At<'_, 'tcx>,
     value: T,
@@ -72,16 +72,10 @@ where
 {
     let fulfill_cx = FulfillmentCtxt::new(at.infcx);
     let mut folder =
-        NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData };
+        NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_goals: vec![] };
     let value = value.try_fold_with(&mut folder)?;
-    let goals = folder
-        .fulfill_cx
-        .drain_stalled_obligations_for_coroutines(at.infcx)
-        .into_iter()
-        .map(|obl| obl.as_goal())
-        .collect();
     let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
-    if errors.is_empty() { Ok((value, goals)) } else { Err(errors) }
+    if errors.is_empty() { Ok((value, folder.stalled_goals)) } else { Err(errors) }
 }
 
 struct NormalizationFolder<'me, 'tcx, E> {
@@ -89,7 +83,7 @@ struct NormalizationFolder<'me, 'tcx, E> {
     fulfill_cx: FulfillmentCtxt<'tcx, E>,
     depth: usize,
     universes: Vec<Option<UniverseIndex>>,
-    _errors: PhantomData<E>,
+    stalled_goals: Vec<Goal<'tcx, ty::Predicate<'tcx>>>,
 }
 
 impl<'tcx, E> NormalizationFolder<'_, 'tcx, E>
@@ -130,10 +124,7 @@ where
         );
 
         self.fulfill_cx.register_predicate_obligation(infcx, obligation);
-        let errors = self.fulfill_cx.select_where_possible(infcx);
-        if !errors.is_empty() {
-            return Err(errors);
-        }
+        self.select_all_and_stall_coroutine_predicates()?;
 
         // Alias is guaranteed to be fully structurally resolved,
         // so we can super fold here.
@@ -184,6 +175,27 @@ where
         self.depth -= 1;
         Ok(result)
     }
+
+    fn select_all_and_stall_coroutine_predicates(&mut self) -> Result<(), Vec<E>> {
+        let errors = self.fulfill_cx.select_where_possible(self.at.infcx);
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        self.stalled_goals.extend(
+            self.fulfill_cx
+                .drain_stalled_obligations_for_coroutines(self.at.infcx)
+                .into_iter()
+                .map(|obl| obl.as_goal()),
+        );
+
+        let errors = self.fulfill_cx.collect_remaining_errors(self.at.infcx);
+        if !errors.is_empty() {
+            return Err(errors);
+        }
+
+        Ok(())
+    }
 }
 
 impl<'tcx, E> FallibleTypeFolder<TyCtxt<'tcx>> for NormalizationFolder<'_, 'tcx, E>
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 5255b57c791..c7ce13c8014 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -1498,7 +1498,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             // However, if we disqualify *all* goals from being cached, perf suffers.
             // This is likely fixed by better caching in general in the new solver.
             // See: <https://github.com/rust-lang/rust/issues/132064>.
-            TypingMode::Analysis { defining_opaque_types, stalled_generators: _ }
+            TypingMode::Analysis {
+                defining_opaque_types_and_generators: defining_opaque_types,
+            }
             | TypingMode::Borrowck { defining_opaque_types } => {
                 defining_opaque_types.is_empty() || !pred.has_opaque_types()
             }
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
index 35cc6f39856..57051e0df55 100644
--- a/compiler/rustc_ty_utils/src/lib.rs
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -32,6 +32,7 @@ mod needs_drop;
 mod opaque_types;
 mod representability;
 pub mod sig_types;
+mod stalled_generators;
 mod structural_match;
 mod ty;
 
@@ -50,4 +51,5 @@ pub fn provide(providers: &mut Providers) {
     ty::provide(providers);
     instance::provide(providers);
     structural_match::provide(providers);
+    stalled_generators::provide(providers);
 }
diff --git a/compiler/rustc_ty_utils/src/opaque_types.rs b/compiler/rustc_ty_utils/src/opaque_types.rs
index b6e19b9901c..cd730aeeea9 100644
--- a/compiler/rustc_ty_utils/src/opaque_types.rs
+++ b/compiler/rustc_ty_utils/src/opaque_types.rs
@@ -1,7 +1,6 @@
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir as hir;
 use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit;
 use rustc_hir::intravisit::Visitor;
 use rustc_middle::query::Providers;
@@ -356,51 +355,6 @@ fn opaque_types_defined_by<'tcx>(
     tcx.mk_local_def_ids(&collector.opaques)
 }
 
-// TODO: Move this out of `opaque_types`
-fn stalled_generators_within<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    item: LocalDefId,
-) -> &'tcx ty::List<LocalDefId> {
-    if !tcx.next_trait_solver_globally() {
-        return ty::List::empty();
-    }
-
-    let body = tcx.hir_body_owned_by(item);
-    let mut collector =
-        StalledGeneratorVisitor { tcx, root_def_id: item.to_def_id(), stalled_coroutines: vec![] };
-    collector.visit_body(body);
-    tcx.mk_local_def_ids(&collector.stalled_coroutines)
-}
-
-struct StalledGeneratorVisitor<'tcx> {
-    tcx: TyCtxt<'tcx>,
-    root_def_id: DefId,
-    stalled_coroutines: Vec<LocalDefId>,
-}
-
-impl<'tcx> Visitor<'tcx> for StalledGeneratorVisitor<'tcx> {
-    fn visit_nested_body(&mut self, id: hir::BodyId) {
-        if self.tcx.typeck_root_def_id(self.tcx.hir_body_owner_def_id(id).to_def_id())
-            == self.root_def_id
-        {
-            let body = self.tcx.hir_body(id);
-            self.visit_body(body);
-        }
-    }
-
-    fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
-        if let hir::ExprKind::Closure(&hir::Closure {
-            def_id,
-            kind: hir::ClosureKind::Coroutine(_),
-            ..
-        }) = ex.kind
-        {
-            self.stalled_coroutines.push(def_id);
-        }
-        intravisit::walk_expr(self, ex);
-    }
-}
-
 pub(super) fn provide(providers: &mut Providers) {
-    *providers = Providers { opaque_types_defined_by, stalled_generators_within, ..*providers };
+    *providers = Providers { opaque_types_defined_by, ..*providers };
 }
diff --git a/compiler/rustc_ty_utils/src/stalled_generators.rs b/compiler/rustc_ty_utils/src/stalled_generators.rs
new file mode 100644
index 00000000000..8b45e8b0f6f
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/stalled_generators.rs
@@ -0,0 +1,54 @@
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit;
+use rustc_hir::intravisit::Visitor;
+use rustc_middle::query::Providers;
+use rustc_middle::ty::{self, TyCtxt};
+
+fn stalled_generators_within<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    item: LocalDefId,
+) -> &'tcx ty::List<LocalDefId> {
+    if !tcx.next_trait_solver_globally() {
+        return ty::List::empty();
+    }
+
+    let body = tcx.hir_body_owned_by(item);
+    let mut collector =
+        StalledGeneratorVisitor { tcx, root_def_id: item.to_def_id(), stalled_coroutines: vec![] };
+    collector.visit_body(body);
+    tcx.mk_local_def_ids(&collector.stalled_coroutines)
+}
+
+struct StalledGeneratorVisitor<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    root_def_id: DefId,
+    stalled_coroutines: Vec<LocalDefId>,
+}
+
+impl<'tcx> Visitor<'tcx> for StalledGeneratorVisitor<'tcx> {
+    fn visit_nested_body(&mut self, id: hir::BodyId) {
+        if self.tcx.typeck_root_def_id(self.tcx.hir_body_owner_def_id(id).to_def_id())
+            == self.root_def_id
+        {
+            let body = self.tcx.hir_body(id);
+            self.visit_body(body);
+        }
+    }
+
+    fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
+        if let hir::ExprKind::Closure(&hir::Closure {
+            def_id,
+            kind: hir::ClosureKind::Coroutine(_),
+            ..
+        }) = ex.kind
+        {
+            self.stalled_coroutines.push(def_id);
+        }
+        intravisit::walk_expr(self, ex);
+    }
+}
+
+pub(super) fn provide(providers: &mut Providers) {
+    *providers = Providers { stalled_generators_within, ..*providers };
+}
diff --git a/compiler/rustc_type_ir/src/infer_ctxt.rs b/compiler/rustc_type_ir/src/infer_ctxt.rs
index 3570de064cb..8fa56c35999 100644
--- a/compiler/rustc_type_ir/src/infer_ctxt.rs
+++ b/compiler/rustc_type_ir/src/infer_ctxt.rs
@@ -65,7 +65,7 @@ pub enum TypingMode<I: Interner> {
     ///     let x: <() as Assoc>::Output = true;
     /// }
     /// ```
-    Analysis { defining_opaque_types: I::LocalDefIds, stalled_generators: I::LocalDefIds },
+    Analysis { defining_opaque_types_and_generators: I::LocalDefIds },
     /// The behavior during MIR borrowck is identical to `TypingMode::Analysis`
     /// except that the initial value for opaque types is the type computed during
     /// HIR typeck with unique unconstrained region inference variables.
@@ -94,25 +94,24 @@ pub enum TypingMode<I: Interner> {
 impl<I: Interner> TypingMode<I> {
     /// Analysis outside of a body does not define any opaque types.
     pub fn non_body_analysis() -> TypingMode<I> {
-        TypingMode::Analysis {
-            defining_opaque_types: Default::default(),
-            stalled_generators: Default::default(),
-        }
+        TypingMode::Analysis { defining_opaque_types_and_generators: Default::default() }
     }
 
     pub fn typeck_for_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
         TypingMode::Analysis {
-            defining_opaque_types: cx.opaque_types_defined_by(body_def_id),
-            stalled_generators: cx.stalled_generators_within(body_def_id),
+            defining_opaque_types_and_generators: cx
+                .opaque_types_and_generators_defined_by(body_def_id),
         }
     }
 
     /// While typechecking a body, we need to be able to define the opaque
     /// types defined by that body.
+    ///
+    /// FIXME: This will be removed because it's generally not correct to define
+    /// opaques outside of HIR typeck.
     pub fn analysis_in_body(cx: I, body_def_id: I::LocalDefId) -> TypingMode<I> {
         TypingMode::Analysis {
-            defining_opaque_types: cx.opaque_types_defined_by(body_def_id),
-            stalled_generators: Default::default(),
+            defining_opaque_types_and_generators: cx.opaque_types_defined_by(body_def_id),
         }
     }
 
diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs
index afd7b88c256..ab38556589e 100644
--- a/compiler/rustc_type_ir/src/interner.rs
+++ b/compiler/rustc_type_ir/src/interner.rs
@@ -332,7 +332,10 @@ pub trait Interner:
 
     fn opaque_types_defined_by(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds;
 
-    fn stalled_generators_within(self, defining_anchor: Self::LocalDefId) -> Self::LocalDefIds;
+    fn opaque_types_and_generators_defined_by(
+        self,
+        defining_anchor: Self::LocalDefId,
+    ) -> Self::LocalDefIds;
 }
 
 /// Imagine you have a function `F: FnOnce(&[T]) -> R`, plus an iterator `iter`
diff --git a/tests/ui/async-await/async-closures/is-not-fn.next.stderr b/tests/ui/async-await/async-closures/is-not-fn.next.stderr
index 0fab1c15f27..970970a9151 100644
--- a/tests/ui/async-await/async-closures/is-not-fn.next.stderr
+++ b/tests/ui/async-await/async-closures/is-not-fn.next.stderr
@@ -1,13 +1,11 @@
-error[E0271]: expected `{async closure@is-not-fn.rs:8:14}` to return `()`, but it returns `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
+error[E0271]: type mismatch resolving `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25} == ()`
   --> $DIR/is-not-fn.rs:8:14
    |
 LL |     needs_fn(async || {});
-   |     -------- ^^^^^^^^^^^ expected `()`, found `async` closure body
+   |     -------- ^^^^^^^^^^^ types differ
    |     |
    |     required by a bound introduced by this call
    |
-   = note:         expected unit type `()`
-           found `async` closure body `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25}`
 note: required by a bound in `needs_fn`
   --> $DIR/is-not-fn.rs:7:25
    |
diff --git a/tests/ui/async-await/async-closures/is-not-fn.rs b/tests/ui/async-await/async-closures/is-not-fn.rs
index e5ab4742dab..c09ccb3fc2b 100644
--- a/tests/ui/async-await/async-closures/is-not-fn.rs
+++ b/tests/ui/async-await/async-closures/is-not-fn.rs
@@ -6,5 +6,6 @@
 fn main() {
     fn needs_fn(x: impl FnOnce()) {}
     needs_fn(async || {});
-    //~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()`
+    //[current]~^ ERROR expected `{async closure@is-not-fn.rs:8:14}` to return `()`
+    //[next]~^^ ERROR type mismatch resolving `{async closure body@$DIR/is-not-fn.rs:8:23: 8:25} == ()`
 }
diff --git a/tests/ui/coroutine/clone-rpit.next.stderr b/tests/ui/coroutine/clone-rpit.next.stderr
deleted file mode 100644
index 213e9e908f5..00000000000
--- a/tests/ui/coroutine/clone-rpit.next.stderr
+++ /dev/null
@@ -1,47 +0,0 @@
-error[E0391]: cycle detected when type-checking `foo`
-  --> $DIR/clone-rpit.rs:13:1
-   |
-LL | pub fn foo<'a, 'b>() -> impl Clone {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: ...which requires coroutine witness types for `foo::{closure#0}`...
-  --> $DIR/clone-rpit.rs:15:5
-   |
-LL |     move |_: ()| {
-   |     ^^^^^^^^^^^^
-note: ...which requires promoting constants in MIR for `foo::{closure#0}`...
-  --> $DIR/clone-rpit.rs:15:5
-   |
-LL |     move |_: ()| {
-   |     ^^^^^^^^^^^^
-note: ...which requires checking if `foo::{closure#0}` contains FFI-unwind calls...
-  --> $DIR/clone-rpit.rs:15:5
-   |
-LL |     move |_: ()| {
-   |     ^^^^^^^^^^^^
-note: ...which requires building MIR for `foo::{closure#0}`...
-  --> $DIR/clone-rpit.rs:15:5
-   |
-LL |     move |_: ()| {
-   |     ^^^^^^^^^^^^
-note: ...which requires match-checking `foo::{closure#0}`...
-  --> $DIR/clone-rpit.rs:15:5
-   |
-LL |     move |_: ()| {
-   |     ^^^^^^^^^^^^
-note: ...which requires type-checking `foo::{closure#0}`...
-  --> $DIR/clone-rpit.rs:15:5
-   |
-LL |     move |_: ()| {
-   |     ^^^^^^^^^^^^
-   = note: ...which again requires type-checking `foo`, completing the cycle
-note: cycle used when match-checking `foo`
-  --> $DIR/clone-rpit.rs:13:1
-   |
-LL | pub fn foo<'a, 'b>() -> impl Clone {
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0391`.
diff --git a/tests/ui/coroutine/clone-rpit.rs b/tests/ui/coroutine/clone-rpit.rs
index 66569b4f427..3882564639b 100644
--- a/tests/ui/coroutine/clone-rpit.rs
+++ b/tests/ui/coroutine/clone-rpit.rs
@@ -1,8 +1,7 @@
 //@ revisions: current next
 //@ ignore-compare-mode-next-solver (explicit revisions)
 //@[next] compile-flags: -Znext-solver
-//@[current] check-pass
-//@[next] known-bug: trait-system-refactor-initiative#82
+//@ check-pass
 
 #![feature(coroutines, coroutine_trait, coroutine_clone)]