astgen: port Phases 1-3 from upstream AstGen.zig

Replace fixed-size GenZir instruction array with shared dynamic scratch
array matching the upstream design. Add expression types: grouped_expression,
unreachable_literal, enum_literal, multiline_string_literal, return, call,
struct_init, try. Add @cImport/@cInclude support. Fix fn_decl src_node
to use the fn_decl node (not proto_node). Fix GenZir unstack ordering
so fn_block is unstacked before adding instructions to decl_block.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 00:42:01 +02:00
parent b12d338f4f
commit 5527ad61e6
2 changed files with 674 additions and 46 deletions

716
astgen.c
View File

@@ -78,27 +78,79 @@ typedef struct {
uint32_t* decl_nodes; // node indices uint32_t* decl_nodes; // node indices
uint32_t decl_table_len; uint32_t decl_table_len;
uint32_t decl_table_cap; uint32_t decl_table_cap;
// Shared dynamic array for GenZir instructions (AstGen.zig:11796).
// Sub-blocks share this array and track their slice via
// instructions_top.
uint32_t* scratch_instructions;
uint32_t scratch_inst_len;
uint32_t scratch_inst_cap;
// Return type ref for the current function (set during fnDecl/testDecl).
uint32_t fn_ret_ty; // ZirInstRef
bool has_compile_errors; bool has_compile_errors;
} AstGenCtx; } AstGenCtx;
// --- GenZir scope (mirrors GenZir struct, AstGen.zig:11756) --- // --- GenZir scope (mirrors GenZir struct, AstGen.zig:11772) ---
// //
// instructions/instructions_len track which instructions belong to this // Sub-blocks share the parent AstGenCtx's scratch_instructions array and
// sub-block (mirroring GenZir.instructions in Zig). In Zig the sub-blocks // record their starting offset (instructions_top). This mirrors the upstream
// share a parent ArrayList and record a starting offset; here we use a // GenZir.instructions / instructions_top design (AstGen.zig:11796-11850).
// simple local array since the bodies are small.
#define GENZIR_MAX_BODY 64
typedef struct { typedef struct {
AstGenCtx* astgen; AstGenCtx* astgen;
uint32_t decl_node_index; uint32_t decl_node_index;
uint32_t decl_line; uint32_t decl_line;
bool is_comptime; bool is_comptime;
uint32_t instructions[GENZIR_MAX_BODY]; bool c_import; // true inside @cImport block
uint32_t instructions_len; uint32_t instructions_top; // start index in shared array
} GenZir; } GenZir;
// --- GenZir instruction helpers (AstGen.zig:11830-11850) ---
// Returns the number of instructions in this scope.
static uint32_t gzInstructionsLen(const GenZir* gz) {
return gz->astgen->scratch_inst_len - gz->instructions_top;
}
// Returns pointer to start of this scope's instructions in the shared array.
static const uint32_t* gzInstructionsSlice(const GenZir* gz) {
return gz->astgen->scratch_instructions + gz->instructions_top;
}
// Mirrors GenZir.unstack (AstGen.zig:11822).
// Restores the shared array length to this scope's start.
static void gzUnstack(GenZir* gz) {
gz->astgen->scratch_inst_len = gz->instructions_top;
}
// Append an instruction index to this scope's portion of the shared array.
static void gzAppendInstruction(GenZir* gz, uint32_t inst_idx) {
AstGenCtx* ag = gz->astgen;
if (ag->scratch_inst_len >= ag->scratch_inst_cap) {
uint32_t new_cap
= ag->scratch_inst_cap > 0 ? ag->scratch_inst_cap * 2 : 64;
uint32_t* p
= realloc(ag->scratch_instructions, new_cap * sizeof(uint32_t));
if (!p)
exit(1);
ag->scratch_instructions = p;
ag->scratch_inst_cap = new_cap;
}
ag->scratch_instructions[ag->scratch_inst_len++] = inst_idx;
}
// Mirrors GenZir.makeSubBlock (AstGen.zig:11852).
static GenZir makeSubBlock(GenZir* parent) {
GenZir sub;
memset(&sub, 0, sizeof(sub));
sub.astgen = parent->astgen;
sub.decl_node_index = parent->decl_node_index;
sub.decl_line = parent->decl_line;
sub.is_comptime = parent->is_comptime;
sub.c_import = parent->c_import;
sub.instructions_top = parent->astgen->scratch_inst_len;
return sub;
}
// --- Capacity helpers --- // --- Capacity helpers ---
static void ensureExtraCapacity(AstGenCtx* ag, uint32_t additional) { static void ensureExtraCapacity(AstGenCtx* ag, uint32_t additional) {
@@ -178,8 +230,7 @@ static uint32_t addInstruction(GenZir* gz, ZirInstTag tag, ZirInstData data) {
ag->inst_datas[idx] = data; ag->inst_datas[idx] = data;
ag->inst_len++; ag->inst_len++;
// Record in sub-block body. // Record in sub-block body.
assert(gz->instructions_len < GENZIR_MAX_BODY); gzAppendInstruction(gz, idx);
gz->instructions[gz->instructions_len++] = idx;
return idx + ZIR_REF_START_INDEX; // toRef() return idx + ZIR_REF_START_INDEX; // toRef()
} }
@@ -205,6 +256,36 @@ static uint32_t addPlNodeBin(
return addInstruction(gz, tag, data); return addInstruction(gz, tag, data);
} }
// Forward declaration.
static int32_t tokenIndexToRelative(const GenZir* gz, uint32_t token);
// Mirrors GenZir.addUnNode (AstGen.zig:12406).
static uint32_t addUnNode(
GenZir* gz, ZirInstTag tag, uint32_t operand, uint32_t node) {
ZirInstData data;
data.un_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
data.un_node.operand = operand;
return addInstruction(gz, tag, data);
}
// Mirrors GenZir.addStrTok (AstGen.zig:12349).
static uint32_t addStrTok(
GenZir* gz, ZirInstTag tag, uint32_t str_index, uint32_t token) {
ZirInstData data;
data.str_tok.start = str_index;
data.str_tok.src_tok = tokenIndexToRelative(gz, token);
return addInstruction(gz, tag, data);
}
// Mirrors GenZir.addPlNodePayloadIndex (AstGen.zig:12332).
static uint32_t addPlNodePayloadIndex(
GenZir* gz, ZirInstTag tag, uint32_t node, uint32_t payload_index) {
ZirInstData data;
data.pl_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
data.pl_node.payload_index = payload_index;
return addInstruction(gz, tag, data);
}
// --- Source cursor (AstGen.zig:13335-13359) --- // --- Source cursor (AstGen.zig:13335-13359) ---
// Mirrors AstGen.advanceSourceCursor (AstGen.zig:13342). // Mirrors AstGen.advanceSourceCursor (AstGen.zig:13342).
@@ -397,11 +478,62 @@ static uint32_t makeBreakInline(GenZir* gz, uint32_t block_inst,
ag->inst_len++; ag->inst_len++;
// Record in sub-block body. // Record in sub-block body.
assert(gz->instructions_len < GENZIR_MAX_BODY); gzAppendInstruction(gz, idx);
gz->instructions[gz->instructions_len++] = idx;
return idx; return idx;
} }
// Mirrors GenZir.makeBlockInst (AstGen.zig:12890).
// Creates a pl_node instruction with payload_index left as 0 (set later).
// Does NOT append to gz's instruction list.
// Returns instruction index (not a ref).
static uint32_t makeBlockInst(
AstGenCtx* ag, ZirInstTag tag, const GenZir* gz, uint32_t node) {
ensureInstCapacity(ag, 1);
uint32_t idx = ag->inst_len;
ag->inst_tags[idx] = tag;
ZirInstData data;
memset(&data, 0, sizeof(data));
data.pl_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
data.pl_node.payload_index = 0; // set later
ag->inst_datas[idx] = data;
ag->inst_len++;
return idx;
}
// Mirrors GenZir.setBlockBody (AstGen.zig:11949).
// Writes Block payload (body_len + instruction indices) to extra.
// Sets the instruction's payload_index. Unstacks gz.
static void setBlockBody(AstGenCtx* ag, GenZir* gz, uint32_t inst) {
uint32_t body_len = gzInstructionsLen(gz);
const uint32_t* body = gzInstructionsSlice(gz);
ensureExtraCapacity(ag, 1 + body_len);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = body_len; // Block.body_len
for (uint32_t i = 0; i < body_len; i++) {
ag->extra[ag->extra_len++] = body[i];
}
ag->inst_datas[inst].pl_node.payload_index = payload_index;
gzUnstack(gz);
}
// Mirrors GenZir.setTryBody (AstGen.zig:11997).
// Writes Try payload (operand + body_len + instruction indices) to extra.
// Sets the instruction's payload_index. Unstacks gz.
static void setTryBody(
AstGenCtx* ag, GenZir* gz, uint32_t inst, uint32_t operand) {
uint32_t body_len = gzInstructionsLen(gz);
const uint32_t* body = gzInstructionsSlice(gz);
ensureExtraCapacity(ag, 2 + body_len);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = operand; // Try.operand
ag->extra[ag->extra_len++] = body_len; // Try.body_len
for (uint32_t i = 0; i < body_len; i++) {
ag->extra[ag->extra_len++] = body[i];
}
ag->inst_datas[inst].pl_node.payload_index = payload_index;
gzUnstack(gz);
}
// Does this Declaration.Flags.Id have a name? (Zir.zig:2762) // Does this Declaration.Flags.Id have a name? (Zir.zig:2762)
static bool declIdHasName(DeclFlagsId id) { static bool declIdHasName(DeclFlagsId id) {
return id != DECL_ID_UNNAMED_TEST && id != DECL_ID_COMPTIME; return id != DECL_ID_UNNAMED_TEST && id != DECL_ID_COMPTIME;
@@ -657,13 +789,26 @@ typedef enum {
RL_NONE, // Just compute the value. RL_NONE, // Just compute the value.
RL_REF, // Compute a pointer to the value. RL_REF, // Compute a pointer to the value.
RL_DISCARD, // Compute but discard (emit ensure_result_non_error). RL_DISCARD, // Compute but discard (emit ensure_result_non_error).
RL_TY, // Coerce to specific type.
RL_COERCED_TY, // Coerce to specific type, result is the coercion.
} ResultLocTag;
typedef struct {
ResultLocTag tag;
uint32_t ty_inst; // ZirInstRef, used for RL_TY/RL_COERCED_TY.
} ResultLoc; } ResultLoc;
#define RL_NONE_VAL ((ResultLoc) { .tag = RL_NONE, .ty_inst = 0 })
#define RL_REF_VAL ((ResultLoc) { .tag = RL_REF, .ty_inst = 0 })
#define RL_DISCARD_VAL ((ResultLoc) { .tag = RL_DISCARD, .ty_inst = 0 })
// --- Expression evaluation (AstGen.zig:634) --- // --- Expression evaluation (AstGen.zig:634) ---
// Forward declaration. // Forward declarations.
static uint32_t expr(GenZir* gz, uint32_t node); static uint32_t expr(GenZir* gz, uint32_t node);
static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node); static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node);
static void emitDbgStmt(GenZir* gz, uint32_t line, uint32_t column);
static void emitDbgNode(GenZir* gz, uint32_t node);
// Mirrors numberLiteral (AstGen.zig:8679). // Mirrors numberLiteral (AstGen.zig:8679).
// Handles literals "0" and "1" as built-in refs. // Handles literals "0" and "1" as built-in refs.
@@ -730,6 +875,60 @@ static uint32_t builtinCallImport(GenZir* gz, uint32_t node) {
return result_ref; return result_ref;
} }
// Mirrors cImport (AstGen.zig:10011).
static uint32_t cImportExpr(GenZir* gz, uint32_t node) {
AstGenCtx* ag = gz->astgen;
AstData nd = ag->tree->nodes.datas[node];
uint32_t body_node = nd.lhs; // first arg = body
uint32_t block_inst = makeBlockInst(ag, ZIR_INST_C_IMPORT, gz, node);
GenZir block_scope = makeSubBlock(gz);
block_scope.is_comptime = true;
block_scope.c_import = true;
uint32_t block_result = expr(&block_scope, body_node);
// ensure_result_used (AstGen.zig:10029).
addUnNode(&block_scope, ZIR_INST_ENSURE_RESULT_USED, block_result, node);
// break_inline if not noreturn (AstGen.zig:10030-10032).
if (block_result != ZIR_REF_UNREACHABLE_VALUE) {
makeBreakInline(&block_scope, block_inst, ZIR_REF_VOID_VALUE,
AST_NODE_OFFSET_NONE);
}
setBlockBody(ag, &block_scope, block_inst);
// block_scope unstacked now, can add to gz.
gzAppendInstruction(gz, block_inst);
return block_inst + ZIR_REF_START_INDEX; // toRef()
}
// Mirrors simpleCBuiltin (AstGen.zig:9938).
static uint32_t simpleCBuiltin(
GenZir* gz, uint32_t node, uint32_t operand_node, uint16_t ext_tag) {
AstGenCtx* ag = gz->astgen;
// Evaluate operand as comptime string.
uint32_t operand = expr(gz, operand_node);
// Emit extended instruction with UnNode payload (AstGen.zig:9954).
ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = operand;
ZirInstData data;
data.extended.opcode = ext_tag;
data.extended.small = 0;
data.extended.operand = payload_index;
addInstruction(gz, ZIR_INST_EXTENDED, data);
return ZIR_REF_VOID_VALUE;
}
// Mirrors builtinCall (AstGen.zig:9191) dispatch. // Mirrors builtinCall (AstGen.zig:9191) dispatch.
static uint32_t builtinCall(GenZir* gz, uint32_t node) { static uint32_t builtinCall(GenZir* gz, uint32_t node) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
@@ -751,9 +950,16 @@ static uint32_t builtinCall(GenZir* gz, uint32_t node) {
} }
uint32_t name_len = name_end - name_start; uint32_t name_len = name_end - name_start;
if (name_len == 6 && memcmp(source + name_start, "import", 6) == 0) { // clang-format off
if (name_len == 6 && memcmp(source + name_start, "import", 6) == 0)
return builtinCallImport(gz, node); return builtinCallImport(gz, node);
if (name_len == 7 && memcmp(source + name_start, "cImport", 7) == 0)
return cImportExpr(gz, node);
if (name_len == 8 && memcmp(source + name_start, "cInclude", 8) == 0) {
AstData nd = tree->nodes.datas[node];
return simpleCBuiltin(gz, node, nd.lhs, (uint16_t)ZIR_EXT_C_INCLUDE);
} }
// clang-format on
// TODO: handle other builtins. // TODO: handle other builtins.
ag->has_compile_errors = true; ag->has_compile_errors = true;
@@ -820,7 +1026,7 @@ static uint32_t identifierExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
for (uint32_t i = 0; i < ag->decl_table_len; i++) { for (uint32_t i = 0; i < ag->decl_table_len; i++) {
if (ag->decl_names[i] == name_str) { if (ag->decl_names[i] == name_str) {
ZirInstTag itag ZirInstTag itag
= (rl == RL_REF) ? ZIR_INST_DECL_REF : ZIR_INST_DECL_VAL; = (rl.tag == RL_REF) ? ZIR_INST_DECL_REF : ZIR_INST_DECL_VAL;
ZirInstData data; ZirInstData data;
data.str_tok.start = name_str; data.str_tok.start = name_str;
data.str_tok.src_tok = tokenIndexToRelative(gz, ident_token); data.str_tok.src_tok = tokenIndexToRelative(gz, ident_token);
@@ -849,7 +1055,7 @@ static uint32_t fieldAccessExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
// Evaluate the LHS object expression (AstGen.zig:6181). // Evaluate the LHS object expression (AstGen.zig:6181).
// For .ref rl, LHS is also evaluated with .ref (AstGen.zig:6161). // For .ref rl, LHS is also evaluated with .ref (AstGen.zig:6161).
ResultLoc lhs_rl = (rl == RL_REF) ? RL_REF : RL_NONE; ResultLoc lhs_rl = (rl.tag == RL_REF) ? RL_REF_VAL : RL_NONE_VAL;
uint32_t lhs = exprRl(gz, lhs_rl, object_node); uint32_t lhs = exprRl(gz, lhs_rl, object_node);
// Emit field_val instruction with Field payload (AstGen.zig:6186-6189). // Emit field_val instruction with Field payload (AstGen.zig:6186-6189).
@@ -859,7 +1065,8 @@ static uint32_t fieldAccessExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
ag->extra[ag->extra_len++] = str_index; // Field.field_name_start ag->extra[ag->extra_len++] = str_index; // Field.field_name_start
// .ref → field_ptr, else → field_val (AstGen.zig:6160-6164). // .ref → field_ptr, else → field_val (AstGen.zig:6160-6164).
ZirInstTag tag = (rl == RL_REF) ? ZIR_INST_FIELD_PTR : ZIR_INST_FIELD_VAL; ZirInstTag tag
= (rl.tag == RL_REF) ? ZIR_INST_FIELD_PTR : ZIR_INST_FIELD_VAL;
ZirInstData data; ZirInstData data;
data.pl_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index; data.pl_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
data.pl_node.payload_index = payload_index; data.pl_node.payload_index = payload_index;
@@ -910,7 +1117,7 @@ static uint32_t ptrTypeExpr(GenZir* gz, uint32_t node) {
} }
// Evaluate element type. // Evaluate element type.
uint32_t elem_type = exprRl(gz, RL_NONE, child_type_node); uint32_t elem_type = exprRl(gz, RL_NONE_VAL, child_type_node);
// Build PtrType payload: { elem_type, src_node }. // Build PtrType payload: { elem_type, src_node }.
ensureExtraCapacity(ag, 2); ensureExtraCapacity(ag, 2);
@@ -939,8 +1146,8 @@ static uint32_t arrayTypeExpr(GenZir* gz, uint32_t node) {
AstData nd = ag->tree->nodes.datas[node]; AstData nd = ag->tree->nodes.datas[node];
// data.lhs = length expr node, data.rhs = element type node. // data.lhs = length expr node, data.rhs = element type node.
uint32_t len = exprRl(gz, RL_NONE, nd.lhs); uint32_t len = exprRl(gz, RL_NONE_VAL, nd.lhs);
uint32_t elem_type = exprRl(gz, RL_NONE, nd.rhs); uint32_t elem_type = exprRl(gz, RL_NONE_VAL, nd.rhs);
return addPlNodeBin(gz, ZIR_INST_ARRAY_TYPE, node, len, elem_type); return addPlNodeBin(gz, ZIR_INST_ARRAY_TYPE, node, len, elem_type);
} }
@@ -1012,12 +1219,12 @@ static uint32_t arrayInitExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
|| tree->source[id_start + 1] == '_'))) { || tree->source[id_start + 1] == '_'))) {
// Inferred length: addInt(elem_count) (AstGen.zig:1452). // Inferred length: addInt(elem_count) (AstGen.zig:1452).
uint32_t len_inst = addInt(gz, elem_count); uint32_t len_inst = addInt(gz, elem_count);
uint32_t elem_type = exprRl(gz, RL_NONE, elem_type_node); uint32_t elem_type = exprRl(gz, RL_NONE_VAL, elem_type_node);
uint32_t array_type_inst = addPlNodeBin(gz, uint32_t array_type_inst = addPlNodeBin(gz,
ZIR_INST_ARRAY_TYPE, type_expr_node, len_inst, elem_type); ZIR_INST_ARRAY_TYPE, type_expr_node, len_inst, elem_type);
// arrayInitExprTyped (AstGen.zig:1507/1509). // arrayInitExprTyped (AstGen.zig:1507/1509).
bool is_ref = (rl == RL_REF); bool is_ref = (rl.tag == RL_REF);
// Build MultiOp payload: operands_len, then type + elements. // Build MultiOp payload: operands_len, then type + elements.
uint32_t operands_len = elem_count + 1; // +1 for type uint32_t operands_len = elem_count + 1; // +1 for type
ensureExtraCapacity(ag, 1 + operands_len); ensureExtraCapacity(ag, 1 + operands_len);
@@ -1025,7 +1232,7 @@ static uint32_t arrayInitExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
ag->extra[ag->extra_len++] = operands_len; ag->extra[ag->extra_len++] = operands_len;
ag->extra[ag->extra_len++] = array_type_inst; // type ref ag->extra[ag->extra_len++] = array_type_inst; // type ref
for (uint32_t i = 0; i < elem_count; i++) { for (uint32_t i = 0; i < elem_count; i++) {
uint32_t elem_ref = exprRl(gz, RL_NONE, elements[i]); uint32_t elem_ref = exprRl(gz, RL_NONE_VAL, elements[i]);
ag->extra[ag->extra_len++] = elem_ref; ag->extra[ag->extra_len++] = elem_ref;
} }
ZirInstTag init_tag ZirInstTag init_tag
@@ -1049,11 +1256,373 @@ static uint32_t arrayInitExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
static uint32_t simpleBinOp(GenZir* gz, uint32_t node, ZirInstTag op_tag) { static uint32_t simpleBinOp(GenZir* gz, uint32_t node, ZirInstTag op_tag) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
AstData nd = ag->tree->nodes.datas[node]; AstData nd = ag->tree->nodes.datas[node];
uint32_t lhs = exprRl(gz, RL_NONE, nd.lhs); uint32_t lhs = exprRl(gz, RL_NONE_VAL, nd.lhs);
uint32_t rhs = exprRl(gz, RL_NONE, nd.rhs); uint32_t rhs = exprRl(gz, RL_NONE_VAL, nd.rhs);
return addPlNodeBin(gz, op_tag, node, lhs, rhs); return addPlNodeBin(gz, op_tag, node, lhs, rhs);
} }
// --- multilineStringLiteral (AstGen.zig:8645) ---
// Port of strLitNodeAsString for multiline strings.
static uint32_t multilineStringLiteral(GenZir* gz, uint32_t node) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node];
uint32_t start_tok = nd.lhs;
uint32_t end_tok = nd.rhs;
uint32_t str_index = ag->string_bytes_len;
// First line: no preceding newline.
for (uint32_t tok_i = start_tok; tok_i <= end_tok; tok_i++) {
uint32_t tok_start = tree->tokens.starts[tok_i];
const char* source = tree->source;
// Skip leading `\\` (2 chars).
uint32_t content_start = tok_start + 2;
// Find end of line.
uint32_t content_end = content_start;
while (content_end < tree->source_len && source[content_end] != '\n')
content_end++;
uint32_t line_len = content_end - content_start;
if (tok_i > start_tok) {
// Prepend newline for lines after the first.
ensureStringBytesCapacity(ag, line_len + 1);
ag->string_bytes[ag->string_bytes_len++] = '\n';
} else {
ensureStringBytesCapacity(ag, line_len);
}
memcpy(ag->string_bytes + ag->string_bytes_len, source + content_start,
line_len);
ag->string_bytes_len += line_len;
}
uint32_t len = ag->string_bytes_len - str_index;
ensureStringBytesCapacity(ag, 1);
ag->string_bytes[ag->string_bytes_len++] = 0; // null terminator
ZirInstData data;
data.str.start = str_index;
data.str.len = len;
return addInstruction(gz, ZIR_INST_STR, data);
}
// --- ret (AstGen.zig:8119) ---
// Simplified: no defer handling.
static uint32_t retExpr(GenZir* gz, uint32_t node) {
const AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
if (!gz->is_comptime) {
emitDbgNode(gz, node);
}
AstData nd = tree->nodes.datas[node];
uint32_t operand_node = nd.lhs; // optional
if (operand_node == 0) {
// Void return (AstGen.zig:8155).
addUnNode(gz, ZIR_INST_RET_NODE, ZIR_REF_VOID_VALUE, node);
return ZIR_REF_UNREACHABLE_VALUE;
}
// Evaluate operand (simplified: no coercion to fn_ret_ty yet).
uint32_t operand = expr(gz, operand_node);
addUnNode(gz, ZIR_INST_RET_NODE, operand, node);
return ZIR_REF_UNREACHABLE_VALUE;
}
// --- calleeExpr (AstGen.zig:10183) ---
// Returns: 0 = direct call, 1 = field call.
typedef struct {
bool is_field;
uint32_t obj_ptr; // for field calls: ref to object
uint32_t field_name_start; // for field calls: string index
uint32_t direct; // for direct calls: ref to callee
} Callee;
static Callee calleeExpr(GenZir* gz, uint32_t fn_expr_node) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstNodeTag tag = tree->nodes.tags[fn_expr_node];
if (tag == AST_NODE_FIELD_ACCESS) {
AstData nd = tree->nodes.datas[fn_expr_node];
uint32_t object_node = nd.lhs;
uint32_t field_ident = nd.rhs;
uint32_t str_index = identAsString(ag, field_ident);
// Evaluate object with .ref rl (AstGen.zig:10207).
uint32_t lhs = exprRl(gz, RL_REF_VAL, object_node);
emitDbgNode(gz, fn_expr_node);
Callee c;
c.is_field = true;
c.obj_ptr = lhs;
c.field_name_start = str_index;
c.direct = 0;
return c;
}
// Default: direct call (AstGen.zig:10235).
Callee c;
c.is_field = false;
c.direct = expr(gz, fn_expr_node);
c.obj_ptr = 0;
c.field_name_start = 0;
return c;
}
// --- callExpr (AstGen.zig:10058) ---
static uint32_t callExpr(GenZir* gz, uint32_t node) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstNodeTag tag = tree->nodes.tags[node];
AstData nd = tree->nodes.datas[node];
// Extract callee and args from AST.
uint32_t fn_expr_node;
uint32_t arg_buf[2];
const uint32_t* args = NULL;
uint32_t args_len = 0;
uint32_t lparen_tok;
switch (tag) {
case AST_NODE_CALL_ONE:
case AST_NODE_CALL_ONE_COMMA: {
fn_expr_node = nd.lhs;
lparen_tok = tree->nodes.main_tokens[node];
if (nd.rhs != 0) {
arg_buf[0] = nd.rhs;
args = arg_buf;
args_len = 1;
}
break;
}
case AST_NODE_CALL:
case AST_NODE_CALL_COMMA: {
fn_expr_node = nd.lhs;
lparen_tok = tree->nodes.main_tokens[node];
uint32_t extra_idx = nd.rhs;
uint32_t range_start = tree->extra_data.arr[extra_idx];
uint32_t range_end = tree->extra_data.arr[extra_idx + 1];
args = tree->extra_data.arr + range_start;
args_len = range_end - range_start;
break;
}
default:
ag->has_compile_errors = true;
return ZIR_REF_VOID_VALUE;
}
Callee callee = calleeExpr(gz, fn_expr_node);
// dbg_stmt before call (AstGen.zig:10082).
{
advanceSourceCursor(ag, tree->tokens.starts[lparen_tok]);
uint32_t line = ag->source_line - gz->decl_line;
uint32_t column = ag->source_column;
emitDbgStmt(gz, line, column);
}
// Reserve instruction slot for call (AstGen.zig:10093).
uint32_t call_index = ag->inst_len;
ensureInstCapacity(ag, 1);
memset(&ag->inst_datas[call_index], 0, sizeof(ZirInstData));
ag->inst_tags[call_index] = (ZirInstTag)0;
ag->inst_len++;
gzAppendInstruction(gz, call_index);
// Process arguments in sub-blocks (AstGen.zig:10100-10115).
// Simplified: we collect arg body lengths into extra.
uint32_t scratch_top = ag->extra_len;
// Reserve space for arg body lengths.
ensureExtraCapacity(ag, args_len);
uint32_t arg_lengths_start = ag->extra_len;
ag->extra_len += args_len;
for (uint32_t i = 0; i < args_len; i++) {
GenZir arg_block = makeSubBlock(gz);
uint32_t arg_ref = expr(&arg_block, args[i]);
// break_inline with param_node src (AstGen.zig:10107).
int32_t param_src
= (int32_t)args[i] - (int32_t)arg_block.decl_node_index;
makeBreakInline(&arg_block, call_index, arg_ref, param_src);
// Copy arg_block body to extra.
uint32_t body_len = gzInstructionsLen(&arg_block);
const uint32_t* body = gzInstructionsSlice(&arg_block);
ensureExtraCapacity(ag, body_len);
for (uint32_t j = 0; j < body_len; j++) {
ag->extra[ag->extra_len++] = body[j];
}
// Record cumulative body length (AstGen.zig:10113).
ag->extra[arg_lengths_start + i]
= ag->extra_len - scratch_top - args_len;
gzUnstack(&arg_block);
}
// Build call payload (AstGen.zig:10124-10168).
if (callee.is_field) {
// FieldCall payload: obj_ptr, field_name_start, flags.
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = callee.obj_ptr;
ag->extra[ag->extra_len++] = callee.field_name_start;
// flags: pop_error_return_trace=true, modifier=auto, args_len
uint32_t flags = (1u << 0) // pop_error_return_trace
| ((args_len & 0x1FFFFFFFu) << 3); // packed_modifier = auto (0)
ag->extra[ag->extra_len++] = flags;
ag->inst_tags[call_index] = ZIR_INST_FIELD_CALL;
ag->inst_datas[call_index].pl_node.src_node
= (int32_t)node - (int32_t)gz->decl_node_index;
ag->inst_datas[call_index].pl_node.payload_index = payload_index;
} else {
// Call payload: callee, flags.
ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = callee.direct;
// flags: pop_error_return_trace=true, modifier=auto, args_len
uint32_t flags = (1u << 0) // pop_error_return_trace
| ((args_len & 0x1FFFFFFFu) << 3); // packed_modifier = auto (0)
ag->extra[ag->extra_len++] = flags;
ag->inst_tags[call_index] = ZIR_INST_CALL;
ag->inst_datas[call_index].pl_node.src_node
= (int32_t)node - (int32_t)gz->decl_node_index;
ag->inst_datas[call_index].pl_node.payload_index = payload_index;
}
return call_index + ZIR_REF_START_INDEX;
}
// --- structInitExpr (AstGen.zig:1674) ---
// Simplified: handles .{} (empty tuple), .{.a = b} (anon init).
static uint32_t structInitExpr(GenZir* gz, ResultLoc rl, uint32_t node) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstNodeTag tag = tree->nodes.tags[node];
AstData nd = tree->nodes.datas[node];
// Extract type_expr and fields.
uint32_t type_expr_node = 0; // 0 = anonymous (.{...})
uint32_t field_buf[2];
const uint32_t* fields = NULL;
uint32_t fields_len = 0;
switch (tag) {
case AST_NODE_STRUCT_INIT_DOT_TWO:
case AST_NODE_STRUCT_INIT_DOT_TWO_COMMA: {
// .{.a = lhs, .b = rhs}
uint32_t idx = 0;
if (nd.lhs != 0)
field_buf[idx++] = nd.lhs;
if (nd.rhs != 0)
field_buf[idx++] = nd.rhs;
fields = field_buf;
fields_len = idx;
break;
}
case AST_NODE_STRUCT_INIT_DOT:
case AST_NODE_STRUCT_INIT_DOT_COMMA: {
uint32_t start = nd.lhs;
uint32_t end = nd.rhs;
fields = tree->extra_data.arr + start;
fields_len = end - start;
break;
}
case AST_NODE_STRUCT_INIT_ONE:
case AST_NODE_STRUCT_INIT_ONE_COMMA: {
type_expr_node = nd.lhs;
if (nd.rhs != 0) {
field_buf[0] = nd.rhs;
fields = field_buf;
fields_len = 1;
}
break;
}
default:
ag->has_compile_errors = true;
return ZIR_REF_VOID_VALUE;
}
if (type_expr_node == 0 && fields_len == 0) {
// .{} with rl.none/ref → empty_tuple (AstGen.zig:1694).
(void)rl;
return ZIR_REF_EMPTY_TUPLE;
}
if (type_expr_node == 0 && fields_len > 0) {
// Anonymous struct init (AstGen.zig:1864).
// StructInitAnon payload: abs_node, abs_line, fields_len.
ensureExtraCapacity(ag, 3 + fields_len * 2);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = node; // abs_node
ag->extra[ag->extra_len++] = ag->source_line; // abs_line
ag->extra[ag->extra_len++] = fields_len;
// Reserve space for field entries.
uint32_t items_start = ag->extra_len;
ag->extra_len += fields_len * 2;
for (uint32_t i = 0; i < fields_len; i++) {
uint32_t field_init = fields[i];
// field name is 2 tokens before the field init's first token.
uint32_t name_token = firstToken(tree, field_init) - 2;
uint32_t str_index = identAsString(ag, name_token);
uint32_t init_ref = expr(gz, field_init);
ag->extra[items_start + i * 2] = str_index;
ag->extra[items_start + i * 2 + 1] = init_ref;
}
return addPlNodePayloadIndex(
gz, ZIR_INST_STRUCT_INIT_ANON, node, payload_index);
}
// Typed init: evaluate type, emit struct_init_empty or struct_init.
if (type_expr_node != 0 && fields_len == 0) {
uint32_t ty_inst = expr(gz, type_expr_node);
return addUnNode(gz, ZIR_INST_STRUCT_INIT_EMPTY, ty_inst, node);
}
// TODO: typed struct init with fields.
ag->has_compile_errors = true;
return ZIR_REF_VOID_VALUE;
}
// --- tryExpr (AstGen.zig:5957) ---
// Simplified: no defer handling.
static uint32_t tryExpr(GenZir* gz, uint32_t node) {
AstGenCtx* ag = gz->astgen;
AstData nd = ag->tree->nodes.datas[node];
uint32_t operand_node = nd.lhs;
if (!gz->is_comptime) {
emitDbgNode(gz, node);
}
// Evaluate operand (AstGen.zig:6001).
uint32_t operand = expr(gz, operand_node);
// Create try block instruction (AstGen.zig:6007).
uint32_t try_inst = makeBlockInst(ag, ZIR_INST_TRY, gz, node);
gzAppendInstruction(gz, try_inst);
// Else scope: extract error code, return it (AstGen.zig:6012-6025).
GenZir else_scope = makeSubBlock(gz);
uint32_t err_code
= addUnNode(&else_scope, ZIR_INST_ERR_UNION_CODE, operand, node);
// ret_node with error code (AstGen.zig:6021).
addUnNode(&else_scope, ZIR_INST_RET_NODE, err_code, node);
setTryBody(ag, &else_scope, try_inst, operand);
// else_scope unstacked by setTryBody.
return try_inst + ZIR_REF_START_INDEX; // toRef()
}
// Mirrors expr (AstGen.zig:634) — main expression dispatcher. // Mirrors expr (AstGen.zig:634) — main expression dispatcher.
static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node) { static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
@@ -1082,7 +1651,7 @@ static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node) {
// address_of (AstGen.zig:953): evaluate operand with .ref rl. // address_of (AstGen.zig:953): evaluate operand with .ref rl.
case AST_NODE_ADDRESS_OF: { case AST_NODE_ADDRESS_OF: {
uint32_t operand_node = ag->tree->nodes.datas[node].lhs; uint32_t operand_node = ag->tree->nodes.datas[node].lhs;
return exprRl(gz, RL_REF, operand_node); return exprRl(gz, RL_REF_VAL, operand_node);
} }
// ptr_type (AstGen.zig:1077-1081). // ptr_type (AstGen.zig:1077-1081).
case AST_NODE_PTR_TYPE_ALIGNED: case AST_NODE_PTR_TYPE_ALIGNED:
@@ -1102,6 +1671,41 @@ static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node) {
// array_cat (AstGen.zig:772): ++ binary operator. // array_cat (AstGen.zig:772): ++ binary operator.
case AST_NODE_ARRAY_CAT: case AST_NODE_ARRAY_CAT:
return simpleBinOp(gz, node, ZIR_INST_ARRAY_CAT); return simpleBinOp(gz, node, ZIR_INST_ARRAY_CAT);
// grouped_expression (AstGen.zig:1100): passthrough.
case AST_NODE_GROUPED_EXPRESSION:
return exprRl(gz, rl, ag->tree->nodes.datas[node].lhs);
// unreachable_literal (AstGen.zig:1012).
case AST_NODE_UNREACHABLE_LITERAL:
return ZIR_REF_UNREACHABLE_VALUE;
// enum_literal (AstGen.zig:993).
case AST_NODE_ENUM_LITERAL: {
uint32_t ident_token = ag->tree->nodes.main_tokens[node];
uint32_t str_index = identAsString(ag, ident_token);
return addStrTok(gz, ZIR_INST_ENUM_LITERAL, str_index, ident_token);
}
// multiline_string_literal (AstGen.zig:8645).
case AST_NODE_MULTILINE_STRING_LITERAL:
return multilineStringLiteral(gz, node);
// return (AstGen.zig:856).
case AST_NODE_RETURN:
return retExpr(gz, node);
// call (AstGen.zig:783-790).
case AST_NODE_CALL_ONE:
case AST_NODE_CALL_ONE_COMMA:
case AST_NODE_CALL:
case AST_NODE_CALL_COMMA:
return callExpr(gz, node);
// struct_init (AstGen.zig:836-839).
case AST_NODE_STRUCT_INIT_DOT_TWO:
case AST_NODE_STRUCT_INIT_DOT_TWO_COMMA:
case AST_NODE_STRUCT_INIT_DOT:
case AST_NODE_STRUCT_INIT_DOT_COMMA:
case AST_NODE_STRUCT_INIT_ONE:
case AST_NODE_STRUCT_INIT_ONE_COMMA:
return structInitExpr(gz, rl, node);
// try (AstGen.zig:831).
case AST_NODE_TRY:
return tryExpr(gz, node);
default: default:
ag->has_compile_errors = true; ag->has_compile_errors = true;
return ZIR_REF_VOID_VALUE; return ZIR_REF_VOID_VALUE;
@@ -1109,7 +1713,7 @@ static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node) {
} }
static uint32_t expr(GenZir* gz, uint32_t node) { static uint32_t expr(GenZir* gz, uint32_t node) {
return exprRl(gz, RL_NONE, node); return exprRl(gz, RL_NONE_VAL, node);
} }
// --- rvalue (AstGen.zig:11029) --- // --- rvalue (AstGen.zig:11029) ---
@@ -1133,8 +1737,9 @@ static void emitDbgStmt(GenZir* gz, uint32_t line, uint32_t column) {
// Check if last instruction is already dbg_stmt; if so, update it. // Check if last instruction is already dbg_stmt; if so, update it.
// (AstGen.zig:13715-13724) // (AstGen.zig:13715-13724)
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
if (gz->instructions_len > 0) { uint32_t gz_len = gzInstructionsLen(gz);
uint32_t last = gz->instructions[gz->instructions_len - 1]; if (gz_len > 0) {
uint32_t last = gzInstructionsSlice(gz)[gz_len - 1];
if (ag->inst_tags[last] == ZIR_INST_DBG_STMT) { if (ag->inst_tags[last] == ZIR_INST_DBG_STMT) {
ag->inst_datas[last].dbg_stmt.line = line; ag->inst_datas[last].dbg_stmt.line = line;
ag->inst_datas[last].dbg_stmt.column = column; ag->inst_datas[last].dbg_stmt.column = column;
@@ -1482,6 +2087,7 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
decl_block.decl_node_index = node; decl_block.decl_node_index = node;
decl_block.decl_line = decl_line; decl_block.decl_line = decl_line;
decl_block.is_comptime = true; decl_block.is_comptime = true;
decl_block.instructions_top = ag->scratch_inst_len;
// Set up fn_block GenZir (AstGen.zig:4837-4845). // Set up fn_block GenZir (AstGen.zig:4837-4845).
GenZir fn_block; GenZir fn_block;
@@ -1490,6 +2096,7 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
fn_block.decl_node_index = node; fn_block.decl_node_index = node;
fn_block.decl_line = decl_line; fn_block.decl_line = decl_line;
fn_block.is_comptime = false; fn_block.is_comptime = false;
fn_block.instructions_top = ag->scratch_inst_len;
// Compute lbrace source location (AstGen.zig:4860-4862). // Compute lbrace source location (AstGen.zig:4860-4862).
advanceSourceCursorToNode(ag, body_node); advanceSourceCursorToNode(ag, body_node);
@@ -1522,17 +2129,26 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
addInstruction(&fn_block, ZIR_INST_RET_IMPLICIT, rdata); addInstruction(&fn_block, ZIR_INST_RET_IMPLICIT, rdata);
} }
// Read fn_block body before unstacking (AstGen.zig:4874).
// Upstream unstacks fn_block inside addFunc before appending the func
// instruction to decl_block. We must unstack fn_block first so that
// addFunc's addInstruction goes into decl_block's range.
const uint32_t* fn_body = gzInstructionsSlice(&fn_block);
uint32_t fn_body_len = gzInstructionsLen(&fn_block);
gzUnstack(&fn_block);
// Create func instruction (AstGen.zig:4874-4897). // Create func instruction (AstGen.zig:4874-4897).
uint32_t func_ref = addFunc(&decl_block, node, body_node, decl_inst, uint32_t func_ref = addFunc(&decl_block, node, body_node, decl_inst,
ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE, fn_block.instructions, ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE, fn_body, fn_body_len,
fn_block.instructions_len, lbrace_line, lbrace_column); lbrace_line, lbrace_column);
// break_inline returning func to declaration (AstGen.zig:4899). // break_inline returning func to declaration (AstGen.zig:4899).
makeBreakInline(&decl_block, decl_inst, func_ref, AST_NODE_OFFSET_NONE); makeBreakInline(&decl_block, decl_inst, func_ref, AST_NODE_OFFSET_NONE);
// setDeclaration (AstGen.zig:4903-4923). // setDeclaration (AstGen.zig:4903-4923).
setDeclaration(ag, decl_inst, decl_line, decl_column, decl_id, test_name, setDeclaration(ag, decl_inst, decl_line, decl_column, decl_id, test_name,
decl_block.instructions, decl_block.instructions_len); gzInstructionsSlice(&decl_block), gzInstructionsLen(&decl_block));
gzUnstack(&decl_block);
(void)gz; (void)gz;
} }
@@ -1562,8 +2178,8 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
is_pub = true; is_pub = true;
} }
// makeDeclaration on proto_node (AstGen.zig:4090). // makeDeclaration on fn_decl node (AstGen.zig:4090).
uint32_t decl_inst = makeDeclaration(ag, proto_node); uint32_t decl_inst = makeDeclaration(ag, node);
wip_decl_insts[*decl_idx] = decl_inst; wip_decl_insts[*decl_idx] = decl_inst;
(*decl_idx)++; (*decl_idx)++;
@@ -1609,6 +2225,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
value_gz.decl_node_index = proto_node; value_gz.decl_node_index = proto_node;
value_gz.decl_line = decl_line; value_gz.decl_line = decl_line;
value_gz.is_comptime = true; value_gz.is_comptime = true;
value_gz.instructions_top = ag->scratch_inst_len;
// fnDeclInner creates the func instruction. // fnDeclInner creates the func instruction.
// Simplified: creates fn_block, processes body, adds func instruction. // Simplified: creates fn_block, processes body, adds func instruction.
@@ -1618,6 +2235,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
fn_block.decl_node_index = proto_node; fn_block.decl_node_index = proto_node;
fn_block.decl_line = decl_line; fn_block.decl_line = decl_line;
fn_block.is_comptime = false; fn_block.is_comptime = false;
fn_block.instructions_top = ag->scratch_inst_len;
// Process function body (AstGen.zig:4358). // Process function body (AstGen.zig:4358).
advanceSourceCursorToNode(ag, body_node); advanceSourceCursorToNode(ag, body_node);
@@ -1649,20 +2267,20 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
// Create func instruction (AstGen.zig:4396). // Create func instruction (AstGen.zig:4396).
uint32_t func_ref; uint32_t func_ref;
// Read fn_block body before unstacking (upstream unstacks inside addFunc).
const uint32_t* fn_body = gzInstructionsSlice(&fn_block);
uint32_t fn_body_len = gzInstructionsLen(&fn_block);
gzUnstack(&fn_block);
if (is_inferred_error) { if (is_inferred_error) {
// Use ret_ref = void_type for !void (same as tests but with
// func_inferred). Actually for !void, ret_ref = .none (void return,
// error inferred).
func_ref = addFunc(&value_gz, node, body_node, decl_inst, ZIR_REF_NONE, func_ref = addFunc(&value_gz, node, body_node, decl_inst, ZIR_REF_NONE,
fn_block.instructions, fn_block.instructions_len, lbrace_line, fn_body, fn_body_len, lbrace_line, lbrace_column);
lbrace_column);
// Patch the tag to func_inferred. // Patch the tag to func_inferred.
ag->inst_tags[func_ref - ZIR_REF_START_INDEX] = ZIR_INST_FUNC_INFERRED; ag->inst_tags[func_ref - ZIR_REF_START_INDEX] = ZIR_INST_FUNC_INFERRED;
} else { } else {
// void return: ret_ref = .none means void. // void return: ret_ref = .none means void.
func_ref = addFunc(&value_gz, node, body_node, decl_inst, ZIR_REF_NONE, func_ref = addFunc(&value_gz, node, body_node, decl_inst, ZIR_REF_NONE,
fn_block.instructions, fn_block.instructions_len, lbrace_line, fn_body, fn_body_len, lbrace_line, lbrace_column);
lbrace_column);
} }
// break_inline returning func to declaration. // break_inline returning func to declaration.
@@ -1673,7 +2291,8 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
= is_pub ? DECL_ID_PUB_CONST_SIMPLE : DECL_ID_CONST_SIMPLE; = is_pub ? DECL_ID_PUB_CONST_SIMPLE : DECL_ID_CONST_SIMPLE;
uint32_t name_str = identAsString(ag, fn_name_token); uint32_t name_str = identAsString(ag, fn_name_token);
setDeclaration(ag, decl_inst, decl_line, decl_column, decl_id, name_str, setDeclaration(ag, decl_inst, decl_line, decl_column, decl_id, name_str,
value_gz.instructions, value_gz.instructions_len); gzInstructionsSlice(&value_gz), gzInstructionsLen(&value_gz));
gzUnstack(&value_gz);
(void)gz; (void)gz;
} }
@@ -1699,6 +2318,7 @@ static void comptimeDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
value_gz.decl_node_index = node; value_gz.decl_node_index = node;
value_gz.decl_line = decl_line; value_gz.decl_line = decl_line;
value_gz.is_comptime = true; value_gz.is_comptime = true;
value_gz.instructions_top = ag->scratch_inst_len;
// For comptime {}: body is empty block → no instructions generated. // For comptime {}: body is empty block → no instructions generated.
// comptime_gz.isEmpty() == true → addBreak(.break_inline, decl_inst, // comptime_gz.isEmpty() == true → addBreak(.break_inline, decl_inst,
@@ -1707,7 +2327,8 @@ static void comptimeDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
&value_gz, decl_inst, ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE); &value_gz, decl_inst, ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
setDeclaration(ag, decl_inst, decl_line, decl_column, DECL_ID_COMPTIME, 0, setDeclaration(ag, decl_inst, decl_line, decl_column, DECL_ID_COMPTIME, 0,
value_gz.instructions, value_gz.instructions_len); gzInstructionsSlice(&value_gz), gzInstructionsLen(&value_gz));
gzUnstack(&value_gz);
(void)gz; (void)gz;
} }
@@ -1732,6 +2353,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
memset(&init_gz, 0, sizeof(init_gz)); memset(&init_gz, 0, sizeof(init_gz));
init_gz.astgen = ag; init_gz.astgen = ag;
init_gz.decl_node_index = node; init_gz.decl_node_index = node;
init_gz.instructions_top = ag->scratch_inst_len;
init_gz.decl_line = ag->source_line; init_gz.decl_line = ag->source_line;
init_gz.is_comptime = true; init_gz.is_comptime = true;
@@ -1757,8 +2379,9 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
uint32_t name_str = identAsString(ag, name_token); uint32_t name_str = identAsString(ag, name_token);
setDeclaration(ag, decl_inst, ag->source_line, decl_column, setDeclaration(ag, decl_inst, ag->source_line, decl_column,
DECL_ID_CONST_SIMPLE, name_str, init_gz.instructions, DECL_ID_CONST_SIMPLE, name_str, gzInstructionsSlice(&init_gz),
init_gz.instructions_len); gzInstructionsLen(&init_gz));
gzUnstack(&init_gz);
(void)gz; (void)gz;
} }
@@ -1901,6 +2524,7 @@ Zir astGen(const Ast* ast) {
free(ag.imports); free(ag.imports);
free(ag.decl_names); free(ag.decl_names);
free(ag.decl_nodes); free(ag.decl_nodes);
free(ag.scratch_instructions);
return zir; return zir;
} }

4
zir.h
View File

@@ -457,6 +457,10 @@ typedef union {
#define ZIR_REF_NEGATIVE_ONE 117 #define ZIR_REF_NEGATIVE_ONE 117
#define ZIR_REF_VOID_VALUE 118 #define ZIR_REF_VOID_VALUE 118
#define ZIR_REF_UNREACHABLE_VALUE 119 #define ZIR_REF_UNREACHABLE_VALUE 119
#define ZIR_REF_NULL_VALUE 120
#define ZIR_REF_BOOL_TRUE 121
#define ZIR_REF_BOOL_FALSE 122
#define ZIR_REF_EMPTY_TUPLE 123
// Ast.Node.OptionalOffset.none = maxInt(i32). // Ast.Node.OptionalOffset.none = maxInt(i32).
#define AST_NODE_OFFSET_NONE ((int32_t)0x7FFFFFFF) #define AST_NODE_OFFSET_NONE ((int32_t)0x7FFFFFFF)