rust-lang/rust · critical
Params should have been canonicalized to placeholders
Error message
Params should have been canonicalized to placeholders
What it means
In the solver's region-constraint destructor, a Component::Param(_) is treated as a hard error because canonicalization is supposed to have rewritten every early-bound region param into a Placeholder before region constraints are processed. Encountering a Param here means the canonicalization step that should have run before evaluating the goal was skipped or is incomplete.
Source
Thrown at compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs:185
components: &[Component<I>],
r: Region<I>,
) -> RegionConstraint<I> {
RegionConstraint::And(
components.into_iter().map(|c| self.destructure_component(c, r)).collect(),
)
}
fn destructure_component(&mut self, c: &Component<I>, r: Region<I>) -> RegionConstraint<I> {
use Component::*;
match c {
Region(c_r) => RegionConstraint::RegionOutlives(*c_r, r),
Placeholder(p) => {
RegionConstraint::PlaceholderTyOutlives(Ty::new_placeholder(self.cx(), *p), r)
}
// The alias is either rigid or ambiguous in which case we'll return with ambiguity.
Alias(_, alias) => self.destructure_alias_outlives(*alias, r),
UnresolvedInferenceVariable(_) => RegionConstraint::Ambiguity,
Param(_) => panic!("Params should have been canonicalized to placeholders"),
EscapingAlias(components) => self.destructure_components(components, r),
}
}
/// Convert an alias outlives constraint into an OR constraint of any number of three
/// separate classes of candidates:
/// 1. component outlives. we turn `Alias<T, 'a>: 'b` into `T: 'b, 'a: 'b`.
/// 2. item bounds. we turn `Alias<T, 'a>: 'b` into `'c: 'b` if `Alias` is
/// defined as `type Alias<T, 'a>: 'c`
/// 3. env assumptions. we defer handling `Alias<T, 'a>: 'b` via where clauses until
/// when exiting the current binder. See [`RegionConstraint::AliasTyOutlivesViaEnv`].
#[instrument(level = "debug", skip(self), ret)]
fn destructure_alias_outlives(
&mut self,
alias: AliasTy<I>,
r: Region<I>,
) -> RegionConstraint<I> {
let item_bounds =View on GitHub (pinned to 22057b88b0)
Solutions
- Report the ICE to rustc with the offending crate and the backtrace.
- Verify the goal was canonicalized (params -> placeholders) before reaching region constraint collection; the bug is the missing canonicalization.
- Disable -Znext-solver to unblock.
- Bisect changes to canonicalization / placeholder instantiation in the new solver.
Defensive patterns
Strategy: validation
Validate before calling
// Region constraints must see placeholders, not Params. Ensure generic params are
// canonicalized before being passed into region solving (this is the compiler's job,
// but you can avoid triggering it by not hand-rolling region-parameterized generics).
fn uses_explicit_lifetime_params(src: &str) -> bool {
src.contains("<'") && src.contains(">")
}
#[test]
fn flag_region_param_heavy_crates() {
if uses_explicit_lifetime_params(include_str!("lib.rs")) {
eprintln!("warning: explicit lifetime params may trigger solver canonicalization bug");
}
} Type guard
trait CanonicalLifetime: 'static {}
// force callers to bind lifetimes to 'static-or-equivalent placeholders, never raw Params
fn requires_canonical<'a, T: CanonicalLifetime>(_x: &'a T) {} Try / catch
use std::panic::{catch_unwind, AssertUnwindSafe};
let ok = catch_unwind(AssertUnwindSafe(|| solve_region_constraints(®ions)));
if ok.is_err() { fall_back_to_legacy_solver_for_regions(); } Prevention
- Prefer `'_` elision and lifetime inference over explicit named lifetime params; the canonicalizer handles inferred lifetimes more reliably.
- Avoid HRTB over multiple lifetimes (`for<'a,'b>`) in public API signatures.
- If you must use explicit lifetimes, write a small `cargo check` gate in CI and treat any region-related ICE as a release blocker.
When it happens
Trigger: Triggered when SolverRegionConstraints::destructure_component (compiler/rustc_next_trait_solver/src/solve/eval_ctxt/solver_region_constraints.rs:175) matches Component::Param(_) — i.e. an outlives constraint over a still-parametric region reached the region-constraint collector without first being canonicalized to a placeholder.
Common situations: ICE during region-outlives / alias-outlives solving under -Znext-solver. Usually a regression in how the search graph or EvalCtxt canonicalizes goals before invoking the region constraint machinery.
Related errors
- unexpected orig_value: {ty:?}
- unexpected orig_value: {ct:?}
- this never happens at the root, we're never in erased mode h
- ConstEquate should not be emitted when `-Znext-solver` is ac
- unexpected nested goal in `relate`: {p:?}
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/4c4c12d3ffe86ddb.json.
Report an issue: GitHub.