From 52ce6ea81a05f91e7c30f5f0f0d611a0230e232a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Motiejus=20Jak=C5=A1tys?= Date: Fri, 13 Feb 2026 22:38:56 +0000 Subject: [PATCH] astgen: add decltest support and within_fn flag in testDecl/fnDecl Port two missing features from upstream AstGen.zig: 1. Handle identifier-named tests (decltest): when the token after `test` is an identifier, set decl_id to DECL_ID_DECLTEST and record the identifier string as the test name. Upstream performs full scope resolution for validation which is skipped here. 2. Add `within_fn` field to AstGenCtx (mirrors AstGen.within_fn). Save, set to true, and restore in both testDecl and fnDecl. This flag propagates to maybe_generic on namespace scopes for container declarations inside function/test bodies. Co-Authored-By: Claude Opus 4.6 --- astgen.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/astgen.c b/astgen.c index 528da42f9e..0d24e13e26 100644 --- a/astgen.c +++ b/astgen.c @@ -113,6 +113,7 @@ typedef struct { uint32_t nodes_need_rl_len; uint32_t nodes_need_rl_cap; bool has_compile_errors; + bool within_fn; // AstGen.zig:49 } AstGenCtx; static void setCompileError(AstGenCtx* ag, const char* where, int line) { @@ -8065,8 +8066,13 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, uint32_t name_len; strLitAsString(ag, test_name_token, &test_name, &name_len); decl_id = DECL_ID_TEST; + } else if (tree->tokens.tags[test_name_token] == TOKEN_IDENTIFIER) { + // Identifier test name (decltest) (AstGen.zig:4763-4834). + // Upstream performs full scope resolution for validation; we skip + // the validation and just record the identifier as the test name. + test_name = identAsString(ag, test_name_token); + decl_id = DECL_ID_DECLTEST; } - // TODO: handle identifier test names (decltest). // Set up decl_block GenZir (AstGen.zig:4735-4743). GenZir decl_block; @@ -8094,9 +8100,12 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, fn_block.break_block = UINT32_MAX; fn_block.any_defer_node = UINT32_MAX; - // Set fn_block and fn_ret_ty for the body (AstGen.zig:4849-4853). + // Set within_fn, fn_block and fn_ret_ty for the body + // (AstGen.zig:4848-4853). + bool prev_within_fn = ag->within_fn; void* prev_fn_block = ag->fn_block; uint32_t prev_fn_ret_ty = ag->fn_ret_ty; + ag->within_fn = true; setFnBlock(ag, &fn_block); ag->fn_ret_ty = ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE; @@ -8109,6 +8118,7 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, uint32_t block_result = fullBodyExpr(&fn_block, &fn_block.base, RL_NONE_VAL, body_node); + ag->within_fn = prev_within_fn; ag->fn_block = prev_fn_block; ag->fn_ret_ty = prev_fn_ret_ty; @@ -8192,6 +8202,11 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, uint32_t decl_line = ag->source_line; uint32_t decl_column = ag->source_column; + // Set this now, since parameter types, return type, etc may be generic + // (AstGen.zig:4097-4100). + bool prev_within_fn = ag->within_fn; + ag->within_fn = true; + // Save source cursor for restoring after ret_gz (AstGen.zig:4387-4388). uint32_t saved_source_offset = ag->source_offset; uint32_t saved_source_line = ag->source_line; @@ -8470,6 +8485,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, uint32_t* wip_decl_insts, fullBodyExpr(&body_gz, &body_gz.base, RL_NONE_VAL, body_node); + ag->within_fn = prev_within_fn; ag->fn_block = prev_fn_block; ag->fn_ret_ty = prev_fn_ret_ty;