commit d08206471bb430724c6fab2684756b13fda19933 (tree)
parent 81ddc5c989b895a3f32a9fdfc6533de4386faa0b
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Sat, 14 Feb 2026 00:46:39 +0000
astgen: fix switchExpr captures, underscore prong, switch_block_ref, labels, and body fixups
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
| M | astgen.c | | | 743 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------ |
1 file changed, 573 insertions(+), 170 deletions(-)
diff --git a/astgen.c b/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);
+
+ // 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;
+ }
+ }
- 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:
+ // 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++;
- break;
- default:
- break;
}
+ (void)is_inline; // inline_cases_len tracking skipped (issue 13)
+ (void)cd;
+ }
+
+ // 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;
+ }
+
+ // 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
+ // Sema expects a dbg_stmt immediately before switch_block(_ref)
// (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);
-
- // --- 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;
+ 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];
- uint32_t hdr = pay_len;
- uint32_t prong_info_slot = 0;
+ 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;
- // 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;
+ 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;
+ }
}
- 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;
- 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;
+ ensurePayCapacity(&pay, &pay_cap, pay_len, 32);
+ uint32_t hdr = pay_len;
+ uint32_t prong_info_slot = 0;
+
+ // 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++;
- 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_len = gzInstructionsLen(&case_scope);
- const uint32_t* body = gzInstructionsSlice(&case_scope);
+ 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;
- pay[prong_info_slot] = body_len & 0x0FFFFFFFu;
+ ensurePayCapacity(&pay, &pay_cap, pay_len, body_len);
+ appendBodyWithFixupsExtraRefsPay(ag, body, raw_body_len,
+ extra_refs, extra_refs_len, &pay, &pay_len, &pay_cap);
- 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;
+ gzUnstack(&case_scope);
}
- 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;
-
- 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.operand (AstGen.zig:8042).
+ ag->extra[ag->extra_len++] = raw_operand;
+ // 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];
- }
- // 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];
- }
- // 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];
+ 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];
+ }
+
+ // 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];
+ }
+
+ // 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;
}