rust-lang/rust · critical

unexpected nested goal in `relate`: {p:?}

Error message

unexpected nested goal in `relate`: {p:?}

What it means

EvalCtxt::relate_and_add_goals calls the delegate's relate(), which returns nested goals; only Subtype, Projection (ClauseKind::Projection), and WellFormed goals are valid here. Any other PredicateKind triggers unreachable! because the type-relation pipeline is not designed to ingest arbitrary goals (e.g. Trait, RegionOutlives, ConstEquate).

Source

Thrown at compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:1254

    #[instrument(level = "trace", skip(self, param_env), ret)]
    pub(super) fn relate<T: Relate<I>>(
        &mut self,
        param_env: I::ParamEnv,
        lhs: T,
        variance: ty::Variance,
        rhs: T,
    ) -> Result<(), NoSolutionOrRerunNonErased> {
        let goals = self.delegate.relate(param_env, lhs, variance, rhs, self.origin_span)?;
        for &goal in goals.iter() {
            let source = match goal.predicate.kind().skip_binder() {
                ty::PredicateKind::Subtype { .. }
                | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) => {
                    GoalSource::TypeRelating
                }
                // FIXME(-Znext-solver=coinductive): should these WF goals also be unproductive?
                ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(_)) => GoalSource::Misc,
                p => unreachable!("unexpected nested goal in `relate`: {p:?}"),
            };
            self.add_goal(source, goal)?;
        }
        Ok(())
    }

    /// Equates two values returning the nested goals without adding them
    /// to the nested goals of the `EvalCtxt`.
    ///
    /// If possible, try using `eq` instead which automatically handles nested
    /// goals correctly.
    #[instrument(level = "trace", skip(self, param_env), ret)]
    pub(super) fn eq_and_get_goals<T: Relate<I>>(
        &self,
        param_env: I::ParamEnv,
        lhs: T,
        rhs: T,
    ) -> Result<Vec<Goal<I, I::Predicate>>, NoSolution> {

View on GitHub (pinned to 22057b88b0)

Solutions

  1. File a rustc ICE report with the {p:?} predicate printed by the panic.
  2. Inspect the delegate's relate implementation for the backend in use and confirm it only emits Subtype/Projection/WellFormed nested goals.
  3. Work around by disabling -Znext-solver.
  4. Bisect changes to the delegate's TypeRelating/Generalizer code that may have started emitting extra goal kinds.
Defensive patterns

Strategy: validation

Validate before calling

// Relate must not produce nested goals. Audit user code that calls traits whose
// associated items impose additional bounds during a relate step.
fn related_traits_introduce_nested_goals(item: &TraitItem) -> bool {
    item.bounds.iter().any(|b| matches!(b, Bound::Trait(_)))
}
// run in a `xshell`-driven lint over the crate before `cargo build`

Type guard

trait RelateFlat: Sized {}
impl<T> RelateFlat for T where T: Copy {}
// narrow relate operations to types that cannot carry nested goals

Try / catch

use std::panic::{catch_unwind, AssertUnwindSafe};
let r = catch_unwind(AssertUnwindSafe(|| relate(a, b, variance)));
match r { Ok(()) => (), Err(_) => log::warn!("nested relate goal; deferring to legacy solver") }

Prevention

When it happens

Trigger: Reached when SolverDelegate::relate (invoked at compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:1245) yields a nested goal whose kind is not Subtype, Clause(Projection), or Clause(WellFormed). Typically caused by a delegate implementation (rustc vs chalk vs external) emitting an unexpected goal kind.

Common situations: ICE encountered during type relation (subtyping/equating/coercion) under -Znext-solver. Almost always a bug in the delegate glue that bridges the infcx to the new solver, not user code.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/db651aa25639c8d1.json. Report an issue: GitHub.