{"id":"df2b9d0cb4121fa4","repo":"rust-lang/rust","slug":"discr-subgoal","errorCode":null,"errorMessage":"discr subgoal...","messagePattern":"discr subgoal\\.\\.\\.","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs","lineNumber":1009,"sourceCode":"            | ty::FnDef(..)\n            | ty::FnPtr(..)\n            | ty::Closure(..)\n            | ty::CoroutineClosure(..)\n            | ty::Infer(ty::IntVar(..) | ty::FloatVar(..))\n            | ty::Coroutine(..)\n            | ty::CoroutineWitness(..)\n            | ty::Never\n            | ty::Foreign(..)\n            | ty::Adt(_, _)\n            | ty::Str\n            | ty::Slice(_)\n            | ty::Dynamic(_, _)\n            | ty::Tuple(_)\n            | ty::Error(_) => self_ty.discriminant_ty(ecx.cx()),\n\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            ),","sourceCodeStart":991,"sourceCodeEnd":1027,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_next_trait_solver/src/solve/normalizes_to.rs#L991-L1027","documentation":"consider_builtin_discriminant_kind_candidate (normalizes_to.rs:1009) computes `<T as DiscriminantKind>::Discriminant` for each type kind. The `ty::UnsafeBinder(_)` arm calls `unimplemented!(\"discr subgoal...\")` — this is deliberately unfinished code (note the inline FIXME(unsafe_binders) comment) rather than an invariant guard. UnsafeBinder is an experimental, still-in-development type kind, and computing its discriminant has not been implemented yet, so the compiler aborts instead of guessing.","triggerScenarios":"Code that uses unsafe binder types (the nightly `unsafe_binder` feature) and triggers a discriminant-kind query on such a type under -Znext-solver — e.g. mem::discriminant, match-equivalent checks, or any path requiring `<T as DiscriminantKind>::Discriminant` where T is an UnsafeBinder.","commonSituations":"Experimenting with the unstable `unsafe_binder` feature on a nightly that has the type kind but not its discriminant handling; a dependency that opts into unsafe_binder behind a feature flag; tracking the upstream RFC as it lands incrementally.","solutions":["Avoid using unsafe binder types in positions where a discriminant query is required until the feature is implemented.","Update to the latest nightly and re-check; if still unimplemented, wait for the unsafe_binder feature to progress (track the FIXME/issue).","Disable -Znext-solver so the legacy solver (which does not exercise this builtin path) is used.","File an issue at https://github.com/rust-lang/rust/issues noting the unsafe_binder discriminant gap, referencing normalizes_to.rs:1009."],"exampleFix":"// before: discriminant query on an unsafe binder type\nunsafe_binder fn handle(b: unsafe binder T) {\n    let _ = std::mem::discriminant(&b); // unimplemented discr subgoal\n}\n\n// after: move the value out of the unsafe binder before querying\nunsafe_binder fn handle(b: unsafe binder T) {\n    let v: T = unsafe { wrap(b) };\n    let _ = std::mem::discriminant(&v);\n}","handlingStrategy":"validation","validationCode":"// Fires while solving a DiscriminantKind subgoal for an ill-formed self type.\n// Constrain to a concrete, well-formed enum/struct before relying on discriminants.\n#[repr(u8)] enum E { A, B, C }\nfn discr_of(e: E) -> <E as core::marker::DiscriminantKind>::Discriminant { e as u8 as _ }\n","typeGuard":"// Restrict discriminant queries to concrete, sized, well-formed enums:\nfn discr<T: Sized + 'static>() -> <T as core::marker::DiscriminantKind>::Discriminant { unimplemented!() }\n","tryCatchPattern":null,"preventionTips":["Avoid projecting `Discriminant` off generic inference variables or unsized types.","Annotate enums with `#[repr(...)]` so the discriminant type is fully determined.","Don't combine `DiscriminantKind` projection with auto-trait/`?Sized` puzzles in generic crates."],"tags":["rust","rustc","trait-solver","next-solver","ice","unimplemented","unsafe-binder","discriminant","builtin-impl"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}