astgen: add genDefers, ret_err_value fast path, fix scope chain

- Add genDefers() with DEFER_NORMAL_ONLY/DEFER_BOTH_SANS_ERR modes
- Add countDefers() for checking defer types in scope chain
- Add genDefers calls to breakExpr, continueExpr, retExpr, tryExpr
- Add fn_block tracking to AstGenCtx (set in fnDecl/testDecl)
- Add return error.Foo fast path using ret_err_value instruction
- Fix fullBodyExpr scope: pass &body_gz.base instead of params_scope
- Fix blockExprStmts: guard genDefers with noreturn_stmt check
- Fix retExpr MAYBE path: correct dbg_stmt/restore ordering
- Save/restore fn_block in containerDecl (set NULL for nested structs)
- addEnsureResult now returns bool indicating noreturn

First ZIR tag mismatch moved from inst[211] to inst[428].

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 14:23:19 +00:00
parent 5cffc20ef3
commit 0d9afc0ae6
2 changed files with 239 additions and 33 deletions

268
astgen.c
View File

@@ -87,6 +87,8 @@ typedef struct {
uint32_t scratch_inst_cap;
// Return type ref for the current function (set during fnDecl/testDecl).
uint32_t fn_ret_ty; // ZirInstRef
// Pointer to the fn_block GenZir for the current function (AstGen.zig:45).
void* fn_block; // GenZir*
// ref_table: deferred REF instructions (AstGen.zig:58-68).
// Key = operand inst index, Value = ref inst index.
uint32_t* ref_table_keys;
@@ -1365,17 +1367,32 @@ static uint32_t rvalue(
// Forward declarations.
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
// --- DeferCounts (AstGen.zig:2966) ---
typedef struct {
bool have_any;
bool have_normal;
bool have_err;
bool need_err_code;
} DeferCounts;
static DeferCounts countDefers(Scope* outer_scope, Scope* inner_scope);
static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static void assignStmt(GenZir* gz, Scope* scope, uint32_t infix_node);
static void assignOp(
GenZir* gz, Scope* scope, uint32_t infix_node, ZirInstTag op_tag);
static void emitDbgStmt(GenZir* gz, uint32_t line, uint32_t column);
static void genDefers(
GenZir* gz, Scope* outer_scope, Scope* inner_scope, int which);
static void emitDbgStmtForceCurrentIndex(
GenZir* gz, uint32_t line, uint32_t column);
static void emitDbgNode(GenZir* gz, uint32_t node);
static void addDbgVar(
GenZir* gz, ZirInstTag tag, uint32_t name, uint32_t inst);
static void addEnsureResult(
static bool addEnsureResult(
GenZir* gz, uint32_t maybe_unused_result, uint32_t statement);
static void blockExprStmts(
GenZir* gz, Scope* scope, const uint32_t* statements, uint32_t stmt_count);
@@ -1387,14 +1404,16 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
static uint32_t blockExprExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t forExpr(GenZir* gz, Scope* scope, uint32_t node);
static uint32_t forExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_statement);
static uint32_t orelseCatchExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_catch);
static uint32_t arrayInitDotExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t switchExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t whileExpr(GenZir* gz, Scope* scope, uint32_t node);
static uint32_t whileExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_statement);
#define EVAL_TO_ERROR_NEVER 0
#define EVAL_TO_ERROR_ALWAYS 1
#define EVAL_TO_ERROR_MAYBE 2
@@ -2215,9 +2234,8 @@ static uint32_t multilineStringLiteral(
}
// --- ret (AstGen.zig:8119) ---
// Simplified: no defer handling.
static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
const AstGenCtx* ag = gz->astgen;
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
// Ensure debug line/column information is emitted for this return
@@ -2228,11 +2246,19 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
uint32_t ret_lc_line = ag->source_line - gz->decl_line;
uint32_t ret_lc_column = ag->source_column;
// AstGen.zig:8123: return outside function is an error.
if (ag->fn_block == NULL) {
SET_ERROR(ag);
return ZIR_REF_UNREACHABLE_VALUE;
}
Scope* defer_outer = &((GenZir*)ag->fn_block)->base;
AstData nd = tree->nodes.datas[node];
uint32_t operand_node = nd.lhs; // optional
if (operand_node == 0) {
// Void return (AstGen.zig:8148-8156).
genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
// Restore error trace unconditionally (AstGen.zig:8153).
ZirInstData rdata;
rdata.un_node.operand = ZIR_REF_NONE;
@@ -2243,6 +2269,20 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
return ZIR_REF_UNREACHABLE_VALUE;
}
// Fast path: return error.Foo (AstGen.zig:8159-8175).
if (tree->nodes.tags[operand_node] == AST_NODE_ERROR_VALUE) {
uint32_t error_token = tree->nodes.main_tokens[operand_node] + 2;
uint32_t err_name_str = identAsString(ag, error_token);
DeferCounts dc = countDefers(defer_outer, scope);
if (!dc.need_err_code) {
genDefers(gz, defer_outer, scope, DEFER_BOTH_SANS_ERR);
emitDbgStmt(gz, ret_lc_line, ret_lc_column);
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.
}
// Evaluate operand with fn_ret_ty as result type (AstGen.zig:8178-8186).
ResultLoc ret_rl = RL_NONE_VAL;
if (ag->fn_ret_ty != 0) {
@@ -2256,24 +2296,37 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
int eval_to_err = nodeMayEvalToError(tree, operand_node);
if (eval_to_err == EVAL_TO_ERROR_NEVER) {
// Returning non-error: pop error trace unconditionally
// (AstGen.zig:8193-8194).
// (AstGen.zig:8190-8198).
genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
ZirInstData rdata;
rdata.un_node.operand = ZIR_REF_NONE;
rdata.un_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
addInstruction(
gz, ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL, rdata);
} else if (eval_to_err == EVAL_TO_ERROR_MAYBE) {
// May be an error: conditionally pop based on value
// (AstGen.zig:8216-8217).
ZirInstData rdata;
rdata.un_node.operand = operand;
rdata.un_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
addInstruction(gz, ZIR_INST_RESTORE_ERR_RET_INDEX_FN_ENTRY, rdata);
// May be an error (AstGen.zig:8208-8220).
DeferCounts dc = countDefers(defer_outer, scope);
if (!dc.have_err) {
// Only regular defers; no branch needed (AstGen.zig:8210-8220).
genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
emitDbgStmt(gz, ret_lc_line, ret_lc_column);
ZirInstData rdata;
rdata.un_node.operand = operand;
rdata.un_node.src_node
= (int32_t)node - (int32_t)gz->decl_node_index;
addInstruction(gz, ZIR_INST_RESTORE_ERR_RET_INDEX_FN_ENTRY, rdata);
addUnNode(gz, ZIR_INST_RET_NODE, operand, 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);
} else {
// .always: error stays on trace, but still need normal defers.
genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
}
// .always: no restore needed (error stays on trace)
// Emit dbg_stmt back at return keyword for error return tracing
// (AstGen.zig:8196).
// Emit dbg_stmt back at return keyword for error return tracing.
emitDbgStmt(gz, ret_lc_line, ret_lc_column);
addUnNode(gz, ZIR_INST_RET_NODE, operand, node);
return ZIR_REF_UNREACHABLE_VALUE;
@@ -2660,7 +2713,6 @@ static uint32_t structInitExpr(
}
// --- tryExpr (AstGen.zig:5957) ---
// Simplified: no defer handling.
static uint32_t tryExpr(GenZir* gz, Scope* scope, uint32_t node) {
AstGenCtx* ag = gz->astgen;
AstData nd = ag->tree->nodes.datas[node];
@@ -2685,6 +2737,12 @@ static uint32_t tryExpr(GenZir* gz, Scope* scope, uint32_t node) {
uint32_t err_code
= addUnNode(&else_scope, ZIR_INST_ERR_UNION_CODE, operand, node);
// Emit defers for error path (AstGen.zig:6019).
if (ag->fn_block != NULL) {
Scope* fn_block_scope = &((GenZir*)ag->fn_block)->base;
genDefers(&else_scope, fn_block_scope, scope, DEFER_BOTH_SANS_ERR);
}
// Emit dbg_stmt at try keyword for error return tracing (AstGen.zig:6020).
emitDbgStmt(&else_scope, try_lc_line, try_lc_column);
@@ -3080,7 +3138,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
// for (AstGen.zig:1043-1060).
case AST_NODE_FOR_SIMPLE:
case AST_NODE_FOR:
return rvalue(gz, rl, forExpr(gz, scope, node), node);
return rvalue(gz, rl, forExpr(gz, scope, node, false), node);
// Merge error sets (AstGen.zig:787).
case AST_NODE_MERGE_ERROR_SETS:
return rvalue(gz, rl,
@@ -3126,6 +3184,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
// Void break (AstGen.zig:2195-2206).
rvalue(gz, block_gz->break_result_info,
ZIR_REF_VOID_VALUE, node);
genDefers(gz, s, scope, DEFER_NORMAL_ONLY);
if (!block_gz->is_comptime) {
ZirInstData rdata;
rdata.un_node.operand
@@ -3142,6 +3201,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
// Value break (AstGen.zig:2208-2228).
uint32_t operand = exprRl(
gz, scope, block_gz->break_result_info, opt_rhs);
genDefers(gz, s, scope, DEFER_NORMAL_ONLY);
if (!block_gz->is_comptime)
restoreErrRetIndex(gz, block_inst,
block_gz->break_result_info, opt_rhs, operand);
@@ -3184,6 +3244,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
if (s->tag == SCOPE_GEN_ZIR) {
GenZir* gz2 = (GenZir*)s;
if (gz2->continue_block != UINT32_MAX) {
genDefers(gz, s, scope, DEFER_NORMAL_ONLY);
addBreak(gz, ZIR_INST_BREAK, gz2->continue_block,
ZIR_REF_VOID_VALUE,
(int32_t)node - (int32_t)gz->decl_node_index);
@@ -3250,7 +3311,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
case AST_NODE_WHILE_SIMPLE:
case AST_NODE_WHILE_CONT:
case AST_NODE_WHILE:
return rvalue(gz, rl, whileExpr(gz, scope, node), node);
return rvalue(gz, rl, whileExpr(gz, scope, node, false), node);
// error_value (AstGen.zig:1005-1010).
case AST_NODE_ERROR_VALUE: {
uint32_t error_token = nd.rhs;
@@ -3784,7 +3845,8 @@ static uint32_t ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
#define FOR_MAX_INPUTS 16
static uint32_t forExpr(GenZir* gz, Scope* scope, uint32_t node) {
static uint32_t forExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_statement) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node];
@@ -4033,7 +4095,14 @@ static uint32_t forExpr(GenZir* gz, Scope* scope, uint32_t node) {
setBlockBody(ag, &loop_scope, loop_inst);
gzAppendInstruction(gz, loop_inst);
return loop_inst + ZIR_REF_START_INDEX;
uint32_t result = loop_inst + ZIR_REF_START_INDEX;
// Emit ensure_result_used when used as statement (AstGen.zig:7121-7123).
if (is_statement) {
addUnNode(gz, ZIR_INST_ENSURE_RESULT_USED, result, node);
}
return result;
}
// --- orelseCatchExpr (AstGen.zig:6031-6142) ---
@@ -4099,7 +4168,8 @@ static uint32_t orelseCatchExpr(
// condbr → then { continue_block { body, break continue }, break cond }
// → else { break loop }
static uint32_t whileExpr(GenZir* gz, Scope* scope, uint32_t node) {
static uint32_t whileExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_statement) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node];
@@ -4160,8 +4230,6 @@ static uint32_t whileExpr(GenZir* gz, Scope* scope, uint32_t node) {
// dbg_stmt + dbg_empty_stmt (AstGen.zig:6737-6745).
advanceSourceCursor(
ag, tree->tokens.starts[lastToken(tree, body_node)]);
fprintf(stderr, "DBG: forExpr dbg_empty_stmt, is_comptime=%d\n",
gz->is_comptime);
emitDbgStmt(gz, ag->source_line - gz->decl_line, ag->source_column);
{
ZirInstData ext_data;
@@ -4187,7 +4255,14 @@ static uint32_t whileExpr(GenZir* gz, Scope* scope, uint32_t node) {
// Wire up condbr (AstGen.zig:6795).
setCondBrPayload(ag, condbr, cond, &then_scope, &else_scope);
return loop_inst + ZIR_REF_START_INDEX;
uint32_t result = loop_inst + ZIR_REF_START_INDEX;
// Emit ensure_result_used when used as statement (AstGen.zig:6812-6813).
if (is_statement) {
addUnNode(gz, ZIR_INST_ENSURE_RESULT_USED, result, node);
}
return result;
}
// --- switchExpr (AstGen.zig:7625-8117) ---
@@ -4997,10 +5072,12 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
// --- addEnsureResult (AstGen.zig:2649) ---
// After evaluating an expression as a statement, optionally emits
// ensure_result_used. For call/field_call, sets flag in extra data instead.
static void addEnsureResult(
// Returns true if the result is noreturn (AstGen.zig:2909).
static bool addEnsureResult(
GenZir* gz, uint32_t maybe_unused_result, uint32_t statement) {
AstGenCtx* ag = gz->astgen;
bool elide_check;
bool is_noreturn = false;
if (maybe_unused_result >= ZIR_REF_START_INDEX) {
uint32_t inst = maybe_unused_result - ZIR_REF_START_INDEX;
ZirInstTag tag = ag->inst_tags[inst];
@@ -5024,7 +5101,7 @@ static void addEnsureResult(
elide_check = true;
break;
}
// Always noreturn → elide.
// Always noreturn → elide (AstGen.zig:2909).
case ZIR_INST_BREAK:
case ZIR_INST_BREAK_INLINE:
case ZIR_INST_CONDBR:
@@ -5040,6 +5117,7 @@ static void addEnsureResult(
case ZIR_INST_TRAP:
case ZIR_INST_CHECK_COMPTIME_CONTROL_FLOW:
case ZIR_INST_SWITCH_CONTINUE:
is_noreturn = true;
elide_check = true;
break;
// Always void → elide.
@@ -5091,13 +5169,117 @@ static void addEnsureResult(
}
} else {
// Named ref constant.
elide_check = (maybe_unused_result == ZIR_REF_UNREACHABLE_VALUE
|| maybe_unused_result == ZIR_REF_VOID_VALUE);
is_noreturn = (maybe_unused_result == ZIR_REF_UNREACHABLE_VALUE);
elide_check
= (is_noreturn || maybe_unused_result == ZIR_REF_VOID_VALUE);
}
if (!elide_check) {
addUnNode(
gz, ZIR_INST_ENSURE_RESULT_USED, maybe_unused_result, statement);
}
return is_noreturn;
}
// --- countDefers (AstGen.zig:2966) ---
// Walk scope chain and count defer types.
static DeferCounts countDefers(Scope* outer_scope, Scope* inner_scope) {
DeferCounts c = { false, false, false, false };
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;
c.have_normal = true;
break;
}
case SCOPE_DEFER_ERROR: {
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).
break;
}
default:
return c;
}
}
c.have_any = c.have_normal || c.have_err;
return c;
}
// --- genDefers (AstGen.zig:3014) ---
// Walk scope chain from inner to outer, emitting .defer instructions.
// which: DEFER_NORMAL_ONLY or DEFER_BOTH_SANS_ERR.
static void genDefers(
GenZir* gz, Scope* outer_scope, Scope* inner_scope, int which) {
Scope* s = inner_scope;
while (s != outer_scope) {
switch (s->tag) {
case SCOPE_GEN_ZIR: {
GenZir* g = (GenZir*)s;
s = g->parent;
break;
}
case SCOPE_LOCAL_VAL: {
ScopeLocalVal* lv = (ScopeLocalVal*)s;
s = lv->parent;
break;
}
case SCOPE_LOCAL_PTR: {
ScopeLocalPtr* lp = (ScopeLocalPtr*)s;
s = lp->parent;
break;
}
case SCOPE_DEFER_NORMAL: {
ScopeDefer* d = (ScopeDefer*)s;
s = d->parent;
// Emit ZIR_INST_DEFER (AstGen.zig:3031).
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 (which == DEFER_BOTH_SANS_ERR) {
// Emit regular DEFER for error defers too (AstGen.zig:3038).
ZirInstData data;
data.defer_data.index = d->index;
data.defer_data.len = d->len;
addInstruction(gz, ZIR_INST_DEFER, data);
}
// DEFER_NORMAL_ONLY: skip error defers (AstGen.zig:3063).
break;
}
case SCOPE_LABEL: {
// Labels store parent in the GenZir they're attached to.
// Just skip by going to the parent scope stored in parent.
// Actually labels don't have a separate parent pointer in our
// representation; they're part of GenZir. This case shouldn't
// appear when walking from blockExprStmts scope.
return;
}
case SCOPE_NAMESPACE:
case SCOPE_TOP:
default:
return;
}
}
}
// --- blockExprStmts (AstGen.zig:2538) ---
@@ -5115,6 +5297,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
uint32_t ptr_idx = 0;
uint32_t defer_idx = 0;
Scope* cur_scope = scope;
bool noreturn_stmt = false;
for (uint32_t i = 0; i < stmt_count; i++) {
if (ag->has_compile_errors)
@@ -5239,21 +5422,25 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
case AST_NODE_WHILE_SIMPLE:
case AST_NODE_WHILE_CONT:
case AST_NODE_WHILE:
(void)whileExpr(gz, cur_scope, stmt);
(void)whileExpr(gz, cur_scope, stmt, true);
break;
case AST_NODE_FOR_SIMPLE:
case AST_NODE_FOR:
(void)forExpr(gz, cur_scope, stmt);
(void)forExpr(gz, cur_scope, stmt, true);
break;
default: {
// Expression statement (AstGen.zig:2627 unusedResultExpr).
emitDbgNode(gz, stmt);
uint32_t result = expr(gz, cur_scope, stmt);
addEnsureResult(gz, result, stmt);
noreturn_stmt = addEnsureResult(gz, result, stmt);
break;
}
}
}
// Emit normal defers at block exit (AstGen.zig:2633-2634).
if (!noreturn_stmt) {
genDefers(gz, scope, cur_scope, DEFER_NORMAL_ONLY);
}
}
// --- fullBodyExpr (AstGen.zig:2358) ---
@@ -6145,6 +6332,10 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
fn_block.instructions_top = ag->scratch_inst_len;
fn_block.break_block = UINT32_MAX;
// Set fn_block for retExpr (AstGen.zig:4849-4852).
void* prev_fn_block = ag->fn_block;
ag->fn_block = &fn_block;
// Compute lbrace source location (AstGen.zig:4860-4862).
advanceSourceCursorToNode(ag, body_node);
uint32_t lbrace_line = ag->source_line - decl_line;
@@ -6153,6 +6344,8 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
// Process test body (AstGen.zig:4864).
fullBodyExpr(&fn_block, &fn_block.base, RL_NONE_VAL, body_node);
ag->fn_block = prev_fn_block;
// If we hit unimplemented features, bail out.
if (ag->has_compile_errors)
return;
@@ -6463,7 +6656,9 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
body_gz.is_comptime = false;
body_gz.instructions_top = ag->scratch_inst_len;
// Set fn_ret_ty for the body (AstGen.zig:4449-4455).
// Set fn_block and fn_ret_ty for the body (AstGen.zig:4442-4455).
void* prev_fn_block = ag->fn_block;
ag->fn_block = &body_gz;
uint32_t prev_fn_ret_ty = ag->fn_ret_ty;
if (is_inferred_error || ret_ref == ZIR_REF_NONE) {
// Non-void non-trivial return type: emit ret_type instruction.
@@ -6495,8 +6690,9 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
uint32_t lbrace_line = ag->source_line - decl_line;
uint32_t lbrace_column = ag->source_column;
fullBodyExpr(&body_gz, params_scope, RL_NONE_VAL, body_node);
fullBodyExpr(&body_gz, &body_gz.base, RL_NONE_VAL, body_node);
ag->fn_block = prev_fn_block;
ag->fn_ret_ty = prev_fn_ret_ty;
if (ag->has_compile_errors) {
@@ -6704,10 +6900,16 @@ static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node) {
return ZIR_REF_VOID_VALUE;
}
// Save/clear fn_block for nested containers (AstGen.zig:5480-5482).
void* prev_fn_block = ag->fn_block;
ag->fn_block = NULL;
// For now, only handle struct containers (AstGen.zig:5481-5496).
// TODO: handle union/enum/opaque.
uint32_t decl_inst = structDeclInner(ag, gz, node, members, members_len);
(void)scope;
ag->fn_block = prev_fn_block;
return decl_inst + ZIR_REF_START_INDEX;
}

View File

@@ -758,6 +758,10 @@ fn dataMatches(tag: Zir.Inst.Tag, ref: Zir.Inst.Data, got: c.ZirInstData) bool {
return @intFromEnum(ref.str.start) == got.str.start and
ref.str.len == got.str.len;
},
.@"defer" => {
return ref.@"defer".index == got.defer_data.index and
ref.@"defer".len == got.defer_data.len;
},
else => return false,
}
}