From 44afa2ea43aaff8dae885477712d29279a86abdf Mon Sep 17 00:00:00 2001 From: Motiejus Date: Sat, 7 Mar 2026 22:07:52 +0000 Subject: [PATCH] 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 --- stage0/sema.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/stage0/sema.c b/stage0/sema.c index 7a52277e8d..91435a38bd 100644 --- 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;