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:
268
astgen.c
268
astgen.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user