diff --git a/astgen.c b/astgen.c index 63f4cb8aa6..992dee53d6 100644 --- 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; + + // --- 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; }