astgen: implement RL_REF_COERCED_TY result location

Add RL_REF_COERCED_TY to the result location enum, matching the upstream
ref_coerced_ty variant. This carries a pointer type through the result
location so that array init and struct init expressions can generate
validate_array_init_ref_ty and struct_init_empty_ref_result instructions.

- Use RL_REF_COERCED_TY in address_of when result type is available
- Handle in arrayInitDotExpr to emit validate_array_init_ref_ty
- Handle in structInitExpr for empty .{} to emit struct_init_empty_ref_result
- Add RL_IS_REF() macro for checking both RL_REF and RL_REF_COERCED_TY
- Update rvalue to treat RL_REF_COERCED_TY like RL_REF

tokenizer_test.zig corpus: instructions now match (7026). Extra and
string_bytes still have diffs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 05:58:07 +00:00
parent 7a51724191
commit 286f78bdd9
2 changed files with 53 additions and 12 deletions

View File

@@ -172,6 +172,7 @@ typedef enum {
RL_COERCED_TY, // Coerce to specific type, result is the coercion.
RL_PTR, // Store result to typed pointer. data=alloc inst, src_node=node.
RL_INFERRED_PTR, // Store result to inferred pointer. data=alloc inst.
RL_REF_COERCED_TY, // Ref with pointer type. data=ptr_ty_inst.
} ResultLocTag;
typedef struct {
@@ -185,6 +186,8 @@ typedef struct {
#define RL_REF_VAL ((ResultLoc) { .tag = RL_REF, .data = 0, .src_node = 0 })
#define RL_DISCARD_VAL \
((ResultLoc) { .tag = RL_DISCARD, .data = 0, .src_node = 0 })
#define RL_IS_REF(rl) \
((rl).tag == RL_REF || (rl).tag == RL_REF_COERCED_TY)
// --- Scope types (AstGen.zig:11621-11768) ---
@@ -1515,7 +1518,8 @@ static uint32_t rvalue(
// ensure_result_non_error (AstGen.zig:11071-11074).
addUnNode(gz, ZIR_INST_ENSURE_RESULT_NON_ERROR, result, node);
return ZIR_REF_VOID_VALUE;
case RL_REF: {
case RL_REF:
case RL_REF_COERCED_TY: {
AstGenCtx* ag = gz->astgen;
uint32_t src_token = firstToken(ag->tree, node);
// If result is not an instruction index (e.g. a well-known ref),
@@ -2315,7 +2319,7 @@ static uint32_t identifierExpr(
case SCOPE_LOCAL_PTR: {
ScopeLocalPtr* lp = (ScopeLocalPtr*)s;
if (lp->name == name_str) {
if (rl.tag == RL_REF)
if (RL_IS_REF(rl))
return lp->ptr;
return addUnNode(gz, ZIR_INST_LOAD, lp->ptr, node);
}
@@ -2349,7 +2353,7 @@ decl_table:
for (uint32_t i = 0; i < ag->decl_table_len; i++) {
if (ag->decl_names[i] == name_str) {
ZirInstTag itag
= (rl.tag == RL_REF) ? ZIR_INST_DECL_REF : ZIR_INST_DECL_VAL;
= (RL_IS_REF(rl)) ? ZIR_INST_DECL_REF : ZIR_INST_DECL_VAL;
ZirInstData data;
data.str_tok.start = name_str;
data.str_tok.src_tok = tokenIndexToRelative(gz, ident_token);
@@ -2379,7 +2383,7 @@ static uint32_t fieldAccessExpr(
// Evaluate the LHS object expression (AstGen.zig:6181).
// For .ref rl, LHS is also evaluated with .ref (AstGen.zig:6161).
ResultLoc lhs_rl = (rl.tag == RL_REF) ? RL_REF_VAL : RL_NONE_VAL;
ResultLoc lhs_rl = (RL_IS_REF(rl)) ? RL_REF_VAL : RL_NONE_VAL;
uint32_t lhs = exprRl(gz, scope, lhs_rl, object_node);
// Emit dbg_stmt for the dot token (AstGen.zig:6183-6184).
@@ -2398,13 +2402,13 @@ static uint32_t fieldAccessExpr(
// .ref → field_ptr, else → field_val (AstGen.zig:6160-6164).
ZirInstTag ftag
= (rl.tag == RL_REF) ? ZIR_INST_FIELD_PTR : ZIR_INST_FIELD_VAL;
= (RL_IS_REF(rl)) ? ZIR_INST_FIELD_PTR : ZIR_INST_FIELD_VAL;
ZirInstData data;
data.pl_node.src_node = (int32_t)node - (int32_t)gz->decl_node_index;
data.pl_node.payload_index = payload_index;
uint32_t access = addInstruction(gz, ftag, data);
// For ref, return directly; otherwise apply rvalue (AstGen.zig:6161-6164).
if (rl.tag == RL_REF)
if (RL_IS_REF(rl))
return access;
return rvalue(gz, rl, access, node);
}
@@ -2561,7 +2565,7 @@ static uint32_t arrayInitExpr(
gz, ZIR_INST_ARRAY_TYPE, type_expr_node, len_inst, elem_type);
// arrayInitExprTyped (AstGen.zig:1598-1642).
bool is_ref = (rl.tag == RL_REF);
bool is_ref = (RL_IS_REF(rl));
uint32_t operands_len = elem_count + 1;
ensureExtraCapacity(ag, 1 + operands_len);
uint32_t payload_index = ag->extra_len;
@@ -3028,6 +3032,10 @@ static uint32_t structInitExpr(
if (type_expr_node == 0 && fields_len == 0) {
// .{} — depends on result location (AstGen.zig:1687-1698).
if (rl.tag == RL_REF_COERCED_TY) {
return addUnNode(gz, ZIR_INST_STRUCT_INIT_EMPTY_REF_RESULT,
rl.data, node);
}
if (rl.tag == RL_TY || rl.tag == RL_COERCED_TY) {
return addUnNode(
gz, ZIR_INST_STRUCT_INIT_EMPTY_RESULT, rl.data, node);
@@ -3135,7 +3143,7 @@ static uint32_t structInitExpr(
ag->extra[items_start + i * 2 + 1] = init_ref;
}
bool is_ref = (rl.tag == RL_REF);
bool is_ref = (RL_IS_REF(rl));
ZirInstTag init_tag
= is_ref ? ZIR_INST_STRUCT_INIT_REF : ZIR_INST_STRUCT_INIT;
return addPlNodePayloadIndex(gz, init_tag, node, payload_index);
@@ -3277,7 +3285,10 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
if (res_ty != 0) {
addUnTok(gz, ZIR_INST_VALIDATE_REF_TY, res_ty,
firstToken(ag->tree, node));
operand_rl = RL_REF_VAL; // simplified: skip ref_coerced_ty
// Pass ref_coerced_ty so init expressions can use the type
// (AstGen.zig:958).
operand_rl = (ResultLoc) { .tag = RL_REF_COERCED_TY,
.data = res_ty, .src_node = 0 };
} else {
operand_rl = RL_REF_VAL;
}
@@ -3453,7 +3464,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
case AST_NODE_DEREF: {
uint32_t lhs = expr(gz, scope, nd.lhs);
addUnNode(gz, ZIR_INST_VALIDATE_DEREF, lhs, node);
if (rl.tag == RL_REF)
if (RL_IS_REF(rl))
return lhs;
return rvalue(gz, rl, addUnNode(gz, ZIR_INST_LOAD, lhs, node), node);
}
@@ -3487,7 +3498,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
}
// arrayAccess (AstGen.zig:6192-6221).
case AST_NODE_ARRAY_ACCESS: {
if (rl.tag == RL_REF) {
if (RL_IS_REF(rl)) {
uint32_t lhs = exprRl(gz, scope, RL_REF_VAL, nd.lhs);
advanceSourceCursorToMainToken(ag, node);
uint32_t rhs = expr(gz, scope, nd.rhs);
@@ -4141,6 +4152,37 @@ static uint32_t arrayInitDotExpr(
gz, ZIR_INST_ARRAY_INIT_ANON, node, payload_index);
return rvalue(gz, rl, result, node);
}
case RL_REF_COERCED_TY: {
// validate_array_init_ref_ty + arrayInitExprTyped
// (AstGen.zig:1527-1532).
uint32_t ptr_ty_inst = rl.data;
ensureExtraCapacity(ag, 2);
uint32_t val_payload = ag->extra_len;
ag->extra[ag->extra_len++] = ptr_ty_inst;
ag->extra[ag->extra_len++] = elem_count;
uint32_t dest_arr_ty_inst = addPlNodePayloadIndex(
gz, ZIR_INST_VALIDATE_ARRAY_INIT_REF_TY, node, val_payload);
// arrayInitExprTyped with elem_ty=none, is_ref=true.
uint32_t operands_len = elem_count + 1;
ensureExtraCapacity(ag, 1 + operands_len);
uint32_t ai_payload = ag->extra_len;
ag->extra[ag->extra_len++] = operands_len;
ag->extra[ag->extra_len++] = dest_arr_ty_inst;
uint32_t extra_start2 = ag->extra_len;
ag->extra_len += elem_count;
for (uint32_t i = 0; i < elem_count; i++) {
uint32_t elem_ty = addPlNodeBin(gz,
ZIR_INST_ARRAY_INIT_ELEM_TYPE, elements[i],
dest_arr_ty_inst, i);
ResultLoc elem_rl = { .tag = RL_COERCED_TY, .data = elem_ty,
.src_node = 0 };
uint32_t elem_ref = exprRl(gz, scope, elem_rl, elements[i]);
ag->extra[extra_start2 + i] = elem_ref;
}
return addPlNodePayloadIndex(
gz, ZIR_INST_ARRAY_INIT_REF, node, ai_payload);
}
case RL_PTR:
// TODO: arrayInitExprPtr (AstGen.zig:1541-1543).
// For now, fall through to anon + rvalue.

View File

@@ -994,7 +994,6 @@ test "astgen: corpus test_all.zig" {
// }
test "astgen: corpus tokenizer_test.zig" {
if (true) return error.SkipZigTest; // TODO: 428 inst diff from ref_coerced_ty RL
const gpa = std.testing.allocator;
try corpusCheck(gpa, "tokenizer_test.zig", @embedFile("tokenizer_test.zig"));
}