feat(asm): Phase A.1 — parse asm { … } into AsmExpr; loud lowering bail

`asm volatile? { "tmpl", [name]? "constraint" (-> Type | = expr), …,
clobbers(.…) }` now parses into a flat-operand AsmExpr/AsmOperand (ast.zig +
parser.zig parseAsmExpr, dispatched from parsePrimary on .kw_asm). `volatile`
and `clobbers` are recognized contextually (not reserved). `-> @place`
write-through is rejected with a clear "Phase 2" parse error.

Codegen is not implemented yet (IR op + LLVM emit are Phases C–E), so lowering
bails LOUD + named via an explicit .asm_expr arm in lower/expr.zig (not the
generic unknown_expr else) — emitPlaceholder makes hasErrors() abort the build
on the message.

The new asm_expr tag forced (and got) arms in three exhaustive Node.Data
switches: sema.zig analyzeNode + findNodeAtOffset, semantic_diagnostics.zig
checkBindingNames — each recurses into template + operand payloads.

Design: adopted the operand auto-naming rule (design §II.5) — name auto-derived
from a {reg} pin, explicit [name] only when it differs or for register-class
operands, echo form rejected. Typing-stage rule; parser stores name: ?[]const u8.

Locked with examples/1640-platform-asm-parse.sx (multi-output divmod: named
operands, register pins, clobbers — parses then bails, called from main).

Also files issue 0137 (pre-existing, orthogonal: `sx run` with no `main`
segfaults via an unguarded JIT entry lookup in target.zig — not an asm bug).

zig build test green (648 corpus, 445 unit).
This commit is contained in:
agra
2026-06-15 20:21:25 +03:00
parent 3c9ecd0b42
commit f8e029d719
12 changed files with 355 additions and 12 deletions

View File

@@ -2189,6 +2189,16 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
.try_expr => |te| self.lowerTry(te.operand, node.span),
.catch_expr => |ce| self.lowerCatch(&ce, node.span),
.caller_location => self.lowerCallerLocation(node),
// Inline assembly parses (Phase A.1) but has no IR op / emit yet
// (Phases CE). Bail LOUDLY with a named diagnostic rather than falling
// into the generic `unknown_expr` arm — the placeholder Ref makes
// `hasErrors()` abort the build on this message (CLAUDE.md no-silent-arm).
.asm_expr => blk: {
if (self.diagnostics) |diags| {
diags.addFmt(.err, node.span, "inline assembly codegen is not yet implemented (ASM stream: lowering + emit land in Phases CE)", .{});
}
break :blk self.emitPlaceholder("inline_asm");
},
else => self.emitError("unknown_expr", node.span),
};
}