astgen: rewrite enumDeclInner to match upstream enum handling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 23:09:34 +00:00
parent ece6f69054
commit e010fa0347

262
astgen.c
View File

@@ -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, static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
const uint32_t* members, uint32_t members_len); const uint32_t* members, uint32_t members_len);
static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, 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( static uint32_t blockExprExpr(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); 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 ifExpr(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node);
@@ -9235,15 +9235,16 @@ typedef struct {
uint32_t bodies_cap; uint32_t bodies_cap;
} WipMembers; } WipMembers;
static WipMembers wipMembersInit(uint32_t decl_count, uint32_t field_count) { // Parameterized init (AstGen.zig:3989 WipMembers.init).
// bits_per_field = 4, max_field_size = 5 static WipMembers wipMembersInitEx(uint32_t decl_count, uint32_t field_count,
uint32_t fields_per_u32 = 8; // 32 / 4 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 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 ? (field_count + fields_per_u32 - 1) / fields_per_u32
: 0; : 0;
uint32_t fields_start = field_bits_start + bit_words; 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 alloc_size = payload_end > 0 ? payload_end : 1;
uint32_t* payload = calloc(alloc_size, sizeof(uint32_t)); uint32_t* payload = calloc(alloc_size, sizeof(uint32_t));
if (!payload) if (!payload)
@@ -9263,6 +9264,11 @@ static WipMembers wipMembersInit(uint32_t decl_count, uint32_t field_count) {
return wm; 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) { static void wipMembersDeinit(WipMembers* wm) {
free(wm->payload); free(wm->payload);
free(wm->bodies); 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. // Returns pointer to decls region and its length.
static const uint32_t* wipMembersDeclsSlice( static const uint32_t* wipMembersDeclsSlice(
const WipMembers* wm, uint32_t* out_len) { 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; void* prev_fn_block = ag->fn_block;
ag->fn_block = NULL; 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). // Dispatch based on container keyword (AstGen.zig:5485-5536).
uint32_t main_token = tree->nodes.main_tokens[node]; uint32_t main_token = tree->nodes.main_tokens[node];
TokenizerTag kw_tag = tree->tokens.tags[main_token]; 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); decl_inst = structDeclInner(ag, gz, node, members, members_len);
break; break;
case TOKEN_KEYWORD_ENUM: 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; break;
default: default:
// union/opaque: fall back to struct for now. // union/opaque: fall back to struct for now.
@@ -9473,14 +9517,26 @@ static uint16_t packEnumDeclSmall(EnumDeclSmall s) {
return r; 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, static void setEnum(AstGenCtx* ag, uint32_t inst, uint32_t src_node,
EnumDeclSmall small, uint32_t fields_len, uint32_t decls_len) { uint32_t tag_type, uint32_t captures_len, uint32_t body_len,
ensureExtraCapacity(ag, 6 + 3); 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; 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; 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++] = ag->source_line;
ag->extra[ag->extra_len++] = src_node; 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) if (small.has_fields_len)
ag->extra[ag->extra_len++] = fields_len; ag->extra[ag->extra_len++] = fields_len;
if (small.has_decls_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; 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, 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; const Ast* tree = ag->tree;
// --- 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); uint32_t decl_inst = reserveInstructionIndex(ag);
gzAppendInstruction(gz, decl_inst); 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;
}
advanceSourceCursorToNode(ag, node); advanceSourceCursorToNode(ag, node);
uint32_t decl_count = scanContainer(ag, members, members_len); // scanContainer to register names in string table (AstGen.zig:5635).
uint32_t field_count = members_len - decl_count; scanContainer(ag, members, members_len);
// Use WipMembers for decls and field data. // Set up block_scope for tag value expressions (AstGen.zig:5624-5632).
// Enum fields: 1 bit per field (has_value), max 2 words per field GenZir block_scope;
// (name + value). memset(&block_scope, 0, sizeof(block_scope));
WipMembers wm = wipMembersInit(decl_count, field_count); 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. // Evaluate tag type argument if present (AstGen.zig:5638-5641).
// We use the same WipMembers but with 1-bit fields. uint32_t arg_inst = ZIR_REF_NONE;
// Actually, upstream uses bits_per_field=1, max_field_size=2. if (arg_node != 0)
// Re-init with correct params would be better but let's reuse. arg_inst = typeExpr(&block_scope, &block_scope.base, arg_node);
// For simplicity: track field data manually.
uint32_t* field_names = NULL;
uint32_t field_names_len = 0;
uint32_t field_names_cap = 0;
// 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++) { for (uint32_t i = 0; i < members_len; i++) {
uint32_t member_node = members[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]; AstNodeTag mtag = tree->nodes.tags[member_node];
switch (mtag) { switch (mtag) {
case AST_NODE_COMPTIME: 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_INIT:
case AST_NODE_CONTAINER_FIELD_ALIGN: case AST_NODE_CONTAINER_FIELD_ALIGN:
case AST_NODE_CONTAINER_FIELD: { 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 main_token = tree->nodes.main_tokens[member_node];
uint32_t field_name = identAsString(ag, main_token); uint32_t field_name = identAsString(ag, main_token);
// Grow field_names array. wipMembersAppendToField(&wm, field_name);
if (field_names_len >= field_names_cap) {
uint32_t new_cap // Extract value expression.
= field_names_cap == 0 ? 8 : field_names_cap * 2; AstData mnd = tree->nodes.datas[member_node];
field_names = realloc(field_names, new_cap * sizeof(uint32_t)); uint32_t value_node = 0;
if (!field_names) if (mtag == AST_NODE_CONTAINER_FIELD_INIT) {
exit(1); value_node = mnd.rhs;
field_names_cap = new_cap; } 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; break;
} }
default: default:
@@ -9580,33 +9706,41 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node,
} }
} }
EnumDeclSmall small; // Emit break_inline if block_scope has instructions
memset(&small, 0, sizeof(small)); // (AstGen.zig:5695-5697).
small.has_fields_len = (field_count > 0); if (gzInstructionsLen(&block_scope) > 0) {
small.has_decls_len = (decl_count > 0); addBreak(&block_scope, ZIR_INST_BREAK_INLINE, decl_inst,
setEnum(ag, decl_inst, node, small, field_count, decl_count); ZIR_REF_VOID_VALUE, AST_NODE_OFFSET_NONE);
}
// Append: decls, field_bits, field_names (AstGen.zig:5724-5729). 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);
// 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; uint32_t decls_len_out;
const uint32_t* decls_slice = wipMembersDeclsSlice(&wm, &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). ensureExtraCapacity(ag, decls_len_out + body_len + fields_len_out);
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);
for (uint32_t i = 0; i < decls_len_out; i++) for (uint32_t i = 0; i < decls_len_out; i++)
ag->extra[ag->extra_len++] = decls_slice[i]; ag->extra[ag->extra_len++] = decls_slice[i];
// Field bits: all zero (no values). // Body instructions with fixups (AstGen.zig:5724).
for (uint32_t i = 0; i < bit_words; i++) for (uint32_t i = 0; i < raw_body_len; i++)
ag->extra[ag->extra_len++] = 0; appendPossiblyRefdBodyInst(ag, body[i]);
// Field names. // Fields (bit bags + field data).
for (uint32_t i = 0; i < field_names_len; i++) for (uint32_t i = 0; i < fields_len_out; i++)
ag->extra[ag->extra_len++] = field_names[i]; ag->extra[ag->extra_len++] = fields_slice[i];
free(field_names); gzUnstack(&block_scope);
wipMembersDeinit(&wm); wipMembersDeinit(&wm);
return decl_inst; return decl_inst;
} }