commit e010fa03475eb9ea7bcb192dedcfee51e2ee3dfd (tree)
parent ece6f690548d3bc776fdfc27d666d1d21a44bcb8
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Fri, 13 Feb 2026 23:09:34 +0000
astgen: rewrite enumDeclInner to match upstream enum handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
| M | astgen.c | | | 264 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------- |
1 file changed, 199 insertions(+), 65 deletions(-)
diff --git a/astgen.c b/astgen.c
@@ -2221,7 +2221,7 @@ static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node);
static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
const uint32_t* members, uint32_t members_len);
static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
- const uint32_t* members, uint32_t members_len);
+ const uint32_t* members, uint32_t members_len, uint32_t arg_node);
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);
@@ -9235,15 +9235,16 @@ typedef struct {
uint32_t bodies_cap;
} WipMembers;
-static WipMembers wipMembersInit(uint32_t decl_count, uint32_t field_count) {
- // bits_per_field = 4, max_field_size = 5
- uint32_t fields_per_u32 = 8; // 32 / 4
+// Parameterized init (AstGen.zig:3989 WipMembers.init).
+static WipMembers wipMembersInitEx(uint32_t decl_count, uint32_t field_count,
+ uint32_t bits_per_field, uint32_t max_field_size) {
+ uint32_t fields_per_u32 = bits_per_field > 0 ? 32 / bits_per_field : 0;
uint32_t field_bits_start = decl_count;
- uint32_t bit_words = field_count > 0
+ uint32_t bit_words = (field_count > 0 && fields_per_u32 > 0)
? (field_count + fields_per_u32 - 1) / fields_per_u32
: 0;
uint32_t fields_start = field_bits_start + bit_words;
- uint32_t payload_end = fields_start + field_count * 5;
+ uint32_t payload_end = fields_start + field_count * max_field_size;
uint32_t alloc_size = payload_end > 0 ? payload_end : 1;
uint32_t* payload = calloc(alloc_size, sizeof(uint32_t));
if (!payload)
@@ -9263,6 +9264,11 @@ static WipMembers wipMembersInit(uint32_t decl_count, uint32_t field_count) {
return wm;
}
+static WipMembers wipMembersInit(uint32_t decl_count, uint32_t field_count) {
+ // bits_per_field = 4, max_field_size = 5
+ return wipMembersInitEx(decl_count, field_count, 4, 5);
+}
+
static void wipMembersDeinit(WipMembers* wm) {
free(wm->payload);
free(wm->bodies);
@@ -9304,6 +9310,29 @@ static void wipMembersFinishBits(WipMembers* wm) {
}
}
+// bits_per_field = 1: bits[0]=have_value (for enum fields).
+static void wipMembersNextFieldEnum(WipMembers* wm, bool have_value) {
+ uint32_t fields_per_u32 = 32; // 32 / 1
+ uint32_t index = wm->field_bits_start + wm->field_index / fields_per_u32;
+ uint32_t bit_bag
+ = (wm->field_index % fields_per_u32 == 0) ? 0 : wm->payload[index];
+ bit_bag >>= 1;
+ bit_bag |= ((uint32_t)(have_value ? 1 : 0)) << 31;
+ wm->payload[index] = bit_bag;
+ wm->field_index++;
+}
+
+static void wipMembersFinishBitsEnum(WipMembers* wm) {
+ uint32_t fields_per_u32 = 32; // 32 / 1
+ uint32_t empty_field_slots
+ = fields_per_u32 - (wm->field_index % fields_per_u32);
+ if (wm->field_index > 0 && empty_field_slots < fields_per_u32) {
+ uint32_t index
+ = wm->field_bits_start + wm->field_index / fields_per_u32;
+ wm->payload[index] >>= empty_field_slots;
+ }
+}
+
// Returns pointer to decls region and its length.
static const uint32_t* wipMembersDeclsSlice(
const WipMembers* wm, uint32_t* out_len) {
@@ -9421,6 +9450,20 @@ static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node) {
void* prev_fn_block = ag->fn_block;
ag->fn_block = NULL;
+ // Extract arg node for container_decl_arg variants (AstGen.zig:5638).
+ // For enum(u8), lhs is the arg node.
+ uint32_t arg_node = 0; // 0 = none
+ switch (tag) {
+ case AST_NODE_CONTAINER_DECL_ARG:
+ case AST_NODE_CONTAINER_DECL_ARG_TRAILING:
+ case AST_NODE_TAGGED_UNION_ENUM_TAG:
+ case AST_NODE_TAGGED_UNION_ENUM_TAG_TRAILING:
+ arg_node = nd.lhs;
+ break;
+ default:
+ break;
+ }
+
// Dispatch based on container keyword (AstGen.zig:5485-5536).
uint32_t main_token = tree->nodes.main_tokens[node];
TokenizerTag kw_tag = tree->tokens.tags[main_token];
@@ -9430,7 +9473,8 @@ static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node) {
decl_inst = structDeclInner(ag, gz, node, members, members_len);
break;
case TOKEN_KEYWORD_ENUM:
- decl_inst = enumDeclInner(ag, gz, node, members, members_len);
+ decl_inst
+ = enumDeclInner(ag, gz, node, members, members_len, arg_node);
break;
default:
// union/opaque: fall back to struct for now.
@@ -9473,14 +9517,26 @@ static uint16_t packEnumDeclSmall(EnumDeclSmall s) {
return r;
}
-// Mirrors GenZir.setEnum (AstGen.zig:13080).
+// Mirrors GenZir.setEnum (AstGen.zig:13064-13123).
static void setEnum(AstGenCtx* ag, uint32_t inst, uint32_t src_node,
- EnumDeclSmall small, uint32_t fields_len, uint32_t decls_len) {
- ensureExtraCapacity(ag, 6 + 3);
+ uint32_t tag_type, uint32_t captures_len, uint32_t body_len,
+ uint32_t fields_len, uint32_t decls_len, bool nonexhaustive,
+ uint8_t name_strategy) {
+ EnumDeclSmall small;
+ memset(&small, 0, sizeof(small));
+ small.has_tag_type = (tag_type != ZIR_REF_NONE);
+ small.has_captures_len = (captures_len != 0);
+ small.has_body_len = (body_len != 0);
+ small.has_fields_len = (fields_len != 0);
+ small.has_decls_len = (decls_len != 0);
+ small.name_strategy = name_strategy;
+ small.nonexhaustive = nonexhaustive;
+
+ ensureExtraCapacity(ag, 6 + 5);
uint32_t payload_index = ag->extra_len;
- // fields_hash (4 words): zero-filled.
+ // fields_hash (4 words): zero-filled; hash comparison skipped in tests.
ag->extra[ag->extra_len++] = 0;
ag->extra[ag->extra_len++] = 0;
ag->extra[ag->extra_len++] = 0;
@@ -9489,6 +9545,13 @@ static void setEnum(AstGenCtx* ag, uint32_t inst, uint32_t src_node,
ag->extra[ag->extra_len++] = ag->source_line;
ag->extra[ag->extra_len++] = src_node;
+ // Trailing data in upstream order (AstGen.zig:13092-13106).
+ if (small.has_tag_type)
+ ag->extra[ag->extra_len++] = tag_type;
+ if (small.has_captures_len)
+ ag->extra[ag->extra_len++] = captures_len;
+ if (small.has_body_len)
+ ag->extra[ag->extra_len++] = body_len;
if (small.has_fields_len)
ag->extra[ag->extra_len++] = fields_len;
if (small.has_decls_len)
@@ -9503,42 +9566,91 @@ static void setEnum(AstGenCtx* ag, uint32_t inst, uint32_t src_node,
ag->inst_datas[inst] = data;
}
-// --- enumDeclInner (AstGen.zig:5508) ---
+// Returns true if the identifier token at `ident_token` is "_".
+static bool tokenIsUnderscore(const Ast* tree, uint32_t ident_token) {
+ uint32_t start = tree->tokens.starts[ident_token];
+ const char* src = tree->source;
+ if (src[start] != '_')
+ return false;
+ // Check that the next character is not alphanumeric/underscore
+ // (i.e., the identifier is exactly "_").
+ char next = src[start + 1];
+ if ((next >= 'a' && next <= 'z') || (next >= 'A' && next <= 'Z')
+ || (next >= '0' && next <= '9') || next == '_')
+ return false;
+ return true;
+}
+
+// --- enumDeclInner (AstGen.zig:5508-5728) ---
+// Handles enum container declarations.
+// arg_node: the tag type expression node (e.g. u8 in enum(u8)), 0 if none.
static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
- const uint32_t* members, uint32_t members_len) {
+ const uint32_t* members, uint32_t members_len, uint32_t arg_node) {
const Ast* tree = ag->tree;
- uint32_t decl_inst = reserveInstructionIndex(ag);
- gzAppendInstruction(gz, decl_inst);
- if (members_len == 0) {
- EnumDeclSmall small;
- memset(&small, 0, sizeof(small));
- setEnum(ag, decl_inst, node, small, 0, 0);
- return decl_inst;
+ // --- First pass: count fields, values, decls, detect nonexhaustive ---
+ // (AstGen.zig:5513-5590)
+ uint32_t total_fields = 0;
+ uint32_t decl_count = 0;
+ uint32_t nonexhaustive_index = UINT32_MAX; // index into members[]
+ for (uint32_t i = 0; i < members_len; i++) {
+ uint32_t member_node = members[i];
+ AstNodeTag mtag = tree->nodes.tags[member_node];
+ switch (mtag) {
+ case AST_NODE_CONTAINER_FIELD_INIT:
+ case AST_NODE_CONTAINER_FIELD_ALIGN:
+ case AST_NODE_CONTAINER_FIELD: {
+ uint32_t main_token = tree->nodes.main_tokens[member_node];
+ // Check for "_" (nonexhaustive marker).
+ if (tokenIsUnderscore(tree, main_token)) {
+ nonexhaustive_index = i;
+ continue;
+ }
+ total_fields++;
+ break;
+ }
+ default:
+ decl_count++;
+ break;
+ }
}
+ bool nonexhaustive = (nonexhaustive_index != UINT32_MAX);
+
+ uint32_t decl_inst = reserveInstructionIndex(ag);
+ gzAppendInstruction(gz, decl_inst);
advanceSourceCursorToNode(ag, node);
- uint32_t decl_count = scanContainer(ag, members, members_len);
- uint32_t field_count = members_len - decl_count;
+ // scanContainer to register names in string table (AstGen.zig:5635).
+ scanContainer(ag, members, members_len);
- // Use WipMembers for decls and field data.
- // Enum fields: 1 bit per field (has_value), max 2 words per field
- // (name + value).
- WipMembers wm = wipMembersInit(decl_count, field_count);
+ // Set up block_scope for tag value expressions (AstGen.zig:5624-5632).
+ GenZir block_scope;
+ memset(&block_scope, 0, sizeof(block_scope));
+ block_scope.base.tag = SCOPE_GEN_ZIR;
+ block_scope.parent = NULL;
+ block_scope.astgen = ag;
+ block_scope.decl_node_index = node;
+ block_scope.decl_line = ag->source_line;
+ block_scope.is_comptime = true;
+ block_scope.instructions_top = ag->scratch_inst_len;
+ block_scope.any_defer_node = UINT32_MAX;
- // Enum fields use 1 bit per field: has_value.
- // We use the same WipMembers but with 1-bit fields.
- // Actually, upstream uses bits_per_field=1, max_field_size=2.
- // Re-init with correct params would be better but let's reuse.
- // For simplicity: track field data manually.
- uint32_t* field_names = NULL;
- uint32_t field_names_len = 0;
- uint32_t field_names_cap = 0;
+ // Evaluate tag type argument if present (AstGen.zig:5638-5641).
+ uint32_t arg_inst = ZIR_REF_NONE;
+ if (arg_node != 0)
+ arg_inst = typeExpr(&block_scope, &block_scope.base, arg_node);
+
+ // WipMembers with bits_per_field=1, max_field_size=2 (AstGen.zig:5645).
+ WipMembers wm = wipMembersInitEx(decl_count, total_fields, 1, 2);
+ // --- Second pass: process members (AstGen.zig:5656-5693) ---
for (uint32_t i = 0; i < members_len; i++) {
uint32_t member_node = members[i];
+ // Skip nonexhaustive marker field (AstGen.zig:5657-5658).
+ if (i == nonexhaustive_index)
+ continue;
AstNodeTag mtag = tree->nodes.tags[member_node];
switch (mtag) {
case AST_NODE_COMPTIME:
@@ -9559,19 +9671,33 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
case AST_NODE_CONTAINER_FIELD_INIT:
case AST_NODE_CONTAINER_FIELD_ALIGN:
case AST_NODE_CONTAINER_FIELD: {
- // Enum field: just a name (AstGen.zig:5617-5670).
+ // Enum field (AstGen.zig:5669-5692).
uint32_t main_token = tree->nodes.main_tokens[member_node];
uint32_t field_name = identAsString(ag, main_token);
- // Grow field_names array.
- if (field_names_len >= field_names_cap) {
- uint32_t new_cap
- = field_names_cap == 0 ? 8 : field_names_cap * 2;
- field_names = realloc(field_names, new_cap * sizeof(uint32_t));
- if (!field_names)
- exit(1);
- field_names_cap = new_cap;
+ wipMembersAppendToField(&wm, field_name);
+
+ // Extract value expression.
+ AstData mnd = tree->nodes.datas[member_node];
+ uint32_t value_node = 0;
+ if (mtag == AST_NODE_CONTAINER_FIELD_INIT) {
+ value_node = mnd.rhs;
+ } else if (mtag == AST_NODE_CONTAINER_FIELD && mnd.rhs != 0) {
+ value_node = tree->extra_data.arr[mnd.rhs + 1];
+ }
+
+ bool have_value = (value_node != 0);
+ wipMembersNextFieldEnum(&wm, have_value);
+
+ // Evaluate tag value expression (AstGen.zig:5690-5691).
+ if (have_value) {
+ ResultLoc val_rl = { .tag = RL_COERCED_TY,
+ .data = arg_inst,
+ .src_node = 0,
+ .ctx = RI_CTX_NONE };
+ uint32_t tag_value_inst = exprRl(
+ &block_scope, &block_scope.base, val_rl, value_node);
+ wipMembersAppendToField(&wm, tag_value_inst);
}
- field_names[field_names_len++] = field_name;
break;
}
default:
@@ -9580,33 +9706,41 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
}
}
- EnumDeclSmall small;
- memset(&small, 0, sizeof(small));
- small.has_fields_len = (field_count > 0);
- small.has_decls_len = (decl_count > 0);
- setEnum(ag, decl_inst, node, small, field_count, decl_count);
+ // Emit break_inline if block_scope has instructions
+ // (AstGen.zig:5695-5697).
+ if (gzInstructionsLen(&block_scope) > 0) {
+ addBreak(&block_scope, ZIR_INST_BREAK_INLINE, decl_inst,
+ ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
+ }
+
+ uint32_t raw_body_len = gzInstructionsLen(&block_scope);
+ const uint32_t* body = gzInstructionsSlice(&block_scope);
+ uint32_t body_len = countBodyLenAfterFixups(ag, body, raw_body_len);
- // Append: decls, field_bits, field_names (AstGen.zig:5724-5729).
+ // setEnum (AstGen.zig:5705-5715).
+ setEnum(ag, decl_inst, node, arg_inst, 0 /* captures_len */, body_len,
+ total_fields, decl_count, nonexhaustive, 0 /* name_strategy */);
+
+ wipMembersFinishBitsEnum(&wm);
+
+ // Append trailing data (AstGen.zig:5718-5725):
+ // captures (none), decls, body, fields.
uint32_t decls_len_out;
const uint32_t* decls_slice = wipMembersDeclsSlice(&wm, &decls_len_out);
+ uint32_t fields_len_out;
+ const uint32_t* fields_slice = wipMembersFieldsSlice(&wm, &fields_len_out);
- // Field bits: 1 bit per field (has_value = false for simple enums).
- uint32_t fields_per_u32 = 32;
- uint32_t bit_words = field_count > 0
- ? (field_count + fields_per_u32 - 1) / fields_per_u32
- : 0;
-
- ensureExtraCapacity(ag, decls_len_out + bit_words + field_names_len);
+ ensureExtraCapacity(ag, decls_len_out + body_len + fields_len_out);
for (uint32_t i = 0; i < decls_len_out; i++)
ag->extra[ag->extra_len++] = decls_slice[i];
- // Field bits: all zero (no values).
- for (uint32_t i = 0; i < bit_words; i++)
- ag->extra[ag->extra_len++] = 0;
- // Field names.
- for (uint32_t i = 0; i < field_names_len; i++)
- ag->extra[ag->extra_len++] = field_names[i];
-
- free(field_names);
+ // Body instructions with fixups (AstGen.zig:5724).
+ for (uint32_t i = 0; i < raw_body_len; i++)
+ appendPossiblyRefdBodyInst(ag, body[i]);
+ // Fields (bit bags + field data).
+ for (uint32_t i = 0; i < fields_len_out; i++)
+ ag->extra[ag->extra_len++] = fields_slice[i];
+
+ gzUnstack(&block_scope);
wipMembersDeinit(&wm);
return decl_inst;
}