diff --git a/astgen.c b/astgen.c index 349c3db6a0..857e76980b 100644 --- a/astgen.c +++ b/astgen.c @@ -78,27 +78,79 @@ typedef struct { uint32_t* decl_nodes; // node indices uint32_t decl_table_len; 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; } 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-block (mirroring GenZir.instructions in Zig). In Zig the sub-blocks -// share a parent ArrayList and record a starting offset; here we use a -// simple local array since the bodies are small. - -#define GENZIR_MAX_BODY 64 +// Sub-blocks share the parent AstGenCtx's scratch_instructions array and +// record their starting offset (instructions_top). This mirrors the upstream +// GenZir.instructions / instructions_top design (AstGen.zig:11796-11850). typedef struct { AstGenCtx* astgen; uint32_t decl_node_index; uint32_t decl_line; bool is_comptime; - uint32_t instructions[GENZIR_MAX_BODY]; - uint32_t instructions_len; + bool c_import; // true inside @cImport block + uint32_t instructions_top; // start index in shared array } 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 --- 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_len++; // Record in sub-block body. - assert(gz->instructions_len < GENZIR_MAX_BODY); - gz->instructions[gz->instructions_len++] = idx; + gzAppendInstruction(gz, idx); return idx + ZIR_REF_START_INDEX; // toRef() } @@ -205,6 +256,36 @@ static uint32_t addPlNodeBin( 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) --- // Mirrors AstGen.advanceSourceCursor (AstGen.zig:13342). @@ -397,11 +478,62 @@ static uint32_t makeBreakInline(GenZir* gz, uint32_t block_inst, ag->inst_len++; // Record in sub-block body. - assert(gz->instructions_len < GENZIR_MAX_BODY); - gz->instructions[gz->instructions_len++] = idx; + gzAppendInstruction(gz, 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) static bool declIdHasName(DeclFlagsId id) { return id != DECL_ID_UNNAMED_TEST && id != DECL_ID_COMPTIME; @@ -657,13 +789,26 @@ typedef enum { RL_NONE, // Just compute the value. RL_REF, // Compute a pointer to the value. 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; +#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) --- -// Forward declaration. +// Forward declarations. static uint32_t expr(GenZir* gz, 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). // 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; } +// 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. static uint32_t builtinCall(GenZir* gz, uint32_t node) { 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; - 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); + 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. 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++) { if (ag->decl_names[i] == name_str) { 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; data.str_tok.start = name_str; 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). // 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); // 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 // .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; data.pl_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index; data.pl_node.payload_index = payload_index; @@ -910,7 +1117,7 @@ static uint32_t ptrTypeExpr(GenZir* gz, uint32_t node) { } // 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 }. ensureExtraCapacity(ag, 2); @@ -939,8 +1146,8 @@ static uint32_t arrayTypeExpr(GenZir* gz, uint32_t node) { AstData nd = ag->tree->nodes.datas[node]; // data.lhs = length expr node, data.rhs = element type node. - uint32_t len = exprRl(gz, RL_NONE, nd.lhs); - uint32_t elem_type = exprRl(gz, RL_NONE, nd.rhs); + uint32_t len = exprRl(gz, RL_NONE_VAL, nd.lhs); + uint32_t elem_type = exprRl(gz, RL_NONE_VAL, nd.rhs); 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] == '_'))) { // Inferred length: addInt(elem_count) (AstGen.zig:1452). 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, ZIR_INST_ARRAY_TYPE, type_expr_node, len_inst, elem_type); // 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. uint32_t operands_len = elem_count + 1; // +1 for type 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++] = array_type_inst; // type ref 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; } 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) { AstGenCtx* ag = gz->astgen; AstData nd = ag->tree->nodes.datas[node]; - uint32_t lhs = exprRl(gz, RL_NONE, nd.lhs); - uint32_t rhs = exprRl(gz, RL_NONE, nd.rhs); + uint32_t lhs = exprRl(gz, RL_NONE_VAL, nd.lhs); + uint32_t rhs = exprRl(gz, RL_NONE_VAL, nd.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. static uint32_t exprRl(GenZir* gz, ResultLoc rl, uint32_t node) { 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. case AST_NODE_ADDRESS_OF: { 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). 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. case AST_NODE_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: ag->has_compile_errors = true; 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) { - return exprRl(gz, RL_NONE, node); + return exprRl(gz, RL_NONE_VAL, node); } // --- 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. // (AstGen.zig:13715-13724) AstGenCtx* ag = gz->astgen; - if (gz->instructions_len > 0) { - uint32_t last = gz->instructions[gz->instructions_len - 1]; + uint32_t gz_len = gzInstructionsLen(gz); + if (gz_len > 0) { + uint32_t last = gzInstructionsSlice(gz)[gz_len - 1]; if (ag->inst_tags[last] == ZIR_INST_DBG_STMT) { ag->inst_datas[last].dbg_stmt.line = line; 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_line = decl_line; decl_block.is_comptime = true; + decl_block.instructions_top = ag->scratch_inst_len; // Set up fn_block GenZir (AstGen.zig:4837-4845). 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_line = decl_line; fn_block.is_comptime = false; + fn_block.instructions_top = ag->scratch_inst_len; // Compute lbrace source location (AstGen.zig:4860-4862). 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); } + // 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). uint32_t func_ref = addFunc(&decl_block, node, body_node, decl_inst, - ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE, fn_block.instructions, - fn_block.instructions_len, lbrace_line, lbrace_column); + ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE, fn_body, fn_body_len, + lbrace_line, lbrace_column); // break_inline returning func to declaration (AstGen.zig:4899). makeBreakInline(&decl_block, decl_inst, func_ref, AST_NODE_OFFSET_NONE); // setDeclaration (AstGen.zig:4903-4923). 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; } @@ -1562,8 +2178,8 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, is_pub = true; } - // makeDeclaration on proto_node (AstGen.zig:4090). - uint32_t decl_inst = makeDeclaration(ag, proto_node); + // makeDeclaration on fn_decl node (AstGen.zig:4090). + uint32_t decl_inst = makeDeclaration(ag, node); wip_decl_insts[*decl_idx] = decl_inst; (*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_line = decl_line; value_gz.is_comptime = true; + value_gz.instructions_top = ag->scratch_inst_len; // fnDeclInner creates the 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_line = decl_line; fn_block.is_comptime = false; + fn_block.instructions_top = ag->scratch_inst_len; // Process function body (AstGen.zig:4358). 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). 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) { - // 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, - fn_block.instructions, fn_block.instructions_len, lbrace_line, - lbrace_column); + fn_body, fn_body_len, lbrace_line, lbrace_column); // Patch the tag to func_inferred. ag->inst_tags[func_ref - ZIR_REF_START_INDEX] = ZIR_INST_FUNC_INFERRED; } else { // void return: ret_ref = .none means void. func_ref = addFunc(&value_gz, node, body_node, decl_inst, ZIR_REF_NONE, - fn_block.instructions, fn_block.instructions_len, lbrace_line, - lbrace_column); + fn_body, fn_body_len, lbrace_line, lbrace_column); } // 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; uint32_t name_str = identAsString(ag, fn_name_token); 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; } @@ -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_line = decl_line; value_gz.is_comptime = true; + value_gz.instructions_top = ag->scratch_inst_len; // For comptime {}: body is empty block → no instructions generated. // 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); 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; } @@ -1732,6 +2353,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, memset(&init_gz, 0, sizeof(init_gz)); init_gz.astgen = ag; init_gz.decl_node_index = node; + init_gz.instructions_top = ag->scratch_inst_len; init_gz.decl_line = ag->source_line; 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); setDeclaration(ag, decl_inst, ag->source_line, decl_column, - DECL_ID_CONST_SIMPLE, name_str, init_gz.instructions, - init_gz.instructions_len); + DECL_ID_CONST_SIMPLE, name_str, gzInstructionsSlice(&init_gz), + gzInstructionsLen(&init_gz)); + gzUnstack(&init_gz); (void)gz; } @@ -1901,6 +2524,7 @@ Zir astGen(const Ast* ast) { free(ag.imports); free(ag.decl_names); free(ag.decl_nodes); + free(ag.scratch_instructions); return zir; } diff --git a/zir.h b/zir.h index 1896adbdf8..3e5f97d6ea 100644 --- a/zir.h +++ b/zir.h @@ -457,6 +457,10 @@ typedef union { #define ZIR_REF_NEGATIVE_ONE 117 #define ZIR_REF_VOID_VALUE 118 #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). #define AST_NODE_OFFSET_NONE ((int32_t)0x7FFFFFFF)