astgen: fix switchExpr captures, underscore prong, switch_block_ref, labels, and body fixups
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
743
astgen.c
743
astgen.c
@@ -6677,14 +6677,105 @@ static uint32_t whileExpr(
|
||||
// Handles switch and switch_comma expressions.
|
||||
// Encoding: switch_block pl_node with SwitchBlock extra payload.
|
||||
|
||||
// Helper: append body instruction with ref_table fixups to pay buffer.
|
||||
// Mirrors appendPossiblyRefdBodyInst (AstGen.zig:13675-13683) but writes
|
||||
// to a dynamically-grown pay buffer instead of extra.
|
||||
static void appendPossiblyRefdBodyInstPay(AstGenCtx* ag, uint32_t body_inst,
|
||||
uint32_t** pay, uint32_t* pay_len, uint32_t* pay_cap) {
|
||||
if (*pay_len >= *pay_cap) {
|
||||
*pay_cap *= 2;
|
||||
uint32_t* p = realloc(*pay, *pay_cap * sizeof(uint32_t));
|
||||
if (!p)
|
||||
abort();
|
||||
*pay = p;
|
||||
}
|
||||
(*pay)[(*pay_len)++] = body_inst;
|
||||
uint32_t ref_inst;
|
||||
if (refTableFetchRemove(ag, body_inst, &ref_inst)) {
|
||||
appendPossiblyRefdBodyInstPay(ag, ref_inst, pay, pay_len, pay_cap);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: append body with fixups and extra refs to pay buffer.
|
||||
// Mirrors appendBodyWithFixupsExtraRefsArrayList (AstGen.zig:13659-13673).
|
||||
static void appendBodyWithFixupsExtraRefsPay(AstGenCtx* ag,
|
||||
const uint32_t* body, uint32_t body_len, const uint32_t* extra_refs,
|
||||
uint32_t extra_refs_len, uint32_t** pay, uint32_t* pay_len_p,
|
||||
uint32_t* pay_cap_p) {
|
||||
for (uint32_t i = 0; i < extra_refs_len; i++) {
|
||||
uint32_t ref_inst;
|
||||
if (refTableFetchRemove(ag, extra_refs[i], &ref_inst)) {
|
||||
appendPossiblyRefdBodyInstPay(
|
||||
ag, ref_inst, pay, pay_len_p, pay_cap_p);
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < body_len; i++) {
|
||||
appendPossiblyRefdBodyInstPay(ag, body[i], pay, pay_len_p, pay_cap_p);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: ensure pay buffer has capacity for `additional` more entries.
|
||||
static void ensurePayCapacity(
|
||||
uint32_t** pay, uint32_t* pay_cap, uint32_t pay_len, uint32_t additional) {
|
||||
uint32_t needed = pay_len + additional;
|
||||
if (needed > *pay_cap) {
|
||||
while (*pay_cap < needed)
|
||||
*pay_cap *= 2;
|
||||
uint32_t* p = realloc(*pay, *pay_cap * sizeof(uint32_t));
|
||||
if (!p)
|
||||
abort();
|
||||
*pay = p;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper: get values for a switch case node. For SWITCH_CASE_ONE /
|
||||
// SWITCH_CASE_INLINE_ONE, returns the single value (or NULL if else).
|
||||
// For SWITCH_CASE / SWITCH_CASE_INLINE, returns the SubRange values.
|
||||
// Returns values count and sets *values_arr to point to the values.
|
||||
// For _ONE variants, writes to single_buf and returns pointer to it.
|
||||
static uint32_t switchCaseValues(const Ast* tree, uint32_t case_node,
|
||||
uint32_t* single_buf, const uint32_t** values_arr) {
|
||||
AstNodeTag ct = tree->nodes.tags[case_node];
|
||||
AstData cd = tree->nodes.datas[case_node];
|
||||
switch (ct) {
|
||||
case AST_NODE_SWITCH_CASE_ONE:
|
||||
case AST_NODE_SWITCH_CASE_INLINE_ONE:
|
||||
if (cd.lhs == 0) {
|
||||
*values_arr = NULL;
|
||||
return 0; // else prong
|
||||
}
|
||||
*single_buf = cd.lhs;
|
||||
*values_arr = single_buf;
|
||||
return 1;
|
||||
case AST_NODE_SWITCH_CASE:
|
||||
case AST_NODE_SWITCH_CASE_INLINE: {
|
||||
uint32_t ist = tree->extra_data.arr[cd.lhs];
|
||||
uint32_t ien = tree->extra_data.arr[cd.lhs + 1];
|
||||
*values_arr = tree->extra_data.arr + ist;
|
||||
return ien - ist;
|
||||
}
|
||||
default:
|
||||
*values_arr = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t switchExpr(
|
||||
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
AstGenCtx* ag = gz->astgen;
|
||||
GenZir* parent_gz, Scope* scope, ResultLoc rl, uint32_t node) {
|
||||
AstGenCtx* ag = parent_gz->astgen;
|
||||
const Ast* tree = ag->tree;
|
||||
bool need_rl = nodesNeedRlContains(ag, node);
|
||||
ResultLoc break_rl = breakResultInfo(gz, rl, node, need_rl);
|
||||
ResultLoc break_rl = breakResultInfo(parent_gz, rl, node, need_rl);
|
||||
AstData nd = tree->nodes.datas[node];
|
||||
|
||||
// Detect label (AstGen.zig:7652-7654 / Ast.zig switchFull).
|
||||
uint32_t main_token = tree->nodes.main_tokens[node];
|
||||
uint32_t label_token = UINT32_MAX; // none
|
||||
if (tree->tokens.tags[main_token] == TOKEN_IDENTIFIER) {
|
||||
label_token = main_token;
|
||||
// switch_token = main_token + 2 (skip label + colon)
|
||||
}
|
||||
|
||||
// AST_NODE_SWITCH: lhs = condition node, rhs = extra index for SubRange.
|
||||
// SubRange[rhs] = { cases_start, cases_end }.
|
||||
// Case nodes are at extra_data[cases_start..cases_end].
|
||||
@@ -6695,119 +6786,370 @@ static uint32_t switchExpr(
|
||||
const uint32_t* case_nodes_arr = tree->extra_data.arr + cases_start;
|
||||
uint32_t case_count = cases_end - cases_start;
|
||||
|
||||
// Save operand source location before evaluating (AstGen.zig:7774-7775).
|
||||
advanceSourceCursorToNode(ag, cond_node);
|
||||
uint32_t operand_lc_line = ag->source_line - gz->decl_line;
|
||||
uint32_t operand_lc_col = ag->source_column;
|
||||
|
||||
// Evaluate switch operand (AstGen.zig:7777).
|
||||
uint32_t cond_ref = expr(gz, scope, cond_node);
|
||||
|
||||
// --- First pass: categorize cases (AstGen.zig:7671-7762) ---
|
||||
// --- First pass: categorize cases (AstGen.zig:7659-7762) ---
|
||||
bool any_payload_is_ref = false;
|
||||
bool any_has_tag_capture = false;
|
||||
bool any_non_inline_capture = false;
|
||||
uint32_t scalar_cases_len = 0;
|
||||
uint32_t multi_cases_len = 0;
|
||||
bool has_else = false;
|
||||
// Underscore prong tracking (AstGen.zig:7667-7670).
|
||||
uint32_t underscore_case_idx = UINT32_MAX; // index into case_nodes_arr
|
||||
uint32_t underscore_node = UINT32_MAX; // the `_` value node
|
||||
// underscore_additional_items: 0=none, 2=under, 4=under_one_item,
|
||||
// 6=under_many_items (matching SpecialProngs bit patterns).
|
||||
uint32_t underscore_additional_items = 2; // .under
|
||||
|
||||
for (uint32_t ci = 0; ci < case_count; ci++) {
|
||||
uint32_t cn = case_nodes_arr[ci];
|
||||
AstNodeTag ct = tree->nodes.tags[cn];
|
||||
AstData cd = tree->nodes.datas[cn];
|
||||
bool is_inline = (ct == AST_NODE_SWITCH_CASE_INLINE_ONE
|
||||
|| ct == AST_NODE_SWITCH_CASE_INLINE);
|
||||
|
||||
switch (ct) {
|
||||
case AST_NODE_SWITCH_CASE_ONE:
|
||||
case AST_NODE_SWITCH_CASE_INLINE_ONE:
|
||||
if (cd.lhs == 0)
|
||||
has_else = true;
|
||||
else if (tree->nodes.tags[cd.lhs] == AST_NODE_SWITCH_RANGE)
|
||||
multi_cases_len++;
|
||||
else
|
||||
scalar_cases_len++;
|
||||
break;
|
||||
case AST_NODE_SWITCH_CASE:
|
||||
case AST_NODE_SWITCH_CASE_INLINE:
|
||||
multi_cases_len++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
// Check payload token for ref/tag capture (AstGen.zig:7673-7689).
|
||||
uint32_t arrow_token = tree->nodes.main_tokens[cn]; // =>
|
||||
if (tree->tokens.tags[arrow_token + 1] == TOKEN_PIPE) {
|
||||
uint32_t payload_token = arrow_token + 2;
|
||||
uint32_t ident = payload_token;
|
||||
if (tree->tokens.tags[payload_token] == TOKEN_ASTERISK) {
|
||||
any_payload_is_ref = true;
|
||||
ident = payload_token + 1;
|
||||
}
|
||||
if (tree->tokens.tags[ident + 1] == TOKEN_COMMA) {
|
||||
any_has_tag_capture = true;
|
||||
}
|
||||
if (!tokenIsUnderscore(tree, ident)) {
|
||||
any_non_inline_capture = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Get values for this case.
|
||||
uint32_t single_buf;
|
||||
const uint32_t* values;
|
||||
uint32_t values_len = switchCaseValues(tree, cn, &single_buf, &values);
|
||||
|
||||
// Check for else prong (values_len == 0) (AstGen.zig:7693-7711).
|
||||
if (values_len == 0) {
|
||||
has_else = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for '_' prong (AstGen.zig:7714-7752).
|
||||
bool case_has_underscore = false;
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
uint32_t val = values[vi];
|
||||
if (tree->nodes.tags[val] == AST_NODE_IDENTIFIER
|
||||
&& isUnderscoreIdent(tree, val)) {
|
||||
underscore_case_idx = ci;
|
||||
underscore_node = val;
|
||||
switch (values_len) {
|
||||
case 1:
|
||||
underscore_additional_items = 2; // .under
|
||||
break;
|
||||
case 2:
|
||||
underscore_additional_items = 4; // .under_one_item
|
||||
break;
|
||||
default:
|
||||
underscore_additional_items = 6; // .under_many_items
|
||||
break;
|
||||
}
|
||||
case_has_underscore = true;
|
||||
}
|
||||
}
|
||||
if (case_has_underscore)
|
||||
continue;
|
||||
|
||||
// Categorize as scalar or multi (AstGen.zig:7754-7758).
|
||||
if (values_len == 1
|
||||
&& tree->nodes.tags[values[0]] != AST_NODE_SWITCH_RANGE) {
|
||||
scalar_cases_len++;
|
||||
} else {
|
||||
multi_cases_len++;
|
||||
}
|
||||
(void)is_inline; // inline_cases_len tracking skipped (issue 13)
|
||||
(void)cd;
|
||||
}
|
||||
|
||||
// Sema expects a dbg_stmt immediately before switch_block
|
||||
// (AstGen.zig:7806).
|
||||
emitDbgStmtForceCurrentIndex(gz, operand_lc_line, operand_lc_col);
|
||||
// --- Create switch_block instruction (AstGen.zig:7809) ---
|
||||
uint32_t switch_inst = makeBlockInst(ag, ZIR_INST_SWITCH_BLOCK, gz, node);
|
||||
// Compute special_prongs (AstGen.zig:7764-7770).
|
||||
// SpecialProngs is a 3-bit field:
|
||||
// bit 0: has_else
|
||||
// bits 1-2: underscore variant (0=none, 1=under, 2=under_one_item,
|
||||
// 3=under_many_items)
|
||||
bool has_under = (underscore_case_idx != UINT32_MAX);
|
||||
uint32_t special_prongs; // 3-bit SpecialProngs enum value
|
||||
{
|
||||
uint32_t else_bit = has_else ? 1u : 0u;
|
||||
uint32_t under_bits = has_under ? underscore_additional_items : 0u;
|
||||
special_prongs = else_bit | under_bits;
|
||||
}
|
||||
|
||||
// --- Single-pass evaluation in source order (AstGen.zig:7849-8027) ---
|
||||
// Case table + payload buffer pattern (like upstream scratch).
|
||||
// Table layout: [else?] [scalar_0..N] [multi_0..N]
|
||||
// Each entry points to the start of that case's data in the buffer.
|
||||
uint32_t table_size
|
||||
= (has_else ? 1 : 0) + scalar_cases_len + multi_cases_len;
|
||||
// Operand result info (AstGen.zig:7772).
|
||||
ResultLoc operand_ri = any_payload_is_ref ? RL_REF_VAL : RL_NONE_VAL;
|
||||
|
||||
// Save operand source location before evaluating (AstGen.zig:7774-7775).
|
||||
advanceSourceCursorToNode(ag, cond_node);
|
||||
uint32_t operand_lc_line = ag->source_line - parent_gz->decl_line;
|
||||
uint32_t operand_lc_col = ag->source_column;
|
||||
|
||||
// Evaluate switch operand (AstGen.zig:7777).
|
||||
uint32_t raw_operand = exprRl(parent_gz, scope, operand_ri, cond_node);
|
||||
|
||||
// Compute typeof for labeled switch continue support
|
||||
// (AstGen.zig:7782-7784).
|
||||
uint32_t raw_operand_ty_ref = 0;
|
||||
if (label_token != UINT32_MAX) {
|
||||
raw_operand_ty_ref
|
||||
= addUnNode(parent_gz, ZIR_INST_TYPEOF, raw_operand, cond_node);
|
||||
}
|
||||
|
||||
// Sema expects a dbg_stmt immediately before switch_block(_ref)
|
||||
// (AstGen.zig:7806).
|
||||
emitDbgStmtForceCurrentIndex(parent_gz, operand_lc_line, operand_lc_col);
|
||||
|
||||
// Create switch_block instruction (AstGen.zig:7808-7809).
|
||||
ZirInstTag switch_tag = any_payload_is_ref ? ZIR_INST_SWITCH_BLOCK_REF
|
||||
: ZIR_INST_SWITCH_BLOCK;
|
||||
uint32_t switch_inst = makeBlockInst(ag, switch_tag, parent_gz, node);
|
||||
|
||||
// Set up block_scope (AstGen.zig:7800-7826).
|
||||
GenZir block_scope = makeSubBlock(parent_gz, scope);
|
||||
block_scope.instructions_top = UINT32_MAX; // unstacked
|
||||
block_scope.break_result_info = break_rl;
|
||||
|
||||
// Label support (AstGen.zig:7811-7826).
|
||||
if (label_token != UINT32_MAX) {
|
||||
block_scope.continue_block = switch_inst;
|
||||
// continue_result_info: for ref, use ref_coerced_ty; else coerced_ty
|
||||
// (AstGen.zig:7813-7818).
|
||||
if (any_payload_is_ref) {
|
||||
block_scope.break_result_info
|
||||
= (ResultLoc) { .tag = RL_REF_COERCED_TY,
|
||||
.data = raw_operand_ty_ref,
|
||||
.src_node = 0,
|
||||
.ctx = RI_CTX_NONE };
|
||||
}
|
||||
// Note: we store continue_result_info as break_result_info on
|
||||
// block_scope for now; the label's block_inst is switch_inst.
|
||||
block_scope.label_token = label_token;
|
||||
block_scope.label_block_inst = switch_inst;
|
||||
}
|
||||
|
||||
// Allocate shared value_placeholder for tag captures
|
||||
// (AstGen.zig:7833-7844).
|
||||
uint32_t tag_inst = 0;
|
||||
if (any_has_tag_capture) {
|
||||
tag_inst = ag->inst_len;
|
||||
ensureInstCapacity(ag, 1);
|
||||
ag->inst_tags[tag_inst] = ZIR_INST_EXTENDED;
|
||||
ZirInstData tdata;
|
||||
memset(&tdata, 0, sizeof(tdata));
|
||||
tdata.extended.opcode = (uint16_t)ZIR_EXT_VALUE_PLACEHOLDER;
|
||||
ag->inst_datas[tag_inst] = tdata;
|
||||
ag->inst_len++;
|
||||
}
|
||||
|
||||
// Case scope — re-used for all cases (AstGen.zig:7829-7830).
|
||||
GenZir case_scope = makeSubBlock(parent_gz, &block_scope.base);
|
||||
case_scope.instructions_top = UINT32_MAX; // unstacked initially
|
||||
|
||||
// --- Payload buffer (AstGen.zig:7789-7798) ---
|
||||
// Table layout: [else?] [under?] [scalar_0..N] [multi_0..N]
|
||||
uint32_t else_tbl = 0;
|
||||
uint32_t scalar_tbl = (has_else ? 1 : 0);
|
||||
uint32_t under_tbl = (has_else ? 1u : 0u);
|
||||
uint32_t scalar_tbl = under_tbl + (has_under ? 1u : 0u);
|
||||
uint32_t multi_tbl = scalar_tbl + scalar_cases_len;
|
||||
uint32_t table_size = multi_tbl + multi_cases_len;
|
||||
|
||||
uint32_t pay_cap = table_size + case_count * 16;
|
||||
if (pay_cap < 64)
|
||||
pay_cap = 64;
|
||||
uint32_t* pay = malloc(pay_cap * sizeof(uint32_t));
|
||||
uint32_t pay_len = table_size;
|
||||
|
||||
uint32_t scalar_ci = 0;
|
||||
uint32_t multi_ci = 0;
|
||||
|
||||
// --- Second pass: emit items and bodies (AstGen.zig:7849-8027) ---
|
||||
for (uint32_t ci = 0; ci < case_count; ci++) {
|
||||
uint32_t cn = case_nodes_arr[ci];
|
||||
AstNodeTag ct = tree->nodes.tags[cn];
|
||||
AstData cd = tree->nodes.datas[cn];
|
||||
bool is_inline = (ct == AST_NODE_SWITCH_CASE_INLINE_ONE
|
||||
|| ct == AST_NODE_SWITCH_CASE_INLINE);
|
||||
|
||||
// Get values for this case.
|
||||
uint32_t single_buf;
|
||||
const uint32_t* values;
|
||||
uint32_t values_len = switchCaseValues(tree, cn, &single_buf, &values);
|
||||
|
||||
// Determine if this is the else, underscore, scalar, or multi case.
|
||||
bool is_else_case = (values_len == 0);
|
||||
bool is_underscore_case = (ci == underscore_case_idx);
|
||||
bool is_multi_case = false;
|
||||
if (!is_else_case && !is_underscore_case) {
|
||||
is_multi_case = (values_len > 1
|
||||
|| (values_len == 1
|
||||
&& tree->nodes.tags[values[0]] == AST_NODE_SWITCH_RANGE));
|
||||
}
|
||||
|
||||
// Parse payload token (AstGen.zig:7855-7921).
|
||||
uint32_t dbg_var_name = 0; // NullTerminatedString, 0 = empty
|
||||
uint32_t dbg_var_inst = 0;
|
||||
uint32_t dbg_var_tag_name = 0;
|
||||
uint32_t dbg_var_tag_inst = 0;
|
||||
bool has_tag_capture = false;
|
||||
ScopeLocalVal capture_val_scope;
|
||||
ScopeLocalVal tag_scope_val;
|
||||
memset(&capture_val_scope, 0, sizeof(capture_val_scope));
|
||||
memset(&tag_scope_val, 0, sizeof(tag_scope_val));
|
||||
|
||||
uint32_t capture = 0; // 0=none, 1=by_val, 2=by_ref
|
||||
|
||||
uint32_t arrow_token = tree->nodes.main_tokens[cn];
|
||||
bool has_payload = (tree->tokens.tags[arrow_token + 1] == TOKEN_PIPE);
|
||||
Scope* sub_scope = &case_scope.base;
|
||||
|
||||
if (has_payload) {
|
||||
uint32_t payload_token = arrow_token + 2;
|
||||
bool capture_is_ref
|
||||
= (tree->tokens.tags[payload_token] == TOKEN_ASTERISK);
|
||||
uint32_t ident = payload_token + (capture_is_ref ? 1u : 0u);
|
||||
|
||||
capture = capture_is_ref ? 2u : 1u; // by_ref : by_val
|
||||
|
||||
if (tokenIsUnderscore(tree, ident)) {
|
||||
// Discard capture (AstGen.zig:7874-7878).
|
||||
if (capture_is_ref) {
|
||||
SET_ERROR(ag);
|
||||
free(pay);
|
||||
return ZIR_REF_VOID_VALUE;
|
||||
}
|
||||
capture = 0; // none
|
||||
// sub_scope stays as &case_scope.base
|
||||
} else {
|
||||
// Named capture (AstGen.zig:7880-7892).
|
||||
uint32_t capture_name = identAsString(ag, ident);
|
||||
capture_val_scope = (ScopeLocalVal) {
|
||||
.base = { .tag = SCOPE_LOCAL_VAL },
|
||||
.parent = &case_scope.base,
|
||||
.gen_zir = &case_scope,
|
||||
.inst = switch_inst + ZIR_REF_START_INDEX,
|
||||
.token_src = ident,
|
||||
.name = capture_name,
|
||||
};
|
||||
dbg_var_name = capture_name;
|
||||
dbg_var_inst = switch_inst + ZIR_REF_START_INDEX;
|
||||
sub_scope = &capture_val_scope.base;
|
||||
}
|
||||
|
||||
// Check for tag capture: ident followed by comma
|
||||
// (AstGen.zig:7895-7921).
|
||||
if (tree->tokens.tags[ident + 1] == TOKEN_COMMA) {
|
||||
uint32_t tag_token = ident + 2;
|
||||
uint32_t tag_name = identAsString(ag, tag_token);
|
||||
|
||||
has_tag_capture = true;
|
||||
|
||||
tag_scope_val = (ScopeLocalVal) {
|
||||
.base = { .tag = SCOPE_LOCAL_VAL },
|
||||
.parent = sub_scope,
|
||||
.gen_zir = &case_scope,
|
||||
.inst = tag_inst + ZIR_REF_START_INDEX,
|
||||
.token_src = tag_token,
|
||||
.name = tag_name,
|
||||
};
|
||||
dbg_var_tag_name = tag_name;
|
||||
dbg_var_tag_inst = tag_inst + ZIR_REF_START_INDEX;
|
||||
sub_scope = &tag_scope_val.base;
|
||||
}
|
||||
}
|
||||
|
||||
ensurePayCapacity(&pay, &pay_cap, pay_len, 32);
|
||||
uint32_t hdr = pay_len;
|
||||
uint32_t prong_info_slot = 0;
|
||||
|
||||
// Ensure capacity for items (generous estimate).
|
||||
if (pay_len + 32 > pay_cap) {
|
||||
pay_cap *= 2;
|
||||
uint32_t* p = realloc(pay, pay_cap * sizeof(uint32_t));
|
||||
if (!p)
|
||||
abort();
|
||||
pay = p;
|
||||
}
|
||||
|
||||
switch (ct) {
|
||||
case AST_NODE_SWITCH_CASE_ONE:
|
||||
case AST_NODE_SWITCH_CASE_INLINE_ONE:
|
||||
if (cd.lhs == 0) {
|
||||
// Else: [prong_info, body...]
|
||||
pay[else_tbl] = hdr;
|
||||
// Determine case kind and fill item data (AstGen.zig:7924-7995).
|
||||
if (is_underscore_case && is_multi_case) {
|
||||
// Underscore case with additional items as multi
|
||||
// (AstGen.zig:7926-7942).
|
||||
pay[under_tbl] = hdr;
|
||||
if (underscore_additional_items == 4) {
|
||||
// One additional item (AstGen.zig:7928-7937).
|
||||
// [item_ref, prong_info]
|
||||
uint32_t item_node;
|
||||
if (values[0] == underscore_node)
|
||||
item_node = values[1];
|
||||
else
|
||||
item_node = values[0];
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope, RL_NONE_VAL,
|
||||
item_node, COMPTIME_REASON_SWITCH_ITEM);
|
||||
prong_info_slot = pay_len++;
|
||||
} else if (tree->nodes.tags[cd.lhs] == AST_NODE_SWITCH_RANGE) {
|
||||
// Single range → multi case:
|
||||
// [items_len=0, ranges_len=1, prong_info, first, last]
|
||||
pay[multi_tbl + multi_ci++] = hdr;
|
||||
pay[pay_len++] = 0;
|
||||
pay[pay_len++] = 1;
|
||||
prong_info_slot = pay_len++;
|
||||
AstData rng = tree->nodes.datas[cd.lhs];
|
||||
pay[pay_len++] = comptimeExpr(gz, scope, RL_NONE_VAL, rng.lhs,
|
||||
COMPTIME_REASON_SWITCH_ITEM);
|
||||
pay[pay_len++] = comptimeExpr(gz, scope, RL_NONE_VAL, rng.rhs,
|
||||
COMPTIME_REASON_SWITCH_ITEM);
|
||||
} else {
|
||||
// Scalar: [item_ref, prong_info, body...]
|
||||
pay[scalar_tbl + scalar_ci++] = hdr;
|
||||
pay[pay_len++] = comptimeExpr(gz, scope, RL_NONE_VAL, cd.lhs,
|
||||
COMPTIME_REASON_SWITCH_ITEM);
|
||||
// Many additional items: multi format
|
||||
// (AstGen.zig:7943-7977).
|
||||
uint32_t nitems = 0;
|
||||
uint32_t nranges = 0;
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
if (values[vi] == underscore_node)
|
||||
continue;
|
||||
if (tree->nodes.tags[values[vi]] == AST_NODE_SWITCH_RANGE)
|
||||
nranges++;
|
||||
else
|
||||
nitems++;
|
||||
}
|
||||
pay[pay_len++] = nitems;
|
||||
pay[pay_len++] = nranges;
|
||||
prong_info_slot = pay_len++;
|
||||
// Non-range items.
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
if (values[vi] == underscore_node)
|
||||
continue;
|
||||
if (tree->nodes.tags[values[vi]]
|
||||
!= AST_NODE_SWITCH_RANGE) {
|
||||
ensurePayCapacity(&pay, &pay_cap, pay_len, 1);
|
||||
pay[pay_len++]
|
||||
= comptimeExpr(parent_gz, scope, RL_NONE_VAL,
|
||||
values[vi], COMPTIME_REASON_SWITCH_ITEM);
|
||||
}
|
||||
}
|
||||
// Range pairs.
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
if (values[vi] == underscore_node)
|
||||
continue;
|
||||
if (tree->nodes.tags[values[vi]]
|
||||
== AST_NODE_SWITCH_RANGE) {
|
||||
AstData rng = tree->nodes.datas[values[vi]];
|
||||
ensurePayCapacity(&pay, &pay_cap, pay_len, 2);
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope,
|
||||
RL_NONE_VAL, rng.lhs, COMPTIME_REASON_SWITCH_ITEM);
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope,
|
||||
RL_NONE_VAL, rng.rhs, COMPTIME_REASON_SWITCH_ITEM);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AST_NODE_SWITCH_CASE:
|
||||
case AST_NODE_SWITCH_CASE_INLINE: {
|
||||
// Multi-item: SubRange[lhs] of items, rhs = body.
|
||||
} else if (is_else_case) {
|
||||
// Else prong (AstGen.zig:7978-7981).
|
||||
pay[else_tbl] = hdr;
|
||||
prong_info_slot = pay_len++;
|
||||
} else if (is_underscore_case) {
|
||||
// Underscore-only prong, no additional items
|
||||
// (AstGen.zig:7982-7986).
|
||||
pay[under_tbl] = hdr;
|
||||
prong_info_slot = pay_len++;
|
||||
} else if (!is_multi_case) {
|
||||
// Scalar case (AstGen.zig:7987-7994).
|
||||
pay[scalar_tbl + scalar_ci++] = hdr;
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope, RL_NONE_VAL,
|
||||
values[0], COMPTIME_REASON_SWITCH_ITEM);
|
||||
prong_info_slot = pay_len++;
|
||||
} else {
|
||||
// Multi case (AstGen.zig:7939-7977 non-underscore path).
|
||||
pay[multi_tbl + multi_ci++] = hdr;
|
||||
uint32_t ist = tree->extra_data.arr[cd.lhs];
|
||||
uint32_t ien = tree->extra_data.arr[cd.lhs + 1];
|
||||
uint32_t nitems = 0, nranges = 0;
|
||||
for (uint32_t j = ist; j < ien; j++) {
|
||||
if (tree->nodes.tags[tree->extra_data.arr[j]]
|
||||
== AST_NODE_SWITCH_RANGE)
|
||||
uint32_t nitems = 0;
|
||||
uint32_t nranges = 0;
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
if (tree->nodes.tags[values[vi]] == AST_NODE_SWITCH_RANGE)
|
||||
nranges++;
|
||||
else
|
||||
nitems++;
|
||||
@@ -6816,128 +7158,189 @@ static uint32_t switchExpr(
|
||||
pay[pay_len++] = nranges;
|
||||
prong_info_slot = pay_len++;
|
||||
// Non-range items.
|
||||
for (uint32_t j = ist; j < ien; j++) {
|
||||
uint32_t item = tree->extra_data.arr[j];
|
||||
if (tree->nodes.tags[item] != AST_NODE_SWITCH_RANGE) {
|
||||
if (pay_len + 2 > pay_cap) {
|
||||
pay_cap *= 2;
|
||||
uint32_t* p = realloc(pay, pay_cap * sizeof(uint32_t));
|
||||
if (!p)
|
||||
abort();
|
||||
pay = p;
|
||||
}
|
||||
pay[pay_len++] = comptimeExpr(gz, scope, RL_NONE_VAL, item,
|
||||
COMPTIME_REASON_SWITCH_ITEM);
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
if (tree->nodes.tags[values[vi]] != AST_NODE_SWITCH_RANGE) {
|
||||
ensurePayCapacity(&pay, &pay_cap, pay_len, 1);
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope,
|
||||
RL_NONE_VAL, values[vi], COMPTIME_REASON_SWITCH_ITEM);
|
||||
}
|
||||
}
|
||||
// Range pairs.
|
||||
for (uint32_t j = ist; j < ien; j++) {
|
||||
uint32_t item = tree->extra_data.arr[j];
|
||||
if (tree->nodes.tags[item] == AST_NODE_SWITCH_RANGE) {
|
||||
AstData rng = tree->nodes.datas[item];
|
||||
if (pay_len + 2 > pay_cap) {
|
||||
pay_cap *= 2;
|
||||
uint32_t* p = realloc(pay, pay_cap * sizeof(uint32_t));
|
||||
if (!p)
|
||||
abort();
|
||||
pay = p;
|
||||
}
|
||||
pay[pay_len++] = comptimeExpr(gz, scope, RL_NONE_VAL,
|
||||
rng.lhs, COMPTIME_REASON_SWITCH_ITEM);
|
||||
pay[pay_len++] = comptimeExpr(gz, scope, RL_NONE_VAL,
|
||||
rng.rhs, COMPTIME_REASON_SWITCH_ITEM);
|
||||
for (uint32_t vi = 0; vi < values_len; vi++) {
|
||||
if (tree->nodes.tags[values[vi]] == AST_NODE_SWITCH_RANGE) {
|
||||
AstData rng = tree->nodes.datas[values[vi]];
|
||||
ensurePayCapacity(&pay, &pay_cap, pay_len, 2);
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope,
|
||||
RL_NONE_VAL, rng.lhs, COMPTIME_REASON_SWITCH_ITEM);
|
||||
pay[pay_len++] = comptimeExpr(parent_gz, scope,
|
||||
RL_NONE_VAL, rng.rhs, COMPTIME_REASON_SWITCH_ITEM);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
// Evaluate body (AstGen.zig:7997-8026).
|
||||
uint32_t body_node = cd.rhs;
|
||||
GenZir case_scope = makeSubBlock(gz, scope);
|
||||
{
|
||||
// Temporarily stack case_scope on parent_gz
|
||||
// (AstGen.zig:7998-8000).
|
||||
case_scope.instructions_top = parent_gz->astgen->scratch_inst_len;
|
||||
|
||||
// Note: upstream regular switchExpr (AstGen.zig:7625) does NOT emit
|
||||
// save_err_ret_index. Only switchExprErrUnion (AstGen.zig:7524) does.
|
||||
if (dbg_var_name != 0) {
|
||||
addDbgVar(&case_scope, ZIR_INST_DBG_VAR_VAL, dbg_var_name,
|
||||
dbg_var_inst);
|
||||
}
|
||||
if (dbg_var_tag_name != 0) {
|
||||
addDbgVar(&case_scope, ZIR_INST_DBG_VAR_VAL, dbg_var_tag_name,
|
||||
dbg_var_tag_inst);
|
||||
}
|
||||
|
||||
// Use fullBodyExpr to process body inline (AstGen.zig:8009).
|
||||
uint32_t result
|
||||
= fullBodyExpr(&case_scope, &case_scope.base, break_rl, body_node);
|
||||
if (!refIsNoReturn(gz, result)) {
|
||||
addBreak(&case_scope, ZIR_INST_BREAK, switch_inst, result,
|
||||
(int32_t)body_node - (int32_t)gz->decl_node_index);
|
||||
uint32_t body_node = cd.rhs;
|
||||
uint32_t result = fullBodyExpr(&case_scope, sub_scope,
|
||||
block_scope.break_result_info, body_node);
|
||||
if (!refIsNoReturn(parent_gz, result)) {
|
||||
addBreak(&case_scope, ZIR_INST_BREAK, switch_inst, result,
|
||||
(int32_t)body_node - (int32_t)parent_gz->decl_node_index);
|
||||
}
|
||||
|
||||
uint32_t raw_body_len = gzInstructionsLen(&case_scope);
|
||||
const uint32_t* body = gzInstructionsSlice(&case_scope);
|
||||
|
||||
// Body fixups with extra refs (AstGen.zig:8016-8025).
|
||||
uint32_t extra_refs[2];
|
||||
uint32_t extra_refs_len = 0;
|
||||
extra_refs[extra_refs_len++] = switch_inst;
|
||||
if (has_tag_capture) {
|
||||
extra_refs[extra_refs_len++] = tag_inst;
|
||||
}
|
||||
|
||||
uint32_t body_len = countBodyLenAfterFixupsExtraRefs(
|
||||
ag, body, raw_body_len, extra_refs, extra_refs_len);
|
||||
|
||||
// Encode ProngInfo (AstGen.zig:8019-8024).
|
||||
uint32_t prong_info = (body_len & 0x0FFFFFFFu)
|
||||
| ((capture & 3u) << 28) | ((is_inline ? 1u : 0u) << 30)
|
||||
| ((has_tag_capture ? 1u : 0u) << 31);
|
||||
pay[prong_info_slot] = prong_info;
|
||||
|
||||
ensurePayCapacity(&pay, &pay_cap, pay_len, body_len);
|
||||
appendBodyWithFixupsExtraRefsPay(ag, body, raw_body_len,
|
||||
extra_refs, extra_refs_len, &pay, &pay_len, &pay_cap);
|
||||
|
||||
gzUnstack(&case_scope);
|
||||
}
|
||||
uint32_t body_len = gzInstructionsLen(&case_scope);
|
||||
const uint32_t* body = gzInstructionsSlice(&case_scope);
|
||||
|
||||
pay[prong_info_slot] = body_len & 0x0FFFFFFFu;
|
||||
|
||||
if (pay_len + body_len > pay_cap) {
|
||||
while (pay_len + body_len > pay_cap)
|
||||
pay_cap *= 2;
|
||||
uint32_t* p = realloc(pay, pay_cap * sizeof(uint32_t));
|
||||
if (!p)
|
||||
abort();
|
||||
pay = p;
|
||||
}
|
||||
for (uint32_t i = 0; i < body_len; i++)
|
||||
pay[pay_len++] = body[i];
|
||||
gzUnstack(&case_scope);
|
||||
}
|
||||
|
||||
// Now add switch_block to parent (AstGen.zig:8034).
|
||||
gzAppendInstruction(parent_gz, switch_inst);
|
||||
|
||||
// --- Serialize to extra in payload order (AstGen.zig:8036-8110) ---
|
||||
ensureExtraCapacity(ag,
|
||||
2 + (uint32_t)(multi_cases_len > 0 ? 1 : 0) + pay_len - table_size);
|
||||
2 + (uint32_t)(multi_cases_len > 0 ? 1 : 0)
|
||||
+ (any_has_tag_capture ? 1u : 0u) + pay_len - table_size);
|
||||
uint32_t payload_index = ag->extra_len;
|
||||
|
||||
ag->extra[ag->extra_len++] = cond_ref;
|
||||
// SwitchBlock.operand (AstGen.zig:8042).
|
||||
ag->extra[ag->extra_len++] = raw_operand;
|
||||
|
||||
uint32_t bits = 0;
|
||||
if (multi_cases_len > 0)
|
||||
bits |= 1u;
|
||||
if (has_else)
|
||||
bits |= (1u << 1);
|
||||
bits |= (scalar_cases_len & 0x1FFFFFFu) << 7;
|
||||
ag->extra[ag->extra_len++] = bits;
|
||||
// SwitchBlock.bits (AstGen.zig:8043-8050).
|
||||
{
|
||||
uint32_t bits = 0;
|
||||
if (multi_cases_len > 0)
|
||||
bits |= 1u; // has_multi_cases (bit 0)
|
||||
bits |= (special_prongs & 7u) << 1; // special_prongs (bits 1-3)
|
||||
if (any_has_tag_capture)
|
||||
bits |= (1u << 4); // any_has_tag_capture (bit 4)
|
||||
if (any_non_inline_capture)
|
||||
bits |= (1u << 5); // any_non_inline_capture (bit 5)
|
||||
// has_continue (bit 6): set if label used for continue.
|
||||
// We don't track used_for_continue precisely, so set it if label
|
||||
// exists (conservative). Skipping precise tracking per issue 9.
|
||||
// Actually: only set if label_token != UINT32_MAX.
|
||||
// The upstream checks block_scope.label.used_for_continue; we
|
||||
// don't track that, so conservatively set it when there's a label.
|
||||
// This is safe: sema handles the flag as an optimization hint.
|
||||
if (label_token != UINT32_MAX)
|
||||
bits |= (1u << 6); // has_continue
|
||||
bits |= (scalar_cases_len & 0x1FFFFFFu) << 7; // scalar_cases_len
|
||||
ag->extra[ag->extra_len++] = bits;
|
||||
}
|
||||
|
||||
// multi_cases_len (AstGen.zig:8053-8055).
|
||||
if (multi_cases_len > 0)
|
||||
ag->extra[ag->extra_len++] = multi_cases_len;
|
||||
|
||||
// Else prong.
|
||||
// tag_inst (AstGen.zig:8057-8059).
|
||||
if (any_has_tag_capture)
|
||||
ag->extra[ag->extra_len++] = tag_inst;
|
||||
|
||||
ag->inst_datas[switch_inst].pl_node.payload_index = payload_index;
|
||||
|
||||
// Else prong (AstGen.zig:8064-8070).
|
||||
if (has_else) {
|
||||
uint32_t si = pay[else_tbl];
|
||||
uint32_t bl = pay[si] & 0x0FFFFFFFu;
|
||||
for (uint32_t i = 0; i < 1 + bl; i++)
|
||||
ag->extra[ag->extra_len++] = pay[si + i];
|
||||
uint32_t prong_info = pay[si];
|
||||
uint32_t bl = prong_info & 0x0FFFFFFFu;
|
||||
uint32_t end = si + 1 + bl;
|
||||
for (uint32_t i = si; i < end; i++)
|
||||
ag->extra[ag->extra_len++] = pay[i];
|
||||
}
|
||||
// Scalar cases.
|
||||
for (uint32_t i = 0; i < scalar_cases_len; i++) {
|
||||
uint32_t si = pay[scalar_tbl + i];
|
||||
uint32_t bl = pay[si + 1] & 0x0FFFFFFFu;
|
||||
for (uint32_t j = 0; j < 2 + bl; j++)
|
||||
ag->extra[ag->extra_len++] = pay[si + j];
|
||||
|
||||
// Underscore prong (AstGen.zig:8071-8093).
|
||||
if (has_under) {
|
||||
uint32_t si = pay[under_tbl];
|
||||
uint32_t body_len_idx = si;
|
||||
uint32_t end = si;
|
||||
switch (underscore_additional_items) {
|
||||
case 2: // none
|
||||
end += 1; // just prong_info
|
||||
break;
|
||||
case 4: // one additional item
|
||||
body_len_idx = si + 1;
|
||||
end += 2; // item + prong_info
|
||||
break;
|
||||
case 6: // many additional items
|
||||
body_len_idx = si + 2;
|
||||
end += 3 + pay[si] + 2 * pay[si + 1]; // hdr + items + ranges
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
uint32_t prong_info = pay[body_len_idx];
|
||||
uint32_t bl = prong_info & 0x0FFFFFFFu;
|
||||
end += bl;
|
||||
for (uint32_t i = si; i < end; i++)
|
||||
ag->extra[ag->extra_len++] = pay[i];
|
||||
}
|
||||
// Multi cases.
|
||||
for (uint32_t i = 0; i < multi_cases_len; i++) {
|
||||
uint32_t si = pay[multi_tbl + i];
|
||||
uint32_t ni = pay[si];
|
||||
uint32_t nr = pay[si + 1];
|
||||
uint32_t bl = pay[si + 2] & 0x0FFFFFFFu;
|
||||
uint32_t total = 3 + ni + nr * 2 + bl;
|
||||
for (uint32_t j = 0; j < total; j++)
|
||||
ag->extra[ag->extra_len++] = pay[si + j];
|
||||
|
||||
// Scalar and multi cases (AstGen.zig:8094-8110).
|
||||
for (uint32_t i = 0; i < scalar_cases_len + multi_cases_len; i++) {
|
||||
uint32_t tbl_idx = scalar_tbl + i;
|
||||
uint32_t si = pay[tbl_idx];
|
||||
uint32_t body_len_idx;
|
||||
uint32_t end = si;
|
||||
if (tbl_idx < multi_tbl) {
|
||||
// Scalar: [item, prong_info, body...]
|
||||
body_len_idx = si + 1;
|
||||
end += 2;
|
||||
} else {
|
||||
// Multi: [items_len, ranges_len, prong_info, items..., ranges...]
|
||||
body_len_idx = si + 2;
|
||||
uint32_t ni = pay[si];
|
||||
uint32_t nr = pay[si + 1];
|
||||
end += 3 + ni + nr * 2;
|
||||
}
|
||||
uint32_t prong_info = pay[body_len_idx];
|
||||
uint32_t bl = prong_info & 0x0FFFFFFFu;
|
||||
end += bl;
|
||||
for (uint32_t j = si; j < end; j++)
|
||||
ag->extra[ag->extra_len++] = pay[j];
|
||||
}
|
||||
|
||||
free(pay);
|
||||
|
||||
ag->inst_datas[switch_inst].pl_node.payload_index = payload_index;
|
||||
gzAppendInstruction(gz, switch_inst);
|
||||
|
||||
// AstGen.zig:8112-8115.
|
||||
bool need_result_rvalue = (break_rl.tag != rl.tag);
|
||||
if (need_result_rvalue)
|
||||
return rvalue(gz, rl, switch_inst + ZIR_REF_START_INDEX, node);
|
||||
return rvalue(parent_gz, rl, switch_inst + ZIR_REF_START_INDEX, node);
|
||||
return switch_inst + ZIR_REF_START_INDEX;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user