From 0d9afc0ae647fb7cd837897cb0b5effb29c0a59e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Thu, 12 Feb 2026 14:23:19 +0000 Subject: [PATCH] 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 --- astgen.c | 268 ++++++++++++++++++++++++++++++++++++++++++------ astgen_test.zig | 4 + 2 files changed, 239 insertions(+), 33 deletions(-) diff --git a/astgen.c b/astgen.c index ecff43a9fd..fd8ab7b2af 100644 --- a/astgen.c +++ b/astgen.c @@ -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; } diff --git a/astgen_test.zig b/astgen_test.zig index a55465d169..fdf9052df5 100644 --- a/astgen_test.zig +++ b/astgen_test.zig @@ -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, } }