diff --git a/stage0/astgen.c b/stage0/astgen.c index 8254ed0e5c..32efc99c20 100644 --- a/stage0/astgen.c +++ b/stage0/astgen.c @@ -2069,6 +2069,79 @@ static void setStruct(AstGenCtx* ag, uint32_t inst, uint32_t src_node, ag->inst_datas[inst] = data; } +// --- setUnion (AstGen.zig:12999) --- + +// UnionDecl.Small bit packing (Zir.zig:3573-3590). +typedef struct { + bool has_tag_type; + bool has_captures_len; + bool has_body_len; + bool has_fields_len; + bool has_decls_len; + uint8_t name_strategy; // 2 bits + uint8_t layout; // 2 bits + bool auto_enum_tag; + bool any_aligned_fields; +} UnionDeclSmall; + +static uint16_t packUnionDeclSmall(UnionDeclSmall s) { + uint16_t r = 0; + if (s.has_tag_type) + r |= (1u << 0); + if (s.has_captures_len) + r |= (1u << 1); + if (s.has_body_len) + r |= (1u << 2); + if (s.has_fields_len) + r |= (1u << 3); + if (s.has_decls_len) + r |= (1u << 4); + r |= (uint16_t)(s.name_strategy & 0x3u) << 5; + r |= (uint16_t)(s.layout & 0x3u) << 7; + if (s.auto_enum_tag) + r |= (1u << 9); + if (s.any_aligned_fields) + r |= (1u << 10); + return r; +} + +// Mirrors GenZir.setUnion (AstGen.zig:12999-13062). +static void setUnion(AstGenCtx* ag, uint32_t inst, uint32_t src_node, + UnionDeclSmall small, uint32_t tag_type, uint32_t captures_len, + uint32_t body_len, uint32_t fields_len, uint32_t decls_len) { + ensureExtraCapacity(ag, 6 + 5); + + uint32_t payload_index = ag->extra_len; + + // 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++] = ag->source_line; + ag->extra[ag->extra_len++] = src_node; + + 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) + ag->extra[ag->extra_len++] = decls_len; + + ag->inst_tags[inst] = ZIR_INST_EXTENDED; + ZirInstData data; + memset(&data, 0, sizeof(data)); + data.extended.opcode = (uint16_t)ZIR_EXT_UNION_DECL; + data.extended.small = packUnionDeclSmall(small); + data.extended.operand = payload_index; + ag->inst_datas[inst] = data; +} + // --- scanContainer (AstGen.zig:13384) --- // Mirrors scanContainer (AstGen.zig:13384). @@ -2619,6 +2692,10 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, uint32_t node, const uint32_t* members, uint32_t members_len, uint8_t layout, uint32_t backing_int_node); +static uint32_t unionDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint8_t layout, uint32_t arg_node, bool auto_enum_tag, + uint8_t name_strategy); static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, uint32_t node, const uint32_t* members, uint32_t members_len, uint32_t arg_node, uint8_t name_strategy); @@ -2793,6 +2870,7 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node); #define COMPTIME_REASON_EXPORT_OPTIONS 15 #define COMPTIME_REASON_CALL_MODIFIER 18 #define COMPTIME_REASON_SHUFFLE_MASK 11 +#define COMPTIME_REASON_DECL_NAME 41 // Mirrors comptimeExpr2 (AstGen.zig:1982). // Evaluates a node in a comptime block_comptime scope. @@ -4007,6 +4085,45 @@ static uint32_t builtinCall( = addPlNodeBin(gz, ZIR_INST_DIV_EXACT, node, lhs, rhs); return rvalue(gz, rl, result, node); } + // @clz — bitBuiltin (AstGen.zig:9475, 9907-9918). + if (name_len == 3 && memcmp(source + name_start, "clz", 3) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + uint32_t result = addUnNode(gz, ZIR_INST_CLZ, operand, node); + return rvalue(gz, rl, result, node); + } + // @ctz — bitBuiltin (AstGen.zig:9476, 9907-9918). + if (name_len == 3 && memcmp(source + name_start, "ctz", 3) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + uint32_t result = addUnNode(gz, ZIR_INST_CTZ, operand, node); + return rvalue(gz, rl, result, node); + } + // @popCount — bitBuiltin (AstGen.zig:9477, 9907-9918). + if (name_len == 8 + && memcmp(source + name_start, "popCount", 8) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + uint32_t result = addUnNode(gz, ZIR_INST_POP_COUNT, operand, node); + return rvalue(gz, rl, result, node); + } + // @byteSwap — bitBuiltin (AstGen.zig:9478, 9907-9918). + if (name_len == 8 + && memcmp(source + name_start, "byteSwap", 8) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + uint32_t result = addUnNode(gz, ZIR_INST_BYTE_SWAP, operand, node); + return rvalue(gz, rl, result, node); + } + // @bitReverse — bitBuiltin (AstGen.zig:9479, 9907-9918). + if (name_len == 10 + && memcmp(source + name_start, "bitReverse", 10) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t operand = expr(gz, scope, nd.lhs); + uint32_t result + = addUnNode(gz, ZIR_INST_BIT_REVERSE, operand, node); + return rvalue(gz, rl, result, node); + } // @bitOffsetOf — AstGen.zig:9490, 9962-9978. if (name_len == 11 && memcmp(source + name_start, "bitOffsetOf", 11) == 0) { @@ -4044,6 +4161,34 @@ static uint32_t builtinCall( addInstruction(gz, ZIR_INST_EXPORT, data); return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node); } + // @hasDecl — hasDeclOrField (AstGen.zig:9472, 9783-9805). + if (name_len == 7 + && memcmp(source + name_start, "hasDecl", 7) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t container_type = typeExpr(gz, scope, nd.lhs); + ResultLoc name_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_SLICE_CONST_U8_TYPE, + .src_node = 0, .ctx = RI_CTX_NONE }; + uint32_t name_inst = comptimeExpr( + gz, scope, name_rl, nd.rhs, COMPTIME_REASON_DECL_NAME); + uint32_t result = addPlNodeBin( + gz, ZIR_INST_HAS_DECL, node, container_type, name_inst); + return rvalue(gz, rl, result, node); + } + // @hasField — hasDeclOrField (AstGen.zig:9473, 9783-9805). + if (name_len == 8 + && memcmp(source + name_start, "hasField", 8) == 0) { + AstData nd = tree->nodes.datas[node]; + uint32_t container_type = typeExpr(gz, scope, nd.lhs); + ResultLoc name_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_SLICE_CONST_U8_TYPE, + .src_node = 0, .ctx = RI_CTX_NONE }; + uint32_t name_inst = comptimeExpr( + gz, scope, name_rl, nd.rhs, COMPTIME_REASON_FIELD_NAME); + uint32_t result = addPlNodeBin( + gz, ZIR_INST_HAS_FIELD, node, container_type, name_inst); + return rvalue(gz, rl, result, node); + } // clang-format on // TODO: handle other builtins. @@ -14093,8 +14238,36 @@ static uint32_t containerDecl( decl_inst = enumDeclInner(ag, gz, scope, node, members, members_len, arg_node, name_strategy); break; + case TOKEN_KEYWORD_UNION: { + // Extract layout (AstGen.zig:5499-5503). + uint8_t layout = 0; // auto + if (main_token > 0) { + TokenizerTag prev_tag = tree->tokens.tags[main_token - 1]; + if (prev_tag == TOKEN_KEYWORD_PACKED) + layout = 2; + else if (prev_tag == TOKEN_KEYWORD_EXTERN) + layout = 1; + } + // Determine auto_enum_tag from node type (AstGen.zig:5505). + bool auto_enum_tag = false; + switch (tag) { + case AST_NODE_TAGGED_UNION: + case AST_NODE_TAGGED_UNION_TRAILING: + case AST_NODE_TAGGED_UNION_TWO: + case AST_NODE_TAGGED_UNION_TWO_TRAILING: + case AST_NODE_TAGGED_UNION_ENUM_TAG: + case AST_NODE_TAGGED_UNION_ENUM_TAG_TRAILING: + auto_enum_tag = true; + break; + default: + break; + } + decl_inst = unionDeclInner(ag, gz, scope, node, members, members_len, + layout, arg_node, auto_enum_tag, name_strategy); + break; + } default: - // union/opaque: fall back to struct for now. + // opaque: fall back to struct for now. decl_inst = structDeclInner( ag, gz, scope, node, members, members_len, 0, 0, name_strategy); break; @@ -14545,6 +14718,267 @@ static uint32_t tupleDecl(AstGenCtx* ag, GenZir* gz, Scope* scope, return idx; } +// --- unionDeclInner (AstGen.zig:5289) --- + +static uint32_t unionDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope, + uint32_t node, const uint32_t* members, uint32_t members_len, + uint8_t layout, uint32_t arg_node, bool auto_enum_tag, + uint8_t name_strategy) { + const Ast* tree = ag->tree; + + uint32_t decl_inst = reserveInstructionIndex(ag); + gzAppendInstruction(gz, decl_inst); + + // Create namespace scope (AstGen.zig:5304-5310). + ScopeNamespace namespace; + scopeNamespaceInit(&namespace, scope, node, decl_inst, gz, ag->within_fn); + + // Create block scope (AstGen.zig:5317-5325). + advanceSourceCursorToNode(ag, node); + GenZir block_scope; + memset(&block_scope, 0, sizeof(block_scope)); + block_scope.base.tag = SCOPE_GEN_ZIR; + block_scope.parent = &namespace.base; + block_scope.astgen = ag; + block_scope.decl_node_index = node; + block_scope.decl_line = gz->decl_line; + block_scope.is_comptime = true; + block_scope.instructions_top = ag->scratch_inst_len; + block_scope.any_defer_node = UINT32_MAX; + + // Scan container (AstGen.zig:5328). + uint32_t decl_count = scanContainer(ag, &namespace, members, members_len); + uint32_t field_count = members_len - decl_count; + + // Layout validation (AstGen.zig:5331-5337). + if (layout != 0 && (auto_enum_tag || arg_node != 0)) { + SET_ERROR(ag); + } + + // Process arg instruction (AstGen.zig:5339-5342). + uint32_t arg_inst = ZIR_REF_NONE; + if (arg_node != 0) { + arg_inst = typeExpr(&block_scope, &namespace.base, arg_node); + } + + // WipMembers with bits_per_field=4, max_field_size=4 (AstGen.zig:5347). + WipMembers wm = wipMembersInitEx(decl_count, field_count, 4, 4); + + bool any_aligned_fields = false; + + // Process each member (AstGen.zig:5359-5428). + for (uint32_t i = 0; i < members_len; i++) { + uint32_t member_node = members[i]; + AstNodeTag mtag = tree->nodes.tags[member_node]; + + switch (mtag) { + // Declarations (containerMember AstGen.zig:5809-5901). + case AST_NODE_COMPTIME: + comptimeDecl(ag, gz, &namespace.base, wm.payload, &wm.decl_index, + member_node); + break; + case AST_NODE_SIMPLE_VAR_DECL: + globalVarDecl(ag, gz, &namespace.base, wm.payload, &wm.decl_index, + member_node); + break; + case AST_NODE_TEST_DECL: + testDecl(ag, gz, &namespace.base, wm.payload, &wm.decl_index, + member_node); + break; + case AST_NODE_FN_DECL: + case AST_NODE_FN_PROTO_SIMPLE: + case AST_NODE_FN_PROTO_MULTI: + case AST_NODE_FN_PROTO_ONE: + case AST_NODE_FN_PROTO: + fnDecl(ag, gz, &namespace.base, wm.payload, &wm.decl_index, + member_node); + break; + case AST_NODE_USINGNAMESPACE: + case AST_NODE_GLOBAL_VAR_DECL: + case AST_NODE_LOCAL_VAR_DECL: + case AST_NODE_ALIGNED_VAR_DECL: + globalVarDecl(ag, gz, &namespace.base, wm.payload, &wm.decl_index, + member_node); + break; + + // Fields (AstGen.zig:5359-5427). + 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]; + AstData nd = tree->nodes.datas[member_node]; + uint32_t type_node = nd.lhs; + uint32_t align_node = 0; + uint32_t value_node = 0; + + switch (mtag) { + case AST_NODE_CONTAINER_FIELD_INIT: + value_node = nd.rhs; + break; + case AST_NODE_CONTAINER_FIELD_ALIGN: + align_node = nd.rhs; + break; + case AST_NODE_CONTAINER_FIELD: + if (nd.rhs != 0) { + align_node = tree->extra_data.arr[nd.rhs]; + value_node = tree->extra_data.arr[nd.rhs + 1]; + } + break; + default: + break; + } + + // convertToNonTupleLike (AstGen.zig:5365, Ast.zig:2635-2641). + bool tuple_like = tree->tokens.tags[main_token] != TOKEN_IDENTIFIER + || tree->tokens.tags[main_token + 1] != TOKEN_COLON; + if (tuple_like && type_node != 0 + && tree->nodes.tags[type_node] == AST_NODE_IDENTIFIER) { + type_node = 0; + tuple_like = false; + } + + // Still tuple-like after conversion: error (AstGen.zig:5366-5368). + if (tuple_like) { + SET_ERROR(ag); + break; + } + + // Comptime check (AstGen.zig:5369-5371). + if (main_token > 0 + && tree->tokens.tags[main_token - 1] + == TOKEN_KEYWORD_COMPTIME) { + SET_ERROR(ag); // union fields cannot be marked comptime + break; + } + + // Field name (AstGen.zig:5373). + uint32_t field_name = identAsString(ag, main_token); + wipMembersAppendToField(&wm, field_name); + + bool have_type = (type_node != 0); + bool have_align = (align_node != 0); + bool have_value = (value_node != 0); + bool field_bits[4] = { have_type, have_align, have_value, false }; + wipMembersNextField(&wm, field_bits); + + // Type expression (AstGen.zig:5382-5387). + if (have_type) { + uint32_t field_type + = typeExpr(&block_scope, &namespace.base, type_node); + wipMembersAppendToField(&wm, field_type); + } else if (arg_inst == ZIR_REF_NONE && !auto_enum_tag) { + SET_ERROR(ag); // union field missing type + break; + } + + // Alignment (AstGen.zig:5388-5395). + if (have_align) { + if (layout == 2) { // packed + SET_ERROR(ag); + break; + } + ResultLoc align_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_U29_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t align_ref = exprRl( + &block_scope, &block_scope.base, align_rl, align_node); + wipMembersAppendToField(&wm, align_ref); + any_aligned_fields = true; + } + + // Value expression / tag value (AstGen.zig:5396-5427). + if (have_value) { + if (arg_inst == ZIR_REF_NONE) { + SET_ERROR(ag); + break; + } + if (!auto_enum_tag) { + SET_ERROR(ag); + break; + } + ResultLoc val_rl = { .tag = RL_COERCED_TY, + .data = arg_inst, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t tag_value = exprRl( + &block_scope, &block_scope.base, val_rl, value_node); + wipMembersAppendToField(&wm, tag_value); + } + break; + } + default: + SET_ERROR(ag); + break; + } + } + + wipMembersFinishBits(&wm); + + // Handle block scope body (AstGen.zig:5433-5438). + uint32_t body_len = 0; + if (gzInstructionsLen(&block_scope) > 0) { + if (!endsWithNoReturn(&block_scope)) { + makeBreakInline(&block_scope, decl_inst, ZIR_REF_VOID_VALUE, + AST_NODE_OFFSET_NONE); + } + uint32_t raw_len = gzInstructionsLen(&block_scope); + const uint32_t* body_slice = gzInstructionsSlice(&block_scope); + body_len = countBodyLenAfterFixups(ag, body_slice, raw_len); + } + + // setUnion (AstGen.zig:5440-5452). + UnionDeclSmall small; + memset(&small, 0, sizeof(small)); + small.has_tag_type = (arg_inst != ZIR_REF_NONE); + small.has_captures_len = (namespace.captures_len > 0); + small.has_body_len = (body_len > 0); + small.has_fields_len = (field_count > 0); + small.has_decls_len = (decl_count > 0); + small.name_strategy = name_strategy; + small.layout = layout; + small.auto_enum_tag = auto_enum_tag; + small.any_aligned_fields = any_aligned_fields; + setUnion(ag, decl_inst, node, small, arg_inst, namespace.captures_len, + body_len, field_count, decl_count); + + // Append trailing data (AstGen.zig:5454-5462). + uint32_t decls_len; + const uint32_t* decls_slice = wipMembersDeclsSlice(&wm, &decls_len); + uint32_t fields_len; + const uint32_t* fields_slice = wipMembersFieldsSlice(&wm, &fields_len); + + ensureExtraCapacity( + ag, namespace.captures_len * 2 + decls_len + body_len + fields_len); + + // Captures (AstGen.zig:5458-5459). + for (uint32_t j = 0; j < namespace.captures_len; j++) + ag->extra[ag->extra_len++] = namespace.capture_keys[j]; + for (uint32_t j = 0; j < namespace.captures_len; j++) + ag->extra[ag->extra_len++] = namespace.capture_vals[j]; + + // Decls (AstGen.zig:5460). + for (uint32_t j = 0; j < decls_len; j++) + ag->extra[ag->extra_len++] = decls_slice[j]; + + // Body (AstGen.zig:5461). + if (body_len > 0) { + uint32_t raw_len = gzInstructionsLen(&block_scope); + const uint32_t* body_data = gzInstructionsSlice(&block_scope); + for (uint32_t j = 0; j < raw_len; j++) + appendPossiblyRefdBodyInst(ag, body_data[j]); + } + + // Fields (AstGen.zig:5462). + for (uint32_t j = 0; j < fields_len; j++) + ag->extra[ag->extra_len++] = fields_slice[j]; + + gzUnstack(&block_scope); + wipMembersDeinit(&wm); + scopeNamespaceDeinit(&namespace); + return decl_inst; +} + // --- structDeclInner (AstGen.zig:4926) --- static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, Scope* scope,