Skip to content

Commit df311e7

Browse files
committed
fix(zig): emit constructorSlots from stack lowering placeholder ops
The Zig compiler's stack lowering was emitting push_data("") for constructor parameter reads instead of proper placeholder ops. This meant emitArtifact never recorded constructorSlots in the JSON, so the SDK couldn't splice constructor args (PubKey, Addr, etc.) into the locking script at deployment. Now lowerPropertyRead emits a .placeholder instruction (with the correct param_index) when a readonly property without an initial value is referenced — matching the TS reference compiler's behavior. Also added placeholder handling to StackInstruction, emitStackInstruction, and the peephole optimizer's isPush/instEql helpers. P2PKH, Escrow, and all other stateless contracts now deploy and call correctly. 35/36 integration tests pass.
1 parent 9a1b43f commit df311e7

File tree

4 files changed

+15
-4
lines changed

4 files changed

+15
-4
lines changed

compilers/zig/src/codegen/emit.zig

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ pub fn emitStackInstruction(ctx: *EmitContext, inst: types.StackInstruction) !vo
230230
0;
231231
try ctx.emitScriptNumber(idx);
232232
},
233+
.placeholder => |ph| {
234+
try ctx.recordConstructorSlot(ph.param_index);
235+
try ctx.emitOpcode(.op_0);
236+
},
233237
}
234238
}
235239

compilers/zig/src/ir/types.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ pub const StackIf = struct { then: []StackOp, @"else": ?[]StackOp = null };
339339
pub const Placeholder = struct { param_index: u32, param_name: []const u8 };
340340
pub const PushValue = union(enum) { bytes: []const u8, integer: i64, boolean: bool };
341341

342-
pub const StackInstruction = union(enum) { op: Opcode, push_data: []const u8, push_int: i64, push_bool: bool, push_codesep_index: void };
342+
pub const StackInstruction = union(enum) { op: Opcode, push_data: []const u8, push_int: i64, push_bool: bool, push_codesep_index: void, placeholder: Placeholder };
343343

344344
// ============================================================================
345345
// Layer 4: Artifact Types (output of Pass 6: Emit) — maps to artifact.ts

compilers/zig/src/passes/peephole.zig

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const max_iterations = 100;
2020
/// Returns true if the instruction is any push variant (push_int, push_data, push_bool).
2121
fn isPush(inst: Inst) bool {
2222
return switch (inst) {
23-
.push_int, .push_data, .push_bool, .push_codesep_index => true,
23+
.push_int, .push_data, .push_bool, .push_codesep_index, .placeholder => true,
2424
.op => false,
2525
};
2626
}
@@ -61,6 +61,7 @@ fn instEql(a: Inst, b: Inst) bool {
6161
.push_bool => |ba| ba == b.push_bool,
6262
.push_data => |da| std.mem.eql(u8, da, b.push_data),
6363
.push_codesep_index => true,
64+
.placeholder => |pa| pa.param_index == b.placeholder.param_index,
6465
};
6566
}
6667

compilers/zig/src/passes/stack_lower.zig

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -938,8 +938,14 @@ const LowerCtx = struct {
938938
}
939939
}
940940
}
941-
// Not found — push placeholder
942-
try self.emitPushData("");
941+
// Not found — push constructor param placeholder
942+
// Determine param_index: count readonly properties before this one
943+
var param_idx: u32 = 0;
944+
for (self.program.properties) |prop| {
945+
if (std.mem.eql(u8, prop.name, prop_name)) break;
946+
if (prop.readonly and prop.initial_value == null) param_idx += 1;
947+
}
948+
try self.emit(.{ .placeholder = .{ .param_index = param_idx, .param_name = prop_name } });
943949
try self.stack.push(self.allocator, bind_name);
944950
self.trackDepth();
945951
}

0 commit comments

Comments
 (0)