rust-lang/rust · critical

unexpected orig_value: {ty:?}

Error message

unexpected orig_value: {ty:?}

What it means

Invariant check in EvalCtxt::build_stalled_on while classifying the 'orig values' of a stalled canonical goal. After stripping universal (placeholder) vars, each remaining generic arg's type is expected to be an inference variable (TyVar/IntVar/FloatVar) or, as a no-op case, a Param/Placeholder. Any other TyKind (e.g. a rigid Alias, a concrete Adt, a Bound) means canonicalization or unification left a non-infer type in the existential orig_values, which the stalled-goal machinery cannot represent.

Source

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

        &self,
        canonical_goal: CanonicalInput<I>,
        certainty: Certainty,
        mut stalled_vars: ThinVec<I::GenericArg>,
        previously_succeeded_in_erased: SucceededInErased<I>,
    ) -> GoalStalledOn<I> {
        // Remove the canonicalized universal vars, since we only care about stalled existentials.
        let mut sub_roots = ThinVec::new();
        stalled_vars.retain(|arg| match arg.kind() {
            // Lifetimes can never stall goals.
            ty::GenericArgKind::Lifetime(_) => false,
            ty::GenericArgKind::Type(ty) => match ty.kind() {
                ty::Infer(ty::TyVar(vid)) => {
                    sub_roots.push(self.delegate.sub_unification_table_root_var(vid));
                    true
                }
                ty::Infer(_) => true,
                ty::Param(_) | ty::Placeholder(_) => false,
                _ => unreachable!("unexpected orig_value: {ty:?}"),
            },
            ty::GenericArgKind::Const(ct) => match ct.kind() {
                ty::ConstKind::Infer(_) => true,
                ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false,
                _ => unreachable!("unexpected orig_value: {ct:?}"),
            },
        });

        GoalStalledOn {
            stalled_vars,
            sub_roots,
            stalled_certainty: certainty,
            opaques: GoalStalledOnOpaques::Yes {
                num_opaques_in_storage: canonical_goal
                    .canonical
                    .value
                    .predefined_opaques_in_body
                    .len(),

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report the ICE to rustc with the reproduction and the {ty:?} value printed in the panic.
  2. Avoid -Znext-solver if the ICE blocks your build until the canonicalization bug is fixed.
  3. If hacking on the solver, audit the upstream code that populates orig_values / stalled_vars to confirm only Infer/Param/Placeholder kinds are inserted.
  4. Bisect solver changes around opaque types and canonicalization, since rigid aliases are the most likely illegal kind to leak in.
Defensive patterns

Strategy: type-guard

Validate before calling

// Walk the crate's type surface (e.g. via `cargo-expand` AST) and reject types whose
// `TyKind` does not round-trip through `orig_value`. Simplified check:
fn ty_is_solver_safe(ty: &syn::Type) -> bool {
    match ty {
        syn::Type::Path(_) | syn::Type::Reference(_) | syn::Type::Array(_) | syn::Type::Slice(_) => true,
        // opaque/impl-trait and bare inference vars at definition sites are the usual trigger
        syn::Type::ImplTrait(_) | syn::Type::Infer(_) | syn::Type::Macro(_) => false,
        _ => true,
    }
}

Type guard

// Rust: a const-evaluable narrowing guard over `&dyn Any` won't help at compile time,
// so encode the invariant as a where-clause the solver accepts before expansion:
trait SolverSafeType: Sized {}
impl<T: 'static> SolverSafeType for T where T: core::fmt::Debug {}
fn accept<T: SolverSafeType>(v: T) -> T { v }

Try / catch

use std::panic::{catch_unwind, AssertUnwindSafe};
let r = catch_unwind(AssertUnwindSafe(|| expand_type(input)));
match r { Ok(ty) => ty, Err(_) => fallback_to_legacy_solver(input) }

Prevention

When it happens

Trigger: Triggered when build_stalled_on (compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs:847) iterates stalled_vars and a GenericArgKind::Type whose kind is neither Infer nor Param/Placeholder is encountered — for example an opaque/alias type or a concrete type leaking into the canonical goal's orig_values.

Common situations: Surfaces as an ICE during trait solving when a goal stalls on inference variables. Typically a compiler bug in canonicalization, opaque-type handling, or a recent change to what kinds may appear in orig_values; not reproducible from normal user code except as the trigger input to the buggy path.

Related errors


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