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:
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",