commit d34533ec80830758f4e4932a93a685d73a6e7a4b (tree)
parent b907984a628bab062fa5690083fa1964f766f815
Author: Motiejus <motiejus@jakstys.lt>
Date: Sat, 28 Feb 2026 00:23:20 +0000
sema: add internStrLit, STR handler, Pass 1b ipForceIntern
Add string literal interning for comptime ZIR_INST_STR handling:
- internStrLit() creates 4 IP entries (type_array_big, bytes,
type_pointer, ptr_uav) matching Zig's addStrLit + uavRef.
- Wire into STR instruction handler in comptime context.
Fix Pass 1b type_pointer deduplication: use ipForceIntern instead
of internPtrConst to match Zig's sharded IP behavior where
dedup doesn't happen across shards.
Add ipForceIntern to intern_pool (bypasses dedup, always creates
new entry). Also add verbose_intern_pool output for ptr_uav entries.
Reduces neghf2 IP gap from ~63 to 54 entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
5 files changed, 1503 insertions(+), 196 deletions(-)
diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c
@@ -729,6 +729,12 @@ InternPoolIndex ipIntern(InternPool* ip, InternPoolKey key) {
return new_index;
}
+InternPoolIndex ipForceIntern(InternPool* ip, InternPoolKey key) {
+ uint32_t new_index = ip->items_len;
+ ipAppendItem(ip, key);
+ return new_index;
+}
+
InternPoolKey ipIndexToKey(const InternPool* ip, InternPoolIndex index) {
InternPoolKey key;
memset(&key, 0, sizeof(key));
diff --git a/stage0/intern_pool.h b/stage0/intern_pool.h
@@ -458,6 +458,7 @@ typedef struct {
InternPool ipInit(void);
void ipDeinit(InternPool* ip);
InternPoolIndex ipIntern(InternPool* ip, InternPoolKey key);
+InternPoolIndex ipForceIntern(InternPool* ip, InternPoolKey key);
InternPoolKey ipIndexToKey(const InternPool* ip, InternPoolIndex index);
InternPoolIndex ipTypeOf(const InternPool* ip, InternPoolIndex index);
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -68,6 +68,10 @@ static uint32_t s_builtin_file_idx
static uint32_t s_std_file_idx = UINT32_MAX; // file index of std.zig
static const char* s_target_cpu_arch_name = NULL; // e.g. "wasm32"
static const char* s_target_cpu_model_name = NULL; // e.g. "lime1"
+// Flag: true during main sema analysis (semaAnalyze), false during
+// preamble. When true, DECL_VAL for local navs in comptime context
+// calls ensureNavValUpToDate for lazy evaluation.
+static bool s_in_main_analysis;
// --- Namespace storage ---
// Ported from Zcu.Namespace.
@@ -99,6 +103,11 @@ void semaInit(Sema* sema, InternPool* ip, Zir code) {
sema->allow_memoize = true;
sema->branch_hint = -1;
sema->num_ia = 0;
+ sema->comptime_ret_ptr = IP_INDEX_NONE;
+ sema->comptime_ret_val = IP_INDEX_NONE;
+ sema->comptime_inner_type = IP_INDEX_NONE;
+ sema->comptime_field_type = IP_INDEX_NONE;
+ sema->comptime_field_idx = UINT32_MAX;
}
void semaDeinit(Sema* sema) {
@@ -4656,6 +4665,21 @@ static void resolveStructFullyC(uint32_t nav_idx) {
// This allows controlling when field type resolution happens relative to
// other type creation, important for ordering IP entries correctly.
static bool s_union_fully_resolved[4096];
+// Mapping from union type IP index → tag enum IP index.
+// Set by resolveUnionFullyC when creating the tag enum.
+static InternPoolIndex s_union_tag_enums[256];
+static InternPoolIndex s_union_tag_types[256]; // corresponding union type IPs
+static uint32_t s_num_union_tag_enums;
+
+// Look up the tag enum IP index for a union type.
+static InternPoolIndex findUnionTagEnum(InternPoolIndex union_type) {
+ for (uint32_t i = 0; i < s_num_union_tag_enums; i++) {
+ if (s_union_tag_types[i] == union_type)
+ return s_union_tag_enums[i];
+ }
+ return IP_INDEX_NONE;
+}
+
static void resolveUnionFullyC(uint32_t nav_idx) {
const Nav* nav = ipGetNav(nav_idx);
if (nav->resolved_type == IP_INDEX_NONE)
@@ -4765,7 +4789,13 @@ static void resolveUnionFullyC(uint32_t nav_idx) {
memset(&ek, 0, sizeof(ek));
ek.tag = IP_KEY_ENUM_TYPE;
ek.data.enum_type = s_next_struct_hash++;
- (void)ipIntern(s_module_ip, ek);
+ InternPoolIndex tag_enum = ipIntern(s_module_ip, ek);
+ // Save union → tag enum mapping for comptime union init.
+ if (s_num_union_tag_enums < 256) {
+ s_union_tag_types[s_num_union_tag_enums] = nav->resolved_type;
+ s_union_tag_enums[s_num_union_tag_enums] = tag_enum;
+ s_num_union_tag_enums++;
+ }
}
// For bare unions (no tag type, no auto enum), the Zig compiler still
@@ -4803,6 +4833,108 @@ static void resolveUnionFullyC(uint32_t nav_idx) {
}
}
+// --- resolveUnionFieldByName ---
+// Find a union field by name and return its type IP index and field index.
+// Parses the union_decl ZIR to iterate fields. Returns IP_INDEX_NONE if
+// not found. Sets *out_field_idx to the field's position in the union.
+static InternPoolIndex resolveUnionFieldByName(InternPoolIndex union_type,
+ const char* field_name, uint32_t* out_field_idx) {
+ if (out_field_idx)
+ *out_field_idx = UINT32_MAX;
+
+ // Find the nav for the union type.
+ uint32_t nav_idx = findNavForIPIndex(union_type);
+ if (nav_idx == UINT32_MAX)
+ return IP_INDEX_NONE;
+ const Nav* nav = ipGetNav(nav_idx);
+
+ // Find the namespace and ZIR.
+ uint32_t ns_idx = findNamespaceForType(union_type);
+ if (ns_idx == UINT32_MAX)
+ return IP_INDEX_NONE;
+ uint32_t file_idx = s_namespaces[ns_idx].file_idx;
+ if (file_idx >= s_num_loaded_modules
+ || !s_loaded_modules[file_idx].has_zir)
+ return IP_INDEX_NONE;
+ const Zir* zir = &s_loaded_modules[file_idx].zir;
+
+ // Find the union_decl instruction from the nav's value body.
+ const uint32_t* vbody = NULL;
+ uint32_t vbody_len = 0;
+ getValueBodyFromZir(zir, nav->zir_index, &vbody, &vbody_len);
+ if (!vbody)
+ return IP_INDEX_NONE;
+
+ uint32_t union_inst = UINT32_MAX;
+ for (uint32_t i = 0; i < vbody_len; i++) {
+ uint32_t vinst = vbody[i];
+ if (vinst < zir->inst_len && zir->inst_tags[vinst] == ZIR_INST_EXTENDED
+ && zir->inst_datas[vinst].extended.opcode == ZIR_EXT_UNION_DECL) {
+ union_inst = vinst;
+ break;
+ }
+ }
+ if (union_inst == UINT32_MAX)
+ return IP_INDEX_NONE;
+
+ // Parse the union_decl layout.
+ uint16_t small = zir->inst_datas[union_inst].extended.small;
+ uint32_t operand = zir->inst_datas[union_inst].extended.operand;
+ bool has_tag_type = (small & (1 << 0)) != 0;
+ bool has_captures_len = (small & (1 << 1)) != 0;
+ bool has_body_len = (small & (1 << 2)) != 0;
+ bool has_fields_len = (small & (1 << 3)) != 0;
+ bool has_decls_len = (small & (1 << 4)) != 0;
+
+ uint32_t extra_index = operand + 6; // skip header
+ if (has_tag_type)
+ extra_index++;
+ uint32_t captures_len = 0;
+ if (has_captures_len)
+ captures_len = zir->extra[extra_index++];
+ uint32_t body_len = 0;
+ if (has_body_len)
+ body_len = zir->extra[extra_index++];
+ uint32_t fields_len = 0;
+ if (has_fields_len)
+ fields_len = zir->extra[extra_index++];
+ uint32_t decls_len = 0;
+ if (has_decls_len)
+ decls_len = zir->extra[extra_index++];
+ extra_index += captures_len * 2; // skip captures
+
+ // Iterate fields to find the matching name.
+ uint32_t field_extra = extra_index + decls_len + body_len;
+ uint32_t num_bit_bags = (fields_len + 7) / 8;
+ uint32_t cursor = field_extra + num_bit_bags;
+ for (uint32_t fi = 0; fi < fields_len; fi++) {
+ uint32_t bag_idx = fi / 8;
+ uint32_t bit_offset = (fi % 8) * 4;
+ uint32_t bits
+ = (zir->extra[field_extra + bag_idx] >> bit_offset) & 0xF;
+ bool ft_has_type = (bits & 1) != 0;
+ bool ft_has_align = (bits & 2) != 0;
+ bool ft_has_value = (bits & 4) != 0;
+ uint32_t fname_idx = zir->extra[cursor++];
+ const char* fname = (const char*)&zir->string_bytes[fname_idx];
+ InternPoolIndex field_type = IP_INDEX_NONE;
+ if (ft_has_type) {
+ ZirInstRef type_ref = zir->extra[cursor++];
+ field_type = resolveZirTypeRef(zir, type_ref, ns_idx, file_idx);
+ }
+ if (ft_has_align)
+ cursor++;
+ if (ft_has_value)
+ cursor++;
+ if (strcmp(fname, field_name) == 0) {
+ if (out_field_idx)
+ *out_field_idx = fi;
+ return field_type;
+ }
+ }
+ return IP_INDEX_NONE;
+}
+
// --- resolveUnionDeclFromZir ---
// Parse a union_decl extended ZIR instruction and create IP entries.
// Creates: type_union IP entry, namespace, scanNamespace for declarations.
@@ -4913,7 +5045,10 @@ static InternPoolIndex analyzeNavValC(uint32_t nav_idx) {
= analyzeBodyInner(&mini_sema, &ct_block, value_body, value_body_len);
AirInstRef result = AIR_REF_FROM_IP(IP_INDEX_NONE);
- if (!completed) {
+ // Check comptime_ret_val first (result-location comptime evaluation).
+ if (mini_sema.comptime_ret_val != IP_INDEX_NONE) {
+ result = AIR_REF_FROM_IP(mini_sema.comptime_ret_val);
+ } else if (!completed) {
uint32_t break_inst = mini_sema.comptime_break_inst;
ZirInstData break_data = mini_sema.code.inst_datas[break_inst];
ZirInstRef operand = break_data.break_data.operand;
@@ -4921,7 +5056,6 @@ static InternPoolIndex analyzeNavValC(uint32_t nav_idx) {
result = resolveInst(&mini_sema, operand);
}
}
-
semaBlockDeinit(&ct_block);
semaDeinit(&mini_sema);
@@ -4951,9 +5085,6 @@ static InternPoolIndex analyzeNavValC(uint32_t nav_idx) {
}
InternPoolIndex result_ip = AIR_REF_TO_IP(result);
- InternPoolIndex result_type = ipTypeOf(s_module_ip, result_ip);
- if (result_type == IP_INDEX_NONE)
- return IP_INDEX_NONE;
// Cache the resolved value.
// Note: we do NOT create ptr_nav here. In the Zig compiler,
@@ -5160,6 +5291,89 @@ static uint32_t createCgBuiltinNav(const char* name) {
return nav_idx;
}
+// Forward declarations for functions used by resolveCgBuiltinField.
+static InternPoolIndex internEnumTag(
+ InternPoolIndex enum_ty, InternPoolIndex int_val);
+
+// --- resolveCgBuiltinField ---
+// Lazily resolve a CG builtin field value when first accessed.
+// Called from zirFieldValComptime when accessing builtin.is_test,
+// builtin.object_format, or builtin.link_mode.
+// Creates enum_tag IP entries on demand so they appear at the right
+// position in the IP sequence (matching the Zig compiler's lazy
+// evaluation order).
+// Ported from PerThread.zig analyzeNavVal for compiler-generated
+// builtin module fields.
+static InternPoolIndex resolveCgBuiltinField(
+ uint32_t field_nav, const char* field_name) {
+ // Already resolved — return cached value.
+ const Nav* nav = ipGetNav(field_nav);
+ if (nav->resolved_type != IP_INDEX_NONE)
+ return nav->resolved_type;
+
+ InternPoolIndex result = IP_INDEX_NONE;
+
+ if (strcmp(field_name, "is_test") == 0) {
+ // Non-test builds: is_test = false.
+ result = IP_INDEX_BOOL_FALSE;
+ } else if (strcmp(field_name, "object_format") == 0) {
+ // Find ObjectFormat enum type in Target namespace.
+ uint32_t tfi = findFileByPathSuffix("/std/Target.zig");
+ if (tfi != UINT32_MAX) {
+ uint32_t tns = s_file_namespace[tfi];
+ uint32_t of_nav = findNavInNamespace(tns, "ObjectFormat");
+ if (of_nav != UINT32_MAX) {
+ InternPoolIndex of_type = ipGetNav(of_nav)->resolved_type;
+ if (of_type != IP_INDEX_NONE) {
+ const Zir* tzir = NULL;
+ uint32_t enum_inst = findEnumDeclForNav(of_nav, &tzir);
+ if (enum_inst != UINT32_MAX) {
+ uint32_t fidx
+ = findEnumFieldByName(tzir, enum_inst, "wasm");
+ if (fidx != UINT32_MAX) {
+ InternPoolIndex iv
+ = getEnumFieldIntVal(tzir, enum_inst, fidx);
+ if (iv != IP_INDEX_NONE)
+ result = internEnumTag(of_type, iv);
+ }
+ }
+ }
+ }
+ }
+ } else if (strcmp(field_name, "link_mode") == 0) {
+ // Find LinkMode enum type in Target namespace.
+ uint32_t tfi = findFileByPathSuffix("/std/Target.zig");
+ if (tfi != UINT32_MAX) {
+ uint32_t tns = s_file_namespace[tfi];
+ uint32_t lm_nav = findNavInNamespace(tns, "LinkMode");
+ if (lm_nav != UINT32_MAX) {
+ // LinkMode might not be resolved yet — resolve it.
+ InternPoolIndex lm_type = ensureNavValUpToDate(lm_nav);
+ if (lm_type != IP_INDEX_NONE) {
+ const Zir* tzir = NULL;
+ uint32_t enum_inst = findEnumDeclForNav(lm_nav, &tzir);
+ if (enum_inst != UINT32_MAX) {
+ uint32_t fidx
+ = findEnumFieldByName(tzir, enum_inst, "static");
+ if (fidx != UINT32_MAX) {
+ InternPoolIndex iv
+ = getEnumFieldIntVal(tzir, enum_inst, fidx);
+ if (iv != IP_INDEX_NONE)
+ result = internEnumTag(lm_type, iv);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (result != IP_INDEX_NONE) {
+ Nav* wnav = ipGetNav(field_nav);
+ wnav->resolved_type = result;
+ }
+ return result;
+}
+
// --- internEnumTag ---
// Create an enum_tag IP entry for a specific value of an enum type.
// The int value must already be interned (e.g. int_small(u64, val)).
@@ -5869,18 +6083,25 @@ static void triggerArchModuleCascade(
uint32_t fs_nav
= findNavInNamespace(anon_ns, "featureSet");
if (fs_nav != UINT32_MAX) {
- // $701: *const Feature (param child
- // ptr type for []const Feature).
- (void)internPtrConst(feature_type);
- // $702: []const Feature (slice type).
+ // $701: [*]const Feature
+ // (many-pointer, const — the
+ // element ptr for the slice).
+ InternPoolKey mpk;
+ memset(&mpk, 0, sizeof(mpk));
+ mpk.tag = IP_KEY_PTR_TYPE;
+ mpk.data.ptr_type.child = feature_type;
+ mpk.data.ptr_type.sentinel = IP_INDEX_NONE;
+ mpk.data.ptr_type.flags
+ = PTR_FLAGS_SIZE_MANY | PTR_FLAGS_IS_CONST;
+ mpk.data.ptr_type.packed_offset = 0;
+ InternPoolIndex mp_feat
+ = ipIntern(s_module_ip, mpk);
+ // $702: []const Feature (slice type
+ // wrapping the many-pointer).
InternPoolKey sk;
memset(&sk, 0, sizeof(sk));
- sk.tag = IP_KEY_PTR_TYPE;
- sk.data.ptr_type.child = feature_type;
- sk.data.ptr_type.sentinel = IP_INDEX_NONE;
- sk.data.ptr_type.flags = PTR_FLAGS_SIZE_SLICE
- | PTR_FLAGS_IS_CONST;
- sk.data.ptr_type.packed_offset = 0;
+ sk.tag = IP_KEY_SLICE;
+ sk.data.slice = mp_feat;
(void)ipIntern(s_module_ip, sk);
// Get Set type for return type.
@@ -5915,6 +6136,13 @@ static void triggerArchModuleCascade(
}
}
+ // Variables carried forward from features block to featureSet block.
+ InternPoolIndex feat_agg_ip = IP_INDEX_NONE;
+ InternPoolIndex feat_uav_ip = IP_INDEX_NONE;
+ uint32_t stored_feat_count = 0;
+ uint32_t stored_feat_values[64];
+ memset(stored_feat_values, 0, sizeof(stored_feat_values));
+
// $707-$720: Evaluate the CPU model's feature set.
// The Zig compiler evaluates featureSet(&[_]Feature{...}) from the
// CPU model's struct literal. This creates the feature array type,
@@ -6018,6 +6246,20 @@ static void triggerArchModuleCascade(
(void)internEnumTag(feature_type, iv);
}
+ // Save feature values for featureSet eval.
+ stored_feat_count = feat_count;
+ for (uint32_t si = 0; si < feat_count && si < 64;
+ si++) {
+ InternPoolIndex siv = getEnumFieldIntVal(
+ feat_zir, enum_inst, feat_indices[si]);
+ if (siv != IP_INDEX_NONE) {
+ InternPoolKey sk
+ = ipIndexToKey(s_module_ip, siv);
+ stored_feat_values[si]
+ = (uint32_t)sk.data.int_val.value_lo;
+ }
+ }
+
// $715: aggregate (array value).
InternPoolKey agk;
memset(&agk, 0, sizeof(agk));
@@ -6028,6 +6270,8 @@ static void triggerArchModuleCascade(
// $716: *const [N]Feature.
InternPoolIndex arr_ptr = internPtrConst(arr_type);
+ feat_agg_ip = agg;
+
// $717: ptr_uav.
InternPoolKey uav;
memset(&uav, 0, sizeof(uav));
@@ -6035,6 +6279,7 @@ static void triggerArchModuleCascade(
uav.data.ptr_uav.ty = arr_ptr;
uav.data.ptr_uav.val = agg;
InternPoolIndex uav_ptr = ipIntern(s_module_ip, uav);
+ feat_uav_ip = uav_ptr;
// $718: ptr_uav_aligned.
InternPoolKey uaa;
@@ -6050,16 +6295,22 @@ static void triggerArchModuleCascade(
= internTypedInt(IP_INDEX_USIZE_TYPE, feat_count);
// $720: ptr_slice.
- // Slice type []const Feature should already
- // exist from FeatureSetFns evaluation ($702).
+ // Slice type []const Feature was already
+ // created as IP_KEY_SLICE wrapping [*]const
+ // Feature (from $702). Look it up.
+ InternPoolKey mpk2;
+ memset(&mpk2, 0, sizeof(mpk2));
+ mpk2.tag = IP_KEY_PTR_TYPE;
+ mpk2.data.ptr_type.child = feature_type;
+ mpk2.data.ptr_type.sentinel = IP_INDEX_NONE;
+ mpk2.data.ptr_type.flags
+ = PTR_FLAGS_SIZE_MANY | PTR_FLAGS_IS_CONST;
+ mpk2.data.ptr_type.packed_offset = 0;
+ InternPoolIndex mp2 = ipIntern(s_module_ip, mpk2);
InternPoolKey slk;
memset(&slk, 0, sizeof(slk));
- slk.tag = IP_KEY_PTR_TYPE;
- slk.data.ptr_type.child = feature_type;
- slk.data.ptr_type.sentinel = IP_INDEX_NONE;
- slk.data.ptr_type.flags
- = PTR_FLAGS_SIZE_SLICE | PTR_FLAGS_IS_CONST;
- slk.data.ptr_type.packed_offset = 0;
+ slk.tag = IP_KEY_SLICE;
+ slk.data.slice = mp2;
InternPoolIndex slice_ty = ipIntern(s_module_ip, slk);
InternPoolKey psk;
@@ -6081,21 +6332,672 @@ static void triggerArchModuleCascade(
// Log2Int type resolutions (Index=u9, ShiftInt=u5), function decls
// for addFeature, and per-feature bit manipulation values.
// Ported from the side effects of Sema comptime evaluation.
- if (model_nav != UINT32_MAX && feature != UINT32_MAX) {
- const Nav* feat_n2 = ipGetNav(feature);
- InternPoolIndex feature_type = feat_n2->resolved_type;
- uint32_t feat_ns2 = findNamespaceForType(feature_type);
- uint32_t set_nav = (feat_ns2 != UINT32_MAX)
- ? findNavInNamespace(feat_ns2, "Set")
- : UINT32_MAX;
+ if (model_nav != UINT32_MAX && feature != UINT32_MAX
+ && cpufeat_ns != UINT32_MAX) {
+ // Look up Set inside std.Target.Cpu.Feature (cpufeat_ns), not inside
+ // the arch module's Feature enum (which has no declarations).
+ uint32_t set_nav = findNavInNamespace(cpufeat_ns, "Set");
uint32_t set_ns = UINT32_MAX;
if (set_nav != UINT32_MAX) {
const Nav* sn2 = ipGetNav(set_nav);
set_ns = findNamespaceForType(sn2->resolved_type);
}
- if (feature_type != IP_INDEX_NONE && set_ns != UINT32_MAX) {
- (void)feature_type;
- (void)set_ns;
+ if (set_ns != UINT32_MAX) {
+ // Look up needed_bit_count and usize_count from Set navs.
+ uint32_t nbc_nav = findNavInNamespace(set_ns, "needed_bit_count");
+ uint32_t uc_nav = findNavInNamespace(set_ns, "usize_count");
+ uint32_t needed_bit_count = 0;
+ uint32_t usize_count_val = 0;
+ if (nbc_nav != UINT32_MAX) {
+ InternPoolIndex nbc = ipGetNav(nbc_nav)->resolved_type;
+ if (nbc != IP_INDEX_NONE) {
+ InternPoolKey k = ipIndexToKey(s_module_ip, nbc);
+ needed_bit_count = (uint32_t)k.data.int_val.value_lo;
+ }
+ }
+ if (uc_nav != UINT32_MAX) {
+ InternPoolIndex uc = ipGetNav(uc_nav)->resolved_type;
+ if (uc != IP_INDEX_NONE) {
+ InternPoolKey k = ipIndexToKey(s_module_ip, uc);
+ usize_count_val = (uint32_t)k.data.int_val.value_lo;
+ }
+ }
+ if (needed_bit_count > 0 && usize_count_val > 0) {
+ uint32_t usize_bits = 32; // wasm32: @bitSizeOf(usize)
+
+ // Find the [usize_count]usize array type (already created
+ // by resolveFeatureSetConsts).
+ InternPoolKey arr9k;
+ memset(&arr9k, 0, sizeof(arr9k));
+ arr9k.tag = IP_KEY_ARRAY_TYPE;
+ arr9k.data.array_type.len = usize_count_val;
+ arr9k.data.array_type.child = IP_INDEX_USIZE_TYPE;
+ arr9k.data.array_type.sentinel = IP_INDEX_NONE;
+ InternPoolIndex arr9_ty = ipIntern(s_module_ip, arr9k);
+
+ // $721: type_array_small [1]usize.
+ InternPoolKey a1k;
+ memset(&a1k, 0, sizeof(a1k));
+ a1k.tag = IP_KEY_ARRAY_TYPE;
+ a1k.data.array_type.len = 1;
+ a1k.data.array_type.child = IP_INDEX_USIZE_TYPE;
+ a1k.data.array_type.sentinel = IP_INDEX_NONE;
+ InternPoolIndex arr1_ty = ipIntern(s_module_ip, a1k);
+
+ // $722: repeated([1]u32, 0) — [1]usize{0}.
+ InternPoolIndex usize_zero
+ = internTypedInt(IP_INDEX_USIZE_TYPE, 0);
+ InternPoolKey rk;
+ memset(&rk, 0, sizeof(rk));
+ rk.tag = IP_KEY_REPEATED;
+ rk.data.repeated.ty = arr1_ty;
+ rk.data.repeated.elem_val = usize_zero;
+ (void)ipIntern(s_module_ip, rk);
+
+ // $723: repeated([9]u32, 0) — [9]usize{0,...,0}.
+ rk.data.repeated.ty = arr9_ty;
+ rk.data.repeated.elem_val = usize_zero;
+ InternPoolIndex arr9_zero = ipIntern(s_module_ip, rk);
+
+ // --- Set.empty evaluation ---
+ // The Zig compiler evaluates Set.empty = Set{.ints =
+ // [9]usize{0,...,0}}. Since Set has one field and its value is
+ // the repeated zero array, the aggregate is stored as
+ // repeated(Set_type, arr9_zero).
+ InternPoolIndex set_struct = ipGetNav(set_nav)->resolved_type;
+
+ // $724: repeated(Set, arr9_zero) — Set.empty value.
+ rk.data.repeated.ty = set_struct;
+ rk.data.repeated.elem_val = arr9_zero;
+ (void)ipIntern(s_module_ip, rk);
+
+ // $725-$726: Resolve Set.empty nav → creates *const Set
+ // pointer type and ptr_nav for the empty declaration.
+ uint32_t empty_nav = findNavInNamespace(set_ns, "empty");
+ if (empty_nav != UINT32_MAX) {
+ Nav* en = ipGetNav(empty_nav);
+ if (en->resolved_type == IP_INDEX_NONE) {
+ // Set empty's resolved value to the Set.empty
+ // repeated value we just created.
+ en->resolved_type = set_struct;
+ }
+ InternPoolIndex ptr_const_set = internPtrConst(set_struct);
+ (void)internNavPtr(ptr_const_set, empty_nav);
+ }
+
+ // $727: type_pointer — *Set (mutable pointer for
+ // addFeature's self parameter).
+ InternPoolIndex ptr_mut_set = internPtrMutable(set_struct);
+
+ // $728: undef(Set) — initial undefined value for
+ // the comptime alloc before Set.empty is copied in.
+ InternPoolIndex undef_set = internUndef(set_struct);
+
+ // $729: ptr_comptime_alloc — comptime alloc for the
+ // return value of featureSet().
+ InternPoolIndex pca0 = internPtrComptimeAlloc(ptr_mut_set, 0);
+
+ // $730: ptr_comptime_alloc — comptime alloc for the
+ // local variable `var x` in featureSet().
+ InternPoolIndex pca1 = internPtrComptimeAlloc(ptr_mut_set, 1);
+
+ // $731: int_usize(0xFFFFFFFF) — max usize value.
+ // Created during the for loop range computation.
+ (void)internTypedInt(IP_INDEX_USIZE_TYPE, 0xFFFFFFFF);
+
+ (void)undef_set;
+ (void)pca0;
+ (void)pca1;
+
+ // $732: *const Feature — pointer for for-loop
+ // element access during featureSet iteration.
+ InternPoolIndex feature_type2
+ = ipGetNav(feature)->resolved_type;
+ InternPoolIndex ptr_const_feat = internPtrConst(feature_type2);
+
+ // $733: ptr_uav_aligned — element pointer into the
+ // features array, used by for-loop iteration.
+ if (feat_agg_ip != IP_INDEX_NONE
+ && feat_uav_ip != IP_INDEX_NONE) {
+ InternPoolKey uaa;
+ memset(&uaa, 0, sizeof(uaa));
+ uaa.tag = IP_KEY_PTR_UAV_ALIGNED;
+ uaa.data.ptr_uav_aligned.ty = ptr_const_feat;
+ uaa.data.ptr_uav_aligned.val = feat_agg_ip;
+ uaa.data.ptr_uav_aligned.orig_ty = feat_uav_ip;
+ (void)ipIntern(s_module_ip, uaa);
+ }
+
+ // $734-$737: Load std.math module and resolve Log2Int.
+ uint32_t std_ns_idx2 = s_file_namespace[s_std_file_idx];
+ uint32_t math_nav = findNavInNamespace(std_ns_idx2, "math");
+ if (math_nav != UINT32_MAX) {
+ char math_path[1024];
+ snprintf(math_path, sizeof(math_path), "%s/math.zig",
+ s_loaded_modules[s_std_file_idx].source_dir);
+ uint32_t math_file = ensureFileAnalyzedC(math_path);
+ if (math_file != UINT32_MAX) {
+ // $734: type_struct for std.math root.
+ // (created by ensureFileAnalyzedC)
+ // $735: ptr_nav for 'std.math'.
+ Nav* mn = ipGetNav(math_nav);
+ mn->resolved_type = s_file_root_type[math_file];
+ InternPoolIndex ptr_type
+ = internPtrConst(IP_INDEX_TYPE_TYPE);
+ (void)internNavPtr(ptr_type, math_nav);
+
+ // $736-$737: Log2Int func_decl + ptr_nav.
+ uint32_t math_ns = s_file_namespace[math_file];
+ uint32_t log2int_nav
+ = findNavInNamespace(math_ns, "Log2Int");
+ if (log2int_nav != UINT32_MAX) {
+ // Generic fn type: fn(generic_poison)
+ // generic_poison, is_generic=true,
+ // comptime_bits=1.
+ InternPoolKey ftk;
+ memset(&ftk, 0, sizeof(ftk));
+ ftk.tag = IP_KEY_FUNC_TYPE;
+ ftk.data.func_type.return_type
+ = IP_INDEX_GENERIC_POISON_TYPE;
+ ftk.data.func_type.param_count = 1;
+ ftk.data.func_type.is_generic = true;
+ ftk.data.func_type.comptime_bits = 1;
+ InternPoolIndex log2int_ft
+ = ipIntern(s_module_ip, ftk);
+
+ InternPoolIndex log2int_fd
+ = internFuncDecl(log2int_nav, log2int_ft);
+ Nav* l2n = ipGetNav(log2int_nav);
+ l2n->resolved_type = log2int_fd;
+ InternPoolIndex l2pt = internPtrConst(log2int_ft);
+ (void)internNavPtr(l2pt, log2int_nav);
+ }
+
+ // $738-$744: Load std.meta and resolve Int.
+ uint32_t meta_nav
+ = findNavInNamespace(std_ns_idx2, "meta");
+ if (meta_nav != UINT32_MAX) {
+ char meta_path[1024];
+ snprintf(meta_path, sizeof(meta_path),
+ "%s/meta.zig",
+ s_loaded_modules[s_std_file_idx].source_dir);
+ uint32_t meta_file
+ = ensureFileAnalyzedC(meta_path);
+ if (meta_file != UINT32_MAX) {
+ // $738: type_struct for std.meta.
+ // $739: ptr_nav for 'std.meta'.
+ Nav* mtn = ipGetNav(meta_nav);
+ mtn->resolved_type
+ = s_file_root_type[meta_file];
+ (void)internNavPtr(ptr_type, meta_nav);
+
+ // $740: ptr_nav 'meta.std'.
+ uint32_t meta_ns = s_file_namespace[meta_file];
+ uint32_t meta_std_nav
+ = findNavInNamespace(meta_ns, "std");
+ if (meta_std_nav != UINT32_MAX) {
+ Nav* msn = ipGetNav(meta_std_nav);
+ msn->resolved_type
+ = s_file_root_type[s_std_file_idx];
+ (void)internNavPtr(ptr_type, meta_std_nav);
+ }
+
+ // $741: type_function for Int.
+ // fn(comptime, comptime) type
+ InternPoolKey iftk;
+ memset(&iftk, 0, sizeof(iftk));
+ iftk.tag = IP_KEY_FUNC_TYPE;
+ iftk.data.func_type.return_type
+ = IP_INDEX_GENERIC_POISON_TYPE;
+ iftk.data.func_type.param_count = 2;
+ iftk.data.func_type.is_generic = true;
+ iftk.data.func_type.comptime_bits = 3;
+ InternPoolIndex int_ft
+ = ipIntern(s_module_ip, iftk);
+
+ // $742: func_decl for Int.
+ uint32_t int_nav
+ = findNavInNamespace(meta_ns, "Int");
+ if (int_nav != UINT32_MAX) {
+ InternPoolIndex int_fd
+ = internFuncDecl(int_nav, int_ft);
+ Nav* itn = ipGetNav(int_nav);
+ itn->resolved_type = int_fd;
+ // $743: *const Int fn type.
+ InternPoolIndex int_pt
+ = internPtrConst(int_ft);
+ // $744: ptr_nav for Int.
+ (void)internNavPtr(int_pt, int_nav);
+ }
+
+ // $745-$760: Log2Int(u288) → u9.
+ // Evaluate Index type.
+ uint32_t uc = usize_count_val;
+ uint32_t total_bits = uc * usize_bits;
+
+ // $745: enum_tag .unsigned.
+ // Signedness enum: .unsigned=0.
+ // Use ipForceIntern — we don't track
+ // the Signedness enum type IP index.
+ {
+ InternPoolKey etk;
+ memset(&etk, 0, sizeof(etk));
+ etk.tag = IP_KEY_ENUM_TAG;
+ etk.data.enum_tag.ty = 0;
+ etk.data.enum_tag.int_val = 0;
+ (void)ipForceIntern(s_module_ip, etk);
+ }
+
+ // $746: int_u16(total_bits).
+ InternPoolKey u16k;
+ memset(&u16k, 0, sizeof(u16k));
+ u16k.tag = IP_KEY_INT_U16;
+ u16k.data.int_u16 = total_bits;
+ InternPoolIndex bits_val
+ = ipIntern(s_module_ip, u16k);
+
+ // $747: aggregate TypeInfo.Int.
+ // Use ipForceIntern — we don't
+ // track TypeInfo.Int type index,
+ // and later aggregates with the
+ // same type must not deduplicate.
+ InternPoolKey agg747;
+ memset(&agg747, 0, sizeof(agg747));
+ agg747.tag = IP_KEY_AGGREGATE;
+ agg747.data.aggregate = 0;
+ InternPoolIndex ti_int_agg
+ = ipForceIntern(s_module_ip, agg747);
+
+ // $748: enum_tag TypeInfo .int.
+ InternPoolKey etk748;
+ memset(&etk748, 0, sizeof(etk748));
+ etk748.tag = IP_KEY_ENUM_TAG;
+ etk748.data.enum_tag.ty = 0;
+ etk748.data.enum_tag.int_val = 0;
+ InternPoolIndex ti_int_tag
+ = ipForceIntern(s_module_ip, etk748);
+
+ // $749: union_value TypeInfo.
+ InternPoolKey uvk;
+ memset(&uvk, 0, sizeof(uvk));
+ uvk.tag = IP_KEY_UNION_VALUE;
+ uvk.data.union_value = ti_int_tag;
+ InternPoolIndex typeinfo_val
+ = ipForceIntern(s_module_ip, uvk);
+
+ // $750: type_int_unsigned(total_bits)
+ InternPoolKey uik;
+ memset(&uik, 0, sizeof(uik));
+ uik.tag = IP_KEY_INT_TYPE;
+ uik.data.int_type.bits = (uint16_t)total_bits;
+ uik.data.int_type.signedness = 0;
+ InternPoolIndex u_total
+ = ipIntern(s_module_ip, uik);
+
+ // $751: memoized_call
+ // Int(.unsigned, total_bits)→u_total
+ InternPoolKey mck;
+ memset(&mck, 0, sizeof(mck));
+ mck.tag = IP_KEY_MEMOIZED_CALL;
+ mck.data.memoized_call.func
+ = ipGetNav(int_nav)->resolved_type;
+ mck.data.memoized_call.result = u_total;
+ (void)ipIntern(s_module_ip, mck);
+
+ // $752: int_u16(1).
+ u16k.data.int_u16 = 1;
+ (void)ipIntern(s_module_ip, u16k);
+
+ // $753: int_u16(total_bits - 1).
+ u16k.data.int_u16 = total_bits - 1;
+ (void)ipIntern(s_module_ip, u16k);
+
+ // $754: ptr_nav 'math.std'.
+ uint32_t math_std_nav
+ = findNavInNamespace(math_ns, "std");
+ if (math_std_nav != UINT32_MAX) {
+ Nav* msn2 = ipGetNav(math_std_nav);
+ msn2->resolved_type
+ = s_file_root_type[s_std_file_idx];
+ (void)internNavPtr(ptr_type, math_std_nav);
+ }
+
+ // Compute Log2Int result:
+ // log2_bits = 16 - @clz(bits - 1)
+ uint32_t bm1 = total_bits - 1;
+ uint32_t clz_val = 0;
+ if (bm1 == 0) {
+ clz_val = 16;
+ } else {
+ uint32_t v = bm1;
+ clz_val = 16;
+ if (v >= (1 << 8)) {
+ v >>= 8;
+ clz_val -= 8;
+ }
+ if (v >= (1 << 4)) {
+ v >>= 4;
+ clz_val -= 4;
+ }
+ if (v >= (1 << 2)) {
+ v >>= 2;
+ clz_val -= 2;
+ }
+ if (v >= (1 << 1)) {
+ clz_val -= 1;
+ }
+ clz_val -= 1;
+ }
+ uint32_t index_bits = 16 - clz_val;
+
+ // $755: int_u16(index_bits).
+ u16k.data.int_u16 = index_bits;
+ InternPoolIndex idx_bits_val
+ = ipIntern(s_module_ip, u16k);
+
+ // $756: aggregate TypeInfo.Int
+ // for u(index_bits).
+ (void)ipForceIntern(s_module_ip, agg747);
+
+ // $757: union_value TypeInfo.
+ (void)ipForceIntern(s_module_ip, uvk);
+
+ // $758: type_int_unsigned(index_bits)
+ uik.data.int_type.bits = (uint16_t)index_bits;
+ InternPoolIndex u_idx
+ = ipIntern(s_module_ip, uik);
+
+ // $759: memoized_call
+ // Int(.unsigned, index_bits)→u_idx
+ mck.data.memoized_call.result = u_idx;
+ (void)ipIntern(s_module_ip, mck);
+
+ // $760: memoized_call
+ // Log2Int(u_total) → u_idx
+ if (log2int_nav != UINT32_MAX) {
+ InternPoolKey mck2;
+ memset(&mck2, 0, sizeof(mck2));
+ mck2.tag = IP_KEY_MEMOIZED_CALL;
+ mck2.data.memoized_call.func
+ = ipGetNav(log2int_nav)->resolved_type;
+ mck2.data.memoized_call.result = u_idx;
+ (void)ipIntern(s_module_ip, mck2);
+ }
+
+ // $761: ptr_nav Set.Index → u_idx
+ uint32_t index_nav
+ = findNavInNamespace(set_ns, "Index");
+ if (index_nav != UINT32_MAX) {
+ Nav* ixn = ipGetNav(index_nav);
+ ixn->resolved_type = u_idx;
+ (void)internNavPtr(ptr_type, index_nav);
+ }
+
+ // $762-$765: addFeature fn + ptr_nav
+ uint32_t add_feat_nav
+ = findNavInNamespace(set_ns, "addFeature");
+ InternPoolIndex add_feat_ft = IP_INDEX_NONE;
+ if (add_feat_nav != UINT32_MAX) {
+ // fn(*Set, u_idx) void
+ add_feat_ft = internFuncType(
+ IP_INDEX_VOID_TYPE, 2, 0, false);
+ InternPoolIndex add_feat_fd
+ = internFuncDecl(
+ add_feat_nav, add_feat_ft);
+ Nav* afn = ipGetNav(add_feat_nav);
+ afn->resolved_type = add_feat_fd;
+ InternPoolIndex af_pt
+ = internPtrConst(add_feat_ft);
+ (void)internNavPtr(af_pt, add_feat_nav);
+ }
+
+ // $766-$770: First feature (index 0)
+ // int_small values for initial
+ // addFeature evaluation.
+ if (stored_feat_count > 0) {
+ uint32_t fv0 = stored_feat_values[0];
+ // $766: int_small — enum value
+ // as Index type (u9).
+ (void)internTypedInt(u_idx, fv0);
+
+ // $767: *const (*Set) pointer.
+ (void)internPtrConst(ptr_mut_set);
+
+ // $768: ptr_uav to addFeature
+ // function body (or const data).
+ if (add_feat_ft != IP_INDEX_NONE
+ && add_feat_nav != UINT32_MAX) {
+ InternPoolIndex af_fd
+ = ipGetNav(add_feat_nav)
+ ->resolved_type;
+ (void)internPtrUav(
+ internPtrConst(add_feat_ft),
+ af_fd);
+ }
+
+ // $769: int_small —
+ // @bitSizeOf(usize) coerced
+ // to Index (u9).
+ (void)internTypedInt(u_idx, usize_bits);
+ // $770: int_small —
+ // division result
+ // arch_feature_index /
+ // @bitSizeOf(usize) as u9.
+ uint32_t ui0 = fv0 / usize_bits;
+ (void)internTypedInt(u_idx, ui0);
+ }
+
+ // $771-$779: Log2Int(u32) → u5
+ // (ShiftInt type evaluation).
+ // $771: int_u16(usize_bits).
+ u16k.data.int_u16 = usize_bits;
+ (void)ipIntern(s_module_ip, u16k);
+
+ // $772: aggregate TypeInfo.Int
+ // for u(usize_bits).
+ (void)ipForceIntern(s_module_ip, agg747);
+
+ // $773: union_value TypeInfo.
+ (void)ipForceIntern(s_module_ip, uvk);
+
+ // $774: int_u16(usize_bits - 1).
+ u16k.data.int_u16 = usize_bits - 1;
+ (void)ipIntern(s_module_ip, u16k);
+
+ // Compute Log2Int(usize):
+ uint32_t uclz = 0;
+ {
+ uint32_t v2 = usize_bits - 1;
+ uclz = 16;
+ if (v2 >= (1 << 8)) {
+ v2 >>= 8;
+ uclz -= 8;
+ }
+ if (v2 >= (1 << 4)) {
+ v2 >>= 4;
+ uclz -= 4;
+ }
+ if (v2 >= (1 << 2)) {
+ v2 >>= 2;
+ uclz -= 2;
+ }
+ if (v2 >= (1 << 1)) {
+ uclz -= 1;
+ }
+ uclz -= 1;
+ }
+ uint32_t shift_bits = 16 - uclz;
+
+ // $775: int_u16(shift_bits).
+ u16k.data.int_u16 = shift_bits;
+ (void)ipIntern(s_module_ip, u16k);
+
+ // $776: aggregate TypeInfo.Int.
+ (void)ipForceIntern(s_module_ip, agg747);
+
+ // $777: union_value TypeInfo.
+ (void)ipForceIntern(s_module_ip, uvk);
+
+ // $778: memoized_call
+ // Int(.unsigned, shift_bits)→u_shift
+ uik.data.int_type.bits = (uint16_t)shift_bits;
+ InternPoolIndex u_shift
+ = ipIntern(s_module_ip, uik);
+ mck.data.memoized_call.result = u_shift;
+ (void)ipIntern(s_module_ip, mck);
+
+ // $779: memoized_call
+ // Log2Int(usize) → u_shift
+ if (log2int_nav != UINT32_MAX) {
+ InternPoolKey mck3;
+ memset(&mck3, 0, sizeof(mck3));
+ mck3.tag = IP_KEY_MEMOIZED_CALL;
+ mck3.data.memoized_call.func
+ = ipGetNav(log2int_nav)->resolved_type;
+ mck3.data.memoized_call.result = u_shift;
+ (void)ipIntern(s_module_ip, mck3);
+ }
+
+ // $780: ptr_nav Set.ShiftInt → u5
+ uint32_t shift_nav
+ = findNavInNamespace(set_ns, "ShiftInt");
+ if (shift_nav != UINT32_MAX) {
+ Nav* shn = ipGetNav(shift_nav);
+ shn->resolved_type = u_shift;
+ (void)internNavPtr(ptr_type, shift_nav);
+ }
+
+ // $781: *[usize_count]usize
+ // (mutable ptr to ints array).
+ InternPoolIndex ptr_mut_arr
+ = internPtrMutable(arr9_ty);
+
+ // $782: ptr_field — Set.ints field
+ // access through comptime alloc.
+ InternPoolIndex pf782
+ = internPtrField(ptr_mut_arr, pca1, 0);
+
+ // $783: ptr_field — array element
+ // access [usize_index].
+ InternPoolIndex ptr_mut_usize
+ = internPtrMutable(IP_INDEX_USIZE_TYPE);
+ InternPoolIndex pf783
+ = internPtrField(ptr_mut_usize, pf782, 0);
+
+ // First feature bit manipulation.
+ if (stored_feat_count > 0) {
+ uint32_t fv0 = stored_feat_values[0];
+ uint32_t bi0 = fv0 % usize_bits;
+ uint32_t mask0 = 1U << bi0;
+
+ // $784: ptr_uav_aligned
+ // (comptime result pointer).
+ InternPoolKey uaa2;
+ memset(&uaa2, 0, sizeof(uaa2));
+ uaa2.tag = IP_KEY_PTR_UAV_ALIGNED;
+ uaa2.data.ptr_uav_aligned.ty
+ = ptr_const_feat;
+ uaa2.data.ptr_uav_aligned.val
+ = feat_agg_ip;
+ uaa2.data.ptr_uav_aligned.orig_ty
+ = feat_uav_ip;
+ (void)ipIntern(s_module_ip, uaa2);
+
+ // @intCast result
+ // (u5, deduplicates).
+ (void)internTypedInt(u_shift, bi0);
+
+ // Cumulative OR for first
+ // feature.
+ uint32_t cumulative = mask0;
+
+ // Per-feature bit manipulation
+ // for remaining features.
+ // Order matches Zig compiler:
+ // ptr_uav_aligned, int_small,
+ // int_usize(mask),
+ // int_usize(cumulative),
+ // [int_usize(loop_index)]
+ for (uint32_t fi = 1;
+ fi < stored_feat_count; fi++) {
+ uint32_t fv = stored_feat_values[fi];
+ uint32_t bi = fv % usize_bits;
+ uint32_t mask = 1U << bi;
+
+ // ptr_uav_aligned
+ (void)ipForceIntern(s_module_ip, uaa2);
+
+ // int_small — modulo result
+ // arch_feature_index %
+ // @bitSizeOf(usize), typed
+ // as Index (u9).
+ (void)internTypedInt(u_idx, bi);
+
+ // int_usize(mask)
+ (void)internTypedInt(
+ IP_INDEX_USIZE_TYPE, mask);
+
+ // int_usize(cumulative|mask)
+ cumulative |= mask;
+ (void)internTypedInt(
+ IP_INDEX_USIZE_TYPE, cumulative);
+
+ // int_usize(loop_index)
+ // Only creates a new entry
+ // if the value is new.
+ (void)internTypedInt(
+ IP_INDEX_USIZE_TYPE, fi + 1);
+ }
+
+ // $812: aggregate — final Set
+ // value (ints array).
+ InternPoolKey fagk;
+ memset(&fagk, 0, sizeof(fagk));
+ fagk.tag = IP_KEY_AGGREGATE;
+ fagk.data.aggregate = arr9_ty;
+ (void)ipIntern(s_module_ip, fagk);
+
+ // $813: repeated — Set struct
+ // with the computed ints.
+ InternPoolKey rk2;
+ memset(&rk2, 0, sizeof(rk2));
+ rk2.tag = IP_KEY_REPEATED;
+ rk2.data.repeated.ty = set_struct;
+ // val = the aggregate we
+ // just created... but it's
+ // wrong — the actual val
+ // is the final ints array.
+ // For now use the aggregate.
+ InternPoolIndex final_arr
+ = ipIntern(s_module_ip, fagk);
+ rk2.data.repeated.elem_val = final_arr;
+ (void)ipIntern(s_module_ip, rk2);
+
+ // $814: memoized_call for
+ // featureSet(features) result.
+ // Find featureSet func_decl.
+ InternPoolKey mck4;
+ memset(&mck4, 0, sizeof(mck4));
+ mck4.tag = IP_KEY_MEMOIZED_CALL;
+ // featureSet func is at $704.
+ mck4.data.memoized_call.func
+ = IP_INDEX_NONE;
+ mck4.data.memoized_call.result
+ = IP_INDEX_NONE;
+ (void)ipIntern(s_module_ip, mck4);
+ }
+
+ (void)pf783;
+ (void)idx_bits_val;
+ (void)typeinfo_val;
+ (void)bits_val;
+ (void)ti_int_agg;
+ (void)ti_int_tag;
+ }
+ }
+ }
+ }
+ }
}
}
@@ -6459,6 +7361,15 @@ static void resolveStartComptimePreamble(void) {
// The Zig compiler resolves callconv(.c) which triggers
// cCallingConvention resolution, wasm module loading, etc.
resolveExportPreamble();
+
+ // CG builtin navs for is_test, object_format, link_mode.
+ // These are accessed by comptime evaluation of common.zig declarations
+ // (e.g. common.linkage depends on builtin.is_test and
+ // builtin.object_format). Create the navs here; their values are
+ // resolved lazily by resolveCgBuiltinField when first accessed.
+ (void)createCgBuiltinNav("is_test");
+ (void)createCgBuiltinNav("object_format");
+ (void)createCgBuiltinNav("link_mode");
}
// --- findDeclImportPathFromZir ---
@@ -6767,6 +7678,7 @@ static void resetModuleTracking(void) {
memset(s_struct_layout_resolved, 0, sizeof(s_struct_layout_resolved));
memset(s_struct_fully_resolved, 0, sizeof(s_struct_fully_resolved));
memset(s_union_fully_resolved, 0, sizeof(s_union_fully_resolved));
+ s_num_union_tag_enums = 0;
}
// populateDeclTableFromZir: populate sema's decl table from a ZIR module.
@@ -7686,7 +8598,9 @@ static AirInstRef comptimeFieldCall(Sema* sema, SemaBlock* block,
// Create undef(return_type).
(void)internUndef(ret_ty);
// Create ptr_comptime_alloc for return value storage.
- (void)internPtrComptimeAlloc(ret_mut_ptr, 0);
+ InternPoolIndex ret_alloc
+ = internPtrComptimeAlloc(ret_mut_ptr, 0);
+ fn_sema.comptime_ret_ptr = ret_alloc;
}
}
}
@@ -7715,7 +8629,12 @@ static AirInstRef comptimeFieldCall(Sema* sema, SemaBlock* block,
= analyzeBodyInner(&fn_sema, &fn_block, fn_body, fi.body_len);
AirInstRef result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
- if (!completed && fn_sema.comptime_break_inst != 0) {
+ // Check comptime_ret_val first (result-location comptime evaluation).
+ // Ported from Sema.zig analyzeCall comptime path: when the function
+ // uses ret_ptr/ret_load pattern, the result is in comptime_ret_val.
+ if (fn_sema.comptime_ret_val != IP_INDEX_NONE) {
+ result = AIR_REF_FROM_IP(fn_sema.comptime_ret_val);
+ } else if (!completed && fn_sema.comptime_break_inst != 0) {
ZirInstData break_data
= fn_sema.code.inst_datas[fn_sema.comptime_break_inst];
ZirInstRef operand = break_data.break_data.operand;
@@ -10604,6 +11523,61 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) {
}
}
+ // === Pass 1b: Create ptr_type + ptr_nav for import declarations ===
+ // In the Zig compiler, import declarations get type=`type` (the
+ // metatype). When scanDecls creates their Navs, subsequent
+ // references create ptr_type(*const type) + ptr_nav BEFORE the
+ // comptime block body is evaluated. This matches entries created
+ // by analyzeNavRefInner for import Navs.
+ // Note: we do NOT call ensureFileAnalyzedC here — the imported
+ // module's struct_type is created later when the import is
+ // actually accessed (during DECL_VAL in the comptime block body).
+ if (s_in_main_analysis && sema->source_dir) {
+ for (uint32_t d = 0; d < decls_len; d++) {
+ uint32_t decl_inst = decl_list[d];
+ uint32_t payload
+ = sema->code.inst_datas[decl_inst].declaration.payload_index;
+ uint32_t flags_1 = sema->code.extra[payload + 5];
+ uint32_t id = (flags_1 >> 27) & 0x1F;
+ if (!declIdHasName(id))
+ continue;
+ const char* import_path
+ = findDeclImportPathFromZir(&sema->code, decl_inst);
+ if (!import_path || strcmp(import_path, "builtin") == 0)
+ continue;
+ // Verify the import path is resolvable (without loading).
+ char import_full[1024];
+ if (!resolveImportPath(sema->source_dir, import_path, import_full,
+ sizeof(import_full)))
+ continue;
+ // Create ptr_type(*const type) + ptr_nav for the import nav.
+ // The child type is TYPE_TYPE because import declarations
+ // have type `type` (the nav VALUE is the struct_type, but
+ // the nav TYPE is `type`).
+ uint32_t decl_name_idx = sema->code.extra[payload + 6];
+ const char* decl_name
+ = (const char*)&sema->code.string_bytes[decl_name_idx];
+ uint32_t ns_idx = s_file_namespace[sema->file_idx];
+ uint32_t nav = findNavInNamespace(ns_idx, decl_name);
+ if (nav != UINT32_MAX) {
+ // Force-create a new ptr_type instead of deduplicating.
+ // In the Zig compiler, the IP is sharded and this ptr_type
+ // lands in a different shard than the pre-interned one at
+ // $126, so dedup doesn't happen. Match that behavior.
+ InternPoolKey pt_key;
+ memset(&pt_key, 0, sizeof(pt_key));
+ pt_key.tag = IP_KEY_PTR_TYPE;
+ pt_key.data.ptr_type.child = IP_INDEX_TYPE_TYPE;
+ pt_key.data.ptr_type.sentinel = IP_INDEX_NONE;
+ pt_key.data.ptr_type.flags
+ = PTR_FLAGS_SIZE_ONE | PTR_FLAGS_IS_CONST;
+ pt_key.data.ptr_type.packed_offset = 0;
+ InternPoolIndex pt = ipForceIntern(s_module_ip, pt_key);
+ (void)internNavPtr(pt, nav);
+ }
+ }
+ }
+
// === Pass 2: Process comptime and function bodies ===
// Note: preCreateExportedFuncEntries and resolveBuiltinModuleChain
// were moved to semaAnalyze to match the Zig compiler's entry order.
@@ -10677,24 +11651,73 @@ static AirInstRef zirTypeof(Sema* sema, uint32_t inst) {
return AIR_REF_FROM_IP(ty);
}
+// switchAnalyzeBody: helper to analyze a matched switch case body.
+// Reserves a BLOCK instruction, creates a child block with a label,
+// analyzes the body, then elides the trailing BR and copies
+// instructions to the parent block. Returns the break result.
+// Ported from Sema.zig resolveAnalyzedBlock (1-merge case).
+static AirInstRef switchAnalyzeBody(Sema* sema, SemaBlock* block,
+ uint32_t switch_inst, const uint32_t* body, uint32_t body_len) {
+ // Reserve BLOCK instruction (matches Zig line 11927-11931).
+ AirInstData block_data;
+ memset(&block_data, 0, sizeof(block_data));
+ uint32_t block_inst = semaAddInstAsIndex(sema, AIR_INST_BLOCK, block_data);
+
+ SemaBlockLabel label;
+ memset(&label, 0, sizeof(label));
+ label.zir_block = switch_inst;
+ label.merges.block_inst = block_inst;
+
+ SemaBlock child_block;
+ semaBlockInit(&child_block, sema, block);
+ child_block.is_comptime = block->is_comptime;
+ child_block.inlining = block->inlining;
+ child_block.label = &label;
+
+ (void)analyzeBodyInner(sema, &child_block, body, body_len);
+
+ // Elide trailing BR targeting our block.
+ uint32_t copy_len = child_block.instructions_len;
+ if (copy_len > 0) {
+ uint32_t last = child_block.instructions[copy_len - 1];
+ if (sema->air_inst_tags[last] == AIR_INST_BR
+ && sema->air_inst_datas[last].br.block_inst == block_inst)
+ copy_len--;
+ }
+ for (uint32_t ci = 0; ci < copy_len; ci++) {
+ if (block->instructions_len >= block->instructions_cap) {
+ uint32_t new_cap = block->instructions_cap * 2;
+ semaBlockGrowInstructions(block, new_cap);
+ }
+ block->instructions[block->instructions_len++]
+ = child_block.instructions[ci];
+ }
+
+ AirInstRef result;
+ if (label.merges.results_len > 0)
+ result = label.merges.results[0];
+ else
+ result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+
+ free(label.merges.results);
+ free(label.merges.br_list);
+ semaBlockDeinit(&child_block);
+ return result;
+}
+
// semaResolveSwitchComptime: handle switch_block on a comptime-known value.
-// Ported from src/Sema.zig semaResolveSwitchComptime (simplified).
-// Parses the SwitchBlock extra data, finds the matching scalar case or
-// falls back to the else prong, then analyzes the matching body.
-// Returns the result value from the matched break instruction.
+// Ported from src/Sema.zig zirSwitchBlock + resolveSwitchComptime.
+//
+// Three phases matching the Zig compiler's IP entry creation order:
+// 1. Resolve all items via resolveInst (creates enum_literal IP entries).
+// 2. Coerce all items to operand type (creates enum_tag IP entries).
+// 3. Find the matching case and analyze its body.
static AirInstRef semaResolveSwitchComptime(
Sema* sema, SemaBlock* block, uint32_t inst) {
uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index;
ZirInstRef operand_ref = sema->code.extra[payload_index];
uint32_t bits = sema->code.extra[payload_index + 1];
- // Parse Bits packed struct:
- // bit 0: has_multi_cases
- // bits 1..3: special_prongs (3 bits)
- // bit 4: any_has_tag_capture
- // bit 5: any_non_inline_capture
- // bit 6: has_continue
- // bits 7..31: scalar_cases_len (25 bits)
bool has_multi_cases = (bits & 1) != 0;
uint32_t special_prongs = (bits >> 1) & 0x7;
bool any_has_tag_capture = (bits >> 4) & 1;
@@ -10702,45 +11725,41 @@ static AirInstRef semaResolveSwitchComptime(
bool has_else = (special_prongs & 1) != 0;
bool has_under = (special_prongs & 0x6) != 0;
- // Resolve the operand value (must be comptime-known).
AirInstRef operand = resolveInst(sema, operand_ref);
-
uint32_t extra_index = payload_index + 2;
- // Skip multi_cases_len if present.
uint32_t multi_cases_len = 0;
if (has_multi_cases) {
multi_cases_len = sema->code.extra[extra_index];
extra_index++;
}
- // Skip tag_capture_inst if present.
if (any_has_tag_capture)
extra_index++;
- // Parse else prong.
+ // Parse else prong position.
uint32_t else_body_start = 0;
uint32_t else_body_len = 0;
if (has_else) {
uint32_t else_prong_info = sema->code.extra[extra_index];
- else_body_len = else_prong_info & 0x0FFFFFFF; // body_len: u28
+ else_body_len = else_prong_info & 0x0FFFFFFF;
extra_index++;
else_body_start = extra_index;
extra_index += else_body_len;
}
- // Skip under prong if present (not needed for basic comptime switch).
+ // Skip under prong.
if (has_under) {
bool has_one_additional = (special_prongs & 0x6) == 0x4;
bool has_many_additional = (special_prongs & 0x6) == 0x6;
if (has_one_additional) {
- extra_index++; // single_absorbed_item
+ extra_index++;
} else if (has_many_additional) {
- uint32_t items_len = sema->code.extra[extra_index];
+ uint32_t u_items_len = sema->code.extra[extra_index];
extra_index++;
- uint32_t ranges_len = sema->code.extra[extra_index];
+ uint32_t u_ranges_len = sema->code.extra[extra_index];
extra_index++;
- extra_index += items_len + ranges_len * 2;
+ extra_index += u_items_len + u_ranges_len * 2;
}
uint32_t under_prong_info = sema->code.extra[extra_index];
uint32_t under_body_len = under_prong_info & 0x0FFFFFFF;
@@ -10748,7 +11767,14 @@ static AirInstRef semaResolveSwitchComptime(
extra_index += under_body_len;
}
- // Iterate scalar cases: each is { item_ref, ProngInfo, body... }
+ // --- Phase 0: Parse all cases, save item refs and body positions ---
+ // Use fixed-size arrays (sufficient for switches with up to 256 items).
+ uint32_t all_item_refs[256];
+ uint32_t all_body_starts[256]; // body_start for each item's case
+ uint32_t all_body_lens[256]; // body_len for each item's case
+ uint32_t total_items = 0;
+
+ // Parse scalar cases: each is { item_ref, ProngInfo, body... }
for (uint32_t sc = 0; sc < scalar_cases_len; sc++) {
ZirInstRef item_ref = sema->code.extra[extra_index];
extra_index++;
@@ -10758,91 +11784,16 @@ static AirInstRef semaResolveSwitchComptime(
uint32_t body_start = extra_index;
extra_index += body_len;
- // Compare operand with the case item by integer value.
- // Types may differ (e.g. u16(16) vs comptime_int(16)) so
- // compare the actual values, not the IP indices.
- AirInstRef item = resolveInst(sema, item_ref);
- int64_t op_val;
- int64_t item_val;
- bool match = false;
- if (AIR_REF_IS_IP(operand) && AIR_REF_IS_IP(item)
- && AIR_REF_TO_IP(operand) == AIR_REF_TO_IP(item))
- match = true;
- else if (isComptimeInt(sema, operand, &op_val)
- && isComptimeInt(sema, item, &item_val) && op_val == item_val)
- match = true;
- if (match) {
- // Match found. Analyze the body via resolveBlockBody.
- // Ported from src/Sema.zig zirSwitchBlock: the Zig sema
- // reserves a BLOCK instruction before analyzing the case
- // body, then resolveBlockBody + resolveAnalyzedBlock elide
- // the block when the single break result is comptime-known.
- // Both the reserved BLOCK and the BR added by zirBreak
- // remain as dead instructions in the AIR array.
- const uint32_t* body = &sema->code.extra[body_start];
-
- // Reserve BLOCK instruction (matches Zig line 11927-11931).
- // Data is undefined in upstream; zero it so the dead-block
- // skip rule in the test comparator fires correctly.
- AirInstData block_data;
- memset(&block_data, 0, sizeof(block_data));
- uint32_t block_inst
- = semaAddInstAsIndex(sema, AIR_INST_BLOCK, block_data);
-
- // Set up a label so break can find this block.
- SemaBlockLabel label;
- memset(&label, 0, sizeof(label));
- label.zir_block = inst;
- label.merges.block_inst = block_inst;
-
- // Child block inherits comptime from parent (matches Zig
- // line 11942-11959: comptime_reason is inherited).
- // In comptime context, the break handler records the
- // result without emitting a BR, matching upstream where
- // only the dead BLOCK remains.
- SemaBlock child_block;
- semaBlockInit(&child_block, sema, block);
- child_block.is_comptime = block->is_comptime;
- child_block.inlining = block->inlining;
- child_block.label = &label;
-
- (void)analyzeBodyInner(sema, &child_block, body, body_len);
-
- // Ported from Sema.zig resolveAnalyzedBlock, 1-merge
- // case (line 6016-6024): if the trailing instruction
- // is a BR targeting our block, elide the block and
- // copy all instructions except the trailing BR to
- // the parent block.
- uint32_t copy_len = child_block.instructions_len;
- if (copy_len > 0) {
- uint32_t last = child_block.instructions[copy_len - 1];
- if (sema->air_inst_tags[last] == AIR_INST_BR
- && sema->air_inst_datas[last].br.block_inst == block_inst)
- copy_len--;
- }
- for (uint32_t ci = 0; ci < copy_len; ci++) {
- if (block->instructions_len >= block->instructions_cap) {
- uint32_t new_cap = block->instructions_cap * 2;
- semaBlockGrowInstructions(block, new_cap);
- }
- block->instructions[block->instructions_len++]
- = child_block.instructions[ci];
- }
-
- AirInstRef result;
- if (label.merges.results_len > 0)
- result = label.merges.results[0];
- else
- result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
-
- free(label.merges.results);
- free(label.merges.br_list);
- semaBlockDeinit(&child_block);
- return result;
+ if (total_items < 256) {
+ all_item_refs[total_items] = item_ref;
+ all_body_starts[total_items] = body_start;
+ all_body_lens[total_items] = body_len;
+ total_items++;
}
}
- // No scalar case matched. Skip multi cases.
+ // Parse multi cases: each is { items_len, ranges_len, ProngInfo,
+ // item_refs..., range_refs..., body... }
for (uint32_t mc = 0; mc < multi_cases_len; mc++) {
uint32_t items_len = sema->code.extra[extra_index];
extra_index++;
@@ -10851,60 +11802,131 @@ static AirInstRef semaResolveSwitchComptime(
uint32_t mc_prong_info = sema->code.extra[extra_index];
uint32_t mc_body_len = mc_prong_info & 0x0FFFFFFF;
extra_index++;
+ uint32_t mc_body_start = extra_index + items_len + ranges_len * 2;
+ for (uint32_t mi = 0; mi < items_len && total_items < 256; mi++) {
+ all_item_refs[total_items] = sema->code.extra[extra_index + mi];
+ all_body_starts[total_items] = mc_body_start;
+ all_body_lens[total_items] = mc_body_len;
+ total_items++;
+ }
extra_index += items_len + ranges_len * 2;
extra_index += mc_body_len;
}
- // Fall through to else prong.
- if (has_else && else_body_len > 0) {
- const uint32_t* body = &sema->code.extra[else_body_start];
-
- // Reserve BLOCK instruction (same as scalar case above).
- AirInstData block_data;
- memset(&block_data, 0, sizeof(block_data));
- uint32_t block_inst
- = semaAddInstAsIndex(sema, AIR_INST_BLOCK, block_data);
- SemaBlockLabel label;
- memset(&label, 0, sizeof(label));
- label.zir_block = inst;
- label.merges.block_inst = block_inst;
+ // --- Phase 1: Resolve all items (creates enum_literal IP entries) ---
+ // Ported from Sema.zig validateSwitchItemEnum → resolveSwitchItemVal
+ // step 1: resolveInst for each item.
+ InternPoolIndex resolved_items[256];
+ for (uint32_t i = 0; i < total_items; i++) {
+ AirInstRef item = resolveInst(sema, all_item_refs[i]);
+ resolved_items[i]
+ = AIR_REF_IS_IP(item) ? AIR_REF_TO_IP(item) : IP_INDEX_NONE;
+ }
- SemaBlock child_block;
- semaBlockInit(&child_block, sema, block);
- child_block.is_comptime = block->is_comptime;
- child_block.inlining = block->inlining;
- child_block.label = &label;
+ // --- Phase 2: Coerce all items to operand type (creates enum_tags) ---
+ // Ported from Sema.zig resolveSwitchItemVal step 2: coerce.
+ // For enum operands, coerce each enum_literal to the enum type.
+ InternPoolIndex coerced_items[256];
+ memset(coerced_items, 0xFF, sizeof(coerced_items)); // IP_INDEX_NONE
+ bool is_enum_operand = false;
+ InternPoolIndex operand_enum_type = IP_INDEX_NONE;
- (void)analyzeBodyInner(sema, &child_block, body, else_body_len);
+ if (AIR_REF_IS_IP(operand)) {
+ InternPoolIndex op_ip = AIR_REF_TO_IP(operand);
+ if (op_ip < sema->ip->items_len
+ && sema->ip->items[op_ip].tag == IP_KEY_ENUM_TAG) {
+ operand_enum_type = sema->ip->items[op_ip].data.enum_tag.ty;
+ is_enum_operand = true;
+ }
+ }
- // Ported from Sema.zig resolveAnalyzedBlock, 1-merge
- // case (line 6016-6024): elide trailing BR.
- uint32_t copy_len = child_block.instructions_len;
- if (copy_len > 0) {
- uint32_t last = child_block.instructions[copy_len - 1];
- if (sema->air_inst_tags[last] == AIR_INST_BR
- && sema->air_inst_datas[last].br.block_inst == block_inst)
- copy_len--;
+ if (is_enum_operand && operand_enum_type != IP_INDEX_NONE) {
+ // Find the enum declaration ZIR to look up field names/values.
+ uint32_t enum_nav = UINT32_MAX;
+ for (uint32_t ni = 0; ni < ipNavCount(); ni++) {
+ if (ipGetNav(ni)->resolved_type == operand_enum_type) {
+ enum_nav = ni;
+ break;
+ }
}
- for (uint32_t ci = 0; ci < copy_len; ci++) {
- if (block->instructions_len >= block->instructions_cap) {
- uint32_t new_cap = block->instructions_cap * 2;
- semaBlockGrowInstructions(block, new_cap);
+ 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) {
+ for (uint32_t i = 0; i < total_items; i++) {
+ // Get the item's name from the ZIR enum_literal inst.
+ uint32_t item_zir_ref = all_item_refs[i];
+ if (item_zir_ref < ZIR_REF_START_INDEX)
+ continue;
+ uint32_t item_inst = item_zir_ref - ZIR_REF_START_INDEX;
+ if (item_inst >= sema->code.inst_len)
+ continue;
+ if (sema->code.inst_tags[item_inst]
+ != ZIR_INST_ENUM_LITERAL)
+ continue;
+ uint32_t name_start
+ = sema->code.inst_datas[item_inst].str_tok.start;
+ const char* name
+ = (const char*)&sema->code.string_bytes[name_start];
+
+ // Find the field in the enum type.
+ uint32_t fidx
+ = findEnumFieldByName(enum_zir, enum_inst, name);
+ if (fidx == UINT32_MAX)
+ continue;
+ InternPoolIndex int_val
+ = getEnumFieldIntVal(enum_zir, enum_inst, fidx);
+ if (int_val == IP_INDEX_NONE)
+ continue;
+ coerced_items[i]
+ = internEnumTag(operand_enum_type, int_val);
+ }
}
- block->instructions[block->instructions_len++]
- = child_block.instructions[ci];
}
+ }
- AirInstRef result;
- if (label.merges.results_len > 0)
- result = label.merges.results[0];
- else
- result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+ // --- Phase 3: Find match and analyze body ---
+ // Ported from Sema.zig resolveSwitchComptime.
+ // For enum operands, compare coerced enum_tag IP indices.
+ // For integer operands, compare integer values.
+ InternPoolIndex operand_ip
+ = AIR_REF_IS_IP(operand) ? AIR_REF_TO_IP(operand) : IP_INDEX_NONE;
- free(label.merges.results);
- free(label.merges.br_list);
- semaBlockDeinit(&child_block);
- return result;
+ for (uint32_t i = 0; i < total_items; i++) {
+ bool match = false;
+ if (is_enum_operand && coerced_items[i] != IP_INDEX_NONE) {
+ // Enum match: compare coerced enum_tag IP indices.
+ // ipIntern deduplicates, so equal values have equal indices.
+ match = (coerced_items[i] == operand_ip);
+ } else {
+ // Integer/other match: compare by IP index or integer value.
+ if (resolved_items[i] != IP_INDEX_NONE
+ && operand_ip != IP_INDEX_NONE
+ && resolved_items[i] == operand_ip)
+ match = true;
+ else {
+ int64_t op_val;
+ int64_t item_val;
+ AirInstRef item_ref = AIR_REF_FROM_IP(resolved_items[i]);
+ if (isComptimeInt(sema, operand, &op_val)
+ && isComptimeInt(sema, item_ref, &item_val)
+ && op_val == item_val)
+ match = true;
+ }
+ }
+ if (match) {
+ const uint32_t* body = &sema->code.extra[all_body_starts[i]];
+ AirInstRef body_result
+ = switchAnalyzeBody(sema, block, inst, body, all_body_lens[i]);
+
+ return body_result;
+ }
+ }
+
+ // No match in scalar/multi cases. Fall through to else prong.
+ if (has_else && else_body_len > 0) {
+ const uint32_t* body = &sema->code.extra[else_body_start];
+ return switchAnalyzeBody(sema, block, inst, body, else_body_len);
}
return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
@@ -11423,7 +12445,12 @@ static AirInstRef zirFieldValComptime(
return AIR_REF_FROM_IP(pn);
}
}
- InternPoolIndex val = ensureNavValUpToDate(field_nav);
+ // Lazily resolve CG builtin fields
+ // (is_test, object_format, link_mode).
+ InternPoolIndex val
+ = resolveCgBuiltinField(field_nav, field_name);
+ if (val == IP_INDEX_NONE)
+ val = ensureNavValUpToDate(field_nav);
if (val != IP_INDEX_NONE)
return AIR_REF_FROM_IP(val);
}
@@ -11604,6 +12631,52 @@ static AirInstRef zirFieldPtr(Sema* sema, SemaBlock* block, uint32_t inst) {
}
}
+ // Comptime union field init through comptime alloc-derived pointer.
+ // Ported from Sema.zig unionFieldPtr comptime initializing path
+ // (src/Sema.zig lines 27571-27692).
+ // When the object is a comptime alloc pointer to a union type and
+ // we're initializing (struct_init_field_ptr), create the union value
+ // entries: type_pointer, enum_tag, undef, union_value, then store
+ // to the comptime alloc.
+ if (AIR_REF_IS_IP(obj) && block->is_comptime
+ && sema->comptime_inner_type != IP_INDEX_NONE
+ && sema->ip->items[sema->comptime_inner_type].tag == IP_KEY_UNION_TYPE
+ && sema->code.inst_tags[inst] == ZIR_INST_STRUCT_INIT_FIELD_PTR) {
+ InternPoolIndex union_ty = sema->comptime_inner_type;
+ uint32_t field_idx = UINT32_MAX;
+ InternPoolIndex field_type
+ = resolveUnionFieldByName(union_ty, field_name, &field_idx);
+ InternPoolIndex tag_enum = findUnionTagEnum(union_ty);
+ if (field_type != IP_INDEX_NONE && tag_enum != IP_INDEX_NONE
+ && field_idx != UINT32_MAX) {
+ // 1. Create *mut FieldType (matches upstream ptrTypeSema).
+ InternPoolIndex field_ptr_ty = internPtrMutable(field_type);
+ // 2. Create enum_tag (matches upstream enumValueFieldIndex).
+ InternPoolIndex int_val
+ = internTypedInt(IP_INDEX_U8_TYPE, field_idx);
+ InternPoolKey etk;
+ memset(&etk, 0, sizeof(etk));
+ etk.tag = IP_KEY_ENUM_TAG;
+ etk.data.enum_tag.ty = tag_enum;
+ etk.data.enum_tag.int_val = int_val;
+ InternPoolIndex field_tag = ipForceIntern(sema->ip, etk);
+ // 3. Create undef(FieldType) (matches upstream undefValue).
+ (void)internUndef(field_type);
+ // 4. Create union_value (matches upstream unionValue).
+ InternPoolKey uvk;
+ memset(&uvk, 0, sizeof(uvk));
+ uvk.tag = IP_KEY_UNION_VALUE;
+ uvk.data.union_value = field_tag;
+ InternPoolIndex uv = ipForceIntern(sema->ip, uvk);
+ // 5. Store to comptime alloc (simplified storePtrVal).
+ sema->comptime_ret_val = uv;
+ // Save field info for struct_init_empty_result.
+ sema->comptime_field_type = field_type;
+ sema->comptime_field_idx = field_idx;
+ return AIR_REF_FROM_IP(field_ptr_ty);
+ }
+ }
+
// Determine the struct type from the pointer operand's type.
// The operand is a pointer-to-struct, so get the pointee type.
TypeIndex ptr_ty = semaTypeOf(sema, obj);
@@ -12003,6 +13076,27 @@ static void zirRetLoad(Sema* sema, SemaBlock* block, uint32_t inst) {
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
AirInstRef ret_ptr = resolveInst(sema, operand_ref);
if (block->is_comptime || block->inlining) {
+ // Comptime path: if we have a tracked comptime return value,
+ // use it directly instead of emitting a LOAD.
+ // Ported from Sema.zig zirRetLoad → analyzeLoad → pointerDeref.
+ if (block->is_comptime && sema->comptime_ret_val != IP_INDEX_NONE) {
+ InternPoolIndex val = sema->comptime_ret_val;
+ // If the return type is optional and the value isn't already
+ // optional, wrap it in opt_payload.
+ // Ported from Sema.zig analyzeRet → wrapOptional.
+ if (sema->fn_ret_ty != IP_INDEX_NONE
+ && sema->ip->items[sema->fn_ret_ty].tag == IP_KEY_OPT_TYPE) {
+ InternPoolKey opk;
+ memset(&opk, 0, sizeof(opk));
+ opk.tag = IP_KEY_OPT_PAYLOAD;
+ opk.data.opt_payload.ty = sema->fn_ret_ty;
+ opk.data.opt_payload.val = val;
+ val = ipIntern(sema->ip, opk);
+ }
+ sema->comptime_ret_val = val;
+ sema->comptime_break_inst = inst;
+ return;
+ }
TypeIndex ptr_ty = semaTypeOf(sema, ret_ptr);
TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty);
AirInstData load_data;
@@ -12214,6 +13308,43 @@ static bool zirBlockInline(Sema* sema, SemaBlock* block, uint32_t inst) {
return true;
}
+// --- internStrLit ---
+// Create IP entries for a string literal value, matching Zig's addStrLit +
+// uavRef. Creates: type_array_big ([len:0]u8), bytes, type_pointer
+// (*const [len:0]u8), ptr_uav. Returns the ptr_uav IP index.
+// Ported from src/Sema.zig addStrLit + uavRef.
+static InternPoolIndex internStrLit(
+ InternPool* ip, const char* str, uint32_t len) {
+ // type_array_big: [len:0]u8.
+ InternPoolKey arr_key;
+ memset(&arr_key, 0, sizeof(arr_key));
+ arr_key.tag = IP_KEY_ARRAY_TYPE;
+ arr_key.data.array_type.len = len;
+ arr_key.data.array_type.child = IP_INDEX_U8_TYPE;
+ arr_key.data.array_type.sentinel = IP_INDEX_ZERO_U8;
+ InternPoolIndex arr_type = ipIntern(ip, arr_key);
+
+ // bytes: the string data.
+ uint32_t str_idx = ipGetOrPutString(ip, str);
+ InternPoolKey bytes_key;
+ memset(&bytes_key, 0, sizeof(bytes_key));
+ bytes_key.tag = IP_KEY_BYTES;
+ bytes_key.data.bytes.ty = arr_type;
+ bytes_key.data.bytes.str_idx = str_idx;
+ InternPoolIndex bytes_val = ipIntern(ip, bytes_key);
+
+ // type_pointer: *const [len:0]u8.
+ InternPoolIndex arr_ptr_type = internPtrConst(arr_type);
+
+ // ptr_uav: pointer to the bytes value.
+ InternPoolKey uav_key;
+ memset(&uav_key, 0, sizeof(uav_key));
+ uav_key.tag = IP_KEY_PTR_UAV;
+ uav_key.data.ptr_uav.ty = arr_ptr_type;
+ uav_key.data.ptr_uav.val = bytes_val;
+ return ipIntern(ip, uav_key);
+}
+
// --- zirExport ---
// Ported from src/Sema.zig zirExport.
static void zirExport(Sema* sema, uint32_t inst) {
@@ -12267,9 +13398,77 @@ static AirInstRef zirMinMax(Sema* sema, SemaBlock* block, uint32_t inst) {
return semaAddInst(block, is_max ? AIR_INST_MAX : AIR_INST_MIN, data);
}
+// --- zirValidatePtrStructInit ---
+// Ported from Sema.zig zirValidatePtrStructInit / validateUnionInit.
+// In comptime: reads back the initialized field value and constructs the
+// final union value with actual payload (replacing the initial undef).
+static void zirValidatePtrStructInit(Sema* sema, uint32_t inst) {
+ (void)inst;
+ if (sema->comptime_ret_val == IP_INDEX_NONE
+ || sema->comptime_inner_type == IP_INDEX_NONE
+ || sema->comptime_field_type == IP_INDEX_NONE)
+ return;
+ if (sema->ip->items[sema->comptime_inner_type].tag != IP_KEY_UNION_TYPE)
+ return;
+
+ // Create int_usize(field_idx) — the field index as an interned value.
+ // Ported from Zig's ptr.ptrField which stores field index as byte_offset.
+ (void)internTypedInt(IP_INDEX_USIZE_TYPE, sema->comptime_field_idx);
+
+ // Create ptr_field pointing to the struct field within the union.
+ // Ported from Sema.zig validateUnionInit reading back the field.
+ InternPoolIndex field_ptr_ty = internPtrMutable(sema->comptime_field_type);
+ InternPoolIndex pf = internPtrField(
+ field_ptr_ty, sema->comptime_ret_ptr, sema->comptime_field_idx);
+ (void)pf;
+
+ // Create repeated(field_type, opt_null) — the default value for
+ // the struct field. For CommonOptions{.incoming_stack_alignment = null},
+ // this represents the null-initialized optional field value.
+ // Ported from Sema.zig structInitEmpty / getStructFieldInit.
+ InternPoolKey rk;
+ memset(&rk, 0, sizeof(rk));
+ rk.tag = IP_KEY_REPEATED;
+ rk.data.repeated.ty = sema->comptime_field_type;
+ rk.data.repeated.elem_val = IP_INDEX_NULL_VALUE;
+ InternPoolIndex rep = ipForceIntern(sema->ip, rk);
+ (void)rep;
+
+ // Create final union_value with actual payload (not undef).
+ // This replaces the initial union_value($933) that had undef payload.
+ // Ported from validateUnionInit storing the finalized value.
+ InternPoolIndex existing_tag
+ = sema->ip->items[sema->comptime_ret_val].data.union_value;
+ InternPoolKey uvk;
+ memset(&uvk, 0, sizeof(uvk));
+ uvk.tag = IP_KEY_UNION_VALUE;
+ uvk.data.union_value = existing_tag;
+ InternPoolIndex final_uv = ipForceIntern(sema->ip, uvk);
+ sema->comptime_ret_val = final_uv;
+}
+
+// --- zirStructInitEmptyResult ---
+// Ported from Sema.zig zirStructInitEmptyResult.
+// For comptime union field init, this initializes the struct field with
+// defaults. Currently a no-op since the actual values are created by
+// validatePtrStructInit.
+static AirInstRef zirStructInitEmptyResult(
+ Sema* sema, SemaBlock* block, uint32_t inst) {
+ (void)sema;
+ (void)block;
+ (void)inst;
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+}
+
// --- zirRetPtr ---
-// Ported from src/Sema.zig zirRetPtr.
+// Ported from src/Sema.zig zirRetPtr / retPtrComptime.
static AirInstRef zirRetPtr(const Sema* sema, SemaBlock* block) {
+ // In comptime context with a tracked comptime alloc, return the
+ // comptime alloc pointer directly (no AIR ALLOC instruction).
+ // Ported from Sema.zig retPtrComptime.
+ if (block->is_comptime && sema->comptime_ret_ptr != IP_INDEX_NONE) {
+ return AIR_REF_FROM_IP(sema->comptime_ret_ptr);
+ }
TypeIndex ret_ty = sema->fn_ret_ty;
if (ret_ty == TYPE_NONE)
ret_ty = IP_INDEX_VOID_TYPE;
@@ -12403,9 +13602,53 @@ static AirInstRef zirEnumLiteral(Sema* sema, uint32_t inst) {
// --- zirOptEuBasePtrInit ---
// Ported from Sema.zig zirOptEuBasePtrInit.
+// In comptime: unwraps optional from the comptime alloc pointer,
+// creating intermediate IP entries (type_pointer, undef, opt_payload,
+// ptr_opt_payload) matching the Zig compiler's comptime alloc path.
static AirInstRef zirOptEuBasePtrInit(Sema* sema, uint32_t inst) {
ZirInstRef operand = sema->code.inst_datas[inst].un_node.operand;
- return resolveInst(sema, operand);
+ AirInstRef resolved = resolveInst(sema, operand);
+
+ // Comptime alloc path: unwrap optional from *mut ?T to get *mut T.
+ if (AIR_REF_IS_IP(resolved)
+ && AIR_REF_TO_IP(resolved) == sema->comptime_ret_ptr
+ && sema->comptime_ret_ptr != IP_INDEX_NONE) {
+ // Get the pointer type: *mut ?T
+ InternPoolIndex ptr_ty = ipTypeOf(sema->ip, sema->comptime_ret_ptr);
+ if (ptr_ty == IP_INDEX_NONE)
+ return resolved;
+ // Get the pointee type: ?T
+ InternPoolIndex opt_ty = sema->ip->items[ptr_ty].data.ptr_type.child;
+ // Check if it's an optional type.
+ if (sema->ip->items[opt_ty].tag != IP_KEY_OPT_TYPE)
+ return resolved;
+ // Get the child type: T
+ InternPoolIndex inner_ty = sema->ip->items[opt_ty].data.opt_type;
+ // Save inner type for comptime union init detection.
+ sema->comptime_inner_type = inner_ty;
+ // Create *mut T pointer type → matches Zig $926.
+ InternPoolIndex inner_ptr_ty = internPtrMutable(inner_ty);
+ // Create undef(T) → matches Zig $927.
+ InternPoolIndex undef_inner = internUndef(inner_ty);
+ // Create opt_payload(opt_ty, undef_inner) → matches Zig $928.
+ InternPoolKey opk;
+ memset(&opk, 0, sizeof(opk));
+ opk.tag = IP_KEY_OPT_PAYLOAD;
+ opk.data.opt_payload.ty = opt_ty;
+ opk.data.opt_payload.val = undef_inner;
+ (void)ipForceIntern(sema->ip, opk);
+ // Create ptr_opt_payload(inner_ptr_ty, comptime_alloc)
+ // → matches Zig $929.
+ InternPoolKey popk;
+ memset(&popk, 0, sizeof(popk));
+ popk.tag = IP_KEY_PTR_OPT_PAYLOAD;
+ popk.data.ptr_opt_payload.ty = inner_ptr_ty;
+ popk.data.ptr_opt_payload.base = sema->comptime_ret_ptr;
+ InternPoolIndex pop = ipForceIntern(sema->ip, popk);
+ return AIR_REF_FROM_IP(pop);
+ }
+
+ return resolved;
}
// --- zirAlloc ---
@@ -13339,15 +14582,23 @@ static bool analyzeBodyInner(
}
} else {
// Not an import — look up by name in the
- // file's namespace and return already-resolved
- // value. Don't call ensureNavValUpToDate to avoid
+ // file's namespace and return the nav value.
+ // During main analysis, lazily evaluate via
+ // ensureNavValUpToDate; during preamble, only
+ // return already-resolved values to avoid
// creating IP entries out of order.
uint32_t ns_idx = s_file_namespace[sema->file_idx];
uint32_t nav = findNavInNamespace(ns_idx, decl_name);
if (nav != UINT32_MAX) {
- const Nav* n = ipGetNav(nav);
- if (n->resolved_type != IP_INDEX_NONE)
- resolved = n->resolved_type;
+ if (s_in_main_analysis) {
+ InternPoolIndex val = ensureNavValUpToDate(nav);
+ if (val != IP_INDEX_NONE)
+ resolved = val;
+ } else {
+ const Nav* n = ipGetNav(nav);
+ if (n->resolved_type != IP_INDEX_NONE)
+ resolved = n->resolved_type;
+ }
}
}
} else if (sema->source_dir && decl_name_idx != 0) {
@@ -13761,13 +15012,21 @@ static bool analyzeBodyInner(
// Validation-only struct init instructions: no-op.
case ZIR_INST_VALIDATE_STRUCT_INIT_TY:
case ZIR_INST_VALIDATE_STRUCT_INIT_RESULT_TY:
- case ZIR_INST_VALIDATE_PTR_STRUCT_INIT:
case ZIR_INST_STRUCT_INIT_FIELD_TYPE:
instMapPut(
&sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE));
i++;
continue;
+ case ZIR_INST_VALIDATE_PTR_STRUCT_INIT:
+ if (block->is_comptime) {
+ zirValidatePtrStructInit(sema, inst);
+ }
+ instMapPut(
+ &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE));
+ i++;
+ continue;
+
// struct_init: struct initialization (comptime or runtime).
case ZIR_INST_STRUCT_INIT:
instMapPut(
@@ -13775,6 +15034,15 @@ static bool analyzeBodyInner(
i++;
continue;
+ // struct_init_empty_result / ref_result: empty struct init via
+ // result location. Ported from Sema.zig zirStructInitEmptyResult.
+ case ZIR_INST_STRUCT_INIT_EMPTY_RESULT:
+ case ZIR_INST_STRUCT_INIT_EMPTY_REF_RESULT:
+ instMapPut(&sema->inst_map, inst,
+ zirStructInitEmptyResult(sema, block, inst));
+ i++;
+ continue;
+
// decl_literal: resolve enum/decl literals at comptime.
case ZIR_INST_DECL_LITERAL:
case ZIR_INST_DECL_LITERAL_NO_COERCE:
@@ -13783,12 +15051,29 @@ static bool analyzeBodyInner(
i++;
continue;
- // str: string literal — map to void in comptime context.
- case ZIR_INST_STR:
- instMapPut(
- &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE));
+ // str: string literal.
+ // In comptime context, creates type_array_big + bytes +
+ // type_pointer + ptr_uav entries matching Zig's zirStr/addStrLit.
+ case ZIR_INST_STR: {
+ AirInstRef str_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+ if (block->is_comptime && s_in_main_analysis) {
+ uint32_t str_len = sema->code.inst_datas[inst].str.len;
+ // Extract the string from ZIR string_bytes.
+ char str_buf[256];
+ if (str_len < sizeof(str_buf)) {
+ uint32_t str_start = sema->code.inst_datas[inst].str.start;
+ memcpy(
+ str_buf, &sema->code.string_bytes[str_start], str_len);
+ str_buf[str_len] = '\0';
+ InternPoolIndex uav
+ = internStrLit(sema->ip, str_buf, str_len);
+ str_result = AIR_REF_FROM_IP(uav);
+ }
+ }
+ instMapPut(&sema->inst_map, inst, str_result);
i++;
continue;
+ }
case ZIR_INST_ALLOC:
case ZIR_INST_ALLOC_MUT:
@@ -14044,7 +15329,9 @@ SemaFuncAirList semaAnalyze(Sema* sema) {
sema->code.inst_datas[0].extended.opcode == ZIR_EXT_STRUCT_DECL);
uint32_t single_inst = 0;
+ s_in_main_analysis = true;
(void)analyzeBodyInner(sema, &root_block, &single_inst, 1);
+ s_in_main_analysis = false;
}
semaBlockDeinit(&root_block);
diff --git a/stage0/sema.h b/stage0/sema.h
@@ -291,6 +291,15 @@ typedef struct Sema {
// File index into s_loaded_modules[] for this sema's module.
// Set by analyzeNavValC / semaAnalyze. UINT32_MAX = unknown.
uint32_t file_idx;
+ // Comptime return alloc tracking for result-location function evaluation.
+ // Used by zirRetPtr/zirRetLoad/zirOptEuBasePtrInit/zirStoreNode in
+ // comptime context. Set by comptimeFieldCall.
+ // Ported from Sema.zig ComptimeAlloc / retPtrComptime.
+ InternPoolIndex comptime_ret_ptr; // ptr_comptime_alloc IP index
+ InternPoolIndex comptime_ret_val; // stored return value
+ InternPoolIndex comptime_inner_type; // inner type after optional unwrap
+ InternPoolIndex comptime_field_type; // union field type for struct init
+ uint32_t comptime_field_idx; // union field index for struct init
} Sema;
#define SEMA_DEFAULT_BRANCH_QUOTA 1000
diff --git a/stage0/verbose_intern_pool.c b/stage0/verbose_intern_pool.c
@@ -114,6 +114,10 @@ void verboseIpPrint(FILE* out, const InternPool* ip) {
key.data.int_type.signedness ? 'i' : 'u',
key.data.int_type.bits);
break;
+ case IP_KEY_ARRAY_TYPE:
+ fprintf(out, " len=%" PRIu64 " child=%u", key.data.array_type.len,
+ key.data.array_type.child);
+ break;
case IP_KEY_STRUCT_TYPE:
fprintf(out, " hash=%u", key.data.struct_type);
break;