rust-lang/rust · critical
we never retry stalled queries if the parent was erased
Error message
we never retry stalled queries if the parent was erased
What it means
Internal `unreachable!` invariant assertion in the trait solver's stalled-query fast path (`rerunning_stalled_goal_may_make_progress`). After a stalled goal previously succeeded in erased (opaque-erasing) mode, the solver asks `should_rerun_after_erased_canonicalization` whether to retry; one possible answer is `EagerlyPropagateToParent`, which is only valid in the full `EvalCtxt` path (mod.rs:757) where opaque accesses can be propagated to the parent query. The fast-path cache invariant asserts this never happens for a cached stalled goal — if it does, the stalled-query cache is inconsistent with the typing mode and the compiler ICEs.
Source
Thrown at compiler/rustc_next_trait_solver/src/solve/eval_ctxt/fast_path.rs:91
.opaque_types_storage_num_entries()
.needs_reevaluation(num_opaques_in_storage)
{
// Unless this goal previously succeeded in erased mode.
// If the stalled goal successfully evaluated while erasing opaque types,
// and the current state of the opaque type storage is not different in a way that is
// relevant, this stalled goal cannot make any progress and we set this variable to true.
let mut previous_erased_run_is_still_valid = false;
if let &SucceededInErased::Yes { accessed_opaques } = previously_succeeded_in_erased
{
match should_rerun_after_erased_canonicalization(
accessed_opaques,
delegate.typing_mode_raw(),
&delegate.clone_opaque_types_lookup_table(),
) {
RerunDecision::Yes => {}
RerunDecision::EagerlyPropagateToParent => {
unreachable!("we never retry stalled queries if the parent was erased")
}
RerunDecision::No => {
previous_erased_run_is_still_valid = true;
}
}
}
if !previous_erased_run_is_still_valid {
return MayMakeProgress;
}
}
}
}
// Otherwise, we can be sure that this stalled goal cannot make any progress
// and we can exit early.
WontMakeProgress(stalled_certainty)
}View on GitHub (pinned to 22057b88b0)
Solutions
- Retry with `-Zdisable-fast-paths` — it short-circuits this fast path and forces the slow path (mod.rs:757) which legitimately handles `EagerlyPropagateToParent`; if that compiles, the bug is fast-path-specific.
- Reduce the reproducer by deleting `impl Trait`/async blocks until the ICE vanishes, then report the minimal case upstream as a next-solver ICE.
- Bisect nightlies — opaque/erased-canonicalization logic changes frequently in the next solver.
- If on a feature branch of rustc, check whether `should_rerun_after_erased_canonicalization` or the stalled-cache population was recently modified; the invariant mismatch is usually a stale `SucceededInErased::Yes` stored where `No` should have been.
Example fix
// before (rustc invocation triggering the ICE) rustc -Znext-solver crate_with_heavy_impl_trait // after — bypass the fast path to confirm and keep compiling while upstream fixes it rustc -Znext-solver -Zdisable-fast-paths crate_with_heavy_impl_trait
Defensive patterns
Strategy: fallback
Validate before calling
// build.rs: detect the trigger pattern (opaque types / impl Trait) in const-adjacent code
// so you can fall back to explicit named types BEFORE the solver ICEs.
fn main() {
for file in ["src/lib.rs"] {
let src = std::fs::read_to_string(file).unwrap_or_default();
let in_const = src.lines().any(|l| l.contains("const fn") || l.contains("const "));
let uses_opaque = src.lines().any(|l| l.contains("-> impl ") || l.contains("async "));
if in_const && uses_opaque {
eprintln!("warning: {} mixes `impl Trait`/async with const evaluation; the next-solver can hit the erased-parent ICE (fast_path.rs:91). Replace opaque returns with explicit named types.", file);
}
}
println!("cargo:rerun-if-changed=src/lib.rs");
} Prevention
- This ICE fires when a stalled goal that previously succeeded in erased mode would need `EagerlyPropagateToParent`, which the solver never does for erased parents (fast_path.rs:91). The trigger is opaque-type (`impl Trait`, `async` blocks) inference interacting with const/erased canonicalization.
- Primary workaround (fallback): replace `-> impl Trait` return types with explicit named types (a concrete struct, `Box<dyn Trait>`, or a named `type` alias) in any code path near const evaluation.
- Avoid nesting `async` blocks / closures that produce opaque types inside generic const code or deep trait-solver recursion.
- Pin concrete types instead of leaving heavy inference variables in const-adjacent generic code; the stall/retry path assumes the parent is non-erased.
- Debug aid: compile with `-Zdisable-fast-paths` to bypass the fast path and confirm the ICE is fast-path-specific before reporting.
- When reporting upstream, include the opaque-type reproducer and the `should_rerun_after_erased_canonicalization` decision path.
When it happens
Trigger: Reached when `rerunning_stalled_goal_may_make_progress` re-checks a previously stalled goal whose `previously_succeeded_in_erased == Yes`, the opaque-type storage has changed enough to need reevaluation, the original typing mode is `TypingMode::ErasedNotCoherence(MayBeErased)`, and `should_rerun_after_erased_canonicalization` returns `EagerlyPropagateToParent`. Concretely: heavy opaque-type inference (async blocks, `impl Trait`, RPITs) under `-Znext-solver` where a goal is stalled, partially evaluated in erased mode, then re-probed by the fast path.
Common situations: Complex generic code with many `impl Trait`/async opaques compiled with `-Znext-solver`; nightly toolchain regression in the trait solver's opaque-type / erased-canonicalization handling; enabling `-Zdisable-fast-paths` may mask it (it forces `MayMakeProgress`); large monomorphized crates that stress the stalled-query cache.
Related errors
- no such thing as an opaque const
- this never happens at the root, we're never in erased mode h
- unexpected orig_value: {ty:?}
- unexpected orig_value: {ct:?}
- ConstEquate should not be emitted when `-Znext-solver` is ac
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/1a31bd165d388367.json.
Report an issue: GitHub.