astgen: add any_defer_node check and reachableExpr to retExpr

Port two missing features from upstream AstGen.zig ret() function:

1. Add any_defer_node field to GenZir (AstGen.zig:11812) to track
   whether we're inside a defer expression. Set it in defer body
   generation and propagate via makeSubBlock. retExpr now checks
   this field and errors with "cannot return from defer expression"
   (AstGen.zig:8127-8135). Also reorder retExpr checks to match
   upstream: fn_block null check first, then any_defer_node check,
   then emitDbgNode.

2. Add reachableExpr wrapper (AstGen.zig:408-416) that calls exprRl
   and checks refIsNoReturn to detect unreachable code. Use it in
   retExpr instead of plain exprRl for the return operand
   (AstGen.zig:8185-8186). nameStratExpr is left as TODO since
   containerDecl does not yet accept a name_strategy parameter.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 22:34:37 +00:00
parent 257236be4c
commit ff99a5157e

View File

@@ -262,6 +262,7 @@ typedef struct {
uint32_t label_token; // UINT32_MAX = no label uint32_t label_token; // UINT32_MAX = no label
uint32_t label_block_inst; // the BLOCK instruction index uint32_t label_block_inst; // the BLOCK instruction index
ResultLoc break_result_info; // RL for break values ResultLoc break_result_info; // RL for break values
uint32_t any_defer_node; // UINT32_MAX = none (AstGen.zig:11812)
} GenZir; } GenZir;
// Scope.LocalVal (AstGen.zig:11682). // Scope.LocalVal (AstGen.zig:11682).
@@ -366,6 +367,7 @@ static GenZir makeSubBlock(GenZir* parent, Scope* scope) {
sub.break_block = UINT32_MAX; sub.break_block = UINT32_MAX;
sub.continue_block = UINT32_MAX; sub.continue_block = UINT32_MAX;
sub.label_token = UINT32_MAX; sub.label_token = UINT32_MAX;
sub.any_defer_node = parent->any_defer_node;
return sub; return sub;
} }
@@ -2299,6 +2301,18 @@ static bool refIsNoReturn(GenZir* gz, uint32_t inst_ref) {
return false; return false;
} }
// Mirrors reachableExpr (AstGen.zig:408-416).
// Wraps exprRl and emits an error if the result is noreturn.
static uint32_t reachableExpr(GenZir* gz, Scope* scope, ResultLoc rl,
uint32_t node, uint32_t reachable_node) {
uint32_t result = exprRl(gz, scope, rl, node);
if (refIsNoReturn(gz, result)) {
(void)reachable_node;
SET_ERROR(gz->astgen);
}
return result;
}
static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node); static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
// SimpleComptimeReason (std.zig:727) — values used in block_comptime payload. // SimpleComptimeReason (std.zig:727) — values used in block_comptime payload.
@@ -3378,6 +3392,18 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree; const Ast* tree = ag->tree;
// AstGen.zig:8123: return outside function is an error.
if (ag->fn_block == NULL) {
SET_ERROR(ag);
return ZIR_REF_UNREACHABLE_VALUE;
}
// AstGen.zig:8127-8135: cannot return from defer expression.
if (gz->any_defer_node != UINT32_MAX) {
SET_ERROR(ag);
return ZIR_REF_UNREACHABLE_VALUE;
}
// Ensure debug line/column information is emitted for this return // Ensure debug line/column information is emitted for this return
// expression (AstGen.zig:8141-8144). // expression (AstGen.zig:8141-8144).
if (!gz->is_comptime) { if (!gz->is_comptime) {
@@ -3386,11 +3412,6 @@ 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;
}
const Scope* defer_outer = &((GenZir*)ag->fn_block)->base; const Scope* defer_outer = &((GenZir*)ag->fn_block)->base;
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
@@ -3441,7 +3462,9 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) {
ret_rl.data = ag->fn_ret_ty; ret_rl.data = ag->fn_ret_ty;
} }
ret_rl.ctx = RI_CTX_RETURN; ret_rl.ctx = RI_CTX_RETURN;
uint32_t operand = exprRl(gz, scope, ret_rl, operand_node); // TODO: nameStratExpr(gz, scope, ret_rl, operand_node, .func) when
// containerDecl supports name_strategy parameter.
uint32_t operand = reachableExpr(gz, scope, ret_rl, operand_node, node);
// Emit RESTORE_ERR_RET_INDEX based on nodeMayEvalToError // Emit RESTORE_ERR_RET_INDEX based on nodeMayEvalToError
// (AstGen.zig:8188-8253). // (AstGen.zig:8188-8253).
@@ -7096,6 +7119,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
: SCOPE_DEFER_ERROR; : SCOPE_DEFER_ERROR;
// Create sub-block for defer body (AstGen.zig:3123-3126). // Create sub-block for defer body (AstGen.zig:3123-3126).
GenZir defer_gen = makeSubBlock(gz, cur_scope); GenZir defer_gen = makeSubBlock(gz, cur_scope);
defer_gen.any_defer_node = stmt; // AstGen.zig:3125
// Evaluate deferred expression (AstGen.zig:3165). // Evaluate deferred expression (AstGen.zig:3165).
// DEFER: lhs is the deferred expression, rhs = 0. // DEFER: lhs is the deferred expression, rhs = 0.
@@ -8055,6 +8079,7 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
decl_block.is_comptime = true; decl_block.is_comptime = true;
decl_block.instructions_top = ag->scratch_inst_len; decl_block.instructions_top = ag->scratch_inst_len;
decl_block.break_block = UINT32_MAX; decl_block.break_block = UINT32_MAX;
decl_block.any_defer_node = UINT32_MAX;
// Set up fn_block GenZir (AstGen.zig:4837-4845). // Set up fn_block GenZir (AstGen.zig:4837-4845).
GenZir fn_block; GenZir fn_block;
@@ -8067,6 +8092,7 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
fn_block.is_comptime = false; fn_block.is_comptime = false;
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;
fn_block.any_defer_node = UINT32_MAX;
// Set fn_block and fn_ret_ty for the body (AstGen.zig:4849-4853). // Set fn_block and fn_ret_ty for the body (AstGen.zig:4849-4853).
void* prev_fn_block = ag->fn_block; void* prev_fn_block = ag->fn_block;
@@ -8241,6 +8267,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
decl_gz.is_comptime = true; decl_gz.is_comptime = true;
decl_gz.instructions_top = ag->scratch_inst_len; decl_gz.instructions_top = ag->scratch_inst_len;
decl_gz.break_block = UINT32_MAX; decl_gz.break_block = UINT32_MAX;
decl_gz.any_defer_node = UINT32_MAX;
// --- Parameter iteration (AstGen.zig:4260-4363) --- // --- Parameter iteration (AstGen.zig:4260-4363) ---
// Walk params, creating param instructions and ScopeLocalVal entries. // Walk params, creating param instructions and ScopeLocalVal entries.
@@ -8405,6 +8432,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
body_gz.decl_line = decl_line; body_gz.decl_line = decl_line;
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;
body_gz.any_defer_node = UINT32_MAX;
// Set fn_block and fn_ret_ty for the body (AstGen.zig:4442-4455). // Set fn_block and fn_ret_ty for the body (AstGen.zig:4442-4455).
void* prev_fn_block = ag->fn_block; void* prev_fn_block = ag->fn_block;
@@ -8535,6 +8563,7 @@ static void comptimeDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
value_gz.decl_line = decl_line; value_gz.decl_line = decl_line;
value_gz.is_comptime = true; value_gz.is_comptime = true;
value_gz.instructions_top = ag->scratch_inst_len; value_gz.instructions_top = ag->scratch_inst_len;
value_gz.any_defer_node = UINT32_MAX;
// For comptime {}: body is empty block → no instructions generated. // For comptime {}: body is empty block → no instructions generated.
// comptime_gz.isEmpty() == true → addBreak(.break_inline, decl_inst, // comptime_gz.isEmpty() == true → addBreak(.break_inline, decl_inst,
@@ -8726,6 +8755,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
type_gz.instructions_top = ag->scratch_inst_len; type_gz.instructions_top = ag->scratch_inst_len;
type_gz.decl_line = ag->source_line; type_gz.decl_line = ag->source_line;
type_gz.is_comptime = true; type_gz.is_comptime = true;
type_gz.any_defer_node = UINT32_MAX;
if (vd.type_node != 0) { if (vd.type_node != 0) {
uint32_t type_inst = typeExpr(&type_gz, &type_gz.base, vd.type_node); uint32_t type_inst = typeExpr(&type_gz, &type_gz.base, vd.type_node);
@@ -8744,6 +8774,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
align_gz.instructions_top = type_top; align_gz.instructions_top = type_top;
align_gz.decl_line = ag->source_line; align_gz.decl_line = ag->source_line;
align_gz.is_comptime = true; align_gz.is_comptime = true;
align_gz.any_defer_node = UINT32_MAX;
if (vd.align_node != 0) { if (vd.align_node != 0) {
uint32_t align_inst = expr(&align_gz, &align_gz.base, vd.align_node); uint32_t align_inst = expr(&align_gz, &align_gz.base, vd.align_node);
@@ -8761,6 +8792,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
linksection_gz.instructions_top = align_top; linksection_gz.instructions_top = align_top;
linksection_gz.decl_line = ag->source_line; linksection_gz.decl_line = ag->source_line;
linksection_gz.is_comptime = true; linksection_gz.is_comptime = true;
linksection_gz.any_defer_node = UINT32_MAX;
if (vd.section_node != 0) { if (vd.section_node != 0) {
uint32_t ls_inst uint32_t ls_inst
@@ -8779,6 +8811,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
addrspace_gz.instructions_top = linksection_top; addrspace_gz.instructions_top = linksection_top;
addrspace_gz.decl_line = ag->source_line; addrspace_gz.decl_line = ag->source_line;
addrspace_gz.is_comptime = true; addrspace_gz.is_comptime = true;
addrspace_gz.any_defer_node = UINT32_MAX;
if (vd.addrspace_node != 0) { if (vd.addrspace_node != 0) {
uint32_t as_inst uint32_t as_inst
@@ -8797,6 +8830,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts,
value_gz.instructions_top = addrspace_top; value_gz.instructions_top = addrspace_top;
value_gz.decl_line = ag->source_line; value_gz.decl_line = ag->source_line;
value_gz.is_comptime = true; value_gz.is_comptime = true;
value_gz.any_defer_node = UINT32_MAX;
if (vd.init_node != UINT32_MAX && vd.init_node != 0) { if (vd.init_node != UINT32_MAX && vd.init_node != 0) {
uint32_t init_ref = expr(&value_gz, &value_gz.base, vd.init_node); uint32_t init_ref = expr(&value_gz, &value_gz.base, vd.init_node);
@@ -9425,6 +9459,7 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
block_scope.decl_line = ag->source_line; block_scope.decl_line = ag->source_line;
block_scope.is_comptime = true; block_scope.is_comptime = true;
block_scope.instructions_top = ag->scratch_inst_len; block_scope.instructions_top = ag->scratch_inst_len;
block_scope.any_defer_node = UINT32_MAX;
bool known_non_opv = false; bool known_non_opv = false;
bool known_comptime_only = false; bool known_comptime_only = false;
@@ -10873,6 +10908,7 @@ Zir astGen(const Ast* ast) {
gen_scope.decl_node_index = 0; // root gen_scope.decl_node_index = 0; // root
gen_scope.decl_line = 0; gen_scope.decl_line = 0;
gen_scope.break_block = UINT32_MAX; gen_scope.break_block = UINT32_MAX;
gen_scope.any_defer_node = UINT32_MAX;
// Get root container members: containerDeclRoot (AstGen.zig:191-195). // Get root container members: containerDeclRoot (AstGen.zig:191-195).
AstData root_data = ast->nodes.datas[0]; AstData root_data = ast->nodes.datas[0];