rust-lang/rust · error
already handled
Error message
already handled
What it means
This `unreachable!` fires in `lower_expr_inner` when an `ExprKind::Paren`, `ExprKind::ForLoop`, or `ExprKind::Closure` reaches the inner expr-lowering switch. These three node kinds are handled earlier in the pipeline (Paren is stripped, ForLoop and Closure have dedicated lowering paths invoked from `lower_expr`/`lower_expr_mut`), so their presence in `lower_expr_inner` means the earlier dispatch was skipped or the AST was malformed. It indicates a structural bug in lowering, not a user error.
Source
Thrown at compiler/rustc_ast_lowering/src/expr.rs:497
ExprKind::UnsafeBinderCast(kind, expr, ty) => hir::ExprKind::UnsafeBinderCast(
*kind,
self.lower_expr(expr),
ty.as_ref().map(|ty| {
self.lower_ty_alloc(
ty,
ImplTraitContext::Disallowed(ImplTraitPosition::Cast),
)
}),
),
ExprKind::Dummy => {
span_bug!(e.span, "lowered ExprKind::Dummy")
}
ExprKind::Try(sub_expr) => self.lower_expr_try(e.span, sub_expr),
ExprKind::Paren(_) | ExprKind::ForLoop { .. } | ExprKind::Closure(..) => {
unreachable!("already handled")
}
ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span),
ExprKind::DirectConstArg(expr) => {
let e = self.emit_bad_direct_const_arg(e.span, expr, "expression");
hir::ExprKind::Err(e)
}
};
hir::Expr { hir_id: expr_hir_id, kind, span }
})
}
pub(crate) fn lower_const_block(&mut self, c: &AnonConst) -> hir::ConstBlock {
self.with_new_scopes(c.value.span, |this| {
let def_id = this.local_def_id(c.id);
let hir_id = this.lower_node_id(c.id);View on GitHub (pinned to 22057b88b0)
Solutions
- If you hit this as a user: report an ICE with the reproducer to https://github.com/rust-lang/rust/issues.
- If developing rustc: ensure any new `lower_expr_inner` call site first dispatches `Paren`/`ForLoop`/`Closure` through `lower_expr`/`lower_expr_mut` or strips them.
- Audit proc-macro output: if a custom proc-macro synthesizes AST, ensure it doesn't emit bare `Paren`/`Closure` kinds in positions the compiler lowers via `lower_expr_inner`.
- Bisect to find the rustc commit that introduced the new (broken) call site.
Example fix
// before — rustc internal: calling lower_expr_inner directly on a Closure node let kind = self.lower_expr_inner(e); // after — route through lower_expr which dispatches Closure first let kind = self.lower_expr(e);
Defensive patterns
Strategy: fallback
Validate before calling
// "already handled" is an internal lowering invariant — an expression // kind reached a branch that asserts it was processed earlier. There is no // caller-side API to validate against it; the defense is source-level // simplification of the offending expression. // // Mitigation pattern: factor the expression into named `let` bindings so // each sub-expression takes a single, standard lowering path. // // Before: let v = f(g(h(x))); // // After: let a = h(x); let b = g(a); let v = f(b);
Prevention
- Simplify complex expressions; extract sub-expressions into named `let` bindings
- Bisect the file/expr that triggers the ICE and minimize the repro
- Try an equivalent but more conventional expression form
- Report as an ICE — this path should be unreachable from valid source
When it happens
Trigger: Triggered when `lower_expr_inner` is called directly (bypassing `lower_expr`) on a node whose kind is `Paren`, `ForLoop { .. }`, or `Closure(..)`. Concretely the arm at expr.rs:496-498 catches these three and aborts. Normally `lower_expr`/`lower_expr_mut` route these elsewhere first.
Common situations: Almost exclusively hit by rustc developers who added a new call site of `lower_expr_inner` without first unwrapping `Paren` or routing `ForLoop`/`Closure` through their dedicated lower functions. End users on stable virtually never see it; nightly users might if a regression lands. Also reachable via procedural-macro-generated AST that injects raw `Paren`/`Closure` nodes where they aren't expected.
Related errors
- shouldn't exist here
- must contain self type as `SelfTy` propagation kind is speci
- arg must exist for infer
- non-`async`/`gen` closure body turned `async`/`gen` during l
- {:?} shouldn't exist here
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/063a2a2197a2ad98.json.
Report an issue: GitHub.