astgen: enable array_list.zig corpus test

Fix multiple bugs found via the array_list.zig corpus test:

- Fix anytype param ref/index double-conversion (addStrTok returns
  a ref, don't add ZIR_REF_START_INDEX again)
- Implement is_generic param tracking via is_used_or_discarded
  pointer in ScopeLocalVal
- Fix globalVarDecl declaration src_line: use type_gz.decl_line
  instead of ag->source_line (which was advanced by init expression)
- Fix cppcheck warning: remove redundant (0u << 2) in bitmask
- Implement fetchRemoveRefEntries and ret_param_refs in addFunc
- Add func_fancy case to buildHashSkipMask in test
- Fix valgrind: zero elem_val_imm padding, skip addNodeExtended
  undefined small field, handle more padding-sensitive tags

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 15:08:48 +00:00
parent 17f44db36f
commit 23beb0aad2
2 changed files with 224 additions and 40 deletions

View File

@@ -296,6 +296,7 @@ typedef struct {
uint32_t inst; // ZirInstRef
uint32_t token_src; // Ast.TokenIndex
uint32_t name; // NullTerminatedString (string table index)
bool* is_used_or_discarded; // NULL if not tracking (AstGen.zig:11691)
} ScopeLocalVal;
// Scope.LocalPtr (AstGen.zig:11704).
@@ -1660,6 +1661,32 @@ static uint32_t countBodyLenAfterFixups(
return countBodyLenAfterFixupsExtraRefs(ag, body, body_len, NULL, 0);
}
// Mirrors fetchRemoveRefEntries (AstGen.zig:14066-14074).
// For each param_inst, removes its ref_table entry (if present) and saves
// the value. Returns the number of entries removed.
static uint32_t fetchRemoveRefEntries(AstGenCtx* ag,
const uint32_t* param_insts, uint32_t param_insts_len, uint32_t* result,
uint32_t result_cap) {
uint32_t count = 0;
for (uint32_t i = 0; i < param_insts_len; i++) {
uint32_t ref_inst;
if (refTableFetchRemove(ag, param_insts[i], &ref_inst)) {
if (count < result_cap)
result[count] = ref_inst;
count++;
}
}
return count;
}
// Mirrors appendBodyWithFixups (AstGen.zig:13649-13657).
static void appendBodyWithFixups(
AstGenCtx* ag, const uint32_t* body, uint32_t body_len) {
for (uint32_t i = 0; i < body_len; i++) {
appendPossiblyRefdBodyInst(ag, body[i]);
}
}
// Mirrors GenZir.setBlockBody (AstGen.zig:11949).
// Writes Block payload (body_len + instruction indices) to extra.
// Sets the instruction's payload_index. Unstacks gz.
@@ -2497,6 +2524,7 @@ static uint32_t rvalue(
// elem_val_imm: operand=result, idx=i.
uint32_t elem_inst = reserveInstructionIndex(gz->astgen);
gz->astgen->inst_tags[elem_inst] = ZIR_INST_ELEM_VAL_IMM;
memset(&gz->astgen->inst_datas[elem_inst], 0, sizeof(ZirInstData));
gz->astgen->inst_datas[elem_inst].elem_val_imm.operand = result;
gz->astgen->inst_datas[elem_inst].elem_val_imm.idx = i;
gzAppendInstruction(gz, elem_inst);
@@ -3837,6 +3865,9 @@ static uint32_t localVarRef(GenZir* gz, Scope* scope, ResultLoc rl,
case SCOPE_LOCAL_VAL: {
ScopeLocalVal* lv = (ScopeLocalVal*)s;
if (lv->name == name_str) {
// Track usage for generic detection (AstGen.zig:8399).
if (lv->is_used_or_discarded != NULL)
*lv->is_used_or_discarded = true;
// Locals cannot shadow, no ambiguity check needed.
uint32_t value_inst;
if (num_namespaces_out != 0) {
@@ -9744,6 +9775,7 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
// Extract type_node and init_node based on variant.
uint32_t type_node = 0;
uint32_t align_node = 0;
uint32_t init_node = 0;
if (tag == AST_NODE_SIMPLE_VAR_DECL) {
@@ -9753,13 +9785,14 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
} else if (tag == AST_NODE_LOCAL_VAR_DECL) {
// lhs = extra_data index, rhs = init.
// extra: {type_node, align_node, addrspace_node, section_node}
// Simplified: just extract type_node.
uint32_t extra_idx = nd.lhs;
type_node = tree->extra_data.arr[extra_idx]; // type_node
align_node = tree->extra_data.arr[extra_idx + 1]; // align_node
init_node = nd.rhs;
} else if (tag == AST_NODE_ALIGNED_VAR_DECL) {
// lhs = align expr, rhs = init.
// No type node in this variant.
align_node = nd.lhs;
init_node = nd.rhs;
} else {
// global_var_decl or unknown — bail.
@@ -9773,6 +9806,18 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
return;
}
// Evaluate alignment expression (AstGen.zig:3227-3230).
uint32_t align_inst = ZIR_REF_NONE;
if (align_node != 0) {
ResultLoc align_rl = { .tag = RL_COERCED_TY,
.data = ZIR_REF_U29_TYPE,
.src_node = 0,
.ctx = RI_CTX_NONE,
.components = NULL,
.components_len = 0 };
align_inst = exprRl(gz, scope, align_rl, align_node);
}
if (is_const) {
// --- CONST path (AstGen.zig:3232-3340) ---
@@ -9823,6 +9868,7 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
val_out->inst = init_ref;
val_out->token_src = name_token;
val_out->name = ident_name;
val_out->is_used_or_discarded = NULL;
*scope_out = &val_out->base;
} else {
// Alloc path (AstGen.zig:3277-3340).
@@ -9906,20 +9952,58 @@ static void varDecl(GenZir* gz, Scope* scope, uint32_t node,
bool resolve_inferred = false;
if (type_node != 0) {
// Typed var: alloc_mut (AstGen.zig:3361-3375).
// Typed var: alloc_mut (AstGen.zig:3346-3365).
uint32_t type_ref = typeExpr(gz, scope, type_node);
ZirInstTag alloc_tag = is_comptime ? ZIR_INST_ALLOC_COMPTIME_MUT
: ZIR_INST_ALLOC_MUT;
alloc_ref = addUnNode(gz, alloc_tag, type_ref, node);
if (align_inst == ZIR_REF_NONE) {
ZirInstTag alloc_tag = is_comptime
? ZIR_INST_ALLOC_COMPTIME_MUT
: ZIR_INST_ALLOC_MUT;
alloc_ref = addUnNode(gz, alloc_tag, type_ref, node);
} else {
// addAllocExtended (AstGen.zig:12781-12830).
ensureExtraCapacity(ag, 3); // src_node + type + align
uint32_t payload_index = ag->extra_len;
int32_t src_off = (int32_t)node - (int32_t)gz->decl_node_index;
memcpy(&ag->extra[ag->extra_len], &src_off, sizeof(uint32_t));
ag->extra_len++;
ag->extra[ag->extra_len++] = type_ref;
ag->extra[ag->extra_len++] = align_inst;
// small: has_type=1, has_align=1, is_const=0, is_comptime
uint16_t small = (uint16_t)(1u | (1u << 1)
| ((is_comptime ? 1u : 0u) << 3));
ZirInstData edata;
edata.extended.opcode = (uint16_t)ZIR_EXT_ALLOC;
edata.extended.small = small;
edata.extended.operand = payload_index;
alloc_ref = addInstruction(gz, ZIR_INST_EXTENDED, edata);
}
} else {
// Inferred type var: alloc_inferred_mut
// (AstGen.zig:3384-3392).
ZirInstTag alloc_tag = is_comptime
? ZIR_INST_ALLOC_INFERRED_COMPTIME_MUT
: ZIR_INST_ALLOC_INFERRED_MUT;
ZirInstData adata;
adata.node = (int32_t)node - (int32_t)gz->decl_node_index;
alloc_ref = addInstruction(gz, alloc_tag, adata);
// (AstGen.zig:3366-3385).
if (align_inst == ZIR_REF_NONE) {
ZirInstTag alloc_tag = is_comptime
? ZIR_INST_ALLOC_INFERRED_COMPTIME_MUT
: ZIR_INST_ALLOC_INFERRED_MUT;
ZirInstData adata;
adata.node = (int32_t)node - (int32_t)gz->decl_node_index;
alloc_ref = addInstruction(gz, alloc_tag, adata);
} else {
// addAllocExtended without type (AstGen.zig:12781-12830).
ensureExtraCapacity(ag, 2); // src_node + align
uint32_t payload_index = ag->extra_len;
int32_t src_off = (int32_t)node - (int32_t)gz->decl_node_index;
memcpy(&ag->extra[ag->extra_len], &src_off, sizeof(uint32_t));
ag->extra_len++;
ag->extra[ag->extra_len++] = align_inst;
// small: has_type=0, has_align=1, is_const=0, is_comptime
uint16_t small
= (uint16_t)((1u << 1) | ((is_comptime ? 1u : 0u) << 3));
ZirInstData edata;
edata.extended.opcode = (uint16_t)ZIR_EXT_ALLOC;
edata.extended.small = small;
edata.extended.operand = payload_index;
alloc_ref = addInstruction(gz, ZIR_INST_EXTENDED, edata);
}
resolve_inferred = true;
}
@@ -11222,7 +11306,7 @@ static uint32_t lastToken(const Ast* tree, uint32_t node) {
// Creates a param instruction with pl_tok data and type body in extra.
static uint32_t addParam(GenZir* gz, GenZir* param_gz, ZirInstTag tag,
uint32_t abs_tok_index, uint32_t name) {
uint32_t abs_tok_index, uint32_t name, bool is_generic) {
AstGenCtx* ag = gz->astgen;
uint32_t body_len = gzInstructionsLen(param_gz);
@@ -11232,7 +11316,8 @@ static uint32_t addParam(GenZir* gz, GenZir* param_gz, ZirInstTag tag,
ensureExtraCapacity(ag, 2 + body_len);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = name;
ag->extra[ag->extra_len++] = body_len & 0x7FFFFFFFu; // is_generic = false
ag->extra[ag->extra_len++]
= (body_len & 0x7FFFFFFFu) | (is_generic ? 0x80000000u : 0u);
for (uint32_t i = 0; i < body_len; i++) {
ag->extra[ag->extra_len++] = param_body[i];
}
@@ -11272,7 +11357,9 @@ static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node,
uint32_t param_block, uint32_t ret_ref, const uint32_t* ret_body,
uint32_t ret_body_len, const uint32_t* body, uint32_t body_len,
const uint32_t* param_insts, uint32_t param_insts_len,
uint32_t lbrace_line, uint32_t lbrace_column, bool is_inferred_error) {
uint32_t lbrace_line, uint32_t lbrace_column, bool is_inferred_error,
bool ret_ty_is_generic, const uint32_t* ret_param_refs,
uint32_t ret_param_refs_len) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
uint32_t rbrace_tok = lastToken(tree, block_node);
@@ -11282,18 +11369,20 @@ static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node,
uint32_t rbrace_column = ag->source_column;
// Build Func payload (Zir.Inst.Func: ret_ty, param_block, body_len).
// (AstGen.zig:12187-12194)
// (AstGen.zig:12182-12194)
uint32_t ret_ty_packed_len;
if (ret_body_len > 0) {
ret_ty_packed_len = ret_body_len; // body-based return type
ret_ty_packed_len
= countBodyLenAfterFixups(ag, ret_param_refs, ret_param_refs_len)
+ countBodyLenAfterFixups(ag, ret_body, ret_body_len);
} else if (ret_ref != ZIR_REF_NONE) {
ret_ty_packed_len = 1; // simple Ref
} else {
ret_ty_packed_len = 0; // void return
}
// Pack RetTy: body_len:u31 | is_generic:bool(u1) = just body_len.
uint32_t ret_ty_packed
= ret_ty_packed_len & 0x7FFFFFFFu; // is_generic=false
// Pack RetTy: body_len:u31 | is_generic:bool(u1).
uint32_t ret_ty_packed = (ret_ty_packed_len & 0x7FFFFFFFu)
| (ret_ty_is_generic ? 0x80000000u : 0u);
uint32_t fixup_body_len = countBodyLenAfterFixupsExtraRefs(
ag, body, body_len, param_insts, param_insts_len);
@@ -11303,10 +11392,11 @@ static uint32_t addFunc(GenZir* gz, uint32_t src_node, uint32_t block_node,
ag->extra[ag->extra_len++] = param_block; // Func.param_block
ag->extra[ag->extra_len++] = fixup_body_len; // Func.body_len
// Trailing ret_ty: either body instructions or a single ref.
// Trailing ret_ty: either body instructions (with ret_param_refs
// prepended) or a single ref. (AstGen.zig:12196-12204)
if (ret_body_len > 0) {
for (uint32_t i = 0; i < ret_body_len; i++)
ag->extra[ag->extra_len++] = ret_body[i];
appendBodyWithFixups(ag, ret_param_refs, ret_param_refs_len);
appendBodyWithFixups(ag, ret_body, ret_body_len);
} else if (ret_ref != ZIR_REF_NONE) {
ag->extra[ag->extra_len++] = ret_ref;
}
@@ -11346,7 +11436,9 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node,
const uint32_t* cc_body, uint32_t cc_body_len, const uint32_t* body,
uint32_t body_len, const uint32_t* param_insts, uint32_t param_insts_len,
uint32_t lbrace_line, uint32_t lbrace_column, bool is_var_args,
bool is_inferred_error, bool is_noinline, uint32_t noalias_bits) {
bool is_inferred_error, bool is_noinline, uint32_t noalias_bits,
bool ret_ty_is_generic, const uint32_t* ret_param_refs,
uint32_t ret_param_refs_len) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
uint32_t rbrace_tok = lastToken(tree, block_node);
@@ -11367,11 +11459,11 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node,
}
// Calculate ret extra len (AstGen.zig:12231-12236).
// Note: ret_param_refs are empty (no generic tracking yet).
uint32_t ret_extra_len = 0;
if (ret_body_len > 0) {
ret_extra_len
= countBodyLenAfterFixups(ag, ret_body, ret_body_len) + 1;
= countBodyLenAfterFixups(ag, ret_param_refs, ret_param_refs_len)
+ countBodyLenAfterFixups(ag, ret_body, ret_body_len) + 1;
} else if (ret_ref != ZIR_REF_NONE) {
ret_extra_len = 1;
}
@@ -11403,7 +11495,8 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node,
bits |= (1u << 6); // has_ret_ty_body
if (noalias_bits != 0)
bits |= (1u << 7); // has_any_noalias
// bit 8 = ret_ty_is_generic (false for now, no generic tracking)
if (ret_ty_is_generic)
bits |= (1u << 8); // ret_ty_is_generic
ag->extra[ag->extra_len++] = bits;
// Trailing cc (AstGen.zig:12143-12151).
@@ -11417,12 +11510,12 @@ static uint32_t addFuncFancy(GenZir* gz, uint32_t src_node,
}
// Trailing ret_ty (AstGen.zig:12152-12164).
// Note: no ret_param_refs (generic tracking not implemented).
if (ret_body_len > 0) {
ag->extra[ag->extra_len++]
= countBodyLenAfterFixups(ag, ret_body, ret_body_len);
for (uint32_t i = 0; i < ret_body_len; i++)
appendPossiblyRefdBodyInst(ag, ret_body[i]);
= countBodyLenAfterFixups(ag, ret_param_refs, ret_param_refs_len)
+ countBodyLenAfterFixups(ag, ret_body, ret_body_len);
appendBodyWithFixups(ag, ret_param_refs, ret_param_refs_len);
appendBodyWithFixups(ag, ret_body, ret_body_len);
} else if (ret_ref != ZIR_REF_NONE) {
ag->extra[ag->extra_len++] = ret_ref;
}
@@ -11573,7 +11666,7 @@ static void testDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
// Create func instruction (AstGen.zig:4874-4897).
uint32_t func_ref = addFunc(&decl_block, node, body_node, decl_inst,
ZIR_REF_ANYERROR_VOID_ERROR_UNION_TYPE, NULL, 0, fn_body, fn_body_len,
NULL, 0, lbrace_line, lbrace_column, false);
NULL, 0, lbrace_line, lbrace_column, false, false, NULL, 0);
// break_inline returning func to declaration (AstGen.zig:4899).
makeBreakInline(&decl_block, decl_inst, func_ref, AST_NODE_OFFSET_NONE);
@@ -11750,6 +11843,8 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t param_insts_len = 0;
// noalias_bits tracking (AstGen.zig:4259).
uint32_t noalias_bits = 0;
// Generic parameter/return type tracking (AstGen.zig:4257).
bool any_param_used = false;
// is_var_args detection (AstGen.zig:4261).
bool is_var_args = false;
@@ -11890,11 +11985,13 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
: ZIR_INST_PARAM_ANYTYPE;
uint32_t anytype_inst = addStrTok(
&decl_gz, anytype_tag, param_name_str, anytype_name_token);
param_inst_ref = anytype_inst + ZIR_REF_START_INDEX;
param_inst_ref = anytype_inst; // already a ref (toRef())
if (param_insts_len < 32)
param_insts[param_insts_len++] = anytype_inst;
param_insts[param_insts_len++]
= anytype_inst - ZIR_REF_START_INDEX; // toIndex()
} else {
// Type-expression parameter (AstGen.zig:4330-4344).
any_param_used = false; // reset before evaluating type body
GenZir param_gz = makeSubBlock(&decl_gz, params_scope);
uint32_t param_type_ref = fullBodyExpr(
&param_gz, params_scope, coerced_type_ri, param_type_node);
@@ -11907,6 +12004,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
uint32_t param_inst_expected = ag->inst_len + 1;
makeBreakInline(&param_gz, param_inst_expected, param_type_ref,
(int32_t)param_type_node - (int32_t)param_gz.decl_node_index);
bool param_type_is_generic = any_param_used;
// Create param instruction (AstGen.zig:4341-4343).
ZirInstTag param_tag
@@ -11915,7 +12013,7 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
? name_token
: tree->nodes.main_tokens[param_type_node];
uint32_t param_inst = addParam(&decl_gz, &param_gz, param_tag,
name_tok_for_src, param_name_str);
name_tok_for_src, param_name_str, param_type_is_generic);
(void)param_inst_expected;
param_inst_ref = param_inst + ZIR_REF_START_INDEX;
if (param_insts_len < 32)
@@ -11931,12 +12029,14 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
lv->inst = param_inst_ref;
lv->token_src = name_token;
lv->name = param_name_str;
lv->is_used_or_discarded = &any_param_used;
params_scope = &lv->base;
}
param_type_i++;
}
// --- Return type (AstGen.zig:4369-4383) ---
any_param_used = false; // reset for return type generic detection
GenZir ret_gz = makeSubBlock(&decl_gz, params_scope);
uint32_t ret_ref = ZIR_REF_NONE;
if (return_type_node != 0) {
@@ -11952,6 +12052,11 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
makeBreakInline(&ret_gz, 0, ret_ref, AST_NODE_OFFSET_NONE);
}
}
// Fetch ref entries for params used in return type (AstGen.zig:4384).
uint32_t ret_param_refs[32];
uint32_t ret_param_refs_len = fetchRemoveRefEntries(
ag, param_insts, param_insts_len, ret_param_refs, 32);
bool ret_ty_is_generic = any_param_used;
// Map void_type → .none (AstGen.zig:12054).
if (ret_ref == ZIR_REF_VOID_TYPE)
ret_ref = ZIR_REF_NONE;
@@ -12124,11 +12229,13 @@ static void fnDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
ret_body, ret_body_len, cc_ref, cc_body, cc_body_len, fn_body,
fn_body_len, param_insts, param_insts_len, lbrace_line,
lbrace_column, is_var_args, is_inferred_error, is_noinline,
noalias_bits);
noalias_bits, ret_ty_is_generic, ret_param_refs,
ret_param_refs_len);
} else {
func_ref = addFunc(&decl_gz, node, body_node, decl_inst, ret_ref,
ret_body, ret_body_len, fn_body, fn_body_len, param_insts,
param_insts_len, lbrace_line, lbrace_column, is_inferred_error);
param_insts_len, lbrace_line, lbrace_column, is_inferred_error,
ret_ty_is_generic, ret_param_refs, ret_param_refs_len);
}
// Patch ret_body break_inline to point to func instruction
@@ -12621,7 +12728,7 @@ static void globalVarDecl(AstGenCtx* ag, GenZir* gz, Scope* scope,
has_special_body, has_lib_name);
setDeclaration(ag, decl_inst,
(SetDeclArgs) { .src_line = ag->source_line,
(SetDeclArgs) { .src_line = type_gz.decl_line,
.src_column = decl_column,
.id = decl_id,
.name = name_str,