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:
13
src/sema.zig
13
src/sema.zig
@@ -1360,6 +1360,13 @@ pub const Analyzer = struct {
|
||||
try self.analyzeNode(eb.body);
|
||||
self.popScope();
|
||||
},
|
||||
.asm_expr => |ae| {
|
||||
// Walk the template and each operand payload (input exprs;
|
||||
// out_value type exprs are leaves). Result-type derivation is
|
||||
// Phase B; lowering bails until then.
|
||||
try self.analyzeNode(ae.template);
|
||||
for (ae.operands) |op| try self.analyzeNode(op.payload);
|
||||
},
|
||||
.impl_block => |ib| {
|
||||
// Each impl block gets its own scope so methods don't conflict across impls
|
||||
try self.pushScope();
|
||||
@@ -1830,6 +1837,12 @@ pub fn findNodeAtOffset(node: *Node, offset: u32) ?*Node {
|
||||
if (findNodeAtOffset(d, offset)) |found| return found;
|
||||
}
|
||||
},
|
||||
.asm_expr => |ae| {
|
||||
if (findNodeAtOffset(ae.template, offset)) |found| return found;
|
||||
for (ae.operands) |op| {
|
||||
if (findNodeAtOffset(op.payload, offset)) |found| return found;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
Reference in New Issue
Block a user