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
- Report the ICE to rust-lang/rust with the asm! block and rustc version.
- Reduce the asm! macro invocation to the smallest block that still panics and include it in the report.
- If using a proc-macro that generates asm!, verify it does not attach register specifiers to const/sym/label operands.
- 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
- Only attach explicit registers (in("rax"), out("rbx"), inout("rcx")) to In/Out/InOut/SplitInOut operands — never to const("..."), sym fn, sym static, or label operands.
- When generating asm! from a macro, separate register operands from non-register operands into distinct lists and never cross-assign clobber names to them.
- Audit hand-written asm! blocks for stray explicit-register specs on a const/sym/label operand before relying on the conflict diagnostics.
- If you wrap asm! in a proc-macro, validate each operand's register spec matches its operand kind before emitting the call.
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
- unsupported integer: {self:?}
- unsupported float: {self:?}
- `homogeneous_aggregate` should not be called for scalable ve
- aggregates can't have `FieldsShape::Primitive`
- unreachable: invalid ExternAbi variant
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/da0a44605fdf0f58.json.
Report an issue: GitHub.