astgen: fix builtinCall rvalue, operand coercion, and result type handling

- Move rvalue calls inside builtinCall (all builtins now call rvalue
  internally, matching upstream) and remove outer rvalue wrap from
  call site
- Add rlResultTypeForCast that errors when no result type is available,
  used by @bitCast, @intCast, @truncate, @ptrCast, @enumFromInt
- Fix @import to compute res_ty from result location instead of
  hardcoding ZIR_REF_NONE
- Fix @embedFile to evaluate operand with coerced_ty=slice_const_u8_type
- Fix @cInclude/simpleCBuiltin to check c_import scope and use
  comptimeExpr with coerced_ty=slice_const_u8_type
- Fix @cImport to pass actual block_result to ensure_result_used instead
  of hardcoded ZIR_REF_VOID_VALUE

Not fixed: Issue 14 (ptrCast nested pointer cast collapsing) — upstream
routes @ptrCast through a dedicated ptrCast() function that walks nested
pointer cast builtins. Currently uses simple typeCast path only.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-13 22:58:47 +00:00
parent 2a1df547d6
commit ece6f69054

132
astgen.c
View File

@@ -1909,6 +1909,16 @@ static uint32_t rlResultType(GenZir* gz, ResultLoc rl, uint32_t node) {
} }
} }
// Mirrors ResultLoc.resultTypeForCast (AstGen.zig:356-368).
// Like rlResultType but errors if no result type is available.
static uint32_t rlResultTypeForCast(GenZir* gz, ResultLoc rl, uint32_t node) {
uint32_t ty = rlResultType(gz, rl, node);
if (ty != 0)
return ty;
SET_ERROR(gz->astgen);
return 0;
}
static bool endsWithNoReturn(GenZir* gz); static bool endsWithNoReturn(GenZir* gz);
// Mirrors Zir.Inst.Tag.isAlwaysVoid (Zir.zig:1343-1608). // Mirrors Zir.Inst.Tag.isAlwaysVoid (Zir.zig:1343-1608).
@@ -2338,6 +2348,8 @@ static uint32_t reachableExprComptime(GenZir* gz, Scope* scope, ResultLoc rl,
static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node); static uint32_t tryResolvePrimitiveIdent(GenZir* gz, uint32_t node);
// SimpleComptimeReason (std.zig:727) — values used in block_comptime payload. // SimpleComptimeReason (std.zig:727) — values used in block_comptime payload.
#define COMPTIME_REASON_C_INCLUDE 9
#define COMPTIME_REASON_C_UNDEF 10
#define COMPTIME_REASON_TYPE 29 #define COMPTIME_REASON_TYPE 29
#define COMPTIME_REASON_ARRAY_SENTINEL 30 #define COMPTIME_REASON_ARRAY_SENTINEL 30
#define COMPTIME_REASON_POINTER_SENTINEL 31 #define COMPTIME_REASON_POINTER_SENTINEL 31
@@ -2502,7 +2514,8 @@ static uint32_t numberLiteral(GenZir* gz, uint32_t node) {
} }
// Mirrors builtinCall (AstGen.zig:9191), @import case (AstGen.zig:9242). // Mirrors builtinCall (AstGen.zig:9191), @import case (AstGen.zig:9242).
static uint32_t builtinCallImport(GenZir* gz, Scope* scope, uint32_t node) { static uint32_t builtinCallImport(
GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
(void)scope; (void)scope;
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
const Ast* tree = ag->tree; const Ast* tree = ag->tree;
@@ -2517,10 +2530,14 @@ static uint32_t builtinCallImport(GenZir* gz, Scope* scope, uint32_t node) {
uint32_t str_index, str_len; uint32_t str_index, str_len;
strLitAsString(ag, str_lit_token, &str_index, &str_len); strLitAsString(ag, str_lit_token, &str_index, &str_len);
// Compute res_ty from result location (AstGen.zig:9257).
uint32_t res_ty_raw = rlResultType(gz, rl, node);
uint32_t res_ty = (res_ty_raw != 0) ? res_ty_raw : ZIR_REF_NONE;
// Write Import payload to extra (Zir.Inst.Import: res_ty, path). // Write Import payload to extra (Zir.Inst.Import: res_ty, path).
ensureExtraCapacity(ag, 2); ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len; uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] = ZIR_REF_NONE; // res_ty = .none ag->extra[ag->extra_len++] = res_ty;
ag->extra[ag->extra_len++] = str_index; // path ag->extra[ag->extra_len++] = str_index; // path
// Create .import instruction with pl_tok data. // Create .import instruction with pl_tok data.
@@ -2532,7 +2549,7 @@ static uint32_t builtinCallImport(GenZir* gz, Scope* scope, uint32_t node) {
// Track import (AstGen.zig:9269). // Track import (AstGen.zig:9269).
addImport(ag, str_index, str_lit_token); addImport(ag, str_index, str_lit_token);
return result_ref; return rvalue(gz, rl, result_ref, node);
} }
// Mirrors cImport (AstGen.zig:10011). // Mirrors cImport (AstGen.zig:10011).
@@ -2548,10 +2565,11 @@ static uint32_t cImportExpr(GenZir* gz, Scope* scope, uint32_t node) {
block_scope.c_import = true; block_scope.c_import = true;
// Use fullBodyExpr to inline unlabeled block body (AstGen.zig:10028). // Use fullBodyExpr to inline unlabeled block body (AstGen.zig:10028).
fullBodyExpr(&block_scope, &block_scope.base, RL_NONE_VAL, body_node); uint32_t block_result = fullBodyExpr(
&block_scope, &block_scope.base, RL_NONE_VAL, body_node);
// ensure_result_used on gz (parent), not block_scope (AstGen.zig:10029). // ensure_result_used on gz (parent), not block_scope (AstGen.zig:10029).
addUnNode(gz, ZIR_INST_ENSURE_RESULT_USED, ZIR_REF_VOID_VALUE, node); addUnNode(gz, ZIR_INST_ENSURE_RESULT_USED, block_result, node);
// break_inline (AstGen.zig:10030-10032). // break_inline (AstGen.zig:10030-10032).
makeBreakInline( makeBreakInline(
@@ -2565,14 +2583,29 @@ static uint32_t cImportExpr(GenZir* gz, Scope* scope, uint32_t node) {
} }
// Mirrors simpleCBuiltin (AstGen.zig:9938). // Mirrors simpleCBuiltin (AstGen.zig:9938).
static uint32_t simpleCBuiltin(GenZir* gz, Scope* scope, uint32_t node, static uint32_t simpleCBuiltin(GenZir* gz, Scope* scope, ResultLoc rl,
uint32_t operand_node, uint16_t ext_tag) { uint32_t node, uint32_t operand_node, uint16_t ext_tag) {
AstGenCtx* ag = gz->astgen; AstGenCtx* ag = gz->astgen;
// Evaluate operand as comptime string. // Check c_import scope (AstGen.zig:9947).
uint32_t operand = expr(gz, scope, operand_node); if (!gz->c_import) {
SET_ERROR(ag);
return ZIR_REF_VOID_VALUE;
}
// Emit extended instruction with UnNode payload (AstGen.zig:9954). // Evaluate operand as comptimeExpr with coerced_ty=slice_const_u8_type
// (AstGen.zig:9948-9954).
uint32_t comptime_reason = (ext_tag == (uint16_t)ZIR_EXT_C_UNDEF)
? COMPTIME_REASON_C_UNDEF
: COMPTIME_REASON_C_INCLUDE;
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, operand_node, comptime_reason);
// Emit extended instruction with UnNode payload (AstGen.zig:9955).
ensureExtraCapacity(ag, 2); ensureExtraCapacity(ag, 2);
uint32_t payload_index = ag->extra_len; uint32_t payload_index = ag->extra_len;
ag->extra[ag->extra_len++] ag->extra[ag->extra_len++]
@@ -2586,7 +2619,7 @@ static uint32_t simpleCBuiltin(GenZir* gz, Scope* scope, uint32_t node,
data.extended.operand = payload_index; data.extended.operand = payload_index;
addInstruction(gz, ZIR_INST_EXTENDED, data); addInstruction(gz, ZIR_INST_EXTENDED, data);
return ZIR_REF_VOID_VALUE; return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
} }
// Mirrors builtinCall (AstGen.zig:9191) dispatch. // Mirrors builtinCall (AstGen.zig:9191) dispatch.
@@ -2613,38 +2646,45 @@ static uint32_t builtinCall(
// clang-format off // clang-format off
if (name_len == 6 && memcmp(source + name_start, "import", 6) == 0) if (name_len == 6 && memcmp(source + name_start, "import", 6) == 0)
return builtinCallImport(gz, scope, node); return builtinCallImport(gz, scope, rl, node);
if (name_len == 7 && memcmp(source + name_start, "cImport", 7) == 0) if (name_len == 7 && memcmp(source + name_start, "cImport", 7) == 0)
return cImportExpr(gz, scope, node); return cImportExpr(gz, scope, node);
if (name_len == 8 && memcmp(source + name_start, "cInclude", 8) == 0) { if (name_len == 8 && memcmp(source + name_start, "cInclude", 8) == 0) {
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
return simpleCBuiltin(gz, scope, node, nd.lhs, (uint16_t)ZIR_EXT_C_INCLUDE); return simpleCBuiltin(gz, scope, rl, node, nd.lhs, (uint16_t)ZIR_EXT_C_INCLUDE);
} }
// @intCast — typeCast pattern (AstGen.zig:9416, 9807-9826). // @intCast — typeCast pattern (AstGen.zig:9416, 9807-9826).
if (name_len == 7 && memcmp(source + name_start, "intCast", 7) == 0) { if (name_len == 7 && memcmp(source + name_start, "intCast", 7) == 0) {
advanceSourceCursorToMainToken(ag, gz, node); advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line; uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column; uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultType(gz, rl, node); uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col); emitDbgStmt(gz, saved_line, saved_col);
return addPlNodeBin(gz, ZIR_INST_INT_CAST, node, return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_INT_CAST, node,
result_type, operand); result_type, operand), node);
} }
// @embedFile (AstGen.zig:9626). // @embedFile — simpleUnOp with coerced_ty (AstGen.zig:9390).
if (name_len == 9 && memcmp(source + name_start, "embedFile", 9) == 0) { if (name_len == 9 && memcmp(source + name_start, "embedFile", 9) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); ResultLoc operand_rl = { .tag = RL_COERCED_TY,
return addUnNode(gz, ZIR_INST_EMBED_FILE, operand, node); .data = ZIR_REF_SLICE_CONST_U8_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_EMBED_FILE, operand, node);
return rvalue(gz, rl, result, node);
} }
// @intFromEnum (AstGen.zig:9478). // @intFromEnum — simpleUnOp (AstGen.zig:9388).
if (name_len == 11 && memcmp(source + name_start, "intFromEnum", 11) == 0) { if (name_len == 11 && memcmp(source + name_start, "intFromEnum", 11) == 0) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
return addUnNode(gz, ZIR_INST_INT_FROM_ENUM, operand, node); uint32_t result = addUnNode(gz, ZIR_INST_INT_FROM_ENUM, operand, node);
return rvalue(gz, rl, result, node);
} }
// @tagName (AstGen.zig:9407) — simpleUnOp with dbg_stmt. // @tagName — simpleUnOp with dbg_stmt (AstGen.zig:9407).
if (name_len == 7 && memcmp(source + name_start, "tagName", 7) == 0) { if (name_len == 7 && memcmp(source + name_start, "tagName", 7) == 0) {
advanceSourceCursorToMainToken(ag, gz, node); advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line; uint32_t saved_line = ag->source_line - gz->decl_line;
@@ -2652,7 +2692,8 @@ static uint32_t builtinCall(
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col); emitDbgStmt(gz, saved_line, saved_col);
return addUnNode(gz, ZIR_INST_TAG_NAME, operand, node); uint32_t result = addUnNode(gz, ZIR_INST_TAG_NAME, operand, node);
return rvalue(gz, rl, result, node);
} }
// @as (AstGen.zig:8909-8920). // @as (AstGen.zig:8909-8920).
if (name_len == 2 && memcmp(source + name_start, "as", 2) == 0) { if (name_len == 2 && memcmp(source + name_start, "as", 2) == 0) {
@@ -2668,44 +2709,46 @@ static uint32_t builtinCall(
advanceSourceCursorToMainToken(ag, gz, node); advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line; uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column; uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultType(gz, rl, node); uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col); emitDbgStmt(gz, saved_line, saved_col);
return addPlNodeBin(gz, ZIR_INST_TRUNCATE, node, return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_TRUNCATE, node,
result_type, operand); result_type, operand), node);
} }
// @ptrCast — typeCast pattern (AstGen.zig:9056, 9807-9826). // @ptrCast — typeCast pattern (AstGen.zig:9056, 9807-9826).
// TODO: Issue 14 — upstream routes through ptrCast() for nested
// pointer cast collapsing. Currently uses simple typeCast path.
if (name_len == 7 && memcmp(source + name_start, "ptrCast", 7) == 0) { if (name_len == 7 && memcmp(source + name_start, "ptrCast", 7) == 0) {
advanceSourceCursorToMainToken(ag, gz, node); advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line; uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column; uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultType(gz, rl, node); uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col); emitDbgStmt(gz, saved_line, saved_col);
return addPlNodeBin(gz, ZIR_INST_PTR_CAST, node, return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_PTR_CAST, node,
result_type, operand); result_type, operand), node);
} }
// @enumFromInt — typeCast pattern (AstGen.zig:9414, 9807-9826). // @enumFromInt — typeCast pattern (AstGen.zig:9414, 9807-9826).
if (name_len == 11 && memcmp(source + name_start, "enumFromInt", 11) == 0) { if (name_len == 11 && memcmp(source + name_start, "enumFromInt", 11) == 0) {
advanceSourceCursorToMainToken(ag, gz, node); advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line; uint32_t saved_line = ag->source_line - gz->decl_line;
uint32_t saved_col = ag->source_column; uint32_t saved_col = ag->source_column;
uint32_t result_type = rlResultType(gz, rl, node); uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
emitDbgStmt(gz, saved_line, saved_col); emitDbgStmt(gz, saved_line, saved_col);
return addPlNodeBin(gz, ZIR_INST_ENUM_FROM_INT, node, return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_ENUM_FROM_INT, node,
result_type, operand); result_type, operand), node);
} }
// @bitCast (AstGen.zig:8944-8958, dispatched at 9313). // @bitCast (AstGen.zig:8944-8958, dispatched at 9313).
if (name_len == 7 && memcmp(source + name_start, "bitCast", 7) == 0) { if (name_len == 7 && memcmp(source + name_start, "bitCast", 7) == 0) {
uint32_t result_type = rlResultType(gz, rl, node); uint32_t result_type = rlResultTypeForCast(gz, rl, node);
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs); uint32_t operand = expr(gz, scope, nd.lhs);
return addPlNodeBin(gz, ZIR_INST_BITCAST, node, return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_BITCAST, node,
result_type, operand); result_type, operand), node);
} }
// @memcpy (AstGen.zig:9631-9637). // @memcpy (AstGen.zig:9631-9637).
if (name_len == 6 && memcmp(source + name_start, "memcpy", 6) == 0) { if (name_len == 6 && memcmp(source + name_start, "memcpy", 6) == 0) {
@@ -2713,7 +2756,7 @@ static uint32_t builtinCall(
uint32_t dst = expr(gz, scope, nd.lhs); uint32_t dst = expr(gz, scope, nd.lhs);
uint32_t src = expr(gz, scope, nd.rhs); uint32_t src = expr(gz, scope, nd.rhs);
addPlNodeBin(gz, ZIR_INST_MEMCPY, node, dst, src); addPlNodeBin(gz, ZIR_INST_MEMCPY, node, dst, src);
return ZIR_REF_VOID_VALUE; return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
} }
// @memset (AstGen.zig:9638-9647). // @memset (AstGen.zig:9638-9647).
if (name_len == 6 && memcmp(source + name_start, "memset", 6) == 0) { if (name_len == 6 && memcmp(source + name_start, "memset", 6) == 0) {
@@ -2723,24 +2766,27 @@ static uint32_t builtinCall(
uint32_t elem_ty = uint32_t elem_ty =
addUnNode(gz, ZIR_INST_INDEXABLE_PTR_ELEM_TYPE, lhs_ty, nd.lhs); addUnNode(gz, ZIR_INST_INDEXABLE_PTR_ELEM_TYPE, lhs_ty, nd.lhs);
ResultLoc val_rl = { ResultLoc val_rl = {
.tag = RL_COERCED_TY, .data = elem_ty, .src_node = 0}; .tag = RL_COERCED_TY, .data = elem_ty, .src_node = 0,
.ctx = RI_CTX_NONE };
uint32_t val = exprRl(gz, scope, val_rl, nd.rhs); uint32_t val = exprRl(gz, scope, val_rl, nd.rhs);
addPlNodeBin(gz, ZIR_INST_MEMSET, node, lhs, val); addPlNodeBin(gz, ZIR_INST_MEMSET, node, lhs, val);
return ZIR_REF_VOID_VALUE; return rvalue(gz, rl, ZIR_REF_VOID_VALUE, node);
} }
// @min (AstGen.zig:9155). // @min (AstGen.zig:9149-9172).
if (name_len == 3 && memcmp(source + name_start, "min", 3) == 0) { if (name_len == 3 && memcmp(source + name_start, "min", 3) == 0) {
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t a = expr(gz, scope, nd.lhs); uint32_t a = expr(gz, scope, nd.lhs);
uint32_t b = expr(gz, scope, nd.rhs); uint32_t b = expr(gz, scope, nd.rhs);
return addPlNodeBin(gz, ZIR_INST_MIN, node, a, b); return rvalue(gz, rl,
addPlNodeBin(gz, ZIR_INST_MIN, node, a, b), node);
} }
// @max (AstGen.zig:9155). // @max (AstGen.zig:9149-9172).
if (name_len == 3 && memcmp(source + name_start, "max", 3) == 0) { if (name_len == 3 && memcmp(source + name_start, "max", 3) == 0) {
AstData nd = tree->nodes.datas[node]; AstData nd = tree->nodes.datas[node];
uint32_t a = expr(gz, scope, nd.lhs); uint32_t a = expr(gz, scope, nd.lhs);
uint32_t b = expr(gz, scope, nd.rhs); uint32_t b = expr(gz, scope, nd.rhs);
return addPlNodeBin(gz, ZIR_INST_MAX, node, a, b); return rvalue(gz, rl,
addPlNodeBin(gz, ZIR_INST_MAX, node, a, b), node);
} }
// clang-format on // clang-format on
@@ -4241,7 +4287,7 @@ static uint32_t exprRl(GenZir* gz, Scope* scope, ResultLoc rl, uint32_t node) {
return rvalue(gz, rl, numberLiteral(gz, node), node); return rvalue(gz, rl, numberLiteral(gz, node), node);
case AST_NODE_BUILTIN_CALL_TWO: case AST_NODE_BUILTIN_CALL_TWO:
case AST_NODE_BUILTIN_CALL_TWO_COMMA: case AST_NODE_BUILTIN_CALL_TWO_COMMA:
return rvalue(gz, rl, builtinCall(gz, scope, rl, node), node); return builtinCall(gz, scope, rl, node);
case AST_NODE_FIELD_ACCESS: case AST_NODE_FIELD_ACCESS:
return fieldAccessExpr(gz, scope, rl, node); return fieldAccessExpr(gz, scope, rl, node);
case AST_NODE_IDENTIFIER: case AST_NODE_IDENTIFIER: