{"id":"316de354f0ae01ea","repo":"rust-lang/rust","slug":"unexpected-self-ty-when-normalizing-t-as-316de3","errorCode":null,"errorMessage":"unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`","messagePattern":"unexpected self ty `(.+?)` when normalizing `<T as DiscriminantKind>::Discriminant`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs","lineNumber":1024,"sourceCode":"\n            ty::UnsafeBinder(_) => {\n                // FIXME(unsafe_binders): instantiate this with placeholders?? i guess??\n                unimplemented!(\"discr subgoal...\")\n            }\n\n            // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid\n            // alias. In case there's a where-bound further constraining this alias it is preferred over\n            // this impl candidate anyways. It's still a bit scuffed.\n            ty::Alias(ty::IsRigid::Yes, _) | ty::Param(_) | ty::Placeholder(..) => {\n                return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {\n                    ecx.instantiate_normalizes_to_as_rigid(goal)?;\n                    ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)\n                });\n            }\n\n            ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_))\n            | ty::Alias(ty::IsRigid::No, _)\n            | ty::Bound(..) => panic!(\n                \"unexpected self ty `{:?}` when normalizing `<T as DiscriminantKind>::Discriminant`\",\n                goal.predicate.self_ty()\n            ),\n        };\n\n        ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| {\n            ecx.instantiate_normalizes_to_term(goal, discriminant_ty.into())?;\n            ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)\n        })\n    }\n\n    fn consider_builtin_destruct_candidate(\n        _ecx: &mut EvalCtxt<'_, D>,\n        goal: Goal<I, Self>,\n    ) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {\n        panic!(\"`Destruct` does not have an associated type: {:?}\", goal);\n    }\n","sourceCodeStart":1006,"sourceCodeEnd":1042,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs#L1006-L1042","documentation":"consider_builtin_discriminant_kind_candidate (normalizes_to.rs:1024) computes the concrete `<T as DiscriminantKind>::Discriminant` type by matching on self_ty.kind(). The final arm panics for Infer(TyVar|Fresh*), non-rigid Alias (still-normalizing projection), and Bound types. By invariant, a discriminant query should only run after the self type is concrete, a rigid alias, a param, or a placeholder (all handled in earlier arms that return early). Reaching this panic means an unresolved inference variable, an un-normalized projection, or a bound type leaked into the discriminant computation.","triggerScenarios":"Code that forces `<T as DiscriminantKind>::Discriminant` while T is an unconstrained inference variable, a non-rigid projection that has not finished normalizing, or a bound type — e.g. mem::discriminant, niche-layout checks, or enum-representation queries on a fully generic T with no constraint, under -Znext-solver with a goal-ordering regression.","commonSituations":"Generic code over `T` that inspects discriminants or relies on niche layout without bounding T; next-solver nightly regressions in normalization ordering; fuzzing the solver with partially-resolved types.","solutions":["Add a bound or concrete annotation so the self type is resolved before its discriminant is queried (e.g. monomorphize or constrain T to a known enum/ADT).","Update to the latest nightly; next-solver goal-ordering bugs around normalization are fixed frequently.","Disable -Znext-solver to use the legacy solver's discriminant handling.","File an ICE with the self_ty from the panic and a minimal reproducer (mem::discriminant / niche-layout code)."],"exampleFix":"// before: discriminant queried on an unconstrained generic\nfn disc<T>(t: &T) {\n    let _ = std::mem::discriminant(t); // <T as DiscriminantKind>::Discriminant on unknown T\n}\n\n// after: constrain T to a concrete enum\nfn disc<E: Copy>(t: &E) where E: 'static {\n    // callers pass a known enum; discriminant type is statically determined\n    let _ = std::mem::discriminant(t);\n}","handlingStrategy":"validation","validationCode":"// DiscriminantKind::Discriminant must normalize against a concrete Self.\n// Pin Self to a concrete enum/struct type:\nenum Tagged { A = 1, B = 2 }\nfn tag_discriminant() -> <Tagged as core::marker::DiscriminantKind>::Discriminant { 1 as _ }\n","typeGuard":"trait KnownDisc: core::marker::DiscriminantKind {}\nimpl<T: Sized + 'static> KnownDisc for T {}\nfn disc<T: KnownDisc>() -> <T as core::marker::DiscriminantKind>::Discriminant { unimplemented!() }\n","tryCatchPattern":null,"preventionTips":["Project `Discriminant` only after `T` is fully inferred and Sized.","Avoid nested generic params whose discriminant depends on an unresolved inference variable.","Prefer `core::mem::Discriminant<T>` from `core::mem::discriminant` over manual normalization."],"tags":["rust","rustc","trait-solver","next-solver","ice","panic","discriminant","inference","projection"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}