{"id":"4ae7aa6c024433dc","repo":"rust-lang/rust","slug":"constkind-param-should-have-been-canonicalized","errorCode":null,"errorMessage":"`ConstKind::Param` should have been canonicalized to `Placeholder`","messagePattern":"`ConstKind::Param` should have been canonicalized to `Placeholder`","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"compiler/rustc_next_trait_solver/src/solve/mod.rs","lineNumber":273,"sourceCode":"                    .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)\n                    .map_err(Into::into);\n            }\n            ty::ConstKind::Error(_) => {\n                return self\n                    .evaluate_added_goals_and_make_canonical_response(Certainty::Yes)\n                    .map_err(Into::into);\n            }\n            ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => {\n                alias_const.type_of(self.cx()).skip_norm_wip()\n            }\n            ty::ConstKind::Alias(ty::IsRigid::No, _) => unimplemented!(\n                \"non-rigid unevaluated constant for compute_const_arg_has_type_goal: {ct:?}\"\n            ),\n            ty::ConstKind::Expr(_) => unimplemented!(\n                \"`feature(generic_const_exprs)` is not supported in the new trait solver\"\n            ),\n            ty::ConstKind::Param(_) => {\n                unreachable!(\"`ConstKind::Param` should have been canonicalized to `Placeholder`\")\n            }\n            ty::ConstKind::Bound(_, _) => panic!(\"escaping bound vars in {:?}\", ct),\n            ty::ConstKind::Value(cv) => cv.ty(),\n            ty::ConstKind::Placeholder(placeholder) => {\n                placeholder.find_const_ty_from_env(goal.param_env)\n            }\n        };\n\n        self.eq(goal.param_env, ct_ty, ty)?;\n        self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)\n    }\n}\n\n#[derive(Debug)]\nenum MergeCandidateInfo {\n    AlwaysApplicable(usize),\n    EqualResponse,\n}","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_next_trait_solver/src/solve/mod.rs#L255-L291","documentation":"An `unreachable!` panic inside `compute_const_arg_has_type_goal` in the new trait solver, asserting that a const argument of `ConstKind::Param` (a generic const parameter, e.g. `N` in `fn foo<const N: usize>()`) should never reach this code because canonicalization is expected to have rewritten it to `ConstKind::Placeholder` first. The canonicalizer lifts late-bound/param vars into placeholders so the solver sees a fully closed term; hitting this arm means the const was leaked into the goal without that rewrite. It is a compiler-internal invariant violation, not a normal user error.","triggerScenarios":"Reached when a `NormalizesTo`/`const_arg_has_type` goal is evaluated whose const argument still carries `ConstKind::Param` after the canonical→placeholder rewrite that `EvalCtxt::canonicalize_const_arg` is responsible for. Concretely, a const generic parameter appears in a position where the solver tries to structurally normalize the const's type (e.g. `<const N: usize>` used inside an alias or where-clause on the new solver, `-Znext-solver`).","commonSituations":"Using unstable const generics features (`generic_const_exprs`, const generic parameters in associated type bounds, min_generic_const_args) on a nightly compiler with the next trait solver enabled. Often surfaces after a rustc upgrade changes how param consts are canonicalized, or in crates that build their own goals via the solver API rather than going through normal type checking.","solutions":["Disable the next trait solver (`-Znext-solver=no` or remove `-Znext-solver`) and reproduce on the stable solver to confirm it is a new-solver-specific bug.","Reduce the crate to a minimal reproducer involving a const generic parameter in a normalization/where-clause position and file an issue against rust-lang/rust with the `A-traits` and `F-next-solver` labels.","If you maintain a crate that calls the solver directly, ensure every const argument is canonicalized (rewriting `ConstKind::Param` → `Placeholder`) before it is passed into `evaluate_added_goals_and_make_canonical_response`.","Pin to an older nightly where the invariant held while the upstream fix lands."],"exampleFix":"// before — const param leaks into a normalization goal\nfn f<const N: usize>() where [(); N]: Sized {}\n// after — avoid routing the const param through a NormalizesTo goal,\n// or wait for the canonicalizer to emit Placeholder before the goal\nfn f<const N: usize>() where [(); N]: Sized {} // works on stable solver\n","handlingStrategy":"type-guard","validationCode":"// Before invoking the solver with a const argument, confirm every\n// const in the query is already canonicalized (Param kinds should\n// have been turned into Placeholder by the canonicalizer).\nfn ensure_const_canonicalized(ct: &Const<'_>) -> Result<(), &'static str> {\n    use rustc_middle::ty::ConstKind;\n    match ct.kind() {\n        ConstKind::Param(_) => Err(\"const carries a non-canonicalized Param kind; canonicalize first\"),\n        _ => Ok(()),\n    }\n}\nfor ct in query.consts() { ensure_const_canonicalized(ct)?; }","typeGuard":"// Returns true only when the const has no raw Param kind, i.e. it is\n// safe to hand to the next solver.\nfn is_canonicalized_const(ct: &Const<'_>) -> bool {\n    use rustc_middle::ty::ConstKind;\n    !matches!(ct.kind(), ConstKind::Param(_))\n}","tryCatchPattern":"// The solver panics on a violated invariant; wrap the call in\n// catch_unwind only as a last-resort containment, then degrade.\nuse std::panic;\nlet res = panic::catch_unwind(panic::AssertUnwindSafe(|| solver.evaluate(goal)));\nmatch res {\n    Ok(o)  => o,\n    Err(_) => { /* fall back to the legacy solver */ }\n}","preventionTips":["Always run arguments through `instantiate_canonical` / canonicalization before entering the next solver.","Never feed raw `ConstKind::Param` consts straight into a goal; substitute parameters first.","When building goals programmatically, prefer `ty::Const::from_anon_const` paths that already canonicalize.","Re-run on a fresh tcx after substituting generic params so no escaping Param survives."],"tags":["rustc","trait-solver","const-generics","compiler-internal","ice"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}