about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2025-09-23 17:33:24 +0200
committerlcnr <rust@lcnr.de>2025-09-26 16:33:15 +0200
commit148fd9ad3c434c26a952e01e37c35aa26cb8315c (patch)
treece2212f59abdb0e93ca76092ab1aaabd29d4cf8d /compiler
parent6b379b560df483307958f2a606a1dd514fff36ca (diff)
downloadrust-148fd9ad3c434c26a952e01e37c35aa26cb8315c.tar.gz
rust-148fd9ad3c434c26a952e01e37c35aa26cb8315c.zip
allow method calls on opaques
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs209
-rw-r--r--compiler/rustc_infer/src/infer/canonical/query_response.rs23
-rw-r--r--compiler/rustc_middle/src/traits/query.rs1
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs3
-rw-r--r--compiler/rustc_next_trait_solver/src/solve/mod.rs1
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs11
6 files changed, 216 insertions, 32 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index 3311c141055..e34ee6258b8 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -13,7 +13,7 @@ use rustc_hir::def::DefKind;
 use rustc_hir_analysis::autoderef::{self, Autoderef};
 use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse};
 use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt};
-use rustc_infer::traits::{ObligationCauseCode, query};
+use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query};
 use rustc_middle::middle::stability;
 use rustc_middle::ty::elaborate::supertrait_def_ids;
 use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, simplify_type};
@@ -30,6 +30,7 @@ use rustc_span::edit_distance::{
 use rustc_span::{DUMMY_SP, Ident, Span, Symbol, sym};
 use rustc_trait_selection::error_reporting::infer::need_type_info::TypeAnnotationNeeded;
 use rustc_trait_selection::infer::InferCtxtExt as _;
+use rustc_trait_selection::solve::Goal;
 use rustc_trait_selection::traits::query::CanonicalMethodAutoderefStepsGoal;
 use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
 use rustc_trait_selection::traits::query::method_autoderef::{
@@ -417,6 +418,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     steps: infcx.tcx.arena.alloc_from_iter([CandidateStep {
                         self_ty: self
                             .make_query_response_ignoring_pending_obligations(var_values, self_ty),
+                        self_ty_is_opaque: false,
                         autoderefs: 0,
                         from_unsafe_deref: false,
                         unsize: false,
@@ -590,6 +592,17 @@ pub(crate) fn method_autoderef_steps<'tcx>(
         }
     }
 
+    // We accept not-yet-defined opaque types in the autoderef
+    // chain to support recursive calls. We do error if the final
+    // infer var is not an opaque.
+    let self_ty_is_opaque = |ty: Ty<'_>| {
+        if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
+            infcx.has_opaques_with_sub_unified_hidden_type(vid)
+        } else {
+            false
+        }
+    };
+
     // If arbitrary self types is not enabled, we follow the chain of
     // `Deref<Target=T>`. If arbitrary self types is enabled, we instead
     // follow the chain of `Receiver<Target=T>`, but we also record whether
@@ -623,6 +636,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
                 let step = CandidateStep {
                     self_ty: infcx
                         .make_query_response_ignoring_pending_obligations(inference_vars, ty),
+                    self_ty_is_opaque: self_ty_is_opaque(ty),
                     autoderefs: d,
                     from_unsafe_deref: reached_raw_pointer,
                     unsize: false,
@@ -643,6 +657,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
                 let step = CandidateStep {
                     self_ty: infcx
                         .make_query_response_ignoring_pending_obligations(inference_vars, ty),
+                    self_ty_is_opaque: self_ty_is_opaque(ty),
                     autoderefs: d,
                     from_unsafe_deref: reached_raw_pointer,
                     unsize: false,
@@ -659,7 +674,11 @@ pub(crate) fn method_autoderef_steps<'tcx>(
     };
     let final_ty = autoderef_via_deref.final_ty();
     let opt_bad_ty = match final_ty.kind() {
-        ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy {
+        ty::Infer(ty::TyVar(_)) if !self_ty_is_opaque(final_ty) => Some(MethodAutoderefBadTy {
+            reached_raw_pointer,
+            ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+        }),
+        ty::Error(_) => Some(MethodAutoderefBadTy {
             reached_raw_pointer,
             ty: infcx.make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
         }),
@@ -670,6 +689,7 @@ pub(crate) fn method_autoderef_steps<'tcx>(
                     inference_vars,
                     Ty::new_slice(infcx.tcx, *elem_ty),
                 ),
+                self_ty_is_opaque: false,
                 autoderefs,
                 // this could be from an unsafe deref if we had
                 // a *mut/const [T; N]
@@ -1234,7 +1254,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 !step.self_ty.value.references_error() && !step.from_unsafe_deref
             })
             .find_map(|step| {
-                let InferOk { value: self_ty, obligations: _ } = self
+                let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
                     .fcx
                     .probe_instantiate_query_response(
                         self.span,
@@ -1245,7 +1265,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                         span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty)
                     });
 
-                let by_value_pick = self.pick_by_value_method(step, self_ty, pick_diag_hints);
+                let by_value_pick = self.pick_by_value_method(
+                    step,
+                    self_ty,
+                    &instantiate_self_ty_obligations,
+                    pick_diag_hints,
+                );
 
                 // Check for shadowing of a by-reference method by a by-value method (see comments on check_for_shadowing)
                 if let Some(by_value_pick) = by_value_pick {
@@ -1256,6 +1281,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                                     by_value_pick,
                                     step,
                                     self_ty,
+                                    &instantiate_self_ty_obligations,
                                     mutbl,
                                     track_unstable_candidates,
                                 ) {
@@ -1270,6 +1296,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 let autoref_pick = self.pick_autorefd_method(
                     step,
                     self_ty,
+                    &instantiate_self_ty_obligations,
                     hir::Mutability::Not,
                     pick_diag_hints,
                     None,
@@ -1283,6 +1310,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                                 autoref_pick,
                                 step,
                                 self_ty,
+                                &instantiate_self_ty_obligations,
                                 hir::Mutability::Mut,
                                 track_unstable_candidates,
                             ) {
@@ -1319,12 +1347,27 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 self.pick_autorefd_method(
                     step,
                     self_ty,
+                    &instantiate_self_ty_obligations,
                     hir::Mutability::Mut,
                     pick_diag_hints,
                     None,
                 )
-                .or_else(|| self.pick_const_ptr_method(step, self_ty, pick_diag_hints))
-                .or_else(|| self.pick_reborrow_pin_method(step, self_ty, pick_diag_hints))
+                .or_else(|| {
+                    self.pick_const_ptr_method(
+                        step,
+                        self_ty,
+                        &instantiate_self_ty_obligations,
+                        pick_diag_hints,
+                    )
+                })
+                .or_else(|| {
+                    self.pick_reborrow_pin_method(
+                        step,
+                        self_ty,
+                        &instantiate_self_ty_obligations,
+                        pick_diag_hints,
+                    )
+                })
             })
     }
 
@@ -1348,6 +1391,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         possible_shadower: &Pick<'tcx>,
         step: &CandidateStep<'tcx>,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         mutbl: hir::Mutability,
         track_unstable_candidates: bool,
     ) -> Result<(), MethodError<'tcx>> {
@@ -1412,6 +1456,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         let potentially_shadowed_pick = self.pick_autorefd_method(
             step,
             self_ty,
+            instantiate_self_ty_obligations,
             mutbl,
             &mut pick_diag_hints,
             Some(&pick_constraints),
@@ -1438,13 +1483,14 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         &self,
         step: &CandidateStep<'tcx>,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
     ) -> Option<PickResult<'tcx>> {
         if step.unsize {
             return None;
         }
 
-        self.pick_method(self_ty, pick_diag_hints, None).map(|r| {
+        self.pick_method(self_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(|r| {
             r.map(|mut pick| {
                 pick.autoderefs = step.autoderefs;
 
@@ -1481,6 +1527,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         &self,
         step: &CandidateStep<'tcx>,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         mutbl: hir::Mutability,
         pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
         pick_constraints: Option<&PickConstraintsForShadowed>,
@@ -1497,7 +1544,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         let region = tcx.lifetimes.re_erased;
 
         let autoref_ty = Ty::new_ref(tcx, region, self_ty, mutbl);
-        self.pick_method(autoref_ty, pick_diag_hints, pick_constraints).map(|r| {
+        self.pick_method(
+            autoref_ty,
+            instantiate_self_ty_obligations,
+            pick_diag_hints,
+            pick_constraints,
+        )
+        .map(|r| {
             r.map(|mut pick| {
                 pick.autoderefs = step.autoderefs;
                 pick.autoref_or_ptr_adjustment =
@@ -1513,6 +1566,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         &self,
         step: &CandidateStep<'tcx>,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
     ) -> Option<PickResult<'tcx>> {
         if !self.tcx.features().pin_ergonomics() {
@@ -1534,14 +1588,16 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
 
         let region = self.tcx.lifetimes.re_erased;
         let autopin_ty = Ty::new_pinned_ref(self.tcx, region, inner_ty, hir::Mutability::Not);
-        self.pick_method(autopin_ty, pick_diag_hints, None).map(|r| {
-            r.map(|mut pick| {
-                pick.autoderefs = step.autoderefs;
-                pick.autoref_or_ptr_adjustment =
-                    Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not));
-                pick
-            })
-        })
+        self.pick_method(autopin_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(
+            |r| {
+                r.map(|mut pick| {
+                    pick.autoderefs = step.autoderefs;
+                    pick.autoref_or_ptr_adjustment =
+                        Some(AutorefOrPtrAdjustment::ReborrowPin(hir::Mutability::Not));
+                    pick
+                })
+            },
+        )
     }
 
     /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a
@@ -1551,6 +1607,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         &self,
         step: &CandidateStep<'tcx>,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
     ) -> Option<PickResult<'tcx>> {
         // Don't convert an unsized reference to ptr
@@ -1563,18 +1620,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         };
 
         let const_ptr_ty = Ty::new_imm_ptr(self.tcx, ty);
-        self.pick_method(const_ptr_ty, pick_diag_hints, None).map(|r| {
-            r.map(|mut pick| {
-                pick.autoderefs = step.autoderefs;
-                pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
-                pick
-            })
-        })
+        self.pick_method(const_ptr_ty, instantiate_self_ty_obligations, pick_diag_hints, None).map(
+            |r| {
+                r.map(|mut pick| {
+                    pick.autoderefs = step.autoderefs;
+                    pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr);
+                    pick
+                })
+            },
+        )
     }
 
     fn pick_method(
         &self,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
         pick_constraints: Option<&PickConstraintsForShadowed>,
     ) -> Option<PickResult<'tcx>> {
@@ -1584,8 +1644,13 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             [("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)]
         {
             debug!("searching {} candidates", kind);
-            let res =
-                self.consider_candidates(self_ty, candidates, pick_diag_hints, pick_constraints);
+            let res = self.consider_candidates(
+                self_ty,
+                instantiate_self_ty_obligations,
+                candidates,
+                pick_diag_hints,
+                pick_constraints,
+            );
             if let Some(pick) = res {
                 return Some(pick);
             }
@@ -1594,6 +1659,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         if self.private_candidate.get().is_none() {
             if let Some(Ok(pick)) = self.consider_candidates(
                 self_ty,
+                instantiate_self_ty_obligations,
                 &self.private_candidates,
                 &mut PickDiagHints {
                     unstable_candidates: None,
@@ -1610,6 +1676,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
     fn consider_candidates(
         &self,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         candidates: &[Candidate<'tcx>],
         pick_diag_hints: &mut PickDiagHints<'_, 'tcx>,
         pick_constraints: Option<&PickConstraintsForShadowed>,
@@ -1626,6 +1693,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                     probe,
                     self.consider_probe(
                         self_ty,
+                        instantiate_self_ty_obligations,
                         probe,
                         &mut pick_diag_hints.unsatisfied_predicates,
                     ),
@@ -1810,10 +1878,11 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         }
     }
 
-    #[instrument(level = "trace", skip(self, possibly_unsatisfied_predicates), ret)]
+    #[instrument(level = "debug", skip(self, possibly_unsatisfied_predicates), ret)]
     fn consider_probe(
         &self,
         self_ty: Ty<'tcx>,
+        instantiate_self_ty_obligations: &[PredicateObligation<'tcx>],
         probe: &Candidate<'tcx>,
         possibly_unsatisfied_predicates: &mut Vec<(
             ty::Predicate<'tcx>,
@@ -1821,8 +1890,6 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             Option<ObligationCause<'tcx>>,
         )>,
     ) -> ProbeResult {
-        debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe);
-
         self.probe(|snapshot| {
             let outer_universe = self.universe();
 
@@ -1830,6 +1897,21 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
             let cause = &self.misc(self.span);
             let ocx = ObligationCtxt::new_with_diagnostics(self);
 
+            // Subtle: we're not *really* instantiating the current self type while
+            // probing, but instead fully recompute the autoderef steps once we've got
+            // a final `Pick`. We can't nicely handle these obligations outside of a probe.
+            //
+            // We simply handle them for each candidate here for now. That's kinda scuffed
+            // and ideally we just put them into the `FnCtxt` right away. We need to consider
+            // them to deal with defining uses in `method_autoderef_steps`.
+            if self.next_trait_solver() {
+                ocx.register_obligations(instantiate_self_ty_obligations.iter().cloned());
+                let errors = ocx.select_where_possible();
+                if !errors.is_empty() {
+                    unreachable!("unexpected autoderef error {errors:?}");
+                }
+            }
+
             let mut trait_predicate = None;
             let (mut xform_self_ty, mut xform_ret_ty);
 
@@ -2072,6 +2154,12 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
                 }
             }
 
+            if self.infcx.next_trait_solver() {
+                if self.should_reject_candidate_due_to_opaque_treated_as_rigid(trait_predicate) {
+                    result = ProbeResult::NoMatch;
+                }
+            }
+
             // Previously, method probe used `evaluate_predicate` to determine if a predicate
             // was impossible to satisfy. This did a leak check, so we must also do a leak
             // check here to prevent backwards-incompatible ambiguity being introduced. See
@@ -2085,6 +2173,71 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {
         })
     }
 
+    /// Trait candidates for not-yet-defined opaque types are a somewhat hacky.
+    ///
+    /// We want to only accept trait methods if they were hold even if the
+    /// opaque types were rigid. To handle this, we both check that for trait
+    /// candidates the goal were to hold even when treating opaques as rigid,
+    /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank).
+    ///
+    /// We also check that all opaque types encountered as self types in the
+    /// autoderef chain don't get constrained when applying the candidate.
+    /// Importantly, this also handles calling methods taking `&self` on
+    /// `impl Trait` to reject the "by-self" candidate.
+    ///
+    /// This needs to happen at the end of `consider_probe` as we need to take
+    /// all the constraints from that into account.
+    #[instrument(level = "debug", skip(self), ret)]
+    fn should_reject_candidate_due_to_opaque_treated_as_rigid(
+        &self,
+        trait_predicate: Option<ty::Predicate<'tcx>>,
+    ) -> bool {
+        // Check whether the trait candidate would not be applicable if the
+        // opaque type were rigid.
+        if let Some(predicate) = trait_predicate {
+            let goal = Goal { param_env: self.param_env, predicate };
+            if !self.infcx.goal_may_hold_opaque_types_jank(goal) {
+                return true;
+            }
+        }
+
+        // Check whether any opaque types in the autoderef chain have been
+        // constrained.
+        for step in self.steps {
+            if step.self_ty_is_opaque {
+                debug!(?step.autoderefs, ?step.self_ty, "self_type_is_opaque");
+                let constrained_opaque = self.probe(|_| {
+                    // If we fail to instantiate the self type of this
+                    // step, this part of the deref-chain is no longer
+                    // reachable. In this case we don't care about opaque
+                    // types there.
+                    let Ok(ok) = self.fcx.probe_instantiate_query_response(
+                        self.span,
+                        self.orig_steps_var_values,
+                        &step.self_ty,
+                    ) else {
+                        debug!("failed to instantiate self_ty");
+                        return false;
+                    };
+                    let ocx = ObligationCtxt::new(self);
+                    let self_ty = ocx.register_infer_ok_obligations(ok);
+                    if !ocx.select_where_possible().is_empty() {
+                        debug!("failed to prove instantiate self_ty obligations");
+                        return false;
+                    }
+
+                    !self.resolve_vars_if_possible(self_ty).is_ty_var()
+                });
+                if constrained_opaque {
+                    debug!("opaque type has been constrained");
+                    return true;
+                }
+            }
+        }
+
+        false
+    }
+
     /// Sometimes we get in a situation where we have multiple probes that are all impls of the
     /// same trait, but we don't know which impl to use. In this case, since in all cases the
     /// external interface of the method can be determined from the trait, it's ok not to decide.
diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs
index 5d1b4be9e57..a20cd31113a 100644
--- a/compiler/rustc_infer/src/infer/canonical/query_response.rs
+++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs
@@ -85,11 +85,32 @@ impl<'tcx> InferCtxt<'tcx> {
     where
         T: Debug + TypeFoldable<TyCtxt<'tcx>>,
     {
+        // While we ignore region constraints and pending obligations,
+        // we do return constrained opaque types to avoid unconstrained
+        // inference variables in the response. This is still slightly
+        // insufficient as ambiguous `Projection` obligations have the
+        // same issue.
+        //
+        // FIXME(-Znext-solver): We could alternatively extend the `var_values`
+        // each time we call `make_query_response_ignoring_pending_obligations`
+        // and equate inference variables created inside of the query this way.
+        // This is what we do for `CanonicalState` and is probably a bit nicer.
+        let opaque_types = if self.next_trait_solver() {
+            self.inner
+                .borrow_mut()
+                .opaque_type_storage
+                .iter_opaque_types()
+                .map(|(k, v)| (k, v.ty))
+                .collect()
+        } else {
+            vec![]
+        };
+
         self.canonicalize_response(QueryResponse {
             var_values: inference_vars,
             region_constraints: QueryRegionConstraints::default(),
             certainty: Certainty::Proven, // Ambiguities are OK!
-            opaque_types: vec![],
+            opaque_types,
             value: answer,
         })
     }
diff --git a/compiler/rustc_middle/src/traits/query.rs b/compiler/rustc_middle/src/traits/query.rs
index c1b7c9b9b4e..c5cd7c54e4e 100644
--- a/compiler/rustc_middle/src/traits/query.rs
+++ b/compiler/rustc_middle/src/traits/query.rs
@@ -154,6 +154,7 @@ impl<'tcx> FromIterator<DropckConstraint<'tcx>> for DropckConstraint<'tcx> {
 #[derive(Debug, HashStable)]
 pub struct CandidateStep<'tcx> {
     pub self_ty: Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>,
+    pub self_ty_is_opaque: bool,
     pub autoderefs: usize,
     /// `true` if the type results from a dereference of a raw pointer.
     /// when assembling candidates, we include these steps, but not when
diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
index 9b32e487607..85110530ae9 100644
--- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs
@@ -194,7 +194,7 @@ where
     D: SolverDelegate<Interner = I>,
     I: Interner,
 {
-    #[instrument(level = "debug", skip(self))]
+    #[instrument(level = "debug", skip(self), ret)]
     fn evaluate_root_goal(
         &self,
         goal: Goal<I, I::Predicate>,
@@ -206,6 +206,7 @@ where
         })
     }
 
+    #[instrument(level = "debug", skip(self), ret)]
     fn root_goal_may_hold_opaque_types_jank(
         &self,
         goal: Goal<Self::Interner, <Self::Interner as Interner>::Predicate>,
diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs
index afb86aaf8ab..a58caeecc33 100644
--- a/compiler/rustc_next_trait_solver/src/solve/mod.rs
+++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs
@@ -381,6 +381,7 @@ where
 }
 
 /// The result of evaluating a goal.
+#[derive_where(Debug; I: Interner)]
 pub struct GoalEvaluation<I: Interner> {
     /// The goal we've evaluated. This is the input goal, but potentially with its
     /// inference variables resolved. This never applies any inference constraints
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index ae731505abf..34e0176d213 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -1,6 +1,6 @@
 use rustc_infer::traits::solve::Goal;
 use rustc_macros::extension;
-use rustc_middle::span_bug;
+use rustc_middle::{span_bug, ty};
 use rustc_next_trait_solver::solve::SolverDelegateEvalExt;
 
 use crate::infer::InferCtxt;
@@ -22,7 +22,7 @@ impl<'tcx> InferCtxt<'tcx> {
     /// for more details.
     fn predicate_may_hold_opaque_types_jank(&self, obligation: &PredicateObligation<'tcx>) -> bool {
         if self.next_trait_solver() {
-            <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(Goal::new(
+            self.goal_may_hold_opaque_types_jank(Goal::new(
                 self.tcx,
                 obligation.param_env,
                 obligation.predicate,
@@ -32,6 +32,13 @@ impl<'tcx> InferCtxt<'tcx> {
         }
     }
 
+    /// See the comment on [OpaqueTypesJank](crate::solve::OpaqueTypesJank)
+    /// for more details.
+    fn goal_may_hold_opaque_types_jank(&self, goal: Goal<'tcx, ty::Predicate<'tcx>>) -> bool {
+        assert!(self.next_trait_solver());
+        <&SolverDelegate<'tcx>>::from(self).root_goal_may_hold_opaque_types_jank(goal)
+    }
+
     /// Evaluates whether the predicate can be satisfied in the given
     /// `ParamEnv`, and returns `false` if not certain. However, this is
     /// not entirely accurate if inference variables are involved.