From 9345c89d43dba4376f185d1f726064b754013b35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 13 Feb 2026 23:58:59 +0000 Subject: [PATCH] astgen: fix globalVarDecl coercion, nameStratExpr, and error diagnostics Co-Authored-By: Claude Opus 4.6 --- astgen.c | 188 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 154 insertions(+), 34 deletions(-) diff --git a/astgen.c b/astgen.c index 56a507301b..ff10954748 100644 --- a/astgen.c +++ b/astgen.c @@ -2325,11 +2325,15 @@ static void blockExprStmts( GenZir* gz, Scope* scope, const uint32_t* statements, uint32_t stmt_count); static uint32_t fullBodyExpr( GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node); -static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node); +static bool nameStratExpr(GenZir* gz, Scope* scope, ResultLoc rl, + uint32_t node, uint8_t name_strategy, uint32_t* out_ref); +static uint32_t containerDecl( + GenZir* gz, Scope* scope, uint32_t node, uint8_t name_strategy); 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, uint8_t name_strategy); static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, - const uint32_t* members, uint32_t members_len, uint32_t arg_node); + const uint32_t* members, uint32_t members_len, uint32_t arg_node, + uint8_t name_strategy); 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); @@ -3737,9 +3741,12 @@ static uint32_t retExpr(GenZir* gz, Scope* scope, uint32_t node) { ret_rl.data = ag->fn_ret_ty; } ret_rl.ctx = RI_CTX_RETURN; - // TODO: nameStratExpr(gz, scope, ret_rl, operand_node, .func) when - // containerDecl supports name_strategy parameter. - uint32_t operand = reachableExpr(gz, scope, ret_rl, operand_node, node); + // nameStratExpr with .func name strategy (AstGen.zig:8185). + uint32_t operand; + if (!nameStratExpr( + gz, scope, ret_rl, operand_node, 1 /* func */, &operand)) { + operand = reachableExpr(gz, scope, ret_rl, operand_node, node); + } // Emit RESTORE_ERR_RET_INDEX based on nodeMayEvalToError // (AstGen.zig:8188-8253). @@ -4554,7 +4561,8 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) { case AST_NODE_TAGGED_UNION_TWO_TRAILING: case AST_NODE_TAGGED_UNION_ENUM_TAG: case AST_NODE_TAGGED_UNION_ENUM_TAG_TRAILING: - return rvalue(gz, rl, containerDecl(gz, scope, node), node); + return rvalue( + gz, rl, containerDecl(gz, scope, node, 2 /* anon */), node); // try (AstGen.zig:1115). case AST_NODE_TRY: return tryExpr(gz, scope, rl, node); @@ -9534,12 +9542,69 @@ static DeclFlagsId computeVarDeclId(bool is_mutable, bool is_pub, return DECL_ID_VAR_SIMPLE; } +// Mirrors nameStratExpr (AstGen.zig:1160-1199). +// Checks if node is a container decl or @Type builtin; if so, dispatches +// with the given name_strategy. Returns true if handled (result stored in +// *out_ref), false if caller should fall back to expr(). +static bool nameStratExpr(GenZir* gz, Scope* scope, ResultLoc rl, + uint32_t node, uint8_t name_strategy, uint32_t* out_ref) { + const AstGenCtx* ag = gz->astgen; + const Ast* tree = ag->tree; + AstNodeTag tag = tree->nodes.tags[node]; + (void)rl; // Used by builtinReify (not yet implemented). + + switch (tag) { + case AST_NODE_CONTAINER_DECL: + case AST_NODE_CONTAINER_DECL_TRAILING: + case AST_NODE_CONTAINER_DECL_TWO: + case AST_NODE_CONTAINER_DECL_TWO_TRAILING: + case AST_NODE_CONTAINER_DECL_ARG: + case AST_NODE_CONTAINER_DECL_ARG_TRAILING: + 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: + *out_ref = containerDecl(gz, scope, node, name_strategy); + return true; + // @Type builtin: upstream calls builtinReify (AstGen.zig:1186-1196). + // Not yet implemented; fall through to expr(). + default: + return false; + } +} + static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, uint32_t* decl_idx, uint32_t node) { const Ast* tree = ag->tree; VarDeclInfo vd = extractVarDecl(tree, node); uint32_t name_token = vd.mut_token + 1; + // "threadlocal variable cannot be constant" (AstGen.zig:4526-4528). + if (vd.is_threadlocal && !vd.is_mutable) { + SET_ERROR(ag); + return; + } + + // lib_name validation (AstGen.zig:4531-4540). + uint32_t lib_name = UINT32_MAX; + if (vd.lib_name_token != UINT32_MAX) { + uint32_t li, ll; + strLitAsString(ag, vd.lib_name_token, &li, &ll); + // "library name cannot contain null bytes" (AstGen.zig:4534-4535). + if (memchr(ag->string_bytes + li, 0, ll) != NULL) { + SET_ERROR(ag); + return; + } + // "library name cannot be empty" (AstGen.zig:4536-4537). + if (ll == 0) { + SET_ERROR(ag); + return; + } + lib_name = li; + } + // advanceSourceCursorToNode before makeDeclaration (AstGen.zig:4542-4546). advanceSourceCursorToNode(ag, node); uint32_t decl_column = ag->source_column; @@ -9548,6 +9613,26 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, wip_decl_insts[*decl_idx] = decl_inst; (*decl_idx)++; + // "extern variables have no initializers" (AstGen.zig:4549-4556). + if (vd.init_node != UINT32_MAX && vd.init_node != 0) { + if (vd.is_extern) { + SET_ERROR(ag); + return; + } + } else { + // "variables must be initialized" (AstGen.zig:4557-4561). + if (!vd.is_extern) { + SET_ERROR(ag); + return; + } + } + + // "unable to infer variable type" (AstGen.zig:4563-4565). + if (vd.is_extern && vd.type_node == 0) { + SET_ERROR(ag); + return; + } + // Set up type sub-block (AstGen.zig:4574-4582). GenZir type_gz; memset(&type_gz, 0, sizeof(type_gz)); @@ -9567,7 +9652,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, // Record type_gz boundary for slicing. uint32_t type_top = ag->scratch_inst_len; - // Align sub-block (AstGen.zig:4592-4596). + // Align sub-block (AstGen.zig:4585-4591). GenZir align_gz; memset(&align_gz, 0, sizeof(align_gz)); align_gz.base.tag = SCOPE_GEN_ZIR; @@ -9579,13 +9664,20 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, align_gz.any_defer_node = UINT32_MAX; if (vd.align_node != 0) { - uint32_t align_inst = expr(&align_gz, &align_gz.base, vd.align_node); + // coerced_align_ri = { .rl = .{ .coerced_ty = .u29_type } } + // (AstGen.zig:389, 4589). + ResultLoc align_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_U29_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t align_inst + = exprRl(&align_gz, &align_gz.base, align_rl, vd.align_node); makeBreakInline(&align_gz, decl_inst, align_inst, 0); } uint32_t align_top = ag->scratch_inst_len; - // Linksection sub-block (AstGen.zig:4598-4602). + // Linksection sub-block (AstGen.zig:4593-4599). GenZir linksection_gz; memset(&linksection_gz, 0, sizeof(linksection_gz)); linksection_gz.base.tag = SCOPE_GEN_ZIR; @@ -9597,14 +9689,20 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, linksection_gz.any_defer_node = UINT32_MAX; if (vd.section_node != 0) { - uint32_t ls_inst - = expr(&linksection_gz, &linksection_gz.base, vd.section_node); + // coerced_linksection_ri = { .rl = .{ .coerced_ty = + // .slice_const_u8_type } } (AstGen.zig:390, 4597). + ResultLoc ls_rl = { .tag = RL_COERCED_TY, + .data = ZIR_REF_SLICE_CONST_U8_TYPE, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t ls_inst = exprRl( + &linksection_gz, &linksection_gz.base, ls_rl, vd.section_node); makeBreakInline(&linksection_gz, decl_inst, ls_inst, 0); } uint32_t linksection_top = ag->scratch_inst_len; - // Addrspace sub-block (AstGen.zig:4604-4608). + // Addrspace sub-block (AstGen.zig:4601-4608). GenZir addrspace_gz; memset(&addrspace_gz, 0, sizeof(addrspace_gz)); addrspace_gz.base.tag = SCOPE_GEN_ZIR; @@ -9616,14 +9714,22 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, addrspace_gz.any_defer_node = UINT32_MAX; if (vd.addrspace_node != 0) { - uint32_t as_inst - = expr(&addrspace_gz, &addrspace_gz.base, vd.addrspace_node); + // Upstream: addBuiltinValue(addrspace_node, .address_space) then + // coerced_ty with that result (AstGen.zig:4605-4606). + uint32_t addrspace_ty = addBuiltinValue( + &addrspace_gz, vd.addrspace_node, ZIR_BUILTIN_VALUE_ADDRESS_SPACE); + ResultLoc as_rl = { .tag = RL_COERCED_TY, + .data = addrspace_ty, + .src_node = 0, + .ctx = RI_CTX_NONE }; + uint32_t as_inst = exprRl( + &addrspace_gz, &addrspace_gz.base, as_rl, vd.addrspace_node); makeBreakInline(&addrspace_gz, decl_inst, as_inst, 0); } uint32_t addrspace_top = ag->scratch_inst_len; - // Value sub-block (AstGen.zig:4610-4620). + // Value sub-block (AstGen.zig:4610-4621). GenZir value_gz; memset(&value_gz, 0, sizeof(value_gz)); value_gz.base.tag = SCOPE_GEN_ZIR; @@ -9635,7 +9741,23 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, value_gz.any_defer_node = UINT32_MAX; if (vd.init_node != UINT32_MAX && vd.init_node != 0) { - uint32_t init_ref = expr(&value_gz, &value_gz.base, vd.init_node); + // Upstream: coerced_ty = decl_inst.toRef() when type_node present + // (AstGen.zig:4614-4616). + ResultLoc init_rl; + memset(&init_rl, 0, sizeof(init_rl)); + if (vd.type_node != 0) { + init_rl.tag = RL_COERCED_TY; + init_rl.data = decl_inst + ZIR_REF_START_INDEX; + } else { + init_rl.tag = RL_NONE; + } + // nameStratExpr: check if init is container decl (AstGen.zig:4617). + uint32_t init_ref; + if (!nameStratExpr(&value_gz, &value_gz.base, init_rl, vd.init_node, + 0 /* parent */, &init_ref)) { + init_ref + = exprRl(&value_gz, &value_gz.base, init_rl, vd.init_node); + } makeBreakInline(&value_gz, decl_inst, init_ref, 0); } @@ -9666,14 +9788,6 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, vd.is_extern, vd.is_export, vd.is_threadlocal, has_type_body, has_special_body, has_lib_name); - // Compute lib_name string index. - uint32_t lib_name = UINT32_MAX; - if (has_lib_name) { - uint32_t li, ll; - strLitAsString(ag, vd.lib_name_token, &li, &ll); - lib_name = li; - } - setDeclaration(ag, decl_inst, (SetDeclArgs) { .src_line = ag->source_line, .src_column = decl_column, @@ -10011,7 +10125,8 @@ static void wipMembersBodiesAppendWithFixups( // --- containerDecl (AstGen.zig:5468) --- // Handles container declarations as expressions (struct{}, enum{}, etc.). -static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node) { +static uint32_t containerDecl( + GenZir* gz, Scope* scope, uint32_t node, uint8_t name_strategy) { AstGenCtx* ag = gz->astgen; const Ast* tree = ag->tree; AstNodeTag tag = tree->nodes.tags[node]; @@ -10085,15 +10200,17 @@ static uint32_t containerDecl(GenZir* gz, Scope* scope, uint32_t node) { uint32_t decl_inst; switch (kw_tag) { case TOKEN_KEYWORD_STRUCT: - decl_inst = structDeclInner(ag, gz, node, members, members_len); + decl_inst = structDeclInner( + ag, gz, node, members, members_len, name_strategy); break; case TOKEN_KEYWORD_ENUM: - decl_inst - = enumDeclInner(ag, gz, node, members, members_len, arg_node); + decl_inst = enumDeclInner( + ag, gz, node, members, members_len, arg_node, name_strategy); break; default: // union/opaque: fall back to struct for now. - decl_inst = structDeclInner(ag, gz, node, members, members_len); + decl_inst = structDeclInner( + ag, gz, node, members, members_len, name_strategy); break; } (void)scope; @@ -10201,7 +10318,8 @@ static bool tokenIsUnderscore(const Ast* tree, uint32_t ident_token) { // 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, uint32_t arg_node) { + const uint32_t* members, uint32_t members_len, uint32_t arg_node, + uint8_t name_strategy) { const Ast* tree = ag->tree; // --- First pass: count fields, values, decls, detect nonexhaustive --- @@ -10334,7 +10452,7 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, // 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 */); + total_fields, decl_count, nonexhaustive, name_strategy); wipMembersFinishBitsEnum(&wm); @@ -10363,7 +10481,7 @@ static uint32_t enumDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, // --- structDeclInner (AstGen.zig:4926) --- 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, uint8_t name_strategy) { const Ast* tree = ag->tree; uint32_t decl_inst = reserveInstructionIndex(ag); gzAppendInstruction(gz, decl_inst); @@ -10372,6 +10490,7 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, if (members_len == 0) { StructDeclSmall small; memset(&small, 0, sizeof(small)); + small.name_strategy = name_strategy; setStruct(ag, decl_inst, node, small, 0, 0, 0); return decl_inst; } @@ -10570,6 +10689,7 @@ static uint32_t structDeclInner(AstGenCtx* ag, GenZir* gz, uint32_t node, small.any_comptime_fields = any_comptime_fields; small.any_default_inits = any_default_inits; small.any_aligned_fields = any_aligned_fields; + small.name_strategy = name_strategy; setStruct(ag, decl_inst, node, small, 0, field_count, decl_count); // Append: captures (none), backing_int (none), decls, fields, bodies @@ -11853,7 +11973,7 @@ Zir astGen(const Ast* ast) { const uint32_t* members = ast->extra_data.arr + members_start; uint32_t members_len = members_end - members_start; - structDeclInner(&ag, &gen_scope, 0, members, members_len); + structDeclInner(&ag, &gen_scope, 0, members, members_len, 0 /* parent */); // Write imports list (AstGen.zig:227-244). writeImports(&ag);