sema: add enum_literal→enum coercion and fix ptr_nav child types

- Add enum_literal + enum → enum peer type resolution in
  semaResolvePeerTypes.
- Add enum_literal → enum coercion in semaCoerce: looks up the literal
  name in the target enum type's ZIR to create an enum_tag entry.
- Make comptime CMP_EQ coerce through peer types before comparing,
  matching the Zig compiler's analyzeCmp → resolvePeerTypes → coerce
  path. This creates enum_tag IP entries as side effects.
- Fix ptr_nav child type: use typeOf(val) instead of always type_type.
  During main analysis, all navs get ptr_type(child=typeOf(val)) +
  ptr_nav entries. During preamble, only type declarations
  (struct/enum/union) get ptr_nav. This matches analyzeNavRefInner.
- Remove debug instrumentation (s_dbg_trace_body, instruction traces).
- Add forward declarations for findEnumFieldByName, getEnumFieldIntVal,
  findEnumDeclForNav, internEnumTag.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-28 01:35:49 +00:00
parent 808f7ef42f
commit ea27893b8c

View File

@@ -370,6 +370,13 @@ static uint32_t semaAddExtra(Sema* sema, uint32_t value) {
// --- Forward declarations ---
static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref);
static uint32_t findEnumFieldByName(
const Zir* zir, uint32_t enum_inst, const char* name);
static InternPoolIndex getEnumFieldIntVal(
const Zir* zir, uint32_t enum_inst, uint32_t field_idx);
static uint32_t findEnumDeclForNav(uint32_t nav_idx, const Zir** out_zir);
static InternPoolIndex internEnumTag(
InternPoolIndex enum_ty, InternPoolIndex int_val);
// --- ZIR instruction handlers ---
// Ported from src/Sema.zig instruction handlers.
@@ -802,6 +809,14 @@ static TypeIndex semaResolvePeerTypes(
return lhs_ty;
return rhs_ty;
}
// enum_literal + enum → enum.
// Ported from src/Sema.zig peer type resolution for enum_literal.
if (lhs_ty == IP_INDEX_ENUM_LITERAL_TYPE
&& sema->ip->items[rhs_ty].tag == IP_KEY_ENUM_TYPE)
return rhs_ty;
if (rhs_ty == IP_INDEX_ENUM_LITERAL_TYPE
&& sema->ip->items[lhs_ty].tag == IP_KEY_ENUM_TYPE)
return lhs_ty;
// Unhandled combination (e.g. void×type in comptime analysis).
// Return lhs as fallback; this path doesn't produce runtime AIR.
return lhs_ty;
@@ -849,6 +864,39 @@ static AirInstRef semaCoerce(
key.data.undef = target_ty;
return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
}
// enum_literal → enum coercion: look up the literal in the enum type
// and create an enum_tag entry.
// Ported from src/Sema.zig coerce .enum → .enum_literal path.
if (src_ty == IP_INDEX_ENUM_LITERAL_TYPE && AIR_REF_IS_IP(ref)
&& sema->ip->items[target_ty].tag == IP_KEY_ENUM_TYPE) {
InternPoolIndex lit_ip = AIR_REF_TO_IP(ref);
uint32_t lit_str = sema->ip->items[lit_ip].data.enum_literal;
const char* lit_name = (const char*)&sema->ip->string_bytes[lit_str];
// Find the nav whose resolved_type matches the enum type.
uint32_t enum_nav = UINT32_MAX;
for (uint32_t ni = 0; ni < ipNavCount(); ni++) {
if (ipGetNav(ni)->resolved_type == target_ty) {
enum_nav = ni;
break;
}
}
if (enum_nav != UINT32_MAX) {
const Zir* enum_zir = NULL;
uint32_t enum_inst = findEnumDeclForNav(enum_nav, &enum_zir);
if (enum_inst != UINT32_MAX && enum_zir != NULL) {
uint32_t fidx
= findEnumFieldByName(enum_zir, enum_inst, lit_name);
if (fidx != UINT32_MAX) {
InternPoolIndex int_val
= getEnumFieldIntVal(enum_zir, enum_inst, fidx);
if (int_val != IP_INDEX_NONE) {
return AIR_REF_FROM_IP(
internEnumTag(target_ty, int_val));
}
}
}
}
}
// Comptime int→int coercion: re-intern with target type.
if (AIR_REF_IS_IP(ref) && isIntegerType(sema->ip, src_ty)
&& isIntegerType(sema->ip, target_ty)) {
@@ -1382,9 +1430,15 @@ static AirInstRef zirArithmetic(
return internComptimeInt(sema, result_ty, r_lo, r_hi);
}
// Comptime equality for non-integer IP values (e.g. enum_literal).
// Comptime equality for non-integer IP values (e.g. enum_tag,
// enum_literal). Coerce through peer types first so that enum_literal
// → enum coercion creates the enum_tag IP entry (matching the Zig
// compiler's analyzeCmp → resolvePeerTypes → coerce path).
if (AIR_REF_IS_IP(lhs) && AIR_REF_IS_IP(rhs)
&& (air_tag == AIR_INST_CMP_EQ || air_tag == AIR_INST_CMP_NEQ)) {
TypeIndex peer_ty = semaResolvePeerTypes(sema, lhs, rhs);
lhs = semaCoerce(sema, block, peer_ty, lhs);
rhs = semaCoerce(sema, block, peer_ty, rhs);
bool eq = (AIR_REF_TO_IP(lhs) == AIR_REF_TO_IP(rhs));
return AIR_REF_FROM_IP((eq == (air_tag == AIR_INST_CMP_EQ))
? IP_INDEX_BOOL_TRUE
@@ -3347,15 +3401,22 @@ static InternPoolIndex resolveZirTypeInst(
if (nav != UINT32_MAX) {
InternPoolIndex result = ensureNavValUpToDate(nav);
if (result != IP_INDEX_NONE && result != IP_INDEX_VOID_TYPE) {
// Create ptr_nav as side effect, matching Zig's
// zirDeclVal → analyzeNavRef → analyzeNavRefInner.
// Create ptr_type + ptr_nav matching analyzeNavRefInner.
// During main analysis: all navs, typeOf(val) child.
// During preamble: type decls only, type_type child.
if (result < s_module_ip->items_len) {
InternPoolKeyTag kt = s_module_ip->items[result].tag;
if (kt == IP_KEY_STRUCT_TYPE || kt == IP_KEY_ENUM_TYPE
|| kt == IP_KEY_UNION_TYPE) {
InternPoolIndex dv_ptr_ty
= internPtrConst(IP_INDEX_TYPE_TYPE);
if (s_in_main_analysis) {
InternPoolIndex val_ty = ipTypeOf(s_module_ip, result);
InternPoolIndex dv_ptr_ty = internPtrConst(val_ty);
(void)internNavPtr(dv_ptr_ty, nav);
} else {
InternPoolKeyTag kt = s_module_ip->items[result].tag;
if (kt == IP_KEY_STRUCT_TYPE || kt == IP_KEY_ENUM_TYPE
|| kt == IP_KEY_UNION_TYPE) {
InternPoolIndex dv_ptr_ty
= internPtrConst(IP_INDEX_TYPE_TYPE);
(void)internNavPtr(dv_ptr_ty, nav);
}
}
}
return result;
@@ -3428,16 +3489,22 @@ static InternPoolIndex resolveZirTypeInst(
if (member_nav != UINT32_MAX) {
InternPoolIndex result = ensureNavValUpToDate(member_nav);
if (result != IP_INDEX_NONE) {
// Create ptr_nav for type declarations only, matching
// analyzeNavRefInner. Value declarations (comptime ints
// etc.) don't get ptr_nav during field_val resolution.
// Create ptr_type + ptr_nav matching analyzeNavRefInner.
// During main analysis, create for ALL navs using
// typeOf(val). During preamble, only for type decls.
if (result < s_module_ip->items_len) {
InternPoolKeyTag kt = s_module_ip->items[result].tag;
if (kt == IP_KEY_STRUCT_TYPE || kt == IP_KEY_ENUM_TYPE
|| kt == IP_KEY_UNION_TYPE) {
InternPoolIndex nav_ptr_ty
= internPtrConst(IP_INDEX_TYPE_TYPE);
if (s_in_main_analysis) {
InternPoolIndex val_ty = ipTypeOf(s_module_ip, result);
InternPoolIndex nav_ptr_ty = internPtrConst(val_ty);
(void)internNavPtr(nav_ptr_ty, member_nav);
} else {
InternPoolKeyTag kt = s_module_ip->items[result].tag;
if (kt == IP_KEY_STRUCT_TYPE || kt == IP_KEY_ENUM_TYPE
|| kt == IP_KEY_UNION_TYPE) {
InternPoolIndex nav_ptr_ty
= internPtrConst(IP_INDEX_TYPE_TYPE);
(void)internNavPtr(nav_ptr_ty, member_nav);
}
}
}
return result;
@@ -14631,12 +14698,12 @@ static bool analyzeBodyInner(
InternPoolIndex val = ensureNavValUpToDate(nav);
if (val != IP_INDEX_NONE) {
resolved = val;
// Create ptr_nav matching Zig's
// analyzeNavRefInner. In Zig, zirDeclVal
// creates ptr_type + ptr_nav for every
// declaration access.
InternPoolIndex pt
= internPtrConst(IP_INDEX_TYPE_TYPE);
// Create ptr_type + ptr_nav matching
// analyzeNavRefInner. Uses typeOf(val) as
// the child type (not always type_type).
InternPoolIndex vt
= ipTypeOf(s_module_ip, val);
InternPoolIndex pt = internPtrConst(vt);
(void)internNavPtr(pt, nav);
}
} else {