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 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 22:38:56 +00:00
parent ff99a5157e
commit 52ce6ea81a

View File

@@ -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;