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