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; uint32_t scratch_inst_cap;
// Return type ref for the current function (set during fnDecl/testDecl). // Return type ref for the current function (set during fnDecl/testDecl).
uint32_t fn_ret_ty; // ZirInstRef 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). // ref_table: deferred REF instructions (AstGen.zig:58-68).
// Key = operand inst index, Value = ref inst index. // Key = operand inst index, Value = ref inst index.
uint32_t* ref_table_keys; uint32_t* ref_table_keys;
@@ -1365,17 +1367,32 @@ static uint32_t rvalue(
// Forward declarations. // Forward declarations.
static uint32_t expr(GenZir* gz, Scope* scope, uint32_t node); 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 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 assignStmt(GenZir* gz, Scope* scope, uint32_t infix_node);
static void assignOp( static void assignOp(
GenZir* gz, Scope* scope, uint32_t infix_node, ZirInstTag op_tag); GenZir* gz, Scope* scope, uint32_t infix_node, ZirInstTag op_tag);
static void emitDbgStmt(GenZir* gz, uint32_t line, uint32_t column); 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( static void emitDbgStmtForceCurrentIndex(
GenZir* gz, uint32_t line, uint32_t column); GenZir* gz, uint32_t line, uint32_t column);
static void emitDbgNode(GenZir* gz, uint32_t node); static void emitDbgNode(GenZir* gz, uint32_t node);
static void addDbgVar( static void addDbgVar(
GenZir* gz, ZirInstTag tag, uint32_t name, uint32_t inst); 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); GenZir* gz, uint32_t maybe_unused_result, uint32_t statement);
static void blockExprStmts( static void blockExprStmts(
GenZir* gz, Scope* scope, const uint32_t* statements, uint32_t stmt_count); 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( static uint32_t blockExprExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); 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 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( static uint32_t orelseCatchExpr(
GenZir* gz, Scope* scope, uint32_t node, bool is_catch); GenZir* gz, Scope* scope, uint32_t node, bool is_catch);
static uint32_t arrayInitDotExpr( static uint32_t arrayInitDotExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
static uint32_t switchExpr( static uint32_t switchExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); 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_NEVER 0
#define EVAL_TO_ERROR_ALWAYS 1 #define EVAL_TO_ERROR_ALWAYS 1
#define EVAL_TO_ERROR_MAYBE 2 #define EVAL_TO_ERROR_MAYBE 2
@@ -2215,9 +2234,8 @@ static uint32_t multilineStringLiteral(
} }
// --- ret (AstGen.zig:8119) --- // --- ret (AstGen.zig:8119) ---
// Simplified: no defer handling.
static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) { 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; const Ast* tree = ag->tree;
// Ensure debug line/column information is emitted for this return // 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_line = ag->source_line - gz->decl_line;
uint32_t ret_lc_column = ag->source_column; 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]; AstData nd = tree->nodes.datas[node];
uint32_t operand_node = nd.lhs; // optional uint32_t operand_node = nd.lhs; // optional
if (operand_node == 0) { if (operand_node == 0) {
// Void return (AstGen.zig:8148-8156). // Void return (AstGen.zig:8148-8156).
genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
// Restore error trace unconditionally (AstGen.zig:8153). // Restore error trace unconditionally (AstGen.zig:8153).
ZirInstData rdata; ZirInstData rdata;
rdata.un_node.operand = ZIR_REF_NONE; 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; 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). // Evaluate operand with fn_ret_ty as result type (AstGen.zig:8178-8186).
ResultLoc ret_rl = RL_NONE_VAL; ResultLoc ret_rl = RL_NONE_VAL;
if (ag->fn_ret_ty != 0) { 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); int eval_to_err = nodeMayEvalToError(tree, operand_node);
if (eval_to_err == EVAL_TO_ERROR_NEVER) { if (eval_to_err == EVAL_TO_ERROR_NEVER) {
// Returning non-error: pop error trace unconditionally // 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; ZirInstData rdata;
rdata.un_node.operand = ZIR_REF_NONE; rdata.un_node.operand = ZIR_REF_NONE;
rdata.un_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index; rdata.un_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
addInstruction( addInstruction(
gz, ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL, rdata); gz, ZIR_INST_RESTORE_ERR_RET_INDEX_UNCONDITIONAL, rdata);
} else if (eval_to_err == EVAL_TO_ERROR_MAYBE) { } else if (eval_to_err == EVAL_TO_ERROR_MAYBE) {
// May be an error: conditionally pop based on value // May be an error (AstGen.zig:8208-8220).
// (AstGen.zig:8216-8217). DeferCounts dc = countDefers(defer_outer, scope);
ZirInstData rdata; if (!dc.have_err) {
rdata.un_node.operand = operand; // Only regular defers; no branch needed (AstGen.zig:8210-8220).
rdata.un_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index; genDefers(gz, defer_outer, scope, DEFER_NORMAL_ONLY);
addInstruction(gz, ZIR_INST_RESTORE_ERR_RET_INDEX_FN_ENTRY, rdata); 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 // Emit dbg_stmt back at return keyword for error return tracing.
// (AstGen.zig:8196).
emitDbgStmt(gz, ret_lc_line, ret_lc_column); emitDbgStmt(gz, ret_lc_line, ret_lc_column);
addUnNode(gz, ZIR_INST_RET_NODE, operand, node); addUnNode(gz, ZIR_INST_RET_NODE, operand, node);
return ZIR_REF_UNREACHABLE_VALUE; return ZIR_REF_UNREACHABLE_VALUE;
@@ -2660,7 +2713,6 @@ static uint32_t structInitExpr(
} }
// --- tryExpr (AstGen.zig:5957) --- // --- tryExpr (AstGen.zig:5957) ---
// Simplified: no defer handling.
static uint32_t tryExpr(GenZir* gz, Scope* scope, uint32_t node) { static uint32_t tryExpr(GenZir* gz, Scope* scope, uint32_t node) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
AstData nd = ag->tree->nodes.datas[node]; 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 uint32_t err_code
= addUnNode(&else_scope, ZIR_INST_ERR_UNION_CODE, operand, node); = 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). // Emit dbg_stmt at try keyword for error return tracing (AstGen.zig:6020).
emitDbgStmt(&else_scope, try_lc_line, try_lc_column); 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). // for (AstGen.zig:1043-1060).
case AST_NODE_FOR_SIMPLE: case AST_NODE_FOR_SIMPLE:
case AST_NODE_FOR: 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). // Merge error sets (AstGen.zig:787).
case AST_NODE_MERGE_ERROR_SETS: case AST_NODE_MERGE_ERROR_SETS:
return rvalue(gz, rl, 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). // Void break (AstGen.zig:2195-2206).
rvalue(gz, block_gz->break_result_info, rvalue(gz, block_gz->break_result_info,
ZIR_REF_VOID_VALUE, node); ZIR_REF_VOID_VALUE, node);
genDefers(gz, s, scope, DEFER_NORMAL_ONLY);
if (!block_gz->is_comptime) { if (!block_gz->is_comptime) {
ZirInstData rdata; ZirInstData rdata;
rdata.un_node.operand 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). // Value break (AstGen.zig:2208-2228).
uint32_t operand = exprRl( uint32_t operand = exprRl(
gz, scope, block_gz->break_result_info, opt_rhs); gz, scope, block_gz->break_result_info, opt_rhs);
genDefers(gz, s, scope, DEFER_NORMAL_ONLY);
if (!block_gz->is_comptime) if (!block_gz->is_comptime)
restoreErrRetIndex(gz, block_inst, restoreErrRetIndex(gz, block_inst,
block_gz->break_result_info, opt_rhs, operand); 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) { if (s->tag == SCOPE_GEN_ZIR) {
GenZir* gz2 = (GenZir*)s; GenZir* gz2 = (GenZir*)s;
if (gz2->continue_block != UINT32_MAX) { if (gz2->continue_block != UINT32_MAX) {
genDefers(gz, s, scope, DEFER_NORMAL_ONLY);
addBreak(gz, ZIR_INST_BREAK, gz2->continue_block, addBreak(gz, ZIR_INST_BREAK, gz2->continue_block,
ZIR_REF_VOID_VALUE, ZIR_REF_VOID_VALUE,
(int32_t)node - (int32_t)gz->decl_node_index); (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_SIMPLE:
case AST_NODE_WHILE_CONT: case AST_NODE_WHILE_CONT:
case AST_NODE_WHILE: 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). // error_value (AstGen.zig:1005-1010).
case AST_NODE_ERROR_VALUE: { case AST_NODE_ERROR_VALUE: {
uint32_t error_token = nd.rhs; 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 #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; AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree; const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node]; 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); setBlockBody(ag, &loop_scope, loop_inst);
gzAppendInstruction(gz, 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) --- // --- orelseCatchExpr (AstGen.zig:6031-6142) ---
@@ -4099,7 +4168,8 @@ static uint32_t orelseCatchExpr(
// condbr → then { continue_block { body, break continue }, break cond } // condbr → then { continue_block { body, break continue }, break cond }
// → else { break loop } // → 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; AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree; const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node]; 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). // dbg_stmt + dbg_empty_stmt (AstGen.zig:6737-6745).
advanceSourceCursor( advanceSourceCursor(
ag, tree->tokens.starts[lastToken(tree, body_node)]); 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); emitDbgStmt(gz, ag->source_line - gz->decl_line, ag->source_column);
{ {
ZirInstData ext_data; ZirInstData ext_data;
@@ -4187,7 +4255,14 @@ static uint32_t whileExpr(GenZir* gz, Scope* scope, uint32_t node) {
// Wire up condbr (AstGen.zig:6795). // Wire up condbr (AstGen.zig:6795).
setCondBrPayload(ag, condbr, cond, &then_scope, &else_scope); 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) --- // --- switchExpr (AstGen.zig:7625-8117) ---
@@ -4997,10 +5072,12 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
// --- addEnsureResult (AstGen.zig:2649) --- // --- addEnsureResult (AstGen.zig:2649) ---
// After evaluating an expression as a statement, optionally emits // After evaluating an expression as a statement, optionally emits
// ensure_result_used. For call/field_call, sets flag in extra data instead. // 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) { GenZir* gz, uint32_t maybe_unused_result, uint32_t statement) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
bool elide_check; bool elide_check;
bool is_noreturn = false;
if (maybe_unused_result >= ZIR_REF_START_INDEX) { if (maybe_unused_result >= ZIR_REF_START_INDEX) {
uint32_t inst = maybe_unused_result - ZIR_REF_START_INDEX; uint32_t inst = maybe_unused_result - ZIR_REF_START_INDEX;
ZirInstTag tag = ag->inst_tags[inst]; ZirInstTag tag = ag->inst_tags[inst];
@@ -5024,7 +5101,7 @@ static void addEnsureResult(
elide_check = true; elide_check = true;
break; break;
} }
// Always noreturn → elide. // Always noreturn → elide (AstGen.zig:2909).
case ZIR_INST_BREAK: case ZIR_INST_BREAK:
case ZIR_INST_BREAK_INLINE: case ZIR_INST_BREAK_INLINE:
case ZIR_INST_CONDBR: case ZIR_INST_CONDBR:
@@ -5040,6 +5117,7 @@ static void addEnsureResult(
case ZIR_INST_TRAP: case ZIR_INST_TRAP:
case ZIR_INST_CHECK_COMPTIME_CONTROL_FLOW: case ZIR_INST_CHECK_COMPTIME_CONTROL_FLOW:
case ZIR_INST_SWITCH_CONTINUE: case ZIR_INST_SWITCH_CONTINUE:
is_noreturn = true;
elide_check = true; elide_check = true;
break; break;
// Always void → elide. // Always void → elide.
@@ -5091,13 +5169,117 @@ static void addEnsureResult(
} }
} else { } else {
// Named ref constant. // Named ref constant.
elide_check = (maybe_unused_result == ZIR_REF_UNREACHABLE_VALUE is_noreturn = (maybe_unused_result == ZIR_REF_UNREACHABLE_VALUE);
|| maybe_unused_result == ZIR_REF_VOID_VALUE); elide_check
= (is_noreturn || maybe_unused_result == ZIR_REF_VOID_VALUE);
} }
if (!elide_check) { if (!elide_check) {
addUnNode( addUnNode(
gz, ZIR_INST_ENSURE_RESULT_USED, maybe_unused_result, statement); 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) --- // --- blockExprStmts (AstGen.zig:2538) ---
@@ -5115,6 +5297,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
uint32_t ptr_idx = 0; uint32_t ptr_idx = 0;
uint32_t defer_idx = 0; uint32_t defer_idx = 0;
Scope* cur_scope = scope; Scope* cur_scope = scope;
bool noreturn_stmt = false;
for (uint32_t i = 0; i < stmt_count; i++) { for (uint32_t i = 0; i < stmt_count; i++) {
if (ag->has_compile_errors) 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_SIMPLE:
case AST_NODE_WHILE_CONT: case AST_NODE_WHILE_CONT:
case AST_NODE_WHILE: case AST_NODE_WHILE:
(void)whileExpr(gz, cur_scope, stmt); (void)whileExpr(gz, cur_scope, stmt, true);
break; break;
case AST_NODE_FOR_SIMPLE: case AST_NODE_FOR_SIMPLE:
case AST_NODE_FOR: case AST_NODE_FOR:
(void)forExpr(gz, cur_scope, stmt); (void)forExpr(gz, cur_scope, stmt, true);
break; break;
default: { default: {
// Expression statement (AstGen.zig:2627 unusedResultExpr). // Expression statement (AstGen.zig:2627 unusedResultExpr).
emitDbgNode(gz, stmt); emitDbgNode(gz, stmt);
uint32_t result = expr(gz, cur_scope, stmt); uint32_t result = expr(gz, cur_scope, stmt);
addEnsureResult(gz, result, stmt); noreturn_stmt = addEnsureResult(gz, result, stmt);
break; 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) --- // --- 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.instructions_top = ag->scratch_inst_len;
fn_block.break_block = UINT32_MAX; 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). // Compute lbrace source location (AstGen.zig:4860-4862).
advanceSourceCursorToNode(ag, body_node); advanceSourceCursorToNode(ag, body_node);
uint32_t lbrace_line = ag->source_line - decl_line; 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). // Process test body (AstGen.zig:4864).
fullBodyExpr(&fn_block, &fn_block.base, RL_NONE_VAL, body_node); 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 we hit unimplemented features, bail out.
if (ag->has_compile_errors) if (ag->has_compile_errors)
return; return;
@@ -6463,7 +6656,9 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
body_gz.is_comptime = false; body_gz.is_comptime = false;
body_gz.instructions_top = ag->scratch_inst_len; 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; uint32_t prev_fn_ret_ty = ag->fn_ret_ty;
if (is_inferred_error || ret_ref == ZIR_REF_NONE) { if (is_inferred_error || ret_ref == ZIR_REF_NONE) {
// Non-void non-trivial return type: emit ret_type instruction. // 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_line = ag->source_line - decl_line;
uint32_t lbrace_column = ag->source_column; 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; ag->fn_ret_ty = prev_fn_ret_ty;
if (ag->has_compile_errors) { 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; 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). // For now, only handle struct containers (AstGen.zig:5481-5496).
// TODO: handle union/enum/opaque. // TODO: handle union/enum/opaque.
uint32_t decl_inst = structDeclInner(ag, gz, node, members, members_len); uint32_t decl_inst = structDeclInner(ag, gz, node, members, members_len);
(void)scope; (void)scope;
ag->fn_block = prev_fn_block;
return decl_inst + ZIR_REF_START_INDEX; 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 return @intFromEnum(ref.str.start) == got.str.start and
ref.str.len == got.str.len; 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, else => return false,
} }
} }