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

  1. 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`).
  2. Bisect nightlies to find the regression and report with a reduced test using `--edition=2021 -Znext-solver`.
  3. 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

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


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/f22bdcc02942925a.json. Report an issue: GitHub.