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

  1. If you hit this as a user: report an ICE with the reproducer to https://github.com/rust-lang/rust/issues.
  2. 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.
  3. 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`.
  4. 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

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


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