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