rust-lang/rust · critical

{op:?} is not a register operand

Error message

{op:?} is not a register operand

What it means

unreachable! in asm lowering (line 361) inside the explicit-register conflict check. After matching the operand against In/Out/InOut/SplitInOut, the compiler asserts that Const, SymFn, SymStatic, and Label operands — which do not bind to a register — never reach the explicit-register-conflict branch. Hitting it means a non-register operand was paired with an explicit register operand (`Reg(reg)`) in a way the AST/HIR shape should forbid.

Source

Thrown at compiler/rustc_ast_lowering/src/asm.rs:361

                    continue;
                }

                // Check for conflicts between explicit register operands.
                if let asm::InlineAsmRegOrRegClass::Reg(reg) = reg {
                    let (input, output) = match op {
                        hir::InlineAsmOperand::In { .. } => (true, false),

                        // Late output do not conflict with inputs, but normal outputs do
                        hir::InlineAsmOperand::Out { late, .. } => (!late, true),

                        hir::InlineAsmOperand::InOut { .. }
                        | hir::InlineAsmOperand::SplitInOut { .. } => (true, true),

                        hir::InlineAsmOperand::Const { .. }
                        | hir::InlineAsmOperand::SymFn { .. }
                        | hir::InlineAsmOperand::SymStatic { .. }
                        | hir::InlineAsmOperand::Label { .. } => {
                            unreachable!("{op:?} is not a register operand");
                        }
                    };

                    // Flag to output the error only once per operand
                    let mut skip = false;

                    let mut check = |used_regs: &mut FxHashMap<asm::InlineAsmReg, usize>,
                                     input,
                                     r: asm::InlineAsmReg| {
                        match used_regs.entry(r) {
                            Entry::Occupied(o) => {
                                if skip {
                                    return;
                                }
                                skip = true;

                                let idx2 = *o.get();
                                let (ref op2, op_sp2) = operands[idx2];

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Report the ICE to rust-lang/rust with the asm! block and rustc version.
  2. Reduce the asm! macro invocation to the smallest block that still panics and include it in the report.
  3. If using a proc-macro that generates asm!, verify it does not attach register specifiers to const/sym/label operands.
  4. Pin to a stable or older nightly where the asm! feature worked.
Defensive patterns

Strategy: type-guard

Validate before calling

// The explicit-register conflict check only applies to In/Out/InOut operands.
// Const/SymFn/SymStatic/Label operands are skipped earlier; reaching the
// register check with one of them is an internal invariant violation.
// Before building the InlineAsm, partition operands:
fn register_operands<'a>(operands: &'a [hir::InlineAsmOperand<'_>]) -> impl Iterator<Item = &'a hir::InlineAsmOperand<'a>> {
    operands.iter().filter(|op| matches!(op, hir::InlineAsmOperand::In { .. } | hir::InlineAsmOperand::Out { .. } | hir::InlineAsmOperand::InOut { .. } | hir::InlineAsmOperand::SplitInOut { .. }))
}

Type guard

fn is_register_operand(op: &hir::InlineAsmOperand<'_>) -> bool {
    matches!(
        op,
        hir::InlineAsmOperand::In { .. }
            | hir::InlineAsmOperand::Out { .. }
            | hir::InlineAsmOperand::InOut { .. }
            | hir::InlineAsmOperand::SplitInOut { .. }
    )
}

Try / catch

// Tooling (rust-analyzer / clippy-like) that lowers asm:
let checked = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| lower_inline_asm(asm)));
match checked {
    Ok(hir_asm) => hir_asm,
    Err(_) => return Err("inline asm lowering panicked: non-register operand reached the register-conflict check"),
}

Prevention

When it happens

Trigger: Triggered during lowering of a core::arch::asm! block where an explicit register constraint (in("rax"), out("rbx"), etc.) is somehow attached to a Const/SymFn/SymStatic/Label operand. Should be structurally impossible because the AST does not allow register specifiers on those operand kinds; reachable only via malformed AST from a proc-macro or an internal bug.

Common situations: Nightly regressions in inline asm lowering; proc-macros that emit asm! AST with mismatched operand/register pairs; fuzzing of asm! inputs. Hand-written asm! with valid syntax hits normal diagnostics, not this panic.

Related errors


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