astgen: rewrite enumDeclInner to match upstream enum handling
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
262
astgen.c
262
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;
|
||||
|
||||
// --- 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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
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];
|
||||
// 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];
|
||||
|
||||
free(field_names);
|
||||
gzUnstack(&block_scope);
|
||||
wipMembersDeinit(&wm);
|
||||
return decl_inst;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user