commit ddc24a0596578b5be39b4635f86fec7f5a64239f (tree)
parent 09b40057d2d3fce78900e7f8d428556a3aa716cd
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Sun, 15 Feb 2026 08:25:24 +0000
astgen: port ret have_err path, comptimeDecl body, @Type name strategy, fix union_init field order, @TypeOf break src_node
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
2 files changed, 118 insertions(+), 31 deletions(-)
diff --git a/stage0/astgen.c b/stage0/astgen.c
@@ -111,9 +111,10 @@ typedef struct {
bool fn_var_args; // AstGen.zig:46
} AstGenCtx;
-#define SET_ERROR(ag) do { \
- (ag)->has_compile_errors = true; \
-} while(0)
+#define SET_ERROR(ag) \
+ do { \
+ (ag)->has_compile_errors = true; \
+ } while (0)
// Set fn_block pointer on AstGenCtx. The caller is responsible for saving
// and restoring the previous value before the pointed-to GenZir goes out
@@ -3823,7 +3824,7 @@ static uint32_t builtinCall(
if (!refIsNoReturn(&typeof_scope, ty_expr_ref)) {
addBreak(&typeof_scope, ZIR_INST_BREAK_INLINE,
typeof_inst, ty_expr_ref,
- (int32_t)nd.lhs - (int32_t)gz->decl_node_index);
+ AST_NODE_OFFSET_NONE);
}
setBlockBody(ag, &typeof_scope, typeof_inst);
// typeof_scope unstacked now, add instruction to gz.
@@ -4445,12 +4446,13 @@ static uint32_t builtinCallMultiArg(GenZir* gz, Scope* scope, ResultLoc rl,
.tag = RL_TY, .data = field_type, .src_node = 0, .ctx = rl.ctx
};
uint32_t init = reachableExpr(gz, scope, init_rl, params[2], node);
- // Emit union_init: payload = union_type, init, field_name_ref.
+ // Emit union_init: payload = union_type, field_name, init
+ // (Zir.zig:3707-3711).
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = union_type;
- ag->extra[ag->extra_len++] = init;
ag->extra[ag->extra_len++] = field_name_ref;
+ ag->extra[ag->extra_len++] = init;
uint32_t result = addPlNodePayloadIndex(
gz, ZIR_INST_UNION_INIT, node, payload_index);
return rvalue(gz, rl, result, node);
@@ -5964,8 +5966,10 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
? addUnNode(gz, ZIR_INST_LOAD, ret_ptr_inst, node)
: operand;
(void)err_code;
- // TODO: genDefers with .both = err_code when errdefer is implemented.
- genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
+ // TODO: genDefers with .both = err_code when remapped_err_code
+ // is implemented. For now, both_sans_err matches when
+ // need_err_code is false (AstGen.zig:8203).
+ genDefers(gz, defer_outer, scope, DEFER_BOTH_SANS_ERR);
emitDbgStmt(gz, ret_lc_line, ret_lc_column);
if (use_ptr) {
addUnNode(gz, ZIR_INST_RET_LOAD, ret_ptr_inst, node);
@@ -5995,15 +5999,54 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
}
return ZIR_REF_UNREACHABLE_VALUE;
}
- // have_err path: emit conditional branch (not yet implemented).
- // Fall through to simplified path.
- genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
- emitDbgStmt(gz, ret_lc_line, ret_lc_column);
+ // have_err path: emit conditional branch for errdefers
+ // (AstGen.zig:8222-8251).
+ uint32_t result = use_ptr
+ ? addUnNode(gz, ZIR_INST_LOAD, ret_ptr_inst, node)
+ : operand;
+ uint32_t is_non_err
+ = addUnNode(gz, ZIR_INST_RET_IS_NON_ERR, result, node);
+ uint32_t condbr = addCondBr(gz, ZIR_INST_CONDBR, node);
+
+ // Then scope: normal defers only (AstGen.zig:8228-8237).
+ GenZir then_scope = makeSubBlock(gz, scope);
+ genDefers(&then_scope, defer_outer, scope, DEFER_NORMAL_ONLY);
+ // Restore error return index unconditionally
+ // (AstGen.zig:8234).
+ {
+ ZirInstData rdata;
+ rdata.un_node.operand = ZIR_REF_NONE;
+ rdata.un_node.src_node
+ = (int32_t)node - (int32_t)gz->decl_node_index;
+ addInstruction(&then_scope,
+ ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL, rdata);
+ }
+ emitDbgStmt(&then_scope, ret_lc_line, ret_lc_column);
if (use_ptr) {
- addUnNode(gz, ZIR_INST_RET_LOAD, ret_ptr_inst, node);
+ addUnNode(&then_scope, ZIR_INST_RET_LOAD, ret_ptr_inst, node);
} else {
- addUnNode(gz, ZIR_INST_RET_NODE, operand, node);
+ addUnNode(&then_scope, ZIR_INST_RET_NODE, operand, node);
+ }
+
+ // Else scope: both error and normal defers
+ // (AstGen.zig:8239-8247).
+ GenZir else_scope = makeSubBlock(gz, scope);
+ if (!dc.need_err_code) {
+ genDefers(&else_scope, defer_outer, scope, DEFER_BOTH_SANS_ERR);
+ } else {
+ // need_err_code: emit err_union_code + genDefers with
+ // .both (not yet implemented; need_err_code is always
+ // false currently).
+ genDefers(&else_scope, defer_outer, scope, DEFER_BOTH_SANS_ERR);
+ }
+ emitDbgStmt(&else_scope, ret_lc_line, ret_lc_column);
+ if (use_ptr) {
+ addUnNode(&else_scope, ZIR_INST_RET_LOAD, ret_ptr_inst, node);
+ } else {
+ addUnNode(&else_scope, ZIR_INST_RET_NODE, operand, node);
}
+
+ setCondBrPayload(ag, condbr, is_non_err, &then_scope, &else_scope);
return ZIR_REF_UNREACHABLE_VALUE;
}
}
@@ -7551,15 +7594,15 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
}
// anyframe_literal (AstGen.zig:1008-1011).
case AST_NODE_ANYFRAME_LITERAL: {
- uint32_t result = addUnNode(gz, ZIR_INST_ANYFRAME_TYPE,
- ZIR_REF_VOID_TYPE, node);
+ uint32_t result
+ = addUnNode(gz, ZIR_INST_ANYFRAME_TYPE, ZIR_REF_VOID_TYPE, node);
return rvalue(gz, rl, result, node);
}
// anyframe_type (AstGen.zig:1012-1016).
case AST_NODE_ANYFRAME_TYPE: {
uint32_t return_type = typeExpr(gz, scope, nd.rhs);
- uint32_t result = addUnNode(gz, ZIR_INST_ANYFRAME_TYPE,
- return_type, node);
+ uint32_t result
+ = addUnNode(gz, ZIR_INST_ANYFRAME_TYPE, return_type, node);
return rvalue(gz, rl, result, node);
}
// error_set_decl (AstGen.zig:5905-5955).
@@ -13634,6 +13677,8 @@ fn_decl_finish:
static void comptimeDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t* wip_decl_insts, uint32_t* decl_idx, uint32_t node) {
+ const Ast* tree = ag->tree;
+
// makeDeclaration before advanceSourceCursorToNode (AstGen.zig:4663-4665).
uint32_t decl_inst = makeDeclaration(ag, node);
wip_decl_insts[*decl_idx] = decl_inst;
@@ -13644,7 +13689,10 @@ static void comptimeDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t decl_line = ag->source_line;
uint32_t decl_column = ag->source_column;
- // Value sub-block (AstGen.zig:4675-4686).
+ // body_node (AstGen.zig:4653).
+ uint32_t body_node = tree->nodes.datas[node].lhs;
+
+ // Value sub-block (AstGen.zig:4671-4686).
GenZir value_gz;
memset(&value_gz, 0, sizeof(value_gz));
value_gz.base.tag = SCOPE_GEN_ZIR;
@@ -13656,11 +13704,16 @@ static void comptimeDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
value_gz.instructions_top = ag->scratch_inst_len;
value_gz.any_defer_node = UINT32_MAX;
- // For comptime {}: body is empty block → no instructions generated.
- // comptime_gz.isEmpty() == true → addBreak(.break_inline, decl_inst,
- // .void_value) (AstGen.zig:4685-4686)
- makeBreakInline(
- &value_gz, decl_inst, ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
+ // Evaluate the body expression (AstGen.zig:4684).
+ uint32_t block_result
+ = fullBodyExpr(&value_gz, &value_gz.base, RL_NONE_VAL, body_node);
+ // Add break_inline if body is empty or not noreturn
+ // (AstGen.zig:4685-4686).
+ if (gzInstructionsLen(&value_gz) == 0
+ || !refIsNoReturn(&value_gz, block_result)) {
+ makeBreakInline(
+ &value_gz, decl_inst, ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
+ }
setDeclaration(ag, decl_inst,
(SetDeclArgs) { .src_line = decl_line,
@@ -13829,10 +13882,9 @@ static DeclFlagsId computeVarDeclId(bool is_mutable, bool is_pub,
// *out_ref), false if caller should fall back to expr().
static bool nameStratExpr(GenZir* gz, Scope* scope, ResultLoc rl,
uint32_t node, uint8_t name_strategy, uint32_t* out_ref) {
- const AstGenCtx* ag = gz->astgen;
+ AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstNodeTag tag = tree->nodes.tags[node];
- (void)rl; // Used by builtinReify (not yet implemented).
switch (tag) {
case AST_NODE_CONTAINER_DECL:
@@ -13849,8 +13901,42 @@ static bool nameStratExpr(GenZir* gz, Scope* scope, ResultLoc rl,
case AST_NODE_TAGGED_UNION_ENUM_TAG_TRAILING:
*out_ref = containerDecl(gz, scope, node, name_strategy);
return true;
- // @Type builtin: upstream calls builtinReify (AstGen.zig:1186-1196).
- // Not yet implemented; fall through to expr().
+ // @Type builtin: builtinReify (AstGen.zig:1186-1196).
+ case AST_NODE_BUILTIN_CALL_TWO:
+ case AST_NODE_BUILTIN_CALL_TWO_COMMA: {
+ uint32_t builtin_token = tree->nodes.main_tokens[node];
+ uint32_t tok_start = tree->tokens.starts[builtin_token];
+ const char* source = tree->source;
+ uint32_t ns = tok_start + 1; // skip '@'
+ if (ns + 4 <= tree->source_len && memcmp(source + ns, "Type", 4) == 0
+ && (ns + 4 >= tree->source_len
+ || !((source[ns + 4] >= 'a' && source[ns + 4] <= 'z')
+ || (source[ns + 4] >= 'A' && source[ns + 4] <= 'Z')
+ || source[ns + 4] == '_'))) {
+ // builtinReify with the given name_strategy
+ // (AstGen.zig:9747-9781).
+ AstData nd = tree->nodes.datas[node];
+ uint32_t arg_node = nd.lhs;
+ uint32_t type_info_ty
+ = addBuiltinValue(gz, node, ZIR_BUILTIN_VALUE_TYPE_INFO);
+ ResultLoc operand_rl = { .tag = RL_COERCED_TY,
+ .data = type_info_ty,
+ .src_node = 0,
+ .ctx = RI_CTX_NONE };
+ uint32_t operand = exprRl(gz, scope, operand_rl, arg_node);
+ ensureExtraCapacity(ag, 3);
+ uint32_t payload_index = ag->extra_len;
+ ag->extra[ag->extra_len++] = node; // absolute node index
+ ag->extra[ag->extra_len++] = operand;
+ ag->extra[ag->extra_len++] = ag->source_line;
+ uint32_t result
+ = addExtendedPayloadSmall(gz, (uint16_t)ZIR_EXT_REIFY,
+ (uint16_t)name_strategy, payload_index);
+ *out_ref = rvalue(gz, rl, result, node);
+ return true;
+ }
+ return false;
+ }
default:
return false;
}
diff --git a/stage0/astgen_test.zig b/stage0/astgen_test.zig
@@ -52,8 +52,10 @@ fn buildHashSkipMask(gpa: Allocator, ref: Zir) ![]bool {
switch (ref_tags[i]) {
.extended => {
const ext = ref_datas[i].extended;
- if (ext.opcode == .struct_decl or ext.opcode == .enum_decl) {
- // StructDecl/EnumDecl starts with fields_hash[4].
+ if (ext.opcode == .struct_decl or ext.opcode == .enum_decl or
+ ext.opcode == .union_decl or ext.opcode == .opaque_decl)
+ {
+ // StructDecl/EnumDecl/UnionDecl/OpaqueDecl starts with fields_hash[4].
const pi = ext.operand;
for (0..4) |j| skip[pi + j] = true;
}
@@ -953,7 +955,6 @@ test "astgen: corpus array_list.zig" {
}
test "astgen: corpus multi_array_list.zig" {
- if (true) return error.SkipZigTest; // TODO: remaining diffs: bool_not, bool_br_and, ret_is_non_err, plus small instruction count differences
const gpa = std.testing.allocator;
try corpusCheck(gpa, @embedFile("../lib/std/multi_array_list.zig"));
}