From 2be9ffc1afd0623ab0697cbaca5693b80c6d362c Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Tue, 10 Dec 2024 22:08:44 +0000
Subject: [PATCH 1/2] Add derived causes for host effect predicates

---
 compiler/rustc_middle/src/traits/mod.rs       | 89 +++++++++++++++----
 compiler/rustc_middle/src/ty/predicate.rs     | 22 +++++
 .../src/solve/effect_goals.rs                 |  2 +-
 .../traits/fulfillment_errors.rs              | 11 +--
 .../src/error_reporting/traits/suggestions.rs | 57 +++++++++++-
 .../src/solve/fulfill.rs                      | 73 ++++++++++++++-
 .../src/traits/effects.rs                     | 21 ++++-
 compiler/rustc_type_ir/src/binder.rs          |  1 +
 .../const-generics/issues/issue-88119.stderr  | 18 ++++
 ...type-const-bound-usage-fail.current.stderr |  2 +-
 ...oc-type-const-bound-usage-fail.next.stderr |  6 +-
 .../assoc-type-const-bound-usage-fail.rs      |  6 +-
 .../const-traits/const-opaque.no.stderr       |  2 +-
 tests/ui/traits/const-traits/const-opaque.rs  |  2 +-
 .../item-bound-entailment-fails.stderr        |  5 ++
 15 files changed, 278 insertions(+), 39 deletions(-)

diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs
index 99211c1f92440..db2bb8a7248a6 100644
--- a/compiler/rustc_middle/src/traits/mod.rs
+++ b/compiler/rustc_middle/src/traits/mod.rs
@@ -125,6 +125,15 @@ impl<'tcx> ObligationCause<'tcx> {
         self
     }
 
+    pub fn derived_host_cause(
+        mut self,
+        parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+        variant: impl FnOnce(DerivedHostCause<'tcx>) -> ObligationCauseCode<'tcx>,
+    ) -> ObligationCause<'tcx> {
+        self.code = variant(DerivedHostCause { parent_host_pred, parent_code: self.code }).into();
+        self
+    }
+
     pub fn to_constraint_category(&self) -> ConstraintCategory<'tcx> {
         match self.code() {
             ObligationCauseCode::MatchImpl(cause, _) => cause.to_constraint_category(),
@@ -278,6 +287,14 @@ pub enum ObligationCauseCode<'tcx> {
     /// Derived obligation for WF goals.
     WellFormedDerived(DerivedCause<'tcx>),
 
+    /// Derived obligation (i.e. `where` clause) on an user-provided impl
+    /// or a trait alias.
+    ImplDerivedHost(Box<ImplDerivedHostCause<'tcx>>),
+
+    /// Derived obligation (i.e. `where` clause) on an user-provided impl
+    /// or a trait alias.
+    BuiltinDerivedHost(DerivedHostCause<'tcx>),
+
     /// Derived obligation refined to point at a specific argument in
     /// a call or method expression.
     FunctionArg {
@@ -437,36 +454,38 @@ pub enum WellFormedLoc {
     },
 }
 
-#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
-#[derive(TypeVisitable, TypeFoldable)]
-pub struct ImplDerivedCause<'tcx> {
-    pub derived: DerivedCause<'tcx>,
-    /// The `DefId` of the `impl` that gave rise to the `derived` obligation.
-    /// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl,
-    /// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle
-    /// that exceptional case where appropriate.
-    pub impl_or_alias_def_id: DefId,
-    /// The index of the derived predicate in the parent impl's predicates.
-    pub impl_def_predicate_index: Option<usize>,
-    pub span: Span,
-}
-
 impl<'tcx> ObligationCauseCode<'tcx> {
     /// Returns the base obligation, ignoring derived obligations.
     pub fn peel_derives(&self) -> &Self {
         let mut base_cause = self;
-        while let Some((parent_code, _)) = base_cause.parent() {
+        while let Some(parent_code) = base_cause.parent() {
             base_cause = parent_code;
         }
         base_cause
     }
 
+    pub fn parent(&self) -> Option<&Self> {
+        match self {
+            ObligationCauseCode::FunctionArg { parent_code, .. } => Some(parent_code),
+            ObligationCauseCode::BuiltinDerived(derived)
+            | ObligationCauseCode::WellFormedDerived(derived)
+            | ObligationCauseCode::ImplDerived(box ImplDerivedCause { derived, .. }) => {
+                Some(&derived.parent_code)
+            }
+            ObligationCauseCode::BuiltinDerivedHost(derived)
+            | ObligationCauseCode::ImplDerivedHost(box ImplDerivedHostCause { derived, .. }) => {
+                Some(&derived.parent_code)
+            }
+            _ => None,
+        }
+    }
+
     /// Returns the base obligation and the base trait predicate, if any, ignoring
     /// derived obligations.
     pub fn peel_derives_with_predicate(&self) -> (&Self, Option<ty::PolyTraitPredicate<'tcx>>) {
         let mut base_cause = self;
         let mut base_trait_pred = None;
-        while let Some((parent_code, parent_pred)) = base_cause.parent() {
+        while let Some((parent_code, parent_pred)) = base_cause.parent_with_predicate() {
             base_cause = parent_code;
             if let Some(parent_pred) = parent_pred {
                 base_trait_pred = Some(parent_pred);
@@ -476,7 +495,7 @@ impl<'tcx> ObligationCauseCode<'tcx> {
         (base_cause, base_trait_pred)
     }
 
-    pub fn parent(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
+    pub fn parent_with_predicate(&self) -> Option<(&Self, Option<ty::PolyTraitPredicate<'tcx>>)> {
         match self {
             ObligationCauseCode::FunctionArg { parent_code, .. } => Some((parent_code, None)),
             ObligationCauseCode::BuiltinDerived(derived)
@@ -573,6 +592,42 @@ pub struct DerivedCause<'tcx> {
     pub parent_code: InternedObligationCauseCode<'tcx>,
 }
 
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
+pub struct ImplDerivedCause<'tcx> {
+    pub derived: DerivedCause<'tcx>,
+    /// The `DefId` of the `impl` that gave rise to the `derived` obligation.
+    /// If the `derived` obligation arose from a trait alias, which conceptually has a synthetic impl,
+    /// then this will be the `DefId` of that trait alias. Care should therefore be taken to handle
+    /// that exceptional case where appropriate.
+    pub impl_or_alias_def_id: DefId,
+    /// The index of the derived predicate in the parent impl's predicates.
+    pub impl_def_predicate_index: Option<usize>,
+    pub span: Span,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
+pub struct DerivedHostCause<'tcx> {
+    /// The trait predicate of the parent obligation that led to the
+    /// current obligation. Note that only trait obligations lead to
+    /// derived obligations, so we just store the trait predicate here
+    /// directly.
+    pub parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+
+    /// The parent trait had this cause.
+    pub parent_code: InternedObligationCauseCode<'tcx>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
+#[derive(TypeVisitable, TypeFoldable)]
+pub struct ImplDerivedHostCause<'tcx> {
+    pub derived: DerivedHostCause<'tcx>,
+    /// The `DefId` of the `impl` that gave rise to the `derived` obligation.
+    pub impl_def_id: DefId,
+    pub span: Span,
+}
+
 #[derive(Clone, Debug, PartialEq, Eq, TypeVisitable)]
 pub enum SelectionError<'tcx> {
     /// The trait is not implemented.
diff --git a/compiler/rustc_middle/src/ty/predicate.rs b/compiler/rustc_middle/src/ty/predicate.rs
index 3ecaa3e22d39c..32d6455e82557 100644
--- a/compiler/rustc_middle/src/ty/predicate.rs
+++ b/compiler/rustc_middle/src/ty/predicate.rs
@@ -634,6 +634,28 @@ impl<'tcx> UpcastFrom<TyCtxt<'tcx>, PolyProjectionPredicate<'tcx>> for Clause<'t
     }
 }
 
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>>
+    for Predicate<'tcx>
+{
+    fn upcast_from(
+        from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+        tcx: TyCtxt<'tcx>,
+    ) -> Self {
+        from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx)
+    }
+}
+
+impl<'tcx> UpcastFrom<TyCtxt<'tcx>, ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>>
+    for Clause<'tcx>
+{
+    fn upcast_from(
+        from: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+        tcx: TyCtxt<'tcx>,
+    ) -> Self {
+        from.map_bound(ty::ClauseKind::HostEffect).upcast(tcx)
+    }
+}
+
 impl<'tcx> UpcastFrom<TyCtxt<'tcx>, NormalizesTo<'tcx>> for Predicate<'tcx> {
     fn upcast_from(from: NormalizesTo<'tcx>, tcx: TyCtxt<'tcx>) -> Self {
         PredicateKind::NormalizesTo(from).upcast(tcx)
diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index ce7552e30f0fe..281796e84b169 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -353,7 +353,7 @@ where
 
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             ecx.add_goals(
-                GoalSource::Misc,
+                GoalSource::ImplWhereBound,
                 const_conditions.into_iter().map(|trait_ref| {
                     goal.with(
                         cx,
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 f9a3040832601..1678aa8c657b4 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
@@ -753,7 +753,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     applied_do_not_recommend = true;
                 }
             }
-            if let Some((parent_cause, _parent_pred)) = base_cause.parent() {
+            if let Some(parent_cause) = base_cause.parent() {
                 base_cause = parent_cause.clone();
             } else {
                 break;
@@ -778,7 +778,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 trait_ref.skip_binder().args.type_at(1).to_opt_closure_kind()
             && !found_kind.extends(expected_kind)
         {
-            if let Some((_, Some(parent))) = obligation.cause.code().parent() {
+            if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() {
                 // If we have a derived obligation, then the parent will be a `AsyncFn*` goal.
                 trait_ref = parent.to_poly_trait_ref();
             } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } =
@@ -926,7 +926,8 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let Some(typeck) = &self.typeck_results else {
             return false;
         };
-        let Some((ObligationCauseCode::QuestionMark, Some(y))) = obligation.cause.code().parent()
+        let Some((ObligationCauseCode::QuestionMark, Some(y))) =
+            obligation.cause.code().parent_with_predicate()
         else {
             return false;
         };
@@ -1179,7 +1180,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
         let mut code = obligation.cause.code();
         let mut pred = obligation.predicate.as_trait_clause();
-        while let Some((next_code, next_pred)) = code.parent() {
+        while let Some((next_code, next_pred)) = code.parent_with_predicate() {
             if let Some(pred) = pred {
                 self.enter_forall(pred, |pred| {
                     diag.note(format!(
@@ -2095,7 +2096,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let mut code = obligation.cause.code();
         let mut trait_pred = trait_predicate;
         let mut peeled = false;
-        while let Some((parent_code, parent_trait_pred)) = code.parent() {
+        while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {
             code = parent_code;
             if let Some(parent_trait_pred) = parent_trait_pred {
                 trait_pred = parent_trait_pred;
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 007a220ae697f..9d85ca1dd4dd7 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs
@@ -464,7 +464,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
 
             // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)
             let mut real_trait_pred = trait_pred;
-            while let Some((parent_code, parent_trait_pred)) = code.parent() {
+            while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {
                 code = parent_code;
                 if let Some(parent_trait_pred) = parent_trait_pred {
                     real_trait_pred = parent_trait_pred;
@@ -1447,7 +1447,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
         let mut span = obligation.cause.span;
         let mut trait_pred = trait_pred;
         let mut code = obligation.cause.code();
-        while let Some((c, Some(parent_trait_pred))) = code.parent() {
+        while let Some((c, Some(parent_trait_pred))) = code.parent_with_predicate() {
             // We want the root obligation, in order to detect properly handle
             // `for _ in &mut &mut vec![] {}`.
             code = c;
@@ -3470,6 +3470,59 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                     )
                 });
             }
+            ObligationCauseCode::ImplDerivedHost(ref data) => {
+                let self_ty =
+                    self.resolve_vars_if_possible(data.derived.parent_host_pred.self_ty());
+                let msg = format!(
+                    "required for `{self_ty}` to implement `{} {}`",
+                    data.derived.parent_host_pred.skip_binder().constness,
+                    data.derived
+                        .parent_host_pred
+                        .map_bound(|pred| pred.trait_ref)
+                        .print_only_trait_path(),
+                );
+                match tcx.hir().get_if_local(data.impl_def_id) {
+                    Some(Node::Item(hir::Item {
+                        kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
+                        ..
+                    })) => {
+                        let mut spans = vec![self_ty.span];
+                        spans.extend(of_trait.as_ref().map(|t| t.path.span));
+                        let mut spans: MultiSpan = spans.into();
+                        spans.push_span_label(data.span, "unsatisfied trait bound introduced here");
+                        err.span_note(spans, msg);
+                    }
+                    _ => {
+                        err.note(msg);
+                    }
+                }
+                ensure_sufficient_stack(|| {
+                    self.note_obligation_cause_code(
+                        body_id,
+                        err,
+                        data.derived.parent_host_pred,
+                        param_env,
+                        &data.derived.parent_code,
+                        obligated_types,
+                        seen_requirements,
+                        long_ty_file,
+                    )
+                });
+            }
+            ObligationCauseCode::BuiltinDerivedHost(ref data) => {
+                ensure_sufficient_stack(|| {
+                    self.note_obligation_cause_code(
+                        body_id,
+                        err,
+                        data.parent_host_pred,
+                        param_env,
+                        &data.parent_code,
+                        obligated_types,
+                        seen_requirements,
+                        long_ty_file,
+                    )
+                });
+            }
             ObligationCauseCode::WellFormedDerived(ref data) => {
                 let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred);
                 let parent_predicate = parent_trait_ref;
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 2b2623a050ec9..7aa82c52c9f26 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -474,8 +474,11 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
         // for normalizes-to.
         let pred_kind = goal.goal().predicate.kind();
         let child_mode = match pred_kind.skip_binder() {
-            ty::PredicateKind::Clause(ty::ClauseKind::Trait(parent_trait_pred)) => {
-                ChildMode::Trait(pred_kind.rebind(parent_trait_pred))
+            ty::PredicateKind::Clause(ty::ClauseKind::Trait(pred)) => {
+                ChildMode::Trait(pred_kind.rebind(pred))
+            }
+            ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(pred)) => {
+                ChildMode::Host(pred_kind.rebind(pred))
             }
             ty::PredicateKind::NormalizesTo(normalizes_to)
                 if matches!(
@@ -492,6 +495,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
         };
 
         let mut impl_where_bound_count = 0;
+        let mut impl_const_condition_bound_count = 0;
         for nested_goal in candidate.instantiate_nested_goals(self.span()) {
             trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
 
@@ -504,7 +508,7 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
 
             let obligation;
             match (child_mode, nested_goal.source()) {
-                (ChildMode::Trait(_), GoalSource::Misc) => {
+                (ChildMode::Trait(_) | ChildMode::Host(_), GoalSource::Misc) => {
                     continue;
                 }
                 (ChildMode::Trait(parent_trait_pred), GoalSource::ImplWhereBound) => {
@@ -517,6 +521,16 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                     ));
                     impl_where_bound_count += 1;
                 }
+                (ChildMode::Host(parent_host_pred), GoalSource::ImplWhereBound) => {
+                    obligation = make_obligation(derive_host_cause(
+                        tcx,
+                        candidate.kind(),
+                        self.obligation.cause.clone(),
+                        impl_const_condition_bound_count,
+                        parent_host_pred,
+                    ));
+                    impl_const_condition_bound_count += 1;
+                }
                 // Skip over a higher-ranked predicate.
                 (_, GoalSource::InstantiateHigherRanked) => {
                     obligation = self.obligation.clone();
@@ -575,6 +589,10 @@ enum ChildMode<'tcx> {
     // and skip all `GoalSource::Misc`, which represent useless obligations
     // such as alias-eq which may not hold.
     Trait(ty::PolyTraitPredicate<'tcx>),
+    // Try to derive an `ObligationCause::{ImplDerived,BuiltinDerived}`,
+    // and skip all `GoalSource::Misc`, which represent useless obligations
+    // such as alias-eq which may not hold.
+    Host(ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>),
     // Skip trying to derive an `ObligationCause` from this obligation, and
     // report *all* sub-obligations as if they came directly from the parent
     // obligation.
@@ -616,3 +634,52 @@ fn derive_cause<'tcx>(
     };
     cause
 }
+
+fn derive_host_cause<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    candidate_kind: inspect::ProbeKind<TyCtxt<'tcx>>,
+    mut cause: ObligationCause<'tcx>,
+    idx: usize,
+    parent_host_pred: ty::Binder<'tcx, ty::HostEffectPredicate<'tcx>>,
+) -> ObligationCause<'tcx> {
+    match candidate_kind {
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::Impl(impl_def_id),
+            result: _,
+        } => {
+            if let Some((_, span)) = tcx
+                .predicates_of(impl_def_id)
+                .instantiate_identity(tcx)
+                .into_iter()
+                .chain(tcx.const_conditions(impl_def_id).instantiate_identity(tcx).into_iter().map(
+                    |(trait_ref, span)| {
+                        (
+                            trait_ref.to_host_effect_clause(
+                                tcx,
+                                parent_host_pred.skip_binder().constness,
+                            ),
+                            span,
+                        )
+                    },
+                ))
+                .nth(idx)
+            {
+                cause =
+                    cause.derived_host_cause(parent_host_pred, |derived| {
+                        ObligationCauseCode::ImplDerivedHost(Box::new(
+                            traits::ImplDerivedHostCause { derived, impl_def_id, span },
+                        ))
+                    })
+            }
+        }
+        inspect::ProbeKind::TraitCandidate {
+            source: CandidateSource::BuiltinImpl(..),
+            result: _,
+        } => {
+            cause =
+                cause.derived_host_cause(parent_host_pred, ObligationCauseCode::BuiltinDerivedHost);
+        }
+        _ => {}
+    };
+    cause
+}
diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs
index 91484ef99dbb8..0ac24eb54e748 100644
--- a/compiler/rustc_trait_selection/src/traits/effects.rs
+++ b/compiler/rustc_trait_selection/src/traits/effects.rs
@@ -1,6 +1,8 @@
 use rustc_hir as hir;
 use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes};
-use rustc_infer::traits::{ImplSource, Obligation, PredicateObligation};
+use rustc_infer::traits::{
+    ImplDerivedHostCause, ImplSource, Obligation, ObligationCauseCode, PredicateObligation,
+};
 use rustc_middle::span_bug;
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_middle::ty::{self, TypingMode};
@@ -248,9 +250,22 @@ fn evaluate_host_effect_from_selection_candiate<'tcx>(
                         tcx.const_conditions(impl_.impl_def_id)
                             .instantiate(tcx, impl_.args)
                             .into_iter()
-                            .map(|(trait_ref, _)| {
-                                obligation.with(
+                            .map(|(trait_ref, span)| {
+                                Obligation::new(
                                     tcx,
+                                    obligation.cause.clone().derived_host_cause(
+                                        ty::Binder::dummy(obligation.predicate),
+                                        |derived| {
+                                            ObligationCauseCode::ImplDerivedHost(Box::new(
+                                                ImplDerivedHostCause {
+                                                    derived,
+                                                    impl_def_id: impl_.impl_def_id,
+                                                    span,
+                                                },
+                                            ))
+                                        },
+                                    ),
+                                    obligation.param_env,
                                     trait_ref
                                         .to_host_effect_clause(tcx, obligation.predicate.constness),
                                 )
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index 47447af22158d..cb59bc608c203 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -89,6 +89,7 @@ impl_binder_encode_decode! {
     ty::ExistentialPredicate<I>,
     ty::TraitRef<I>,
     ty::ExistentialTraitRef<I>,
+    ty::HostEffectPredicate<I>,
 }
 
 impl<I: Interner, T> Binder<I, T>
diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr
index 370244fe8c984..f219c90849a26 100644
--- a/tests/ui/const-generics/issues/issue-88119.stderr
+++ b/tests/ui/const-generics/issues/issue-88119.stderr
@@ -11,12 +11,30 @@ error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{con
    |
 LL | impl<T: ?Sized + ConstName> const ConstName for &T
    |                                                 ^^ cannot normalize `<&T as ConstName>::{constant#0}`
+   |
+note: required for `&T` to implement `~const ConstName`
+  --> $DIR/issue-88119.rs:19:35
+   |
+LL | impl<T: ?Sized + ConstName> const ConstName for &T
+   |                                   ^^^^^^^^^     ^^
+LL | where
+LL |     [(); name_len::<T>()]:,
+   |     --------------------- unsatisfied trait bound introduced here
 
 error[E0284]: type annotations needed: cannot normalize `<&mut T as ConstName>::{constant#0}`
   --> $DIR/issue-88119.rs:26:49
    |
 LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
    |                                                 ^^^^^^ cannot normalize `<&mut T as ConstName>::{constant#0}`
+   |
+note: required for `&mut T` to implement `~const ConstName`
+  --> $DIR/issue-88119.rs:26:35
+   |
+LL | impl<T: ?Sized + ConstName> const ConstName for &mut T
+   |                                   ^^^^^^^^^     ^^^^^^
+LL | where
+LL |     [(); name_len::<T>()]:,
+   |     --------------------- unsatisfied trait bound introduced here
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr
index 9c29a894749e9..ccd4af99cf41b 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr
@@ -5,7 +5,7 @@ LL |     T::Assoc::func();
    |     ^^^^^^^^
 
 error[E0277]: the trait bound `T: ~const Trait` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5
+  --> $DIR/assoc-type-const-bound-usage-fail.rs:20:5
    |
 LL |     <T as Trait>::Assoc::func();
    |     ^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr
index 9c29a894749e9..ee1b663b99981 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr
@@ -1,11 +1,11 @@
-error[E0277]: the trait bound `T: ~const Trait` is not satisfied
+error[E0277]: the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
   --> $DIR/assoc-type-const-bound-usage-fail.rs:17:5
    |
 LL |     T::Assoc::func();
    |     ^^^^^^^^
 
-error[E0277]: the trait bound `T: ~const Trait` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5
+error[E0277]: the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
+  --> $DIR/assoc-type-const-bound-usage-fail.rs:20:5
    |
 LL |     <T as Trait>::Assoc::func();
    |     ^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs
index 3761fea19684d..224ebb2daf16e 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs
@@ -15,9 +15,11 @@ trait Trait {
 
 const fn unqualified<T: Trait>() {
     T::Assoc::func();
-    //~^ ERROR the trait bound `T: ~const Trait` is not satisfied
+    //[current]~^ ERROR the trait bound `T: ~const Trait` is not satisfied
+    //[next]~^^ ERROR the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
     <T as Trait>::Assoc::func();
-    //~^ ERROR the trait bound `T: ~const Trait` is not satisfied
+    //[current]~^ ERROR the trait bound `T: ~const Trait` is not satisfied
+    //[next]~^^ ERROR the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
 }
 
 const fn works<T: ~const Trait>() {
diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr
index 1278e1257467b..33aa1065df356 100644
--- a/tests/ui/traits/const-traits/const-opaque.no.stderr
+++ b/tests/ui/traits/const-traits/const-opaque.no.stderr
@@ -12,7 +12,7 @@ note: required by a bound in `bar`
 LL | const fn bar<T: ~const Foo>(t: T) -> impl ~const Foo {
    |                 ^^^^^^ required by this bound in `bar`
 
-error[E0277]: the trait bound `(): const Foo` is not satisfied
+error[E0277]: the trait bound `impl Foo: const Foo` is not satisfied
   --> $DIR/const-opaque.rs:33:12
    |
 LL |     opaque.method();
diff --git a/tests/ui/traits/const-traits/const-opaque.rs b/tests/ui/traits/const-traits/const-opaque.rs
index 96cdd7d9f2619..bfcadd521a54e 100644
--- a/tests/ui/traits/const-traits/const-opaque.rs
+++ b/tests/ui/traits/const-traits/const-opaque.rs
@@ -31,7 +31,7 @@ const _: () = {
     let opaque = bar(());
     //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied
     opaque.method();
-    //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied
+    //[no]~^ ERROR the trait bound `impl Foo: const Foo` is not satisfied
     std::mem::forget(opaque);
 };
 
diff --git a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr
index 3fc6f58470947..0d53bc5897ef3 100644
--- a/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr
+++ b/tests/ui/traits/const-traits/item-bound-entailment-fails.stderr
@@ -16,6 +16,11 @@ error[E0277]: the trait bound `T: ~const Bar` is not satisfied
 LL |     type Assoc<T> = C<T>
    |                     ^^^^
    |
+note: required for `C<T>` to implement `~const Bar`
+  --> $DIR/item-bound-entailment-fails.rs:14:15
+   |
+LL | impl<T> const Bar for C<T> where T: ~const Bar {}
+   |               ^^^     ^^^^          ------ unsatisfied trait bound introduced here
 note: required by a bound in `Foo::Assoc`
   --> $DIR/item-bound-entailment-fails.rs:5:20
    |

From ebdf19a8bbd1efb533bab35b0a2b327e4a33282f Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Mon, 23 Dec 2024 20:17:53 +0000
Subject: [PATCH 2/2] Recurse on GAT where clauses in fulfillment error proof
 tree visitor

---
 .../src/solve/effect_goals.rs                      |  4 ++--
 .../rustc_trait_selection/src/solve/fulfill.rs     | 14 +++++++++-----
 compiler/rustc_type_ir/src/solve/mod.rs            |  4 ++++
 ...oc-type-const-bound-usage-fail-2.current.stderr |  2 +-
 ...assoc-type-const-bound-usage-fail-2.next.stderr |  6 +++---
 .../assoc-type-const-bound-usage-fail-2.rs         |  6 ++----
 ...ssoc-type-const-bound-usage-fail.current.stderr |  2 +-
 .../assoc-type-const-bound-usage-fail.next.stderr  |  6 +++---
 .../assoc-type-const-bound-usage-fail.rs           |  6 ++----
 .../ui/traits/const-traits/const-opaque.no.stderr  |  2 +-
 tests/ui/traits/const-traits/const-opaque.rs       |  2 +-
 11 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
index 281796e84b169..7669a305d58d1 100644
--- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs
@@ -103,7 +103,7 @@ where
                 |ecx| {
                     // Const conditions must hold for the implied const bound to hold.
                     ecx.add_goals(
-                        GoalSource::Misc,
+                        GoalSource::AliasBoundConstCondition,
                         cx.const_conditions(alias_ty.def_id)
                             .iter_instantiated(cx, alias_ty.args)
                             .map(|trait_ref| {
@@ -353,7 +353,7 @@ where
 
         ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {
             ecx.add_goals(
-                GoalSource::ImplWhereBound,
+                GoalSource::AliasBoundConstCondition,
                 const_conditions.into_iter().map(|trait_ref| {
                     goal.with(
                         cx,
diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs
index 7aa82c52c9f26..986176aca770e 100644
--- a/compiler/rustc_trait_selection/src/solve/fulfill.rs
+++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs
@@ -413,6 +413,7 @@ impl<'tcx> BestObligation<'tcx> {
                                     matches!(
                                         nested_goal.source(),
                                         GoalSource::ImplWhereBound
+                                            | GoalSource::AliasBoundConstCondition
                                             | GoalSource::InstantiateHigherRanked
                                             | GoalSource::AliasWellFormed
                                     ) && match self.consider_ambiguities {
@@ -495,7 +496,6 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
         };
 
         let mut impl_where_bound_count = 0;
-        let mut impl_const_condition_bound_count = 0;
         for nested_goal in candidate.instantiate_nested_goals(self.span()) {
             trace!(nested_goal = ?(nested_goal.goal(), nested_goal.source(), nested_goal.result()));
 
@@ -521,21 +521,25 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> {
                     ));
                     impl_where_bound_count += 1;
                 }
-                (ChildMode::Host(parent_host_pred), GoalSource::ImplWhereBound) => {
+                (
+                    ChildMode::Host(parent_host_pred),
+                    GoalSource::ImplWhereBound | GoalSource::AliasBoundConstCondition,
+                ) => {
                     obligation = make_obligation(derive_host_cause(
                         tcx,
                         candidate.kind(),
                         self.obligation.cause.clone(),
-                        impl_const_condition_bound_count,
+                        impl_where_bound_count,
                         parent_host_pred,
                     ));
-                    impl_const_condition_bound_count += 1;
+                    impl_where_bound_count += 1;
                 }
                 // Skip over a higher-ranked predicate.
                 (_, GoalSource::InstantiateHigherRanked) => {
                     obligation = self.obligation.clone();
                 }
-                (ChildMode::PassThrough, _) | (_, GoalSource::AliasWellFormed) => {
+                (ChildMode::PassThrough, _)
+                | (_, GoalSource::AliasWellFormed | GoalSource::AliasBoundConstCondition) => {
                     obligation = make_obligation(self.obligation.cause.clone());
                 }
             }
diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs
index 8fe512026e5d9..1ae904d50e066 100644
--- a/compiler/rustc_type_ir/src/solve/mod.rs
+++ b/compiler/rustc_type_ir/src/solve/mod.rs
@@ -68,6 +68,10 @@ pub enum GoalSource {
     /// FIXME(-Znext-solver=coinductive): Explain how and why this
     /// changes whether cycles are coinductive.
     ImplWhereBound,
+    /// Const conditions that need to hold for `~const` alias bounds to hold.
+    ///
+    /// FIXME(-Znext-solver=coinductive): Are these even coinductive?
+    AliasBoundConstCondition,
     /// Instantiating a higher-ranked goal and re-proving it.
     InstantiateHigherRanked,
     /// Predicate required for an alias projection to be well-formed.
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr
index 03da9159bea0b..4cd87002e4910 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.current.stderr
@@ -5,7 +5,7 @@ LL |     T::Assoc::<U>::func();
    |     ^^^^^^^^^^^^^
 
 error[E0277]: the trait bound `U: ~const Other` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5
+  --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5
    |
 LL |     <T as Trait>::Assoc::<U>::func();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr
index ce58b486a16ef..4cd87002e4910 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.next.stderr
@@ -1,11 +1,11 @@
-error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
+error[E0277]: the trait bound `U: ~const Other` is not satisfied
   --> $DIR/assoc-type-const-bound-usage-fail-2.rs:24:5
    |
 LL |     T::Assoc::<U>::func();
    |     ^^^^^^^^^^^^^
 
-error[E0277]: the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage-fail-2.rs:27:5
+error[E0277]: the trait bound `U: ~const Other` is not satisfied
+  --> $DIR/assoc-type-const-bound-usage-fail-2.rs:26:5
    |
 LL |     <T as Trait>::Assoc::<U>::func();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs
index bdd98eaf541f8..e1c30b5361124 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail-2.rs
@@ -22,11 +22,9 @@ trait Other {}
 
 const fn fails<T: ~const Trait, U: Other>() {
     T::Assoc::<U>::func();
-    //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied
-    //[next]~^^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
+    //~^ ERROR the trait bound `U: ~const Other` is not satisfied
     <T as Trait>::Assoc::<U>::func();
-    //[current]~^ ERROR the trait bound `U: ~const Other` is not satisfied
-    //[next]~^^ ERROR the trait bound `<T as Trait>::Assoc<U>: ~const Trait` is not satisfied
+    //~^ ERROR the trait bound `U: ~const Other` is not satisfied
 }
 
 const fn works<T: ~const Trait, U: ~const Other>() {
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr
index ccd4af99cf41b..9c29a894749e9 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.current.stderr
@@ -5,7 +5,7 @@ LL |     T::Assoc::func();
    |     ^^^^^^^^
 
 error[E0277]: the trait bound `T: ~const Trait` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage-fail.rs:20:5
+  --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5
    |
 LL |     <T as Trait>::Assoc::func();
    |     ^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr
index ee1b663b99981..9c29a894749e9 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.next.stderr
@@ -1,11 +1,11 @@
-error[E0277]: the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
+error[E0277]: the trait bound `T: ~const Trait` is not satisfied
   --> $DIR/assoc-type-const-bound-usage-fail.rs:17:5
    |
 LL |     T::Assoc::func();
    |     ^^^^^^^^
 
-error[E0277]: the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
-  --> $DIR/assoc-type-const-bound-usage-fail.rs:20:5
+error[E0277]: the trait bound `T: ~const Trait` is not satisfied
+  --> $DIR/assoc-type-const-bound-usage-fail.rs:19:5
    |
 LL |     <T as Trait>::Assoc::func();
    |     ^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs
index 224ebb2daf16e..3761fea19684d 100644
--- a/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs
+++ b/tests/ui/traits/const-traits/assoc-type-const-bound-usage-fail.rs
@@ -15,11 +15,9 @@ trait Trait {
 
 const fn unqualified<T: Trait>() {
     T::Assoc::func();
-    //[current]~^ ERROR the trait bound `T: ~const Trait` is not satisfied
-    //[next]~^^ ERROR the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
+    //~^ ERROR the trait bound `T: ~const Trait` is not satisfied
     <T as Trait>::Assoc::func();
-    //[current]~^ ERROR the trait bound `T: ~const Trait` is not satisfied
-    //[next]~^^ ERROR the trait bound `<T as Trait>::Assoc: ~const Trait` is not satisfied
+    //~^ ERROR the trait bound `T: ~const Trait` is not satisfied
 }
 
 const fn works<T: ~const Trait>() {
diff --git a/tests/ui/traits/const-traits/const-opaque.no.stderr b/tests/ui/traits/const-traits/const-opaque.no.stderr
index 33aa1065df356..1278e1257467b 100644
--- a/tests/ui/traits/const-traits/const-opaque.no.stderr
+++ b/tests/ui/traits/const-traits/const-opaque.no.stderr
@@ -12,7 +12,7 @@ note: required by a bound in `bar`
 LL | const fn bar<T: ~const Foo>(t: T) -> impl ~const Foo {
    |                 ^^^^^^ required by this bound in `bar`
 
-error[E0277]: the trait bound `impl Foo: const Foo` is not satisfied
+error[E0277]: the trait bound `(): const Foo` is not satisfied
   --> $DIR/const-opaque.rs:33:12
    |
 LL |     opaque.method();
diff --git a/tests/ui/traits/const-traits/const-opaque.rs b/tests/ui/traits/const-traits/const-opaque.rs
index bfcadd521a54e..96cdd7d9f2619 100644
--- a/tests/ui/traits/const-traits/const-opaque.rs
+++ b/tests/ui/traits/const-traits/const-opaque.rs
@@ -31,7 +31,7 @@ const _: () = {
     let opaque = bar(());
     //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied
     opaque.method();
-    //[no]~^ ERROR the trait bound `impl Foo: const Foo` is not satisfied
+    //[no]~^ ERROR the trait bound `(): const Foo` is not satisfied
     std::mem::forget(opaque);
 };