From 91e1d1bd2e14e80e3685749bfa936c9f4c882abe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 13 Feb 2026 22:42:31 +0000 Subject: [PATCH] astgen: add reachableExprComptime and comptime_token handling in varDecl Port two missing features from upstream AstGen.zig varDecl: 1. Add reachableExprComptime (AstGen.zig:418-438) which wraps init expressions in comptimeExpr when force_comptime is set, and checks for noreturn results. Replace plain exprRl calls in all three varDecl paths (const rvalue, const alloc, var) with reachableExprComptime. 2. Extract comptime_token by scanning backwards from mut_token (matching Ast.zig fullVarDeclComponents). For const path, set force_comptime to wrap init in comptime block. For var path, use comptime_token to set is_comptime which selects alloc_comptime_mut/alloc_inferred_comptime_mut tags and sets maybe_comptime on the scope. Co-Authored-By: Claude Opus 4.6 --- astgen.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/astgen.c b/astgen.c index 0d24e13e26..5e7378f152 100644 --- a/astgen.c +++ b/astgen.c @@ -2314,6 +2314,27 @@ static uint32_t reachableExpr(GenZir* gz, Scope* scope, ResultLoc rl, return result; } +// Forward declaration needed by reachableExprComptime. +static uint32_t comptimeExpr( + GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node, uint32_t reason); + +// Mirrors reachableExprComptime (AstGen.zig:418-438). +// Like reachableExpr but optionally wraps in comptimeExpr when +// comptime_reason is non-zero (i.e. force_comptime is set). +static uint32_t reachableExprComptime(GenZir* gz, Scope* scope, ResultLoc rl, + uint32_t node, uint32_t reachable_node, uint32_t comptime_reason) { + uint32_t result; + if (comptime_reason != 0) + result = comptimeExpr(gz, scope, rl, node, comptime_reason); + else + result = exprRl(gz, scope, rl, node); + if (refIsNoReturn(gz, result)) { + (void)reachable_node; + SET_ERROR(gz->astgen); + } + return result; +} + static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node); // SimpleComptimeReason (std.zig:727) — values used in block_comptime payload. @@ -6586,6 +6607,14 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, uint32_t ident_name = identAsString(ag, name_token); + // Extract comptime_token by scanning backwards from mut_token + // (Ast.zig:2012-2023, fullVarDeclComponents). + bool has_comptime_token = false; + if (mut_token > 0 + && tree->tokens.tags[mut_token - 1] == TOKEN_KEYWORD_COMPTIME) { + has_comptime_token = true; + } + // Extract type_node and init_node based on variant. uint32_t type_node = 0; uint32_t init_node = 0; @@ -6619,6 +6648,12 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, if (is_const) { // --- CONST path (AstGen.zig:3232-3340) --- + + // `comptime const` is a non-fatal error; treat it like the init was + // marked `comptime` (AstGen.zig:3234-3239). + uint32_t force_comptime + = has_comptime_token ? COMPTIME_REASON_COMPTIME_KEYWORD : 0; + if (!nodesNeedRlContains(ag, node)) { // Rvalue path (AstGen.zig:3246-3271). // Evaluate type annotation and build result_info @@ -6638,7 +6673,8 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, } // Evaluate init expression (AstGen.zig:3251-3252). - uint32_t init_ref = exprRl(gz, scope, result_info, init_node); + uint32_t init_ref = reachableExprComptime( + gz, scope, result_info, init_node, node, force_comptime); if (ag->has_compile_errors) return; @@ -6699,7 +6735,8 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, init_rl.src_node = 0; } init_rl.ctx = RI_CTX_CONST_INIT; - uint32_t init_ref = exprRl(gz, scope, init_rl, init_node); + uint32_t init_ref = reachableExprComptime( + gz, scope, init_rl, init_node, node, force_comptime); if (ag->has_compile_errors) return; @@ -6733,20 +6770,22 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, } else { // --- VAR path (AstGen.zig:3342-3416) --- + // comptime_token handling (AstGen.zig:3343-3345). + bool is_comptime = has_comptime_token || gz->is_comptime; + uint32_t alloc_ref; bool resolve_inferred = false; if (type_node != 0) { // Typed var: alloc_mut (AstGen.zig:3361-3375). uint32_t type_ref = typeExpr(gz, scope, type_node); - ZirInstTag alloc_tag = gz->is_comptime - ? ZIR_INST_ALLOC_COMPTIME_MUT - : ZIR_INST_ALLOC_MUT; + ZirInstTag alloc_tag = is_comptime ? ZIR_INST_ALLOC_COMPTIME_MUT + : ZIR_INST_ALLOC_MUT; alloc_ref = addUnNode(gz, alloc_tag, type_ref, node); } else { // Inferred type var: alloc_inferred_mut // (AstGen.zig:3384-3392). - ZirInstTag alloc_tag = gz->is_comptime + ZirInstTag alloc_tag = is_comptime ? ZIR_INST_ALLOC_INFERRED_COMPTIME_MUT : ZIR_INST_ALLOC_INFERRED_MUT; ZirInstData adata; @@ -6768,7 +6807,10 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, var_init_rl.src_node = 0; } var_init_rl.ctx = RI_CTX_NONE; - uint32_t init_ref = exprRl(gz, scope, var_init_rl, init_node); + uint32_t comptime_reason + = has_comptime_token ? COMPTIME_REASON_COMPTIME_KEYWORD : 0; + uint32_t init_ref = reachableExprComptime( + gz, scope, var_init_rl, init_node, node, comptime_reason); (void)init_ref; if (ag->has_compile_errors) @@ -6791,7 +6833,7 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node, ptr_out->ptr = final_ptr; ptr_out->token_src = name_token; ptr_out->name = ident_name; - ptr_out->maybe_comptime = gz->is_comptime; + ptr_out->maybe_comptime = is_comptime; *scope_out = &ptr_out->base; } }