commit 44afa2ea43aaff8dae885477712d29279a86abdf (tree)
parent 12cb9899aa2af07a9a70e9a2c19cb4188f76689f
Author: Motiejus <motiejus@jakstys.lt>
Date: Sat, 7 Mar 2026 22:07:52 +0000
sema: handle dead comptime branches in zirFieldPtr/zirCall/zirOptionalPayload
Port upstream Zig behavior for dead comptime branches: when the C sema
evaluates branches that the Zig compiler's comptime evaluator would skip,
return void values instead of crashing. Also add enum_literal fallback
in zirDeclLiteralComptime matching upstream's resolveTypeOrPoison path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
1 file changed, 32 insertions(+), 1 deletion(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -8807,6 +8807,13 @@ static AirInstRef zirCall(
}
if (func_inst == UINT32_MAX) {
+ // Dead comptime branch: the callee reference chain resolved to void
+ // because a prior instruction in this dead branch returned a
+ // placeholder. In upstream Zig, dead branches are never evaluated.
+ // Return void to continue past the dead path.
+ if (block->is_comptime) {
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+ }
UNIMPLEMENTED("zirCall: cannot resolve callee");
}
@@ -10651,6 +10658,15 @@ static AirInstRef zirFieldPtr(Sema* sema, SemaBlock* block, uint32_t inst) {
}
}
if (struct_ty == IP_INDEX_VOID_TYPE) {
+ // Dead comptime branch: the pointer operand resolved to a non-pointer
+ // type (e.g. void). In upstream Zig, fieldPtr checks
+ // object_ptr_ty.zigTypeTag == .pointer and fails otherwise. In the
+ // upstream comptime evaluator, dead branches are never evaluated, so
+ // this case never arises. Our C sema evaluates all branches, so we
+ // return a harmless void value for the dead path.
+ if (block->is_comptime) {
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+ }
UNIMPLEMENTED("zirFieldPtr: unresolved struct type");
}
@@ -10920,7 +10936,17 @@ static AirInstRef zirDeclLiteralComptime(Sema* sema, uint32_t inst) {
}
}
- UNIMPLEMENTED("zirDeclLiteralComptime: field lookup failed");
+ // Ported from Sema.zig zirDeclLiteral: if resolveTypeOrPoison returns
+ // null (LHS cannot be coerced to type → generic_poison), treat the
+ // field name as an enum_literal. This handles dead comptime branches
+ // where the LHS resolved to a non-type value (e.g. void).
+ {
+ InternPoolKey elkey;
+ memset(&elkey, 0, sizeof(elkey));
+ elkey.tag = IP_KEY_ENUM_LITERAL;
+ elkey.data.enum_literal = ipGetOrPutString(sema->ip, field_name);
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, elkey));
+ }
}
// --- mergesAppend ---
@@ -12679,6 +12705,11 @@ static AirInstRef zirOptionalPayload(
// Get the optional child type.
InternPoolKey ty_key = sema->ip->items[operand_ty];
if (ty_key.tag != IP_KEY_OPT_TYPE) {
+ // Dead comptime branch: operand resolved to a non-optional value
+ // (e.g. void). Upstream Zig doesn't evaluate dead branches.
+ if (block->is_comptime) {
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+ }
SEMA_FAIL("expected optional type");
}
InternPoolIndex child_type = ty_key.data.opt_type;