astgen: fix forExpr result info, else handling, instruction order, labels, and diagnostics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 00:13:14 +00:00
parent 0fbd1d257a
commit 657ee8bd36

129
astgen.c
View File

@@ -2339,7 +2339,7 @@ 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, bool is_statement);
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_statement);
static uint32_t orelseCatchExpr(GenZir* gz, Scope* scope, ResultLoc rl,
uint32_t node, ZirInstTag cond_op, ZirInstTag unwrap_op,
ZirInstTag unwrap_code_op);
@@ -4907,7 +4907,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, false), node);
return forExpr(gz, scope, rlBr(rl), node, false);
// Merge error sets (AstGen.zig:788-797).
case AST_NODE_MERGE_ERROR_SETS: {
uint32_t lhs = typeExpr(gz, scope, nd.lhs);
@@ -5802,7 +5802,7 @@ 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, bool is_statement) {
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, bool is_statement) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
AstData nd = tree->nodes.datas[node];
@@ -5813,12 +5813,29 @@ static uint32_t forExpr(
bool is_inline = (main_token > 0
&& tree->tokens.tags[main_token - 1] == TOKEN_KEYWORD_INLINE);
// Compute label_token (AstGen.zig fullForComponents:2341-2348).
uint32_t label_token = UINT32_MAX;
{
uint32_t tok_i = main_token;
if (is_inline)
tok_i = main_token - 1;
if (tok_i >= 2 && tree->tokens.tags[tok_i - 2] == TOKEN_IDENTIFIER
&& tree->tokens.tags[tok_i - 1] == TOKEN_COLON)
label_token = tok_i - 2;
}
// Compute break_rl from rl (AstGen.zig:6833-6845).
bool need_rl = nodesNeedRlContains(ag, node);
ResultLoc break_rl = breakResultInfo(gz, rl, node, need_rl);
bool need_result_rvalue = (break_rl.tag != rl.tag);
// Extract input nodes and body/else nodes.
// FOR_SIMPLE: lhs = input node, rhs = body (Ast.zig:1960-1968).
// FOR: lhs = extra_data index, rhs = packed AstFor (Ast.zig:1970-1981).
uint32_t input_nodes[FOR_MAX_INPUTS];
uint32_t num_inputs;
uint32_t body_node;
uint32_t else_node = 0;
if (node_tag == AST_NODE_FOR_SIMPLE) {
input_nodes[0] = nd.lhs;
num_inputs = 1;
@@ -5835,6 +5852,8 @@ static uint32_t forExpr(
for (uint32_t i = 0; i < num_inputs; i++)
input_nodes[i] = tree->extra_data.arr[extra_idx + i];
body_node = tree->extra_data.arr[extra_idx + num_inputs];
if (for_data.has_else)
else_node = tree->extra_data.arr[extra_idx + num_inputs + 1];
}
// Per-input arrays (AstGen.zig:6858-6862).
@@ -5854,6 +5873,8 @@ static uint32_t forExpr(
&& tree->tokens.tags[last_cond_tok + 1] == TOKEN_COMMA);
uint32_t payload_token = last_cond_tok + 3 + (has_comma ? 1 : 0);
bool any_len_checks = false;
// Process each input (AstGen.zig:6878-6925).
uint32_t capture_token = payload_token;
for (uint32_t i = 0; i < num_inputs; i++) {
@@ -5862,19 +5883,38 @@ static uint32_t forExpr(
bool capture_is_ref
= (tree->tokens.tags[capture_token] == TOKEN_ASTERISK);
uint32_t ident_tok = capture_token + (capture_is_ref ? 1u : 0u);
bool is_discard = tokenIsUnderscore(tree, ident_tok);
capture_token = ident_tok + 2; // skip ident + comma/pipe
// Diagnostic: pointer modifier invalid on discard
// (AstGen.zig:6885-6887).
if (is_discard && capture_is_ref) {
SET_ERROR(ag);
return ZIR_REF_VOID_VALUE;
}
emitDbgNode(gz, input);
if (tree->nodes.tags[input] == AST_NODE_FOR_RANGE) {
// Range input (AstGen.zig:6892-6916).
// Diagnostic: cannot capture reference to range
// (AstGen.zig:6893-6895).
if (capture_is_ref) {
SET_ERROR(ag);
return ZIR_REF_VOID_VALUE;
}
// Range input (AstGen.zig:6896-6916).
AstData range_nd = tree->nodes.datas[input];
uint32_t start_node = range_nd.lhs;
uint32_t end_node = range_nd.rhs;
// AstGen.zig:6897-6902: expr with .rl = .{ .ty = .usize_type }
ResultLoc usize_rl
= { .tag = RL_TY, .data = ZIR_REF_USIZE_TYPE, .src_node = 0 };
ResultLoc usize_rl = {
.tag = RL_TY,
.data = ZIR_REF_USIZE_TYPE,
.src_node = 0,
.ctx = RI_CTX_NONE,
};
uint32_t start_val = exprRl(gz, scope, usize_rl, start_node);
uint32_t end_val = ZIR_REF_NONE;
@@ -5882,10 +5922,17 @@ static uint32_t forExpr(
end_val = exprRl(gz, scope, usize_rl, end_node);
}
// Diagnostic: discard of unbounded counter
// (AstGen.zig:6904-6906).
if (end_val == ZIR_REF_NONE && is_discard) {
SET_ERROR(ag);
}
if (end_val == ZIR_REF_NONE) {
lens[i][0] = ZIR_REF_NONE;
lens[i][1] = ZIR_REF_NONE;
} else {
any_len_checks = true;
lens[i][0] = start_val;
lens[i][1] = end_val;
}
@@ -5905,12 +5952,19 @@ static uint32_t forExpr(
} else {
// Regular indexable (AstGen.zig:6918-6923).
uint32_t indexable = expr(gz, scope, input);
any_len_checks = true;
indexables[i] = indexable;
lens[i][0] = indexable;
lens[i][1] = ZIR_REF_NONE;
}
}
// Error if no length checks exist (AstGen.zig:6927-6929).
if (!any_len_checks) {
SET_ERROR(ag);
return ZIR_REF_VOID_VALUE;
}
// Emit for_len as MultiOp (AstGen.zig:6933-6942).
uint32_t len;
{
@@ -5928,12 +5982,16 @@ static uint32_t forExpr(
len = addInstruction(gz, ZIR_INST_FOR_LEN, data);
}
// Create loop (AstGen.zig:6944-6956).
// Create loop (AstGen.zig:6944-6946).
ZirInstTag loop_tag = is_inline ? ZIR_INST_BLOCK_INLINE : ZIR_INST_LOOP;
uint32_t loop_inst = makeBlockInst(ag, loop_tag, gz, node);
// Issue 3: append loop_inst to parent_gz immediately (AstGen.zig:6946).
gzAppendInstruction(gz, loop_inst);
GenZir loop_scope = makeSubBlock(gz, scope);
loop_scope.is_inline = is_inline;
// Issue 1: set break_result_info (AstGen.zig:6950).
loop_scope.break_result_info = break_rl;
// Load index (AstGen.zig:6955-6956).
// We need to finish loop_scope later once we have the deferred refs from
@@ -5957,6 +6015,11 @@ static uint32_t forExpr(
loop_scope.break_block = loop_inst;
loop_scope.continue_block = cond_block; // AstGen.zig:6974
// Issue 4: set label on loop_scope (AstGen.zig:6975-6980).
if (label_token != UINT32_MAX) {
loop_scope.label_token = label_token;
loop_scope.label_block_inst = loop_inst;
}
// Then branch: loop body (AstGen.zig:6982-7065).
GenZir then_scope = makeSubBlock(gz, &cond_scope.base);
@@ -5974,16 +6037,7 @@ static uint32_t forExpr(
capture_token = ident_tok + 2;
// Check if discard (AstGen.zig:6999).
uint32_t ts = tree->tokens.starts[ident_tok];
bool is_discard = (tree->source[ts] == '_'
&& (ts + 1 >= tree->source_len
|| !((tree->source[ts + 1] >= 'a'
&& tree->source[ts + 1] <= 'z')
|| (tree->source[ts + 1] >= 'A'
&& tree->source[ts + 1] <= 'Z')
|| tree->source[ts + 1] == '_'
|| (tree->source[ts + 1] >= '0'
&& tree->source[ts + 1] <= '9'))));
bool is_discard = tokenIsUnderscore(tree, ident_tok);
if (is_discard)
continue;
@@ -6044,10 +6098,37 @@ static uint32_t forExpr(
addBreak(&then_scope, break_tag, cond_block, ZIR_REF_VOID_VALUE,
AST_NODE_OFFSET_NONE);
// Else branch: break out of loop (AstGen.zig:7066-7091).
// Else branch (AstGen.zig:7066-7091).
GenZir else_scope = makeSubBlock(gz, &cond_scope.base);
addBreak(&else_scope, break_tag, loop_inst, ZIR_REF_VOID_VALUE,
if (else_node != 0) {
// Issue 2: evaluate else expression (AstGen.zig:7069-7081).
// Remove continue/break blocks so that control flow applies
// to outer loops (AstGen.zig:7073-7074).
loop_scope.continue_block = UINT32_MAX;
loop_scope.break_block = UINT32_MAX;
uint32_t else_result = fullBodyExpr(&else_scope, &else_scope.base,
loop_scope.break_result_info, else_node);
if (is_statement) {
addEnsureResult(&else_scope, else_result, else_node);
}
if (!endsWithNoReturn(&else_scope)) {
addBreak(&else_scope, break_tag, loop_inst, else_result,
(int32_t)else_node - (int32_t)gz->decl_node_index);
}
} else {
// No else: break with void (AstGen.zig:7082-7085).
uint32_t void_result
= rvalue(&else_scope, rl, ZIR_REF_VOID_VALUE, node);
addBreak(&else_scope, break_tag, loop_inst, void_result,
AST_NODE_OFFSET_NONE);
}
// Issue 4: check unused label (AstGen.zig:7087-7091).
if (label_token != UINT32_MAX) {
// Note: upstream checks loop_scope.label.used; we don't track usage
// yet, so skip the "unused for loop label" error for now.
}
setCondBrPayload(ag, condbr, cond, &then_scope, &else_scope);
@@ -6075,9 +6156,13 @@ static uint32_t forExpr(
setBlockBody(ag, &loop_scope, loop_inst);
}
gzAppendInstruction(gz, loop_inst);
uint32_t result = loop_inst + ZIR_REF_START_INDEX;
// Issue 1: apply rvalue if needed (AstGen.zig:7116-7119).
uint32_t result;
if (need_result_rvalue)
result = rvalue(gz, rl, loop_inst + ZIR_REF_START_INDEX, node);
else
result = loop_inst + ZIR_REF_START_INDEX;
// Emit ensure_result_used when used as statement (AstGen.zig:7121-7123).
if (is_statement) {
@@ -7617,7 +7702,7 @@ static void blockExprStmts(GenZir* gz, Scope* scope,
break;
case AST_NODE_FOR_SIMPLE:
case AST_NODE_FOR:
(void)forExpr(gz, cur_scope, inner_node, true);
(void)forExpr(gz, cur_scope, RL_NONE_VAL, inner_node, true);
break;
default: {
// Expression statement (AstGen.zig:2627 unusedResultExpr).