astgen: implement 20+ builtins, multi-arg dispatch, extended helpers

Add builtins: @sizeOf, @alignOf, @typeInfo (simpleUnOpType pattern),
@compileError, @setEvalBranchQuota, @typeName (simpleUnOp pattern),
@This (addNodeExtended), @memmove, @FieldType, @reduce,
@addWithOverflow/@subWithOverflow/@mulWithOverflow/@shlWithOverflow
(overflowArithmetic), @alignCast/@constCast/@volatileCast (ptrCast
family), @errSetCast, @Type (reify), @TypeOf (typeof_builtin block),
@unionInit (3-arg via builtinCallMultiArg), @intFromPtr, @intFromBool,
@floatFromInt, @intFromFloat, @floatCast.

Add helper functions: addNodeExtended, addExtendedPayload,
addExtendedPayloadSmall. Add is_typeof field to GenZir.
Add AST_NODE_BUILTIN_CALL dispatch for 3+ arg builtins.
Add COMPTIME_REASON_COMPILE_ERROR_STRING and UNION_FIELD_NAME.

Add skipped corpus tests for array_list.zig, multi_array_list.zig,
Sema.zig (blocked on identifier resolution across namespace scopes).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 12:25:39 +00:00
parent b10557306d
commit 285935bd41
2 changed files with 495 additions and 32 deletions

View File

@@ -117,12 +117,8 @@ typedef struct {
bool fn_var_args; // AstGen.zig:46
} AstGenCtx;
static void setCompileError(AstGenCtx* ag, const char* where, int line) {
(void)where;
(void)line;
ag->has_compile_errors = true;
}
#define SET_ERROR(ag) setCompileError(ag, __func__, __LINE__)
static void setCompileError(AstGenCtx* ag) { ag->has_compile_errors = true; }
#define SET_ERROR(ag) setCompileError(ag)
// Set fn_block pointer on AstGenCtx. The caller is responsible for saving
// and restoring the previous value before the pointed-to GenZir goes out
@@ -257,6 +253,7 @@ typedef struct {
bool is_comptime;
bool is_inline; // true for inline for/while, labeled blocks in comptime
bool c_import; // true inside @cImport block
bool is_typeof; // true inside @TypeOf scope
uint32_t instructions_top; // start index in shared array
uint32_t break_block; // UINT32_MAX = none (AstGen.zig:11780)
uint32_t continue_block; // UINT32_MAX = none (AstGen.zig:11784)
@@ -365,6 +362,7 @@ static GenZir makeSubBlock(GenZir* parent, Scope* scope) {
sub.decl_line = parent->decl_line;
sub.is_comptime = parent->is_comptime;
sub.c_import = parent->c_import;
sub.is_typeof = parent->is_typeof;
sub.instructions_top = parent->astgen->scratch_inst_len;
sub.break_block = UINT32_MAX;
sub.continue_block = UINT32_MAX;
@@ -595,6 +593,61 @@ static uint32_t addBuiltinValue(
return idx + ZIR_REF_START_INDEX;
}
// Mirrors GenZir.addNodeExtended (AstGen.zig:12765-12779).
// Creates an extended instruction with operand = node offset.
static uint32_t addNodeExtended(
GenZir* gz, uint16_t opcode, uint32_t src_node) {
AstGenCtx* ag = gz->astgen;
ensureInstCapacity(ag, 1);
uint32_t idx = ag->inst_len;
ag->inst_tags[idx] = ZIR_INST_EXTENDED;
ZirInstData data;
data.extended.opcode = opcode;
data.extended.small = 0;
data.extended.operand
= (uint32_t)((int32_t)src_node - (int32_t)gz->decl_node_index);
ag->inst_datas[idx] = data;
ag->inst_len++;
gzAppendInstruction(gz, idx);
return idx + ZIR_REF_START_INDEX;
}
// Mirrors GenZir.addExtendedPayload (AstGen.zig:12781).
// Creates an extended instruction with given payload_index and small=0.
static uint32_t addExtendedPayload(
GenZir* gz, uint16_t opcode, uint32_t payload_index) {
AstGenCtx* ag = gz->astgen;
ensureInstCapacity(ag, 1);
uint32_t idx = ag->inst_len;
ag->inst_tags[idx] = ZIR_INST_EXTENDED;
ZirInstData data;
data.extended.opcode = opcode;
data.extended.small = 0;
data.extended.operand = payload_index;
ag->inst_datas[idx] = data;
ag->inst_len++;
gzAppendInstruction(gz, idx);
return idx + ZIR_REF_START_INDEX;
}
// Mirrors GenZir.addExtendedPayloadSmall (variant with small field).
// Creates an extended instruction with given payload_index and small value.
static uint32_t addExtendedPayloadSmall(
GenZir* gz, uint16_t opcode, uint16_t small, uint32_t payload_index) {
AstGenCtx* ag = gz->astgen;
ensureInstCapacity(ag, 1);
uint32_t idx = ag->inst_len;
ag->inst_tags[idx] = ZIR_INST_EXTENDED;
ZirInstData data;
data.extended.opcode = opcode;
data.extended.small = small;
data.extended.operand = payload_index;
ag->inst_datas[idx] = data;
ag->inst_len++;
gzAppendInstruction(gz, idx);
return idx + ZIR_REF_START_INDEX;
}
// --- Source cursor (AstGen.zig:13335-13359) ---
// Mirrors AstGen.advanceSourceCursor (AstGen.zig:13342).
@@ -2481,8 +2534,10 @@ static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
#define COMPTIME_REASON_FIELD_NAME 42
#define COMPTIME_REASON_COMPTIME_KEYWORD 53
#define COMPTIME_REASON_ARRAY_MUL_FACTOR 22
#define COMPTIME_REASON_COMPILE_ERROR_STRING 19
#define COMPTIME_REASON_SWITCH_ITEM 56
#define COMPTIME_REASON_TUPLE_FIELD_DEFAULT_VALUE 57
#define COMPTIME_REASON_UNION_FIELD_NAME 45
// Mirrors comptimeExpr2 (AstGen.zig:1982).
// Evaluates a node in a comptime block_comptime scope.
@@ -2973,6 +3028,345 @@ static uint32_t builtinCall(
gz, ZIR_INST_FIELD_VAL_NAMED, node, lhs, fname);
return rvalue(gz, rl, result, node);
}
// @sizeOf — simpleUnOpType (AstGen.zig:9381).
if (name_len == 6 && memcmp(source + name_start, "sizeOf", 6) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t operand = typeExpr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_SIZE_OF, operand, node);
return rvalue(gz, rl, result, node);
}
// @alignOf — simpleUnOpType (AstGen.zig:9383).
if (name_len == 7 && memcmp(source + name_start, "alignOf", 7) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t operand = typeExpr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_ALIGN_OF, operand, node);
return rvalue(gz, rl, result, node);
}
// @typeInfo — simpleUnOpType (AstGen.zig:9380).
if (name_len == 8 && memcmp(source + name_start, "typeInfo", 8) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t operand = typeExpr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_TYPE_INFO, operand, node);
return rvalue(gz, rl, result, node);
}
// @compileError — simpleUnOp (AstGen.zig:9386, 9841-9861).
if (name_len == 12
&& memcmp(source + name_start, "compileError", 12) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
ResultLoc operand_rl = { .tag = RL_COERCED_TY,
.data = ZIR_REF_SLICE_CONST_U8_TYPE, .src_node = 0,
.ctx = RI_CTX_NONE };
uint32_t operand = comptimeExpr(gz, scope, operand_rl, nd.lhs,
COMPTIME_REASON_COMPILE_ERROR_STRING);
uint32_t result
= addUnNode(gz, ZIR_INST_COMPILE_ERROR, operand, node);
return rvalue(gz, rl, result, node);
}
// @setEvalBranchQuota — simpleUnOp (AstGen.zig:9387, 9841-9861).
if (name_len == 18
&& memcmp(source + name_start, "setEvalBranchQuota", 18) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
ResultLoc operand_rl = { .tag = RL_COERCED_TY,
.data = ZIR_REF_U32_TYPE, .src_node = 0, .ctx = RI_CTX_NONE };
uint32_t operand = exprRl(gz, scope, operand_rl, nd.lhs);
uint32_t result = addUnNode(
gz, ZIR_INST_SET_EVAL_BRANCH_QUOTA, operand, node);
return rvalue(gz, rl, result, node);
}
// @typeName — simpleUnOp (AstGen.zig:9408, 9841-9861).
if (name_len == 8 && memcmp(source + name_start, "typeName", 8) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result = addUnNode(gz, ZIR_INST_TYPE_NAME, operand, node);
return rvalue(gz, rl, result, node);
}
// @This (AstGen.zig:9371).
if (name_len == 4 && memcmp(source + name_start, "This", 4) == 0)
return rvalue(
gz, rl, addNodeExtended(gz, (uint16_t)ZIR_EXT_THIS, node), node);
// @memmove (AstGen.zig:9648-9654).
if (name_len == 7 && memcmp(source + name_start, "memmove", 7) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t dst = expr(gz, scope, nd.lhs);
uint32_t src_op = expr(gz, scope, nd.rhs);
addPlNodeBin(gz, ZIR_INST_MEMMOVE, node, dst, src_op);
return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
}
// @FieldType (AstGen.zig:9301-9309).
if (name_len == 9 && memcmp(source + name_start, "FieldType", 9) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t ty_inst = typeExpr(gz, scope, nd.lhs);
ResultLoc name_rl = { .tag = RL_COERCED_TY,
.data = ZIR_REF_SLICE_CONST_U8_TYPE, .src_node = 0,
.ctx = RI_CTX_NONE };
uint32_t name_inst = comptimeExpr(
gz, scope, name_rl, nd.rhs, COMPTIME_REASON_FIELD_NAME);
uint32_t result = addPlNodeBin(
gz, ZIR_INST_FIELD_TYPE_REF, node, ty_inst, name_inst);
return rvalue(gz, rl, result, node);
}
// @reduce (AstGen.zig:9539-9548).
if (name_len == 6 && memcmp(source + name_start, "reduce", 6) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t reduce_op_ty
= addBuiltinValue(gz, node, ZIR_BUILTIN_VALUE_REDUCE_OP);
ResultLoc op_rl = { .tag = RL_COERCED_TY,
.data = reduce_op_ty, .src_node = 0, .ctx = RI_CTX_NONE };
uint32_t op = exprRl(gz, scope, op_rl, nd.lhs);
uint32_t scalar = expr(gz, scope, nd.rhs);
uint32_t result
= addPlNodeBin(gz, ZIR_INST_REDUCE, node, op, scalar);
return rvalue(gz, rl, result, node);
}
// @addWithOverflow — overflowArithmetic (AstGen.zig:9550, 10040-10056).
if (name_len == 15
&& memcmp(source + name_start, "addWithOverflow", 15) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = lhs;
ag->extra[ag->extra_len++] = rhs;
uint32_t result = addExtendedPayload(
gz, (uint16_t)ZIR_EXT_ADD_WITH_OVERFLOW, payload_index);
return rvalue(gz, rl, result, node);
}
// @subWithOverflow (AstGen.zig:9551, 10040-10056).
if (name_len == 15
&& memcmp(source + name_start, "subWithOverflow", 15) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = lhs;
ag->extra[ag->extra_len++] = rhs;
uint32_t result = addExtendedPayload(
gz, (uint16_t)ZIR_EXT_SUB_WITH_OVERFLOW, payload_index);
return rvalue(gz, rl, result, node);
}
// @mulWithOverflow (AstGen.zig:9552, 10040-10056).
if (name_len == 15
&& memcmp(source + name_start, "mulWithOverflow", 15) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = lhs;
ag->extra[ag->extra_len++] = rhs;
uint32_t result = addExtendedPayload(
gz, (uint16_t)ZIR_EXT_MUL_WITH_OVERFLOW, payload_index);
return rvalue(gz, rl, result, node);
}
// @shlWithOverflow (AstGen.zig:9553, 10040-10056).
if (name_len == 15
&& memcmp(source + name_start, "shlWithOverflow", 15) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t lhs = expr(gz, scope, nd.lhs);
uint32_t rhs = expr(gz, scope, nd.rhs);
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = lhs;
ag->extra[ag->extra_len++] = rhs;
uint32_t result = addExtendedPayload(
gz, (uint16_t)ZIR_EXT_SHL_WITH_OVERFLOW, payload_index);
return rvalue(gz, rl, result, node);
}
// @alignCast — ptrCast family (AstGen.zig:9464-9469, 8969-9087).
// Simplified: standalone @alignCast uses ptr_cast_full with align_cast flag.
if (name_len == 9
&& memcmp(source + name_start, "alignCast", 9) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
// align_cast flag = bit 1 (FullPtrCastFlags: ptr_cast=0, align_cast=1)
uint16_t flags = 0x02;
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = result_type;
ag->extra[ag->extra_len++] = operand;
uint32_t result = addExtendedPayloadSmall(
gz, (uint16_t)ZIR_EXT_PTR_CAST_FULL, flags, payload_index);
return rvalue(gz, rl, result, node);
}
// @constCast (AstGen.zig:9464-9469, 8969-9087).
if (name_len == 9
&& memcmp(source + name_start, "constCast", 9) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
// const_cast flag = bit 3 (FullPtrCastFlags packed u5)
uint16_t flags = 0x08;
ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = operand;
uint32_t result = addExtendedPayloadSmall(
gz, (uint16_t)ZIR_EXT_PTR_CAST_NO_DEST, flags, payload_index);
return rvalue(gz, rl, result, node);
}
// @volatileCast (AstGen.zig:9464-9469, 8969-9087).
if (name_len == 12
&& memcmp(source + name_start, "volatileCast", 12) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
// volatile_cast flag = bit 4 (FullPtrCastFlags packed u5)
uint16_t flags = 0x10;
ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = operand;
uint32_t result = addExtendedPayloadSmall(
gz, (uint16_t)ZIR_EXT_PTR_CAST_NO_DEST, flags, payload_index);
return rvalue(gz, rl, result, node);
}
// @Type (reify) (AstGen.zig:9426-9428, 9747-9781).
if (name_len == 4 && memcmp(source + name_start, "Type", 4) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t type_info_ty
= addBuiltinValue(gz, node, ZIR_BUILTIN_VALUE_TYPE_INFO);
ResultLoc operand_rl = { .tag = RL_COERCED_TY,
.data = type_info_ty, .src_node = 0, .ctx = RI_CTX_NONE };
uint32_t operand = exprRl(gz, scope, operand_rl, nd.lhs);
// Reify payload: absolute node, operand, src_line.
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = node; // absolute node index
ag->extra[ag->extra_len++] = operand;
ag->extra[ag->extra_len++] = ag->source_line;
// name_strat = .anon = 2
uint32_t result = addExtendedPayloadSmall(
gz, (uint16_t)ZIR_EXT_REIFY, 2, payload_index);
return rvalue(gz, rl, result, node);
}
// @TypeOf (AstGen.zig:9314, 9089-9147) — single-arg case.
if (name_len == 6 && memcmp(source + name_start, "TypeOf", 6) == 0) {
AstData nd = tree->nodes.datas[node];
uint32_t typeof_inst
= makeBlockInst(ag, ZIR_INST_TYPEOF_BUILTIN, gz, node);
GenZir typeof_scope = makeSubBlock(gz, &gz->base);
typeof_scope.is_comptime = false;
typeof_scope.is_typeof = true;
typeof_scope.c_import = false;
uint32_t ty_expr_ref = reachableExpr(
&typeof_scope, &typeof_scope.base, RL_NONE_VAL, nd.lhs, node);
if (!refIsNoReturn(&typeof_scope, ty_expr_ref)) {
addBreak(&typeof_scope, ZIR_INST_BREAK_INLINE,
typeof_inst, ty_expr_ref,
(int32_t)nd.lhs - (int32_t)gz->decl_node_index);
}
setBlockBody(ag, &typeof_scope, typeof_inst);
// typeof_scope unstacked now, add instruction to gz.
gzAppendInstruction(gz, typeof_inst);
return rvalue(gz, rl, typeof_inst + ZIR_REF_START_INDEX, node);
}
// @intFromPtr — simpleUnOp with dbg_stmt (AstGen.zig:9392).
if (name_len == 10
&& memcmp(source + name_start, "intFromPtr", 10) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
uint32_t result
= addUnNode(gz, ZIR_INST_INT_FROM_PTR, operand, node);
return rvalue(gz, rl, result, node);
}
// @intFromBool — simpleUnOp (AstGen.zig:9389).
if (name_len == 11
&& memcmp(source + name_start, "intFromBool", 11) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
uint32_t result
= addUnNode(gz, ZIR_INST_INT_FROM_BOOL, operand, node);
return rvalue(gz, rl, result, node);
}
// @floatFromInt — typeCast (AstGen.zig:9418, 9807-9826).
if (name_len == 12
&& memcmp(source + name_start, "floatFromInt", 12) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_FLOAT_FROM_INT,
node, result_type, operand), node);
}
// @intFromFloat — typeCast (AstGen.zig:9419, 9807-9826).
if (name_len == 12
&& memcmp(source + name_start, "intFromFloat", 12) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_INT_FROM_FLOAT,
node, result_type, operand), node);
}
// @floatCast — typeCast (AstGen.zig:9420, 9807-9826).
if (name_len == 9
&& memcmp(source + name_start, "floatCast", 9) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_FLOAT_CAST,
node, result_type, operand), node);
}
// @errSetCast — extended error_cast (AstGen.zig:9454-9463).
if (name_len == 10
&& memcmp(source + name_start, "errSetCast", 10) == 0) {
emitDbgNode(gz, node);
uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++]
= (uint32_t)((int32_t)node - (int32_t)gz->decl_node_index);
ag->extra[ag->extra_len++] = result_type;
ag->extra[ag->extra_len++] = operand;
uint32_t result = addExtendedPayload(
gz, (uint16_t)ZIR_EXT_ERROR_CAST, payload_index);
return rvalue(gz, rl, result, node);
}
// clang-format on
// TODO: handle other builtins.
@@ -2980,6 +3374,62 @@ static uint32_t builtinCall(
return ZIR_REF_VOID_VALUE;
}
// Mirrors builtinCall for 3+ arg builtins (AST_NODE_BUILTIN_CALL).
// params are in extra_data[params_start..params_end].
static uint32_t builtinCallMultiArg(GenZir* gz, Scope* scope, ResultLoc rl,
uint32_t node, uint32_t params_start, uint32_t params_end) {
AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree;
uint32_t builtin_token = tree->nodes.main_tokens[node];
uint32_t tok_start = tree->tokens.starts[builtin_token];
const char* source = tree->source;
uint32_t name_start = tok_start + 1;
uint32_t name_end = name_start;
while (name_end < tree->source_len
&& ((source[name_end] >= 'a' && source[name_end] <= 'z')
|| (source[name_end] >= 'A' && source[name_end] <= 'Z')
|| source[name_end] == '_')) {
name_end++;
}
uint32_t name_len = name_end - name_start;
const uint32_t* params = tree->extra_data.arr + params_start;
uint32_t param_count = params_end - params_start;
// @unionInit (AstGen.zig:9315, 8922-8942).
if (name_len == 9 && memcmp(source + name_start, "unionInit", 9) == 0
&& param_count == 3) {
uint32_t union_type = typeExpr(gz, scope, params[0]);
ResultLoc name_rl = { .tag = RL_COERCED_TY,
.data = ZIR_REF_SLICE_CONST_U8_TYPE,
.src_node = 0,
.ctx = RI_CTX_NONE };
uint32_t field_name_ref = comptimeExpr(
gz, scope, name_rl, params[1], COMPTIME_REASON_UNION_FIELD_NAME);
// Get field type via field_type_ref.
uint32_t field_type = addPlNodeBin(
gz, ZIR_INST_FIELD_TYPE_REF, node, union_type, field_name_ref);
// Evaluate init value coerced to field type.
ResultLoc init_rl = {
.tag = RL_TY, .data = field_type, .src_node = 0, .ctx = rl.ctx
};
uint32_t init = reachableExpr(gz, scope, init_rl, params[2], node);
// Emit union_init: payload = union_type, init, field_name_ref.
ensureExtraCapacity(ag, 3);
uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = union_type;
ag->extra[ag->extra_len++] = init;
ag->extra[ag->extra_len++] = field_name_ref;
uint32_t result = addPlNodePayloadIndex(
gz, ZIR_INST_UNION_INIT, node, payload_index);
return rvalue(gz, rl, result, node);
}
// TODO: handle other multi-arg builtins.
SET_ERROR(ag);
return ZIR_REF_VOID_VALUE;
}
// --- identifier (AstGen.zig:8282) ---
// Simplified: handles decl_val resolution for container-level declarations.
@@ -4538,6 +4988,9 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
case AST_NODE_BUILTIN_CALL_TWO:
case AST_NODE_BUILTIN_CALL_TWO_COMMA:
return builtinCall(gz, scope, rl, node);
case AST_NODE_BUILTIN_CALL:
case AST_NODE_BUILTIN_CALL_COMMA:
return builtinCallMultiArg(gz, scope, rl, node, nd.lhs, nd.rhs);
case AST_NODE_FIELD_ACCESS:
return fieldAccessExpr(gz, scope, rl, node);
case AST_NODE_IDENTIFIER:
@@ -13249,37 +13702,29 @@ static bool rlExpr(
(void)rlExpr(ag, nd.rhs, block, RL_RI_TYPE_ONLY);
return false;
case AST_NODE_PTR_TYPE: {
AstPtrType pt;
pt.sentinel = tree->extra_data.arr[nd.lhs];
pt.align_node = tree->extra_data.arr[nd.lhs + 1];
pt.addrspace_node = tree->extra_data.arr[nd.lhs + 2];
const AstPtrType* pt
= (const AstPtrType*)(tree->extra_data.arr + nd.lhs);
(void)rlExpr(ag, nd.rhs, block, RL_RI_TYPE_ONLY);
if (pt.sentinel != 0)
(void)rlExpr(ag, pt.sentinel, block, RL_RI_TYPE_ONLY);
if (pt.align_node != 0)
(void)rlExpr(ag, pt.align_node, block, RL_RI_TYPE_ONLY);
if (pt.addrspace_node != 0)
(void)rlExpr(ag, pt.addrspace_node, block, RL_RI_TYPE_ONLY);
if (pt->sentinel != UINT32_MAX)
(void)rlExpr(ag, pt->sentinel, block, RL_RI_TYPE_ONLY);
if (pt->align_node != UINT32_MAX)
(void)rlExpr(ag, pt->align_node, block, RL_RI_TYPE_ONLY);
if (pt->addrspace_node != UINT32_MAX)
(void)rlExpr(ag, pt->addrspace_node, block, RL_RI_TYPE_ONLY);
return false;
}
case AST_NODE_PTR_TYPE_BIT_RANGE: {
AstPtrTypeBitRange pt;
pt.sentinel = tree->extra_data.arr[nd.lhs];
pt.align_node = tree->extra_data.arr[nd.lhs + 1];
pt.addrspace_node = tree->extra_data.arr[nd.lhs + 2];
pt.bit_range_start = tree->extra_data.arr[nd.lhs + 3];
pt.bit_range_end = tree->extra_data.arr[nd.lhs + 4];
const AstPtrTypeBitRange* pt
= (const AstPtrTypeBitRange*)(tree->extra_data.arr + nd.lhs);
(void)rlExpr(ag, nd.rhs, block, RL_RI_TYPE_ONLY);
if (pt.sentinel != 0)
(void)rlExpr(ag, pt.sentinel, block, RL_RI_TYPE_ONLY);
if (pt.align_node != 0)
(void)rlExpr(ag, pt.align_node, block, RL_RI_TYPE_ONLY);
if (pt.addrspace_node != 0)
(void)rlExpr(ag, pt.addrspace_node, block, RL_RI_TYPE_ONLY);
if (pt.bit_range_start != 0) {
(void)rlExpr(ag, pt.bit_range_start, block, RL_RI_TYPE_ONLY);
(void)rlExpr(ag, pt.bit_range_end, block, RL_RI_TYPE_ONLY);
}
if (pt->sentinel != UINT32_MAX)
(void)rlExpr(ag, pt->sentinel, block, RL_RI_TYPE_ONLY);
// align_node is always present for bit_range.
(void)rlExpr(ag, pt->align_node, block, RL_RI_TYPE_ONLY);
if (pt->addrspace_node != UINT32_MAX)
(void)rlExpr(ag, pt->addrspace_node, block, RL_RI_TYPE_ONLY);
(void)rlExpr(ag, pt->bit_range_start, block, RL_RI_TYPE_ONLY);
(void)rlExpr(ag, pt->bit_range_end, block, RL_RI_TYPE_ONLY);
return false;
}

View File

@@ -852,6 +852,24 @@ test "astgen: corpus astgen_test.zig" {
try corpusCheck(gpa, @embedFile("astgen_test.zig"));
}
test "astgen: corpus array_list.zig" {
if (true) return error.SkipZigTest; // TODO: identifier resolution across namespace scopes (fn params in returned struct)
const gpa = std.testing.allocator;
try corpusCheck(gpa, @embedFile("../lib/std/array_list.zig"));
}
test "astgen: corpus multi_array_list.zig" {
if (true) return error.SkipZigTest; // TODO: identifier resolution across namespace scopes
const gpa = std.testing.allocator;
try corpusCheck(gpa, @embedFile("../lib/std/multi_array_list.zig"));
}
test "astgen: corpus Sema.zig" {
if (true) return error.SkipZigTest; // TODO: too large, work on smaller files first
const gpa = std.testing.allocator;
try corpusCheck(gpa, @embedFile("../src/Sema.zig"));
}
test "astgen: enum decl" {
const gpa = std.testing.allocator;
const source: [:0]const u8 = "const E = enum { a, b, c };";