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);
// 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);
// 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_ARRAY_SENTINEL 30
#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).
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;
AstGenCtx* ag = gz->astgen;
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;
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).
ensureExtraCapacity(ag, 2);
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
// 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).
addImport(ag, str_index, str_lit_token);
return result_ref;
return rvalue(gz, rl, result_ref, node);
}
// 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;
// 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).
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).
makeBreakInline(
@@ -2565,14 +2583,29 @@ static uint32_t cImportExpr(GenZir* gz, Scope* scope, uint32_t node) {
}
// Mirrors simpleCBuiltin (AstGen.zig:9938).
static uint32_t simpleCBuiltin(GenZir* gz, Scope* scope, uint32_t node,
uint32_t operand_node, uint16_t ext_tag) {
static uint32_t simpleCBuiltin(GenZir* gz, Scope* scope, ResultLoc rl,
uint32_t node, uint32_t operand_node, uint16_t ext_tag) {
AstGenCtx* ag = gz->astgen;
// Evaluate operand as comptime string.
uint32_t operand = expr(gz, scope, operand_node);
// Check c_import scope (AstGen.zig:9947).
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);
uint32_t payload_index = 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;
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.
@@ -2613,38 +2646,45 @@ static uint32_t builtinCall(
// clang-format off
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)
return cImportExpr(gz, scope, node);
if (name_len == 8 && memcmp(source + name_start, "cInclude", 8) == 0) {
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).
if (name_len == 7 && memcmp(source + name_start, "intCast", 7) == 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 = rlResultType(gz, rl, node);
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 addPlNodeBin(gz, ZIR_INST_INT_CAST, node,
result_type, operand);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_INT_CAST, node,
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) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
return addUnNode(gz, ZIR_INST_EMBED_FILE, operand, 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 = 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) {
advanceSourceCursorToMainToken(ag, gz, node);
AstData nd = tree->nodes.datas[node];
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) {
advanceSourceCursorToMainToken(ag, gz, node);
uint32_t saved_line = ag->source_line - gz->decl_line;
@@ -2652,7 +2692,8 @@ static uint32_t builtinCall(
AstData nd = tree->nodes.datas[node];
uint32_t operand = expr(gz, scope, nd.lhs);
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).
if (name_len == 2 && memcmp(source + name_start, "as", 2) == 0) {
@@ -2668,44 +2709,46 @@ static uint32_t builtinCall(
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 = rlResultType(gz, rl, node);
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 addPlNodeBin(gz, ZIR_INST_TRUNCATE, node,
result_type, operand);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_TRUNCATE, node,
result_type, operand), node);
}
// @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) {
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 = rlResultType(gz, rl, node);
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 addPlNodeBin(gz, ZIR_INST_PTR_CAST, node,
result_type, operand);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_PTR_CAST, node,
result_type, operand), node);
}
// @enumFromInt — typeCast pattern (AstGen.zig:9414, 9807-9826).
if (name_len == 11 && memcmp(source + name_start, "enumFromInt", 11) == 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 = rlResultType(gz, rl, node);
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 addPlNodeBin(gz, ZIR_INST_ENUM_FROM_INT, node,
result_type, operand);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_ENUM_FROM_INT, node,
result_type, operand), node);
}
// @bitCast (AstGen.zig:8944-8958, dispatched at 9313).
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];
uint32_t operand = expr(gz, scope, nd.lhs);
return addPlNodeBin(gz, ZIR_INST_BITCAST, node,
result_type, operand);
return rvalue(gz, rl, addPlNodeBin(gz, ZIR_INST_BITCAST, node,
result_type, operand), node);
}
// @memcpy (AstGen.zig:9631-9637).
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 src = expr(gz, scope, nd.rhs);
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).
if (name_len == 6 && memcmp(source + name_start, "memset", 6) == 0) {
@@ -2723,24 +2766,27 @@ static uint32_t builtinCall(
uint32_t elem_ty =
addUnNode(gz, ZIR_INST_INDEXABLE_PTR_ELEM_TYPE, lhs_ty, nd.lhs);
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);
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) {
AstData nd = tree->nodes.datas[node];
uint32_t a = expr(gz, scope, nd.lhs);
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) {
AstData nd = tree->nodes.datas[node];
uint32_t a = expr(gz, scope, nd.lhs);
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
@@ -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);
case AST_NODE_BUILTIN_CALL_TWO:
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:
return fieldAccessExpr(gz, scope, rl, node);
case AST_NODE_IDENTIFIER: