rust-lang/rust · critical

no sub-expr expected for {:?}

Error message

no sub-expr expected for {:?}

What it means

unreachable! in expr_guaranteed_to_constitute_read_for_never (compiler/rustc_middle/src/hir/mod.rs:299). When walking a place-expression child of a parent Expr, the match covers every ExprKind that can have a value-producing sub-expression; the leaf kinds (ConstBlock, Loop, Lit, Path, Continue, OffsetOf, Err) are listed as unreachable because they have no direct sub-exprs. Hitting this means the HIR contained an Expr child under one of those leaf kinds, which the compiler considers malformed.

Source

Thrown at compiler/rustc_middle/src/hir/mod.rs:299

                    | ExprKind::Block(_, _)
                    | ExprKind::AssignOp(_, _, _)
                    | ExprKind::Index(_, _, _)
                    | ExprKind::Break(_, _)
                    | ExprKind::Ret(_)
                    | ExprKind::Become(_)
                    | ExprKind::InlineAsm(_)
                    | ExprKind::Struct(_, _, _)
                    | ExprKind::Repeat(_, _)
                    | ExprKind::Yield(_, _) => true,

                    // These expressions have no (direct) sub-exprs.
                    ExprKind::ConstBlock(_)
                    | ExprKind::Loop(_, _, _, _)
                    | ExprKind::Lit(_)
                    | ExprKind::Path(_)
                    | ExprKind::Continue(_)
                    | ExprKind::OffsetOf(_, _)
                    | ExprKind::Err(_) => unreachable!("no sub-expr expected for {:?}", expr.kind),
                }
            }

            // If we have a subpattern that performs a read, we want to consider this
            // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
            Node::LetStmt(LetStmt { init: Some(target), pat, .. }) => {
                assert_eq!(target.hir_id, expr.hir_id);
                pat.is_guaranteed_to_constitute_read_for_never()
            }

            // These nodes (if they have a sub-expr) do constitute a read.
            Node::Block(_)
            | Node::Arm(_)
            | Node::ExprField(_)
            | Node::AnonConst(_)
            | Node::ConstBlock(_)
            | Node::ConstArg(_)
            | Node::Stmt(_)

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report as a rustc ICE with the ExprKind shown in the message and the source span.
  2. Bisect across nightly versions to find the change that introduced the unexpected sub-expression (likely a parser or HIR refactor).
  3. If you write proc-macros, ensure generated Expr nodes do not attach child Exprs to leaf kinds like Lit/Path/Loop.
Defensive patterns

Strategy: type-guard

Validate before calling

// Before requesting a sub-expression, verify the parent
// expression kind actually carries one.
use rustc_hir::{Expr, ExprKind, HirId};
use rustc_middle::ty::TyCtxt;

fn has_expected_sub_expr(expr: &Expr<'_>) -> bool {
    matches!(
        expr.kind,
        ExprKind::Binary(..)
            | ExprKind::Assign(..)
            | ExprKind::AssignOp(..)
            | ExprKind::Call(..)
            | ExprKind::MethodCall(..)
            | ExprKind::Match(..)
    )
}

Type guard

// Narrow an Expr to one that is safe to descend into for a
// sub-expression.
use rustc_hir::{Expr, ExprKind};

fn expr_with_sub_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
    match expr.kind {
        ExprKind::Binary(_, lhs, _) => Some(lhs),
        ExprKind::Assign(lhs, _, _) => Some(lhs),
        ExprKind::Call(callee, _) => Some(callee),
        ExprKind::Match(scrut, _, _) => Some(scrut),
        _ => None, // kinds with no sub-expr => do not descend
    }
}

Prevention

When it happens

Trigger: An Expr node whose kind is one of the listed leaf variants unexpectedly has a child expression passed to expr_guaranteed_to_constitute_read_for_never. Caused by malformed HIR from a parser/recovery bug, a proc-macro that synthesized an inconsistent Expr tree, or a refactor adding a new sub-expr to a leaf kind without updating this match.

Common situations: Nightly-only features or proc-macros that build unusual Expr shapes; changes to the never-type/`!` coercion logic; recovery after a syntax error producing an ExprKind::Err with children.

Related errors


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