zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit bbe6c341915c7cd26e0928198ca7324c49bbe2aa (tree)
parent 3620dc3957e3fbaad5da386e114f74fc7d68fd80
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date:   Mon, 16 Feb 2026 11:59:27 +0000

astgen.c: implement errdefer payload capture and genDefersBoth for defer.zig

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mstage0/astgen.c | 178++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mstage0/astgen_test.zig | 2+-
2 files changed, 155 insertions(+), 25 deletions(-)

diff --git a/stage0/astgen.c b/stage0/astgen.c @@ -323,6 +323,7 @@ typedef struct { Scope* parent; uint32_t index; uint32_t len; + uint32_t remapped_err_code; // UINT32_MAX = none (AstGen.zig:11732) } ScopeDefer; // Scope.Label — for labeled blocks and loops. @@ -2697,6 +2698,7 @@ static uint32_t expr(GenZir* gz, Scope* scope, uint32_t node); // --- DefersToEmit (AstGen.zig:3008) --- #define DEFER_NORMAL_ONLY 0 #define DEFER_BOTH_SANS_ERR 1 +#define DEFER_BOTH 2 // --- DeferCounts (AstGen.zig:2966) --- typedef struct { @@ -2723,6 +2725,8 @@ static uint32_t shiftOp( static void emitDbgStmt(GenZir* gz, uint32_t line, uint32_t column); static void genDefers( GenZir* gz, const Scope* outer_scope, Scope* inner_scope, int which); +static void genDefersBoth(GenZir* gz, const Scope* outer_scope, + Scope* inner_scope, uint32_t err_code); static void emitDbgStmtForceCurrentIndex( GenZir* gz, uint32_t line, uint32_t column); static void emitDbgNode(GenZir* gz, uint32_t node); @@ -6802,7 +6806,13 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) { addStrTok(gz, ZIR_INST_RET_ERR_VALUE, err_name_str, error_token); return ZIR_REF_UNREACHABLE_VALUE; } - // need_err_code path: not implemented yet, fall through to general. + // need_err_code path (AstGen.zig:8171-8175). + uint32_t err_code_fast = addStrTok( + gz, ZIR_INST_RET_ERR_VALUE_CODE, err_name_str, error_token); + genDefersBoth(gz, defer_outer, scope, err_code_fast); + emitDbgStmt(gz, ret_lc_line, ret_lc_column); + addUnNode(gz, ZIR_INST_RET_NODE, err_code_fast, node); + return ZIR_REF_UNREACHABLE_VALUE; } // Evaluate operand with result location (AstGen.zig:8178-8186). @@ -6856,11 +6866,8 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) { uint32_t err_code = use_ptr ? addUnNode(gz, ZIR_INST_LOAD, ret_ptr_inst, node) : operand; - (void)err_code; - // 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); + // genDefers with .both = err_code (AstGen.zig:8203). + genDefersBoth(gz, defer_outer, scope, err_code); emitDbgStmt(gz, ret_lc_line, ret_lc_column); if (use_ptr) { addUnNode(gz, ZIR_INST_RET_LOAD, ret_ptr_inst, node); @@ -6926,9 +6933,10 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) { 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); + // .both (AstGen.zig:8242-8244). + uint32_t err_code_val = addUnNode( + &else_scope, ZIR_INST_ERR_UNION_CODE, result, node); + genDefersBoth(&else_scope, defer_outer, scope, err_code_val); } emitDbgStmt(&else_scope, ret_lc_line, ret_lc_column); if (use_ptr) { @@ -7538,7 +7546,7 @@ static uint32_t tryExpr( // Emit defers for error path (AstGen.zig:6019). if (ag->fn_block != NULL) { const Scope* fn_block_scope = &((GenZir*)ag->fn_block)->base; - genDefers(&else_scope, fn_block_scope, scope, DEFER_BOTH_SANS_ERR); + genDefersBoth(&else_scope, fn_block_scope, scope, err_code); } // Emit dbg_stmt at try keyword for error return tracing (AstGen.zig:6020). @@ -12627,8 +12635,10 @@ static DeferCounts countDefers(const Scope* outer_scope, Scope* inner_scope) { ScopeDefer* d = (ScopeDefer*)s; s = d->parent; c.have_err = true; - // need_err_code if remapped_err_code exists (we don't - // implement err capture yet, so always false). + // need_err_code if remapped_err_code exists + // (AstGen.zig:2993-2994). + if (d->remapped_err_code != UINT32_MAX) + c.need_err_code = true; break; } default: @@ -12702,6 +12712,72 @@ static void genDefers( } } +// genDefers with .both = err_code (AstGen.zig:3040-3061). +// Emits both normal and error defers, using defer_err_code when there is a +// remapped_err_code payload. +static void genDefersBoth(GenZir* gz, const Scope* outer_scope, + Scope* inner_scope, uint32_t err_code) { + AstGenCtx* ag = gz->astgen; + Scope* s = inner_scope; + while (s != outer_scope) { + switch (s->tag) { + case SCOPE_GEN_ZIR: + s = ((GenZir*)s)->parent; + break; + case SCOPE_LOCAL_VAL: + s = ((ScopeLocalVal*)s)->parent; + break; + case SCOPE_LOCAL_PTR: + s = ((ScopeLocalPtr*)s)->parent; + break; + case SCOPE_DEFER_NORMAL: { + ScopeDefer* d = (ScopeDefer*)s; + s = d->parent; + ZirInstData data; + data.defer_data.index = d->index; + data.defer_data.len = d->len; + addInstruction(gz, ZIR_INST_DEFER, data); + break; + } + case SCOPE_DEFER_ERROR: { + ScopeDefer* d = (ScopeDefer*)s; + s = d->parent; + if (d->remapped_err_code != UINT32_MAX) { + // Emit defer_err_code instruction (AstGen.zig:3041-3058). + ensureExtraCapacity(ag, 3); + uint32_t payload_index = ag->extra_len; + ag->extra[ag->extra_len++] = d->remapped_err_code; + ag->extra[ag->extra_len++] = d->index; + ag->extra[ag->extra_len++] = d->len; + ensureInstCapacity(ag, 1); + uint32_t new_index = ag->inst_len; + ag->inst_tags[new_index] = ZIR_INST_DEFER_ERR_CODE; + ZirInstData data; + data.defer_err_code.err_code = err_code; + data.defer_err_code.payload_index = payload_index; + ag->inst_datas[new_index] = data; + ag->inst_len++; + gzAppendInstruction(gz, new_index); + } else { + // No remapped err code; emit regular defer + // (AstGen.zig:3059-3061). + ZirInstData data; + data.defer_data.index = d->index; + data.defer_data.len = d->len; + addInstruction(gz, ZIR_INST_DEFER, data); + } + break; + } + case SCOPE_LABEL: + return; + case SCOPE_NAMESPACE: + case SCOPE_TOP: + default: + return; + } + } +} + // --- blockExprStmts (AstGen.zig:2538) --- // Processes block statements sequentially, threading scope. @@ -12808,10 +12884,10 @@ static void blockExprStmts(GenZir* gz, Scope* scope, SET_ERROR(ag); } break; - // defer/errdefer (AstGen.zig:2580-2581). + // defer/errdefer (AstGen.zig:2580-2581, deferStmt:3116-3187). case AST_NODE_DEFER: case AST_NODE_ERRDEFER: { - if (defer_idx >= 128) { + if (defer_idx >= 128 || val_idx >= 128) { SET_ERROR(ag); break; } @@ -12822,10 +12898,53 @@ static void blockExprStmts(GenZir* gz, Scope* scope, GenZir defer_gen = makeSubBlock(gz, cur_scope); defer_gen.any_defer_node = stmt; // AstGen.zig:3125 - // Evaluate deferred expression (AstGen.zig:3165). - // DEFER: lhs is the deferred expression, rhs = 0. - // ERRDEFER: lhs is optional error capture token, rhs is expr. AstData dnd = ag->tree->nodes.datas[stmt]; + + // Handle errdefer payload capture (AstGen.zig:3129-3158). + uint32_t opt_remapped_err_code = UINT32_MAX; + Scope* sub_scope = &defer_gen.base; + if (scope_tag == SCOPE_DEFER_ERROR && dnd.lhs != UINT32_MAX) { + uint32_t payload_token = dnd.lhs; + uint32_t ident_name = identAsString(ag, payload_token); + if (tokenIsUnderscore(ag->tree, payload_token)) { + // Discard of error capture; emit compile error + // (AstGen.zig:3134-3136). + SET_ERROR(ag); + break; + } + // Create value_placeholder instruction + // (AstGen.zig:3138-3147). + uint32_t remapped_err_code = ag->inst_len; + opt_remapped_err_code = remapped_err_code; + ensureInstCapacity(ag, 1); + ag->inst_tags[remapped_err_code] = ZIR_INST_EXTENDED; + ZirInstData edata; + memset(&edata, 0xaa, sizeof(edata)); + edata.extended.opcode + = (uint16_t)ZIR_EXT_VALUE_PLACEHOLDER; + ag->inst_datas[remapped_err_code] = edata; + ag->inst_len++; + uint32_t remapped_err_code_ref + = remapped_err_code + ZIR_REF_START_INDEX; + // Create local_val scope for the capture + // (AstGen.zig:3149-3158). + val_scopes[val_idx] = (ScopeLocalVal) { + .base = { .tag = SCOPE_LOCAL_VAL }, + .parent = &defer_gen.base, + .gen_zir = gz, + .name = ident_name, + .inst = remapped_err_code_ref, + .token_src = payload_token, + .is_used_or_discarded = NULL, + }; + sub_scope = &val_scopes[val_idx].base; + val_idx++; + // Emit dbg_var_val (AstGen.zig:3157). + addDbgVar(gz, ZIR_INST_DBG_VAR_VAL, ident_name, + remapped_err_code_ref); + } + + // Determine the expression node (AstGen.zig:3160-3163). uint32_t expr_node; if (tag == AST_NODE_DEFER) { expr_node = dnd.lhs; @@ -12834,23 +12953,33 @@ static void blockExprStmts(GenZir* gz, Scope* scope, } // unusedResultExpr pattern (AstGen.zig:3165, 2641-2646). emitDbgNode(&defer_gen, expr_node); - uint32_t defer_result - = expr(&defer_gen, &defer_gen.base, expr_node); + uint32_t defer_result = expr(&defer_gen, sub_scope, expr_node); addEnsureResult(&defer_gen, defer_result, expr_node); // Add break_inline at end (AstGen.zig:3167). addBreak(&defer_gen, ZIR_INST_BREAK_INLINE, 0, ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE); - // Write body to extra (AstGen.zig:3173-3175). + // Write body to extra (AstGen.zig:3169-3175). uint32_t raw_body_len = gzInstructionsLen(&defer_gen); const uint32_t* body = gzInstructionsSlice(&defer_gen); uint32_t extra_index = ag->extra_len; - uint32_t fixup_len - = countBodyLenAfterFixups(ag, body, raw_body_len); + uint32_t fixup_len; + if (opt_remapped_err_code != UINT32_MAX) { + fixup_len = countBodyLenAfterFixupsExtraRefs( + ag, body, raw_body_len, &opt_remapped_err_code, 1); + } else { + fixup_len = countBodyLenAfterFixupsExtraRefs( + ag, body, raw_body_len, NULL, 0); + } ensureExtraCapacity(ag, fixup_len); - for (uint32_t b = 0; b < raw_body_len; b++) - appendPossiblyRefdBodyInst(ag, body[b]); + if (opt_remapped_err_code != UINT32_MAX) { + appendBodyWithFixupsExtraRefs( + ag, body, raw_body_len, &opt_remapped_err_code, 1); + } else { + appendBodyWithFixupsExtraRefs( + ag, body, raw_body_len, NULL, 0); + } gzUnstack(&defer_gen); // Create scope (AstGen.zig:3179-3185). @@ -12859,6 +12988,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope, .parent = cur_scope, .index = extra_index, .len = fixup_len, + .remapped_err_code = opt_remapped_err_code, }; cur_scope = &defer_scopes[defer_idx].base; defer_idx++; diff --git a/stage0/astgen_test.zig b/stage0/astgen_test.zig @@ -1230,7 +1230,7 @@ const corpus_files = .{ "../test/behavior/const_slice_child.zig", "../test/behavior/decl_literals.zig", "../test/behavior/decltest.zig", - //"../test/behavior/defer.zig", + "../test/behavior/defer.zig", //"../test/behavior/destructure.zig", "../test/behavior/duplicated_test_names.zig", "../test/behavior/empty_union.zig",