about summary refs log tree commit diff
path: root/compiler/rustc_trait_selection/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src')
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs4
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs22
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs7
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs27
-rw-r--r--compiler/rustc_trait_selection/src/infer.rs2
-rw-r--r--compiler/rustc_trait_selection/src/lib.rs2
-rw-r--r--compiler/rustc_trait_selection/src/solve.rs5
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill.rs117
-rw-r--r--compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs16
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalize.rs104
-rw-r--r--compiler/rustc_trait_selection/src/traits/fulfill.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs240
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs3
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs2
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs4
-rw-r--r--compiler/rustc_trait_selection/src/traits/wf.rs79
16 files changed, 389 insertions, 247 deletions
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs
index 1c3e570b676..d8b405e904c 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/call_kind.rs
@@ -81,9 +81,7 @@ pub fn call_kind<'tcx>(
         }
     });
 
-    let fn_call = parent.and_then(|p| {
-        lang_items::FN_TRAITS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
-    });
+    let fn_call = parent.filter(|&p| tcx.fn_trait_kind_from_def_id(p).is_some());
 
     let operator = if !from_hir_call && let Some(p) = parent {
         lang_items::OPERATORS.iter().filter_map(|&l| tcx.lang_items().get(l)).find(|&id| id == p)
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 5648021f613..4330f1f2292 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -146,7 +146,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             && leaf_trait_predicate.def_id() != root_pred.def_id()
                             // The root trait is not `Unsize`, as to avoid talking about it in
                             // `tests/ui/coercion/coerce-issue-49593-box-never.rs`.
-                            && Some(root_pred.def_id()) != self.tcx.lang_items().unsize_trait()
+                            && !self.tcx.is_lang_item(root_pred.def_id(), LangItem::Unsize)
                         {
                             (
                                 self.resolve_vars_if_possible(
@@ -1523,19 +1523,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     return None;
                 };
 
-                let trait_assoc_item = self.tcx.opt_associated_item(proj.projection_term.def_id)?;
-                let trait_assoc_ident = trait_assoc_item.ident(self.tcx);
-
                 let mut associated_items = vec![];
                 self.tcx.for_each_relevant_impl(
                     self.tcx.trait_of_item(proj.projection_term.def_id)?,
                     proj.projection_term.self_ty(),
                     |impl_def_id| {
                         associated_items.extend(
-                            self.tcx
-                                .associated_items(impl_def_id)
-                                .in_definition_order()
-                                .find(|assoc| assoc.ident(self.tcx) == trait_assoc_ident),
+                            self.tcx.associated_items(impl_def_id).in_definition_order().find(
+                                |assoc| {
+                                    assoc.trait_item_def_id == Some(proj.projection_term.def_id)
+                                },
+                            ),
                         );
                     },
                 );
@@ -2274,10 +2272,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // auto-traits or fundamental traits that might not be exactly what
         // the user might expect to be presented with. Instead this is
         // useful for less general traits.
-        if peeled
-            && !self.tcx.trait_is_auto(def_id)
-            && !self.tcx.lang_items().iter().any(|(_, id)| id == def_id)
-        {
+        if peeled && !self.tcx.trait_is_auto(def_id) && self.tcx.as_lang_item(def_id).is_none() {
             let impl_candidates = self.find_similar_impl_candidates(trait_pred);
             self.report_similar_impl_candidates(
                 &impl_candidates,
@@ -3013,8 +3008,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         // This shouldn't be common unless manually implementing one of the
         // traits manually, but don't make it more confusing when it does
         // happen.
-        if Some(expected_trait_ref.def_id) != self.tcx.lang_items().coroutine_trait() && not_tupled
-        {
+        if !self.tcx.is_lang_item(expected_trait_ref.def_id, LangItem::Coroutine) && not_tupled {
             return Ok(self.report_and_explain_type_error(
                 TypeTrace::trait_refs(&obligation.cause, expected_trait_ref, found_trait_ref),
                 obligation.param_env,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
index 7d95a7b3fed..de251ae2893 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -3844,12 +3844,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     );
                     if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder()
                         && let ty::ClauseKind::Trait(pred) = clause
-                        && [
-                            tcx.lang_items().fn_once_trait(),
-                            tcx.lang_items().fn_mut_trait(),
-                            tcx.lang_items().fn_trait(),
-                        ]
-                        .contains(&Some(pred.def_id()))
+                        && tcx.fn_trait_kind_from_def_id(pred.def_id()).is_some()
                     {
                         if let [stmt, ..] = block.stmts
                             && let hir::StmtKind::Semi(value) = stmt.kind
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 756d9a57b93..1063115ed23 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -9,7 +9,7 @@ use rustc_errors::{
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
-use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, IsAnonInPath, Node};
+use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
 use rustc_macros::{Diagnostic, Subdiagnostic};
 use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
 use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt};
@@ -551,19 +551,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
 
             impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
                 fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
-                    let make_suggestion = |lifetime: &hir::Lifetime| {
-                        if lifetime.is_anon_in_path == IsAnonInPath::Yes
-                            && lifetime.ident.span.is_empty()
-                        {
-                            format!("{}, ", self.suggestion_param_name)
-                        } else if lifetime.ident.name == kw::UnderscoreLifetime
-                            && lifetime.ident.span.is_empty()
-                        {
-                            format!("{} ", self.suggestion_param_name)
-                        } else {
-                            self.suggestion_param_name.clone()
-                        }
-                    };
                     match ty.kind {
                         hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
                             for segment in path.segments {
@@ -572,7 +559,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
                                         matches!(
                                             arg,
                                             hir::GenericArg::Lifetime(lifetime)
-                                                if lifetime.is_anon_in_path == IsAnonInPath::Yes
+                                                if lifetime.is_syntactically_hidden()
                                         )
                                     }) {
                                         self.suggestions.push((
@@ -591,10 +578,10 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
                                             if let hir::GenericArg::Lifetime(lifetime) = arg
                                                 && lifetime.is_anonymous()
                                             {
-                                                self.suggestions.push((
-                                                    lifetime.ident.span,
-                                                    make_suggestion(lifetime),
-                                                ));
+                                                self.suggestions.push(
+                                                    lifetime
+                                                        .suggestion(&self.suggestion_param_name),
+                                                );
                                             }
                                         }
                                     }
@@ -602,7 +589,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
                             }
                         }
                         hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
-                            self.suggestions.push((lifetime.ident.span, make_suggestion(lifetime)));
+                            self.suggestions.push(lifetime.suggestion(&self.suggestion_param_name));
                         }
                         _ => {}
                     }
diff --git a/compiler/rustc_trait_selection/src/infer.rs b/compiler/rustc_trait_selection/src/infer.rs
index 84ac229b743..0dab3adadb0 100644
--- a/compiler/rustc_trait_selection/src/infer.rs
+++ b/compiler/rustc_trait_selection/src/infer.rs
@@ -34,7 +34,7 @@ impl<'tcx> InferCtxt<'tcx> {
 
         // FIXME(#132279): This should be removed as it causes us to incorrectly
         // handle opaques in their defining scope.
-        if !(param_env, ty).has_infer() {
+        if !self.next_trait_solver() && !(param_env, ty).has_infer() {
             return self.tcx.type_is_copy_modulo_regions(self.typing_env(param_env), ty);
         }
 
diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs
index 93c11805304..7613a0cef52 100644
--- a/compiler/rustc_trait_selection/src/lib.rs
+++ b/compiler/rustc_trait_selection/src/lib.rs
@@ -14,6 +14,7 @@
 #![allow(internal_features)]
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
+#![cfg_attr(bootstrap, feature(let_chains))]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![doc(rust_logo)]
 #![feature(assert_matches)]
@@ -23,7 +24,6 @@
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]
 #![feature(iterator_try_reduce)]
-#![feature(let_chains)]
 #![feature(never_type)]
 #![feature(rustdoc_internals)]
 #![feature(try_blocks)]
diff --git a/compiler/rustc_trait_selection/src/solve.rs b/compiler/rustc_trait_selection/src/solve.rs
index d425ab50ae0..0c2451a80a7 100644
--- a/compiler/rustc_trait_selection/src/solve.rs
+++ b/compiler/rustc_trait_selection/src/solve.rs
@@ -9,5 +9,8 @@ mod select;
 pub(crate) use delegate::SolverDelegate;
 pub use fulfill::{FulfillmentCtxt, NextSolverError};
 pub(crate) use normalize::deeply_normalize_for_diagnostics;
-pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes};
+pub use normalize::{
+    deeply_normalize, deeply_normalize_with_skipped_universes,
+    deeply_normalize_with_skipped_universes_and_ambiguous_goals,
+};
 pub use select::InferCtxtSelectExt;
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 192e632a2d5..848d0646d00 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -1,18 +1,26 @@
 use std::marker::PhantomData;
 use std::mem;
+use std::ops::ControlFlow;
 
 use rustc_data_structures::thinvec::ExtractIf;
+use rustc_hir::def_id::LocalDefId;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::{
     FromSolverError, PredicateObligation, PredicateObligations, TraitEngine,
 };
+use rustc_middle::ty::{
+    self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, TypingMode,
+};
 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::*;
 use super::Certainty;
 use super::delegate::SolverDelegate;
+use super::inspect::{self, ProofTreeInferCtxtExt};
 use crate::traits::{FulfillmentError, ScrubbedTraitError};
 
 mod derive_errors;
@@ -39,7 +47,7 @@ pub struct FulfillmentCtxt<'tcx, E: 'tcx> {
     _errors: PhantomData<E>,
 }
 
-#[derive(Default)]
+#[derive(Default, Debug)]
 struct ObligationStorage<'tcx> {
     /// Obligations which resulted in an overflow in fulfillment itself.
     ///
@@ -55,20 +63,23 @@ impl<'tcx> ObligationStorage<'tcx> {
         self.pending.push(obligation);
     }
 
+    fn has_pending_obligations(&self) -> bool {
+        !self.pending.is_empty() || !self.overflowed.is_empty()
+    }
+
     fn clone_pending(&self) -> PredicateObligations<'tcx> {
         let mut obligations = self.pending.clone();
         obligations.extend(self.overflowed.iter().cloned());
         obligations
     }
 
-    fn take_pending(&mut self) -> PredicateObligations<'tcx> {
-        let mut obligations = mem::take(&mut self.pending);
-        obligations.append(&mut self.overflowed);
-        obligations
-    }
-
-    fn unstalled_for_select(&mut self) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'tcx {
-        mem::take(&mut self.pending).into_iter()
+    fn drain_pending(
+        &mut self,
+        cond: impl Fn(&PredicateObligation<'tcx>) -> bool,
+    ) -> PredicateObligations<'tcx> {
+        let (unstalled, pending) = mem::take(&mut self.pending).into_iter().partition(cond);
+        self.pending = pending;
+        unstalled
     }
 
     fn on_fulfillment_overflow(&mut self, infcx: &InferCtxt<'tcx>) {
@@ -160,7 +171,7 @@ where
             }
 
             let mut has_changed = false;
-            for obligation in self.obligations.unstalled_for_select() {
+            for obligation in self.obligations.drain_pending(|_| true) {
                 let goal = obligation.as_goal();
                 let result = <&SolverDelegate<'tcx>>::from(infcx)
                     .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span)
@@ -196,15 +207,95 @@ where
     }
 
     fn has_pending_obligations(&self) -> bool {
-        !self.obligations.pending.is_empty() || !self.obligations.overflowed.is_empty()
+        self.obligations.has_pending_obligations()
     }
 
     fn pending_obligations(&self) -> PredicateObligations<'tcx> {
         self.obligations.clone_pending()
     }
 
-    fn drain_unstalled_obligations(&mut self, _: &InferCtxt<'tcx>) -> PredicateObligations<'tcx> {
-        self.obligations.take_pending()
+    fn drain_stalled_obligations_for_coroutines(
+        &mut self,
+        infcx: &InferCtxt<'tcx>,
+    ) -> PredicateObligations<'tcx> {
+        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,
+                            cache: Default::default(),
+                        },
+                    )
+                    .is_break()
+            })
+        })
+    }
+}
+
+/// 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,
+    cache: DelayedSet<Ty<'tcx>>,
+}
+
+impl<'tcx> inspect::ProofTreeVisitor<'tcx> for StalledOnCoroutines<'tcx> {
+    type Result = ControlFlow<()>;
+
+    fn span(&self) -> rustc_span::Span {
+        self.span
+    }
+
+    fn visit_goal(&mut self, inspect_goal: &super::inspect::InspectGoal<'_, 'tcx>) -> Self::Result {
+        inspect_goal.goal().predicate.visit_with(self)?;
+
+        if let Some(candidate) = inspect_goal.unique_applicable_candidate() {
+            candidate.visit_nested_no_probe(self)
+        } else {
+            ControlFlow::Continue(())
+        }
+    }
+}
+
+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))
+        {
+            return ControlFlow::Break(());
+        }
+
+        ty.super_visit_with(self)
     }
 }
 
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
index d8dcd12aecb..f6c650c68c0 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs
@@ -1,5 +1,6 @@
 use std::ops::ControlFlow;
 
+use rustc_hir::LangItem;
 use rustc_infer::infer::InferCtxt;
 use rustc_infer::traits::solve::{CandidateSource, GoalSource, MaybeCause};
 use rustc_infer::traits::{
@@ -109,10 +110,16 @@ pub(super) fn fulfillment_error_for_stalled<'tcx>(
                 false,
             ),
             Ok((_, Certainty::Yes)) => {
-                bug!("did not expect successful goal when collecting ambiguity errors")
+                bug!(
+                    "did not expect successful goal when collecting ambiguity errors for `{:?}`",
+                    infcx.resolve_vars_if_possible(root_obligation.predicate),
+                )
             }
             Err(_) => {
-                bug!("did not expect selection error when collecting ambiguity errors")
+                bug!(
+                    "did not expect selection error when collecting ambiguity errors for `{:?}`",
+                    infcx.resolve_vars_if_possible(root_obligation.predicate),
+                )
             }
         }
     });
@@ -452,9 +459,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
         // We do this as a separate loop so that we do not choose to tell the user about some nested
         // goal before we encounter a `T: FnPtr` nested goal.
         for nested_goal in &nested_goals {
-            if let Some(fn_ptr_trait) = tcx.lang_items().fn_ptr_trait()
-                && let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
-                && poly_trait_pred.def_id() == fn_ptr_trait
+            if let Some(poly_trait_pred) = nested_goal.goal().predicate.as_trait_clause()
+                && tcx.is_lang_item(poly_trait_pred.def_id(), LangItem::FnPtrTrait)
                 && let Err(NoSolution) = nested_goal.result()
             {
                 return ControlFlow::Break(self.obligation.clone());
diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs
index 79fb044a67f..5f1e63ab225 100644
--- a/compiler/rustc_trait_selection/src/solve/normalize.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalize.rs
@@ -1,10 +1,10 @@
 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;
 use rustc_infer::infer::at::At;
+use rustc_infer::traits::solve::Goal;
 use rustc_infer::traits::{FromSolverError, Obligation, TraitEngine};
 use rustc_middle::traits::ObligationCause;
 use rustc_middle::ty::{
@@ -45,11 +45,37 @@ where
     T: TypeFoldable<TyCtxt<'tcx>>,
     E: FromSolverError<'tcx, NextSolverError<'tcx>>,
 {
+    let (value, goals) =
+        deeply_normalize_with_skipped_universes_and_ambiguous_goals(at, value, universes)?;
+    assert_eq!(goals, vec![]);
+
+    Ok(value)
+}
+
+/// Deeply normalize all aliases in `value`. This does not handle inference and expects
+/// its input to be already fully resolved.
+///
+/// Additionally takes a list of universes which represents the binders which have been
+/// entered before passing `value` to the function. This is currently needed for
+/// `normalize_erasing_regions`, which skips binders as it walks through a type.
+///
+/// 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,
+    universes: Vec<Option<UniverseIndex>>,
+) -> Result<(T, Vec<Goal<'tcx, ty::Predicate<'tcx>>>), Vec<E>>
+where
+    T: TypeFoldable<TyCtxt<'tcx>>,
+    E: FromSolverError<'tcx, NextSolverError<'tcx>>,
+{
     let fulfill_cx = FulfillmentCtxt::new(at.infcx);
     let mut folder =
-        NormalizationFolder { at, fulfill_cx, depth: 0, universes, _errors: PhantomData };
-
-    value.try_fold_with(&mut folder)
+        NormalizationFolder { at, fulfill_cx, depth: 0, universes, stalled_goals: vec![] };
+    let value = value.try_fold_with(&mut folder)?;
+    let errors = folder.fulfill_cx.select_all_or_error(at.infcx);
+    if errors.is_empty() { Ok((value, folder.stalled_goals)) } else { Err(errors) }
 }
 
 struct NormalizationFolder<'me, 'tcx, E> {
@@ -57,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>
@@ -98,10 +124,7 @@ where
         );
 
         self.fulfill_cx.register_predicate_obligation(infcx, obligation);
-        let errors = self.fulfill_cx.select_all_or_error(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.
@@ -139,7 +162,7 @@ where
 
         let result = if infcx.predicate_may_hold(&obligation) {
             self.fulfill_cx.register_predicate_obligation(infcx, obligation);
-            let errors = self.fulfill_cx.select_all_or_error(infcx);
+            let errors = self.fulfill_cx.select_where_possible(infcx);
             if !errors.is_empty() {
                 return Err(errors);
             }
@@ -152,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>
@@ -254,27 +298,31 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for DeeplyNormalizeForDiagnosticsFolder<'_,
 
     fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
         let infcx = self.at.infcx;
-        infcx
-            .commit_if_ok(|_| {
-                deeply_normalize_with_skipped_universes(
-                    self.at,
-                    ty,
-                    vec![None; ty.outer_exclusive_binder().as_usize()],
-                )
-            })
-            .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ty.super_fold_with(self))
+        let result =
+            infcx.commit_if_ok(|_| {
+                deeply_normalize_with_skipped_universes_and_ambiguous_goals::<
+                    _,
+                    ScrubbedTraitError<'tcx>,
+                >(self.at, ty, vec![None; ty.outer_exclusive_binder().as_usize()])
+            });
+        match result {
+            Ok((ty, _)) => ty,
+            Err(_) => ty.super_fold_with(self),
+        }
     }
 
     fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
         let infcx = self.at.infcx;
-        infcx
-            .commit_if_ok(|_| {
-                deeply_normalize_with_skipped_universes(
-                    self.at,
-                    ct,
-                    vec![None; ct.outer_exclusive_binder().as_usize()],
-                )
-            })
-            .unwrap_or_else(|_: Vec<ScrubbedTraitError<'tcx>>| ct.super_fold_with(self))
+        let result =
+            infcx.commit_if_ok(|_| {
+                deeply_normalize_with_skipped_universes_and_ambiguous_goals::<
+                    _,
+                    ScrubbedTraitError<'tcx>,
+                >(self.at, ct, vec![None; ct.outer_exclusive_binder().as_usize()])
+            });
+        match result {
+            Ok((ct, _)) => ct,
+            Err(_) => ct.super_fold_with(self),
+        }
     }
 }
diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs
index 1b76d48e431..a11f8d3a9ec 100644
--- a/compiler/rustc_trait_selection/src/traits/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs
@@ -162,7 +162,7 @@ where
         self.select(selcx)
     }
 
-    fn drain_unstalled_obligations(
+    fn drain_stalled_obligations_for_coroutines(
         &mut self,
         infcx: &InferCtxt<'tcx>,
     ) -> PredicateObligations<'tcx> {
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index 0dce504903c..99fa791b375 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -965,36 +965,38 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                 let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
 
                 let tcx = selcx.tcx();
-                let lang_items = selcx.tcx().lang_items();
-                if [
-                    lang_items.coroutine_trait(),
-                    lang_items.future_trait(),
-                    lang_items.iterator_trait(),
-                    lang_items.async_iterator_trait(),
-                    lang_items.fn_trait(),
-                    lang_items.fn_mut_trait(),
-                    lang_items.fn_once_trait(),
-                    lang_items.async_fn_trait(),
-                    lang_items.async_fn_mut_trait(),
-                    lang_items.async_fn_once_trait(),
-                ]
-                .contains(&Some(trait_ref.def_id))
-                {
-                    true
-                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncFnKindHelper) {
-                    // FIXME(async_closures): Validity constraints here could be cleaned up.
-                    if obligation.predicate.args.type_at(0).is_ty_var()
-                        || obligation.predicate.args.type_at(4).is_ty_var()
-                        || obligation.predicate.args.type_at(5).is_ty_var()
-                    {
-                        candidate_set.mark_ambiguous();
-                        true
-                    } else {
-                        obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some()
-                            && obligation.predicate.args.type_at(1).to_opt_closure_kind().is_some()
+                match selcx.tcx().as_lang_item(trait_ref.def_id) {
+                    Some(
+                        LangItem::Coroutine
+                        | LangItem::Future
+                        | LangItem::Iterator
+                        | LangItem::AsyncIterator
+                        | LangItem::Fn
+                        | LangItem::FnMut
+                        | LangItem::FnOnce
+                        | LangItem::AsyncFn
+                        | LangItem::AsyncFnMut
+                        | LangItem::AsyncFnOnce,
+                    ) => true,
+                    Some(LangItem::AsyncFnKindHelper) => {
+                        // FIXME(async_closures): Validity constraints here could be cleaned up.
+                        if obligation.predicate.args.type_at(0).is_ty_var()
+                            || obligation.predicate.args.type_at(4).is_ty_var()
+                            || obligation.predicate.args.type_at(5).is_ty_var()
+                        {
+                            candidate_set.mark_ambiguous();
+                            true
+                        } else {
+                            obligation.predicate.args.type_at(0).to_opt_closure_kind().is_some()
+                                && obligation
+                                    .predicate
+                                    .args
+                                    .type_at(1)
+                                    .to_opt_closure_kind()
+                                    .is_some()
+                        }
                     }
-                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::DiscriminantKind) {
-                    match self_ty.kind() {
+                    Some(LangItem::DiscriminantKind) => match self_ty.kind() {
                         ty::Bool
                         | ty::Char
                         | ty::Int(_)
@@ -1031,9 +1033,8 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Placeholder(..)
                         | ty::Infer(..)
                         | ty::Error(_) => false,
-                    }
-                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::AsyncDestruct) {
-                    match self_ty.kind() {
+                    },
+                    Some(LangItem::AsyncDestruct) => match self_ty.kind() {
                         ty::Bool
                         | ty::Char
                         | ty::Int(_)
@@ -1068,101 +1069,104 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         | ty::Placeholder(..)
                         | ty::Infer(_)
                         | ty::Error(_) => false,
-                    }
-                } else if tcx.is_lang_item(trait_ref.def_id, LangItem::PointeeTrait) {
-                    let tail = selcx.tcx().struct_tail_raw(
-                        self_ty,
-                        |ty| {
-                            // We throw away any obligations we get from this, since we normalize
-                            // and confirm these obligations once again during confirmation
-                            normalize_with_depth(
-                                selcx,
-                                obligation.param_env,
-                                obligation.cause.clone(),
-                                obligation.recursion_depth + 1,
-                                ty,
-                            )
-                            .value
-                        },
-                        || {},
-                    );
+                    },
+                    Some(LangItem::PointeeTrait) => {
+                        let tail = selcx.tcx().struct_tail_raw(
+                            self_ty,
+                            |ty| {
+                                // We throw away any obligations we get from this, since we normalize
+                                // and confirm these obligations once again during confirmation
+                                normalize_with_depth(
+                                    selcx,
+                                    obligation.param_env,
+                                    obligation.cause.clone(),
+                                    obligation.recursion_depth + 1,
+                                    ty,
+                                )
+                                .value
+                            },
+                            || {},
+                        );
 
-                    match tail.kind() {
-                        ty::Bool
-                        | ty::Char
-                        | ty::Int(_)
-                        | ty::Uint(_)
-                        | ty::Float(_)
-                        | ty::Str
-                        | ty::Array(..)
-                        | ty::Pat(..)
-                        | ty::Slice(_)
-                        | ty::RawPtr(..)
-                        | ty::Ref(..)
-                        | ty::FnDef(..)
-                        | ty::FnPtr(..)
-                        | ty::Dynamic(..)
-                        | ty::Closure(..)
-                        | ty::CoroutineClosure(..)
-                        | ty::Coroutine(..)
-                        | ty::CoroutineWitness(..)
-                        | ty::Never
-                        // Extern types have unit metadata, according to RFC 2850
-                        | ty::Foreign(_)
-                        // If returned by `struct_tail` this is a unit struct
-                        // without any fields, or not a struct, and therefore is Sized.
-                        | ty::Adt(..)
-                        // If returned by `struct_tail` this is the empty tuple.
-                        | ty::Tuple(..)
-                        // Integers and floats are always Sized, and so have unit type metadata.
-                        | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..))
-                        // This happens if we reach the recursion limit when finding the struct tail.
-                        | ty::Error(..) => true,
-
-                        // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
-                        // Otherwise, type parameters, opaques, and unnormalized projections have
-                        // unit metadata if they're known (e.g. by the param_env) to be sized.
-                        ty::Param(_) | ty::Alias(..)
-                            if self_ty != tail
-                                || selcx.infcx.predicate_must_hold_modulo_regions(
-                                    &obligation.with(
-                                        selcx.tcx(),
-                                        ty::TraitRef::new(
+                        match tail.kind() {
+                            ty::Bool
+                            | ty::Char
+                            | ty::Int(_)
+                            | ty::Uint(_)
+                            | ty::Float(_)
+                            | ty::Str
+                            | ty::Array(..)
+                            | ty::Pat(..)
+                            | ty::Slice(_)
+                            | ty::RawPtr(..)
+                            | ty::Ref(..)
+                            | ty::FnDef(..)
+                            | ty::FnPtr(..)
+                            | ty::Dynamic(..)
+                            | ty::Closure(..)
+                            | ty::CoroutineClosure(..)
+                            | ty::Coroutine(..)
+                            | ty::CoroutineWitness(..)
+                            | ty::Never
+                            // Extern types have unit metadata, according to RFC 2850
+                            | ty::Foreign(_)
+                            // If returned by `struct_tail` this is a unit struct
+                            // without any fields, or not a struct, and therefore is Sized.
+                            | ty::Adt(..)
+                            // If returned by `struct_tail` this is the empty tuple.
+                            | ty::Tuple(..)
+                            // Integers and floats are always Sized, and so have unit type metadata.
+                            | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..))
+                            // This happens if we reach the recursion limit when finding the struct tail.
+                            | ty::Error(..) => true,
+
+                            // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
+                            // Otherwise, type parameters, opaques, and unnormalized projections have
+                            // unit metadata if they're known (e.g. by the param_env) to be sized.
+                            ty::Param(_) | ty::Alias(..)
+                                if self_ty != tail
+                                    || selcx.infcx.predicate_must_hold_modulo_regions(
+                                        &obligation.with(
                                             selcx.tcx(),
-                                            selcx.tcx().require_lang_item(
-                                                LangItem::Sized,
-                                                Some(obligation.cause.span),
+                                            ty::TraitRef::new(
+                                                selcx.tcx(),
+                                                selcx.tcx().require_lang_item(
+                                                    LangItem::Sized,
+                                                    Some(obligation.cause.span),
+                                                ),
+                                                [self_ty],
                                             ),
-                                            [self_ty],
                                         ),
-                                    ),
-                                ) =>
-                        {
-                            true
-                        }
+                                    ) =>
+                            {
+                                true
+                            }
 
-                        ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
+                            ty::UnsafeBinder(_) => todo!("FIXME(unsafe_binder)"),
 
-                        // FIXME(compiler-errors): are Bound and Placeholder types ever known sized?
-                        ty::Param(_)
-                        | ty::Alias(..)
-                        | ty::Bound(..)
-                        | ty::Placeholder(..)
-                        | ty::Infer(..) => {
-                            if tail.has_infer_types() {
-                                candidate_set.mark_ambiguous();
+                            // FIXME(compiler-errors): are Bound and Placeholder types ever known sized?
+                            ty::Param(_)
+                            | ty::Alias(..)
+                            | ty::Bound(..)
+                            | ty::Placeholder(..)
+                            | ty::Infer(..) => {
+                                if tail.has_infer_types() {
+                                    candidate_set.mark_ambiguous();
+                                }
+                                false
                             }
-                            false
                         }
                     }
-                } else if tcx.trait_is_auto(trait_ref.def_id) {
-                    tcx.dcx().span_delayed_bug(
-                        tcx.def_span(obligation.predicate.def_id),
-                        "associated types not allowed on auto traits",
-                    );
-                    false
-                } else {
-                    bug!("unexpected builtin trait with associated type: {trait_ref:?}")
+                    _ if tcx.trait_is_auto(trait_ref.def_id) => {
+                        tcx.dcx().span_delayed_bug(
+                            tcx.def_span(obligation.predicate.def_id),
+                            "associated types not allowed on auto traits",
+                        );
+                        false
+                    }
+                    _ => {
+                        bug!("unexpected builtin trait with associated type: {trait_ref:?}")
+                    }
                 }
             }
             ImplSource::Param(..) => {
diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
index 4eecde00eaa..f059bd00768 100644
--- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs
@@ -117,8 +117,7 @@ fn relate_mir_and_user_args<'tcx>(
             CRATE_DEF_ID,
             ObligationCauseCode::AscribeUserTypeProvePredicate(predicate_span),
         );
-        let instantiated_predicate =
-            ocx.normalize(&cause.clone(), param_env, instantiated_predicate);
+        let instantiated_predicate = ocx.normalize(&cause, param_env, instantiated_predicate);
 
         ocx.register_obligation(Obligation::new(tcx, cause, param_env, instantiated_predicate));
     }
diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
index cf6d2bc151f..00101010f14 100644
--- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs
@@ -240,8 +240,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
             if !drcx.args_may_unify(obligation_args, bound_trait_ref.skip_binder().args) {
                 continue;
             }
-            // FIXME(oli-obk): it is suspicious that we are dropping the constness and
-            // polarity here.
             let wc = self.where_clause_may_apply(stack, bound_trait_ref)?;
             if wc.may_apply() {
                 candidates.vec.push(ParamCandidate(bound));
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index 4d88a23250a..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 }
+            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_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs
index 54b6c22b2d8..cad7e42fd82 100644
--- a/compiler/rustc_trait_selection/src/traits/wf.rs
+++ b/compiler/rustc_trait_selection/src/traits/wf.rs
@@ -1,3 +1,8 @@
+//! Core logic responsible for determining what it means for various type system
+//! primitives to be "well formed". Actually checking whether these primitives are
+//! well formed is performed elsewhere (e.g. during type checking or item well formedness
+//! checking).
+
 use std::iter;
 
 use rustc_hir as hir;
@@ -15,12 +20,13 @@ use tracing::{debug, instrument, trace};
 
 use crate::infer::InferCtxt;
 use crate::traits;
+
 /// Returns the set of obligations needed to make `arg` well-formed.
 /// If `arg` contains unresolved inference variables, this may include
 /// further WF obligations. However, if `arg` IS an unresolved
 /// inference variable, returns `None`, because we are not able to
-/// make any progress at all. This is to prevent "livelock" where we
-/// say "$0 is WF if $0 is WF".
+/// make any progress at all. This is to prevent cycles where we
+/// say "?0 is WF if ?0 is WF".
 pub fn obligations<'tcx>(
     infcx: &InferCtxt<'tcx>,
     param_env: ty::ParamEnv<'tcx>,
@@ -29,14 +35,14 @@ pub fn obligations<'tcx>(
     arg: GenericArg<'tcx>,
     span: Span,
 ) -> Option<PredicateObligations<'tcx>> {
-    // Handle the "livelock" case (see comment above) by bailing out if necessary.
+    // Handle the "cycle" case (see comment above) by bailing out if necessary.
     let arg = match arg.unpack() {
         GenericArgKind::Type(ty) => {
             match ty.kind() {
                 ty::Infer(ty::TyVar(_)) => {
                     let resolved_ty = infcx.shallow_resolve(ty);
                     if resolved_ty == ty {
-                        // No progress, bail out to prevent "livelock".
+                        // No progress, bail out to prevent cycles.
                         return None;
                     } else {
                         resolved_ty
@@ -51,7 +57,7 @@ pub fn obligations<'tcx>(
                 ty::ConstKind::Infer(_) => {
                     let resolved = infcx.shallow_resolve_const(ct);
                     if resolved == ct {
-                        // No progress.
+                        // No progress, bail out to prevent cycles.
                         return None;
                     } else {
                         resolved
@@ -74,7 +80,7 @@ pub fn obligations<'tcx>(
         recursion_depth,
         item: None,
     };
-    wf.compute(arg);
+    wf.add_wf_preds_for_generic_arg(arg);
     debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out);
 
     let result = wf.normalize(infcx);
@@ -97,7 +103,7 @@ pub fn unnormalized_obligations<'tcx>(
 
     // However, if `arg` IS an unresolved inference variable, returns `None`,
     // because we are not able to make any progress at all. This is to prevent
-    // "livelock" where we say "$0 is WF if $0 is WF".
+    // cycles where we say "?0 is WF if ?0 is WF".
     if arg.is_non_region_infer() {
         return None;
     }
@@ -115,7 +121,7 @@ pub fn unnormalized_obligations<'tcx>(
         recursion_depth: 0,
         item: None,
     };
-    wf.compute(arg);
+    wf.add_wf_preds_for_generic_arg(arg);
     Some(wf.out)
 }
 
@@ -140,7 +146,7 @@ pub fn trait_obligations<'tcx>(
         recursion_depth: 0,
         item: Some(item),
     };
-    wf.compute_trait_pred(trait_pred, Elaborate::All);
+    wf.add_wf_preds_for_trait_pred(trait_pred, Elaborate::All);
     debug!(obligations = ?wf.out);
     wf.normalize(infcx)
 }
@@ -171,7 +177,7 @@ pub fn clause_obligations<'tcx>(
     // It's ok to skip the binder here because wf code is prepared for it
     match clause.kind().skip_binder() {
         ty::ClauseKind::Trait(t) => {
-            wf.compute_trait_pred(t, Elaborate::None);
+            wf.add_wf_preds_for_trait_pred(t, Elaborate::None);
         }
         ty::ClauseKind::HostEffect(..) => {
             // Technically the well-formedness of this predicate is implied by
@@ -179,22 +185,22 @@ pub fn clause_obligations<'tcx>(
         }
         ty::ClauseKind::RegionOutlives(..) => {}
         ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => {
-            wf.compute(ty.into());
+            wf.add_wf_preds_for_generic_arg(ty.into());
         }
         ty::ClauseKind::Projection(t) => {
-            wf.compute_alias_term(t.projection_term);
-            wf.compute(t.term.into_arg());
+            wf.add_wf_preds_for_alias_term(t.projection_term);
+            wf.add_wf_preds_for_generic_arg(t.term.into_arg());
         }
         ty::ClauseKind::ConstArgHasType(ct, ty) => {
-            wf.compute(ct.into());
-            wf.compute(ty.into());
+            wf.add_wf_preds_for_generic_arg(ct.into());
+            wf.add_wf_preds_for_generic_arg(ty.into());
         }
         ty::ClauseKind::WellFormed(arg) => {
-            wf.compute(arg);
+            wf.add_wf_preds_for_generic_arg(arg);
         }
 
         ty::ClauseKind::ConstEvaluatable(ct) => {
-            wf.compute(ct.into());
+            wf.add_wf_preds_for_generic_arg(ct.into());
         }
     }
 
@@ -372,14 +378,18 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
     }
 
     /// Pushes the obligations required for `trait_ref` to be WF into `self.out`.
-    fn compute_trait_pred(&mut self, trait_pred: ty::TraitPredicate<'tcx>, elaborate: Elaborate) {
+    fn add_wf_preds_for_trait_pred(
+        &mut self,
+        trait_pred: ty::TraitPredicate<'tcx>,
+        elaborate: Elaborate,
+    ) {
         let tcx = self.tcx();
         let trait_ref = trait_pred.trait_ref;
 
         // Negative trait predicates don't require supertraits to hold, just
         // that their args are WF.
         if trait_pred.polarity == ty::PredicatePolarity::Negative {
-            self.compute_negative_trait_pred(trait_ref);
+            self.add_wf_preds_for_negative_trait_pred(trait_ref);
             return;
         }
 
@@ -445,15 +455,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 
     // Compute the obligations that are required for `trait_ref` to be WF,
     // given that it is a *negative* trait predicate.
-    fn compute_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) {
+    fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) {
         for arg in trait_ref.args {
-            self.compute(arg);
+            self.add_wf_preds_for_generic_arg(arg);
         }
     }
 
     /// Pushes the obligations required for an alias (except inherent) to be WF
     /// into `self.out`.
-    fn compute_alias_term(&mut self, data: ty::AliasTerm<'tcx>) {
+    fn add_wf_preds_for_alias_term(&mut self, data: ty::AliasTerm<'tcx>) {
         // A projection is well-formed if
         //
         // (a) its predicates hold (*)
@@ -478,13 +488,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         let obligations = self.nominal_obligations(data.def_id, data.args);
         self.out.extend(obligations);
 
-        self.compute_projection_args(data.args);
+        self.add_wf_preds_for_projection_args(data.args);
     }
 
     /// Pushes the obligations required for an inherent alias to be WF
     /// into `self.out`.
     // FIXME(inherent_associated_types): Merge this function with `fn compute_alias`.
-    fn compute_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
+    fn add_wf_preds_for_inherent_projection(&mut self, data: ty::AliasTy<'tcx>) {
         // An inherent projection is well-formed if
         //
         // (a) its predicates hold (*)
@@ -511,7 +521,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
         data.args.visit_with(self);
     }
 
-    fn compute_projection_args(&mut self, args: GenericArgsRef<'tcx>) {
+    fn add_wf_preds_for_projection_args(&mut self, args: GenericArgsRef<'tcx>) {
         let tcx = self.tcx();
         let cause = self.cause(ObligationCauseCode::WellFormed(None));
         let param_env = self.param_env;
@@ -557,7 +567,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
 
     /// Pushes all the predicates needed to validate that `ty` is WF into `out`.
     #[instrument(level = "debug", skip(self))]
-    fn compute(&mut self, arg: GenericArg<'tcx>) {
+    fn add_wf_preds_for_generic_arg(&mut self, arg: GenericArg<'tcx>) {
         arg.visit_with(self);
         debug!(?self.out);
     }
@@ -596,7 +606,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
             .collect()
     }
 
-    fn from_object_ty(
+    fn add_wf_preds_for_dyn_ty(
         &mut self,
         ty: Ty<'tcx>,
         data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
@@ -651,6 +661,13 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> {
                     outlives,
                 ));
             }
+
+            // We don't add any wf predicates corresponding to the trait ref's generic arguments
+            // which allows code like this to compile:
+            // ```rust
+            // trait Trait<T: Sized> {}
+            // fn foo(_: &dyn Trait<[u32]>) {}
+            // ```
         }
     }
 }
@@ -761,7 +778,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 self.out.extend(obligations);
             }
             ty::Alias(ty::Inherent, data) => {
-                self.compute_inherent_projection(data);
+                self.add_wf_preds_for_inherent_projection(data);
                 return; // Subtree handled by compute_inherent_projection.
             }
 
@@ -895,7 +912,7 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
                 //
                 // Here, we defer WF checking due to higher-ranked
                 // regions. This is perhaps not ideal.
-                self.from_object_ty(t, data, r);
+                self.add_wf_preds_for_dyn_ty(t, data, r);
 
                 // FIXME(#27579) RFC also considers adding trait
                 // obligations that don't refer to Self and
@@ -917,11 +934,11 @@ impl<'a, 'tcx> TypeVisitor<TyCtxt<'tcx>> for WfPredicates<'a, 'tcx> {
             // 1. Check if they have been resolved, and if so proceed with
             //    THAT type.
             // 2. If not, we've at least simplified things (e.g., we went
-            //    from `Vec<$0>: WF` to `$0: WF`), so we can
+            //    from `Vec?0>: WF` to `?0: WF`), so we can
             //    register a pending obligation and keep
             //    moving. (Goal is that an "inductive hypothesis"
             //    is satisfied to ensure termination.)
-            // See also the comment on `fn obligations`, describing "livelock"
+            // See also the comment on `fn obligations`, describing cycle
             // prevention, which happens before this can be reached.
             ty::Infer(_) => {
                 let cause = self.cause(ObligationCauseCode::WellFormed(None));