{"id":"66214520a0236005","repo":"rust-lang/rust","slug":"unexpected-orig-value-ty","errorCode":null,"errorMessage":"unexpected orig_value: {ty:?}","messagePattern":"unexpected orig_value: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs","lineNumber":866,"sourceCode":"        &self,\n        canonical_goal: CanonicalInput<I>,\n        certainty: Certainty,\n        mut stalled_vars: ThinVec<I::GenericArg>,\n        previously_succeeded_in_erased: SucceededInErased<I>,\n    ) -> GoalStalledOn<I> {\n        // Remove the canonicalized universal vars, since we only care about stalled existentials.\n        let mut sub_roots = ThinVec::new();\n        stalled_vars.retain(|arg| match arg.kind() {\n            // Lifetimes can never stall goals.\n            ty::GenericArgKind::Lifetime(_) => false,\n            ty::GenericArgKind::Type(ty) => match ty.kind() {\n                ty::Infer(ty::TyVar(vid)) => {\n                    sub_roots.push(self.delegate.sub_unification_table_root_var(vid));\n                    true\n                }\n                ty::Infer(_) => true,\n                ty::Param(_) | ty::Placeholder(_) => false,\n                _ => unreachable!(\"unexpected orig_value: {ty:?}\"),\n            },\n            ty::GenericArgKind::Const(ct) => match ct.kind() {\n                ty::ConstKind::Infer(_) => true,\n                ty::ConstKind::Param(_) | ty::ConstKind::Placeholder(_) => false,\n                _ => unreachable!(\"unexpected orig_value: {ct:?}\"),\n            },\n        });\n\n        GoalStalledOn {\n            stalled_vars,\n            sub_roots,\n            stalled_certainty: certainty,\n            opaques: GoalStalledOnOpaques::Yes {\n                num_opaques_in_storage: canonical_goal\n                    .canonical\n                    .value\n                    .predefined_opaques_in_body\n                    .len(),","sourceCodeStart":848,"sourceCodeEnd":884,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs#L848-L884","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Report the ICE to rustc with the reproduction and the {ty:?} value printed in the panic.","Avoid -Znext-solver if the ICE blocks your build until the canonicalization bug is fixed.","If hacking on the solver, audit the upstream code that populates orig_values / stalled_vars to confirm only Infer/Param/Placeholder kinds are inserted.","Bisect solver changes around opaque types and canonicalization, since rigid aliases are the most likely illegal kind to leak in."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"// Walk the crate's type surface (e.g. via `cargo-expand` AST) and reject types whose\n// `TyKind` does not round-trip through `orig_value`. Simplified check:\nfn ty_is_solver_safe(ty: &syn::Type) -> bool {\n    match ty {\n        syn::Type::Path(_) | syn::Type::Reference(_) | syn::Type::Array(_) | syn::Type::Slice(_) => true,\n        // opaque/impl-trait and bare inference vars at definition sites are the usual trigger\n        syn::Type::ImplTrait(_) | syn::Type::Infer(_) | syn::Type::Macro(_) => false,\n        _ => true,\n    }\n}","typeGuard":"// Rust: a const-evaluable narrowing guard over `&dyn Any` won't help at compile time,\n// so encode the invariant as a where-clause the solver accepts before expansion:\ntrait SolverSafeType: Sized {}\nimpl<T: 'static> SolverSafeType for T where T: core::fmt::Debug {}\nfn accept<T: SolverSafeType>(v: T) -> T { v }","tryCatchPattern":"use std::panic::{catch_unwind, AssertUnwindSafe};\nlet r = catch_unwind(AssertUnwindSafe(|| expand_type(input)));\nmatch r { Ok(ty) => ty, Err(_) => fallback_to_legacy_solver(input) }","preventionTips":["Avoid surfacing `impl Trait` in positions where the solver must relate it to an already-substituted `orig_value`.","Prefer concrete generic params over inferred/anonymous types at FFI and macro expansion boundaries.","Run `cargo check` early and often on type-heavy changes; this panic is reachable purely from `check`."],"tags":["rust","rustc","trait-solver","ice","next-solver","canonicalization","stalled-goals"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}