rust-lang/rust · critical
escaping bound vars in {:?}
Error message
escaping bound vars in {:?} What it means
A `panic!` in `compute_const_arg_has_type_goal` triggered when a const argument is a `ConstKind::Bound` — i.e. it references a late-bound variable (de Bruijn index) that has escaped the binder it was introduced in. The new solver only ever handles fully instantiated, closed terms; a bound var present here means a binder was dropped or skipped during substitution/instantiation. This is a compiler invariant, not valid user IR.
Source
Thrown at compiler/rustc_next_trait_solver/src/solve/mod.rs:275
}
ty::ConstKind::Error(_) => {
return self
.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
.map_err(Into::into);
}
ty::ConstKind::Alias(ty::IsRigid::Yes, alias_const) => {
alias_const.type_of(self.cx()).skip_norm_wip()
}
ty::ConstKind::Alias(ty::IsRigid::No, _) => unimplemented!(
"non-rigid unevaluated constant for compute_const_arg_has_type_goal: {ct:?}"
),
ty::ConstKind::Expr(_) => unimplemented!(
"`feature(generic_const_exprs)` is not supported in the new trait solver"
),
ty::ConstKind::Param(_) => {
unreachable!("`ConstKind::Param` should have been canonicalized to `Placeholder`")
}
ty::ConstKind::Bound(_, _) => panic!("escaping bound vars in {:?}", ct),
ty::ConstKind::Value(cv) => cv.ty(),
ty::ConstKind::Placeholder(placeholder) => {
placeholder.find_const_ty_from_env(goal.param_env)
}
};
self.eq(goal.param_env, ct_ty, ty)?;
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes).map_err(Into::into)
}
}
#[derive(Debug)]
enum MergeCandidateInfo {
AlwaysApplicable(usize),
EqualResponse,
}
impl<D, I> EvalCtxt<'_, D>View on GitHub (pinned to 22057b88b0)
Solutions
- Confirm the const is fully instantiated before entering the solver: every binder over consts should pass through `instantiate_binder_with_infer` (or the placeholder equivalent under `-Znext-solver`).
- Bisect nightlies to find the regression and report with a reduced test using `--edition=2021 -Znext-solver`.
- Switch off the next solver to verify it is new-solver-specific.
Example fix
// before — late-bound const var escapes its binder
for<const N: usize> || {}
// after — keep the bound var under its binder, or instantiate it
fn g<F: for<const N: usize> Fn() -> [(); N]>(_: F) {}
Defensive patterns
Strategy: validation
Validate before calling
// Reject goals whose types or consts still contain escaping late-bound
// vars before passing them to the solver.
fn has_escaping_bvars(tcx: TyCtxt<'_>, t: impl Visit) -> bool {
struct EscapeCheck { has_escaping: bool }
// bound vars are escaping when their DebruijnIndex is > outermost.
// rustc exposes this via `tcx.fold_regions` / util::has_escaping_bound_vars.
t.has_escaping_bound_vars()
}
if has_escaping_bvars(tcx, goal) { return Err("goal has escaping bound vars"); } Type guard
// Narrow to a goal known to be free of escaping bound vars.
fn is_closed_goal(tcx: TyCtxt<'_>, goal: &Goal<'_>) -> bool {
!goal.predicate.visit_with(&mut EscapingBoundVarDetector).has_found()
} Try / catch
use std::panic;
let r = panic::catch_unwind(panic::AssertUnwindSafe(|| solver.evaluate(goal)));
match r {
Ok(v) => v,
Err(_) => Err("next solver rejected escaping bound vars; re-instantiate under a binder"),
} Prevention
- Instantiate / skim late-bound vars (`for<'a>`) before constructing a goal so nothing escapes its binder.
- Avoid HRTBs nested inside associated-type projections; they frequently leave escaping vars.
- Use `tcx.replace_escaping_bound_vars_uncached` to eliminate them at the call site.
- Treat any goal built from a `Binder` as suspect until it is fully instantiated.
When it happens
Trigger: Hit when a const bound variable (introduced by `for<'a>`/higher-ranked or by a late-bound const param) is not replaced with a fresh inference variable/placeholder before the const flows into a `const_arg_has_type` goal. Reproducible with higher-ranked const bounds or nested binders combined with the next solver.
Common situations: Nightly-only features mixing higher-ranked bounds with const generics, or a buggy macro that synthesizes `ty::Const` with leftover `Bound` kinds. Also appears after refactors of `instantiate_binder_with_infer` that forget to substitute a const binder.
Related errors
- `ConstKind::Param` should have been canonicalized to `Placeh
- unexpected orig_value: {ct:?}
- ConstEquate should not be emitted when `-Znext-solver` is ac
- unexpected const kind: {:?}
- non-rigid unevaluated constant for compute_const_arg_has_typ
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/f22bdcc02942925a.json.
Report an issue: GitHub.