zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 6a1f0158b3f8df73c41978a0aa6f9d1e27d4e619 (tree)
parent e2e49483379da9d001f0adc7924d59eab45a0e21
Author: Motiejus <motiejus@jakstys.lt>
Date:   Fri, 27 Feb 2026 13:42:19 +0000

sema: strip flag, lime1 model, cascade fixes, lint cleanup

- Add `strip` field to Sema struct; suppress debug instructions
  (dbg_stmt, dbg_var_val, dbg_inline_block, dbg_arg_inline) when
  strip=true, matching Zig's behavior for ReleaseSmall
- Add lime1 CPU model ptr_nav in triggerArchModuleCascade (default
  model for wasm32 target)
- Add comptime tracker tags for target_ptr and struct_field_ptr
- Add intern helpers: internPtrMutable, internUndef,
  internPtrComptimeAlloc, internPtrUav, internPtrField
- Add findStructFieldIndexFromZir for field lookup in struct_decl ZIR
- Add analyzeNavValC for general const declaration evaluation
- Add zirFieldValComptime namespace lookup on resolved types
- Expand triggerArchModuleCascade: CpuFeature, FeatureSetFns, Feature,
  featureSet, Set, addFeature
- Fix lint: const pointers, duplicate condition, remove unused intern
  functions (internUnionValue, internOptPayload, internOptNull,
  internRepeated, internPtrUavAligned, internPtrSlice)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mstage0/intern_pool.c | 1+
Mstage0/sema.c | 932+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mstage0/sema.h | 4++++
Mstage0/stages_test.zig | 2++
4 files changed, 903 insertions(+), 36 deletions(-)

diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c @@ -1,5 +1,6 @@ #include "intern_pool.h" #include <assert.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> diff --git a/stage0/sema.c b/stage0/sema.c @@ -66,6 +66,8 @@ static uint32_t s_cg_builtin_nav static uint32_t s_builtin_file_idx = UINT32_MAX; // file index of std/builtin.zig 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" // --- Namespace storage --- // Ported from Zcu.Namespace. @@ -196,12 +198,15 @@ static void instMapPut(InstMap* map, uint32_t zir_inst, AirInstRef ref) { // --- Comptime tracker helpers --- // Track comptime type-info values for cross-module inline evaluation. // tag: 0=none, 1=type_info(type), 2=float_info(bits), -// 3=int_info(signedness<<16|bits), 4=reify_int(int_info_ip) +// 3=int_info(signedness<<16|bits), 4=reify_int(int_info_ip), +// 5=target_ptr(ptr_nav IP), 6=struct_field_ptr(base_ip) #define CT_TAG_NONE 0 #define CT_TAG_TYPE_INFO 1 #define CT_TAG_FLOAT_INFO 2 #define CT_TAG_INT_INFO 3 #define CT_TAG_REIFY_INT 4 +#define CT_TAG_TARGET_PTR 5 +#define CT_TAG_STRUCT_FIELD_PTR 6 // Helper to read a bool through a pointer, preventing cppcheck from // assuming the value is unchanged across function calls that receive @@ -363,8 +368,8 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref); // dbg_stmt: emit AIR_INST_DBG_STMT with line/column from ZIR. // Ported from src/Sema.zig zirDbgStmt. static void zirDbgStmt(Sema* sema, SemaBlock* block, uint32_t inst) { - // In comptime blocks, debug statements are elided. - if (block->is_comptime) + // In comptime blocks or stripped modules, debug statements are elided. + if (block->is_comptime || sema->strip) return; uint32_t line = sema->code.inst_datas[inst].dbg_stmt.line; @@ -419,7 +424,7 @@ static uint32_t semaAppendAirString(Sema* sema, const char* str) { // Ported from src/Sema.zig zirDbgVar. static void zirDbgVar( Sema* sema, SemaBlock* block, uint32_t inst, AirInstTag air_tag) { - if (block->is_comptime) { + if (block->is_comptime || sema->strip) { return; } @@ -2432,6 +2437,67 @@ static InternPoolIndex internNavPtr( return ipIntern(s_module_ip, key); } +// --- internPtrMutable --- +// Create a single-item mutable pointer type: *child_type. +// Like internPtrConst but without the IS_CONST flag. +static InternPoolIndex internPtrMutable(InternPoolIndex child_type) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_PTR_TYPE; + key.data.ptr_type.child = child_type; + key.data.ptr_type.sentinel = IP_INDEX_NONE; + key.data.ptr_type.flags = PTR_FLAGS_SIZE_ONE; + key.data.ptr_type.packed_offset = 0; + return ipIntern(s_module_ip, key); +} + +// --- internUndef --- +// Create an undef value for the given type. +static InternPoolIndex internUndef(InternPoolIndex type_idx) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_UNDEF; + key.data.undef = type_idx; + return ipIntern(s_module_ip, key); +} + +// --- internPtrComptimeAlloc --- +// Create a ptr_comptime_alloc entry (comptime return value slot). +static InternPoolIndex internPtrComptimeAlloc( + InternPoolIndex ptr_type, uint32_t alloc_index) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_PTR_COMPTIME_ALLOC; + key.data.ptr_comptime_alloc.ty = ptr_type; + key.data.ptr_comptime_alloc.alloc_index = alloc_index; + return ipIntern(s_module_ip, key); +} + +// --- internPtrUav --- +// Create a ptr_uav entry (unique addressable value). +static InternPoolIndex internPtrUav( + InternPoolIndex ptr_type, InternPoolIndex val) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_PTR_UAV; + key.data.ptr_uav.ty = ptr_type; + key.data.ptr_uav.val = val; + return ipIntern(s_module_ip, key); +} + +// --- internPtrField --- +// Create a ptr_field entry (field access on a pointer). +static InternPoolIndex internPtrField( + InternPoolIndex ptr_type, InternPoolIndex base, uint32_t field_index) { + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_PTR_FIELD; + key.data.ptr_field.ty = ptr_type; + key.data.ptr_field.base = base; + key.data.ptr_field.field_index = field_index; + return ipIntern(s_module_ip, key); +} + // --- findNavInNamespace --- // Look up a Nav by name in a namespace. The name is compared against // the Nav's name string from the owning ZIR's string_bytes. @@ -3545,6 +3611,300 @@ static void resolveStructFieldTypesC(const Zir* zir, uint32_t struct_inst, } } +// --- findStructFieldIndexFromZir --- +// Parse a struct_decl ZIR instruction to find a field by name. +// Returns the 0-based field index, or UINT32_MAX if not found. +// Ported from the field iteration in resolveStructFieldTypesC. +static uint32_t findStructFieldIndexFromZir( + const Zir* zir, uint32_t struct_inst, const char* field_name) { + if (zir->inst_tags[struct_inst] != ZIR_INST_EXTENDED) + return UINT32_MAX; + if (zir->inst_datas[struct_inst].extended.opcode != ZIR_EXT_STRUCT_DECL) + return UINT32_MAX; + + uint16_t small = zir->inst_datas[struct_inst].extended.small; + uint32_t operand = zir->inst_datas[struct_inst].extended.operand; + + uint32_t ei + = operand + 6; // skip header (fields_hash×4, src_line, src_node) + + bool has_captures = (small & (1 << 0)) != 0; + bool has_fields = (small & (1 << 1)) != 0; + bool has_decls = (small & (1 << 2)) != 0; + bool has_backing_int = (small & (1 << 3)) != 0; + + uint32_t captures_len = 0; + uint32_t fields_len = 0; + uint32_t decls_len = 0; + if (has_captures) + captures_len = zir->extra[ei++]; + if (has_fields) + fields_len = zir->extra[ei++]; + if (has_decls) + decls_len = zir->extra[ei++]; + + if (fields_len == 0) + return UINT32_MAX; + + ei += captures_len * 2; + if (has_backing_int) { + uint32_t bi_body = zir->extra[ei++]; + ei += (bi_body == 0) ? 1 : bi_body; + } + ei += decls_len; + + // Parse field bit bags. + uint32_t bags_start = ei; + uint32_t bags_count = (fields_len + 7) / 8; + ei += bags_count; + + bool any_default_inits = (small & (1 << 10)) != 0; + + // Iterate fields to find the matching name. + for (uint32_t fi = 0; fi < fields_len; fi++) { + uint32_t bag_i = fi / 8; + uint32_t bit_off = (fi % 8) * 4; + uint32_t bits = (zir->extra[bags_start + bag_i] >> bit_off) & 0xf; + bool f_has_align = (bits & 1) != 0; + bool f_has_init = ((bits >> 1) & 1) != 0; + bool f_has_type_body = ((bits >> 3) & 1) != 0; + + uint32_t name_idx = zir->extra[ei++]; + const char* fname = (const char*)&zir->string_bytes[name_idx]; + + uint32_t type_body_len = 0; + if (f_has_type_body) + type_body_len = zir->extra[ei]; + ei++; // skip field type ref or type body len + if (f_has_align) + ei++; + if (f_has_init && any_default_inits) + ei++; + + if (strcmp(fname, field_name) == 0) + return fi; + + // Skip body instructions for this field. + (void)type_body_len; // used indirectly via resolveStructFieldType + } + return UINT32_MAX; +} + +// --- findStructDeclInst --- +// Find the ZIR struct_decl instruction for a struct type. +// For file root structs, returns 0 (the root instruction). +// For nested structs, finds the nav whose resolved_type matches and +// parses its declaration's value body to find the struct_decl. +// Sets *out_zir to the ZIR containing the declaration. +// Returns UINT32_MAX on failure. +static uint32_t findStructDeclInst( + InternPoolIndex struct_type, const Zir** out_zir) { + uint32_t ns_idx = findNamespaceForType(struct_type); + if (ns_idx == UINT32_MAX) + return UINT32_MAX; + 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 UINT32_MAX; + const Zir* zir = &s_loaded_modules[file_idx].zir; + *out_zir = zir; + if (zir->inst_len == 0) + return UINT32_MAX; + + // File root struct: instruction 0. + if (s_file_root_type[file_idx] == struct_type) + return 0; + + // Nested struct: find the nav with this resolved_type and parse + // its declaration to locate the struct_decl instruction. + for (uint32_t ni = 0; ni < s_num_namespaces; ni++) { + if (s_namespaces[ni].file_idx != file_idx) + continue; + for (uint32_t pi = 0; pi < s_namespaces[ni].pub_nav_count; pi++) { + uint32_t nv = s_namespaces[ni].pub_navs[pi]; + const Nav* n = ipGetNav(nv); + if (n->resolved_type != struct_type) + continue; + const uint32_t* vb = NULL; + uint32_t vb_len = 0; + getValueBodyFromZir(zir, n->zir_index, &vb, &vb_len); + for (uint32_t vi = 0; vi < vb_len; vi++) { + if (zir->inst_tags[vb[vi]] != ZIR_INST_EXTENDED) + continue; + if (zir->inst_datas[vb[vi]].extended.opcode + == ZIR_EXT_STRUCT_DECL) + return vb[vi]; + } + } + } + return UINT32_MAX; +} + +// --- findStructFieldIndexByType --- +// Find a field index by name in a struct type, looking up through +// the namespace → file → ZIR. Handles both root and nested structs. +// Returns UINT32_MAX if not found. +static uint32_t findStructFieldIndexByType( + InternPoolIndex struct_type, const char* field_name) { + const Zir* zir = NULL; + uint32_t inst = findStructDeclInst(struct_type, &zir); + if (inst == UINT32_MAX) + return UINT32_MAX; + return findStructFieldIndexFromZir(zir, inst, field_name); +} + +// Forward declaration (defined after resolveStructFieldType). +static InternPoolIndex resolveStructFieldType(const Zir* zir, + uint32_t struct_inst, uint32_t target_field, uint32_t struct_ns, + uint32_t file_idx); + +// --- resolveFieldTypeByIP --- +// Resolve the type of a struct field given the struct type and field index. +// Returns IP_INDEX_NONE on failure. +static InternPoolIndex resolveFieldTypeByIP( + InternPoolIndex struct_type, uint32_t field_idx) { + const Zir* zir = NULL; + uint32_t inst = findStructDeclInst(struct_type, &zir); + if (inst == UINT32_MAX) + return IP_INDEX_NONE; + uint32_t ns_idx = findNamespaceForType(struct_type); + if (ns_idx == UINT32_MAX) + return IP_INDEX_NONE; + uint32_t file_idx = s_namespaces[ns_idx].file_idx; + return resolveStructFieldType(zir, inst, field_idx, ns_idx, file_idx); +} + +// --- findEnumDeclForNav --- +// Find the ZIR enum_decl instruction for an enum type nav. +// Parses the nav's declaration value body to find the EXTENDED/ENUM_DECL. +// Sets *out_zir to the ZIR. Returns UINT32_MAX on failure. +static uint32_t findEnumDeclForNav(uint32_t nav_idx, const Zir** out_zir) { + const Nav* n = ipGetNav(nav_idx); + uint32_t ns_idx = n->namespace_idx; + if (ns_idx == UINT32_MAX || ns_idx >= s_num_namespaces) + return UINT32_MAX; + 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 UINT32_MAX; + const Zir* zir = &s_loaded_modules[file_idx].zir; + *out_zir = zir; + + const uint32_t* vb = NULL; + uint32_t vb_len = 0; + getValueBodyFromZir(zir, n->zir_index, &vb, &vb_len); + for (uint32_t vi = 0; vi < vb_len; vi++) { + if (zir->inst_tags[vb[vi]] != ZIR_INST_EXTENDED) + continue; + if (zir->inst_datas[vb[vi]].extended.opcode == ZIR_EXT_ENUM_DECL) + return vb[vi]; + } + return UINT32_MAX; +} + +// --- resolveStructFieldType --- +// Resolve the type of a specific struct field from ZIR. +// Returns the IP index of the field's type, or IP_INDEX_NONE on failure. +// Ported from resolveStructFieldTypesC (single-field version). +static InternPoolIndex resolveStructFieldType(const Zir* zir, + uint32_t struct_inst, uint32_t target_field, uint32_t struct_ns, + uint32_t file_idx) { + if (zir->inst_tags[struct_inst] != ZIR_INST_EXTENDED) + return IP_INDEX_NONE; + if (zir->inst_datas[struct_inst].extended.opcode != ZIR_EXT_STRUCT_DECL) + return IP_INDEX_NONE; + + uint16_t small = zir->inst_datas[struct_inst].extended.small; + uint32_t operand = zir->inst_datas[struct_inst].extended.operand; + + uint32_t ei = operand + 6; + bool has_captures = (small & (1 << 0)) != 0; + bool has_fields = (small & (1 << 1)) != 0; + bool has_decls = (small & (1 << 2)) != 0; + bool has_backing_int = (small & (1 << 3)) != 0; + + uint32_t captures_len = 0; + uint32_t fields_len = 0; + uint32_t decls_len = 0; + if (has_captures) + captures_len = zir->extra[ei++]; + if (has_fields) + fields_len = zir->extra[ei++]; + if (has_decls) + decls_len = zir->extra[ei++]; + + if (target_field >= fields_len) + return IP_INDEX_NONE; + + ei += captures_len * 2; + if (has_backing_int) { + uint32_t bi_body = zir->extra[ei++]; + ei += (bi_body == 0) ? 1 : bi_body; + } + ei += decls_len; + + uint32_t bags_start = ei; + uint32_t bags_count = (fields_len + 7) / 8; + ei += bags_count; + + bool any_default_inits = (small & (1 << 10)) != 0; + + // Skip fields until we reach target_field, recording body lengths. + uint32_t body_skip = 0; + for (uint32_t fi = 0; fi <= target_field; fi++) { + uint32_t bag_i = fi / 8; + uint32_t bit_off = (fi % 8) * 4; + uint32_t bits = (zir->extra[bags_start + bag_i] >> bit_off) & 0xf; + bool f_has_align = (bits & 1) != 0; + bool f_has_init = ((bits >> 1) & 1) != 0; + bool f_has_type_body = ((bits >> 3) & 1) != 0; + + ei++; // skip field_name + + uint32_t type_body_len = 0; + uint32_t type_ref = 0; + if (f_has_type_body) { + type_body_len = zir->extra[ei]; + } else { + type_ref = zir->extra[ei]; + } + ei++; + + uint32_t align_body_len = 0; + uint32_t init_body_len = 0; + if (f_has_align) { + align_body_len = zir->extra[ei++]; + } + if (f_has_init && any_default_inits) { + init_body_len = zir->extra[ei++]; + } + + if (fi == target_field) { + // This is the field we want. Skip the body data + // accumulated from previous fields, then resolve type. + ei += body_skip; + if (f_has_type_body && type_body_len > 0) { + uint32_t last_zi = zir->extra[ei + type_body_len - 1]; + if (last_zi < zir->inst_len + && zir->inst_tags[last_zi] == ZIR_INST_BREAK_INLINE) { + ZirInstRef op + = zir->inst_datas[last_zi].break_data.operand; + return resolveZirTypeRef(zir, op, struct_ns, file_idx); + } + } else if (!f_has_type_body && type_ref < ZIR_REF_START_INDEX) { + // Simple type reference (pre-interned). + return type_ref; + } else if (!f_has_type_body && type_ref >= ZIR_REF_START_INDEX) { + return resolveZirTypeRef(zir, type_ref, struct_ns, file_idx); + } + return IP_INDEX_NONE; + } + + body_skip += type_body_len + align_body_len + init_body_len; + } + return IP_INDEX_NONE; +} + // --- resolveStructFieldInitsC --- // Evaluate struct field default values from ZIR init bodies. // For each field with an init body, reads the break_inline operand, @@ -4625,9 +4985,18 @@ static InternPoolIndex ensureNavValUpToDate(uint32_t nav_idx) { const char* import_path = (const char*)&zir->string_bytes[path_idx]; - // Skip builtin imports (not a real file). - if (strcmp(import_path, "builtin") == 0) + // @import("builtin") — resolve to the compiler-generated + // builtin module's root struct type. + if (strcmp(import_path, "builtin") == 0) { + if (s_cg_builtin_ns_idx != UINT32_MAX) { + InternPoolIndex cg_struct + = s_namespaces[s_cg_builtin_ns_idx].owner_type; + Nav* wnav = ipGetNav(nav_idx); + wnav->resolved_type = cg_struct; + return cg_struct; + } break; + } char import_full[1024]; if (!resolveImportPath(s_loaded_modules[file_idx].source_dir, @@ -5153,21 +5522,23 @@ static void resolveTargetModuleChain(void) { // std.SemanticVersion.Range. uint32_t vr_nav = resolveNavType(os_ns, "VersionRange"); - // Target.zig has `const std = @import("std")`. The Zig - // compiler creates a ptr_nav for it when resolving - // VersionRange's field types (which reference - // std.SemanticVersion). + // Target.zig has `const std = @import("std")`. + // Set resolved_type so downstream code (e.g. SemanticVersion + // field type resolution) can resolve std.* references. + // The ptr_nav for this import is created later by the + // cascade (not here — Zig compiler defers it). { uint32_t std_import_nav = findNavInNamespace(target_ns, "std"); if (std_import_nav != UINT32_MAX) { Nav* si = ipGetNav(std_import_nav); if (si->resolved_type == IP_INDEX_NONE) si->resolved_type = s_file_root_type[s_std_file_idx]; - (void)internNavPtr(ptr_type, std_import_nav); } } // Load std.SemanticVersion (needed by Os.VersionRange) + // Only set resolved_type — the ptr_nav is created later + // by demand-driven resolution (not here). uint32_t sem_nav = findNavInNamespace(std_ns_idx, "SemanticVersion"); if (sem_nav != UINT32_MAX) { @@ -5183,7 +5554,6 @@ static void resolveTargetModuleChain(void) { if (sem_file != UINT32_MAX) { Nav* snav = ipGetNav(sem_nav); snav->resolved_type = s_file_root_type[sem_file]; - (void)internNavPtr(ptr_type, sem_nav); } } } @@ -5206,6 +5576,145 @@ static void resolveTargetModuleChain(void) { (void)target_zir; } +// --- resolveNavRef --- +// Resolve a nav's value and create ptr_type + ptr_nav for it. +// This matches the Zig compiler's analyzeNavRefInner: first resolve +// the nav value via ensureNavResolved, then create ptr_nav. +// Returns the ptr_nav IP index, or IP_INDEX_NONE. +static InternPoolIndex resolveNavRef(uint32_t nav_idx) { + (void)ensureNavValUpToDate(nav_idx); + const Nav* n = ipGetNav(nav_idx); + if (n->resolved_type == IP_INDEX_NONE) + return IP_INDEX_NONE; + InternPoolIndex val_ty = ipTypeOf(s_module_ip, n->resolved_type); + if (val_ty == IP_INDEX_NONE) + val_ty = IP_INDEX_TYPE_TYPE; + InternPoolIndex ptr_ty = internPtrConst(val_ty); + return internNavPtr(ptr_ty, nav_idx); +} + +// --- triggerArchModuleCascade --- +// Trigger the arch-specific module import cascade that the Zig compiler +// performs during cCallingConvention() evaluation. When the Zig compiler +// dereferences *const Target in loadComptimePtrInner, it cascades through +// nav resolution into importing the arch-specific module (e.g. +// Target/wasm.zig for wasm32). This creates type_struct, ptr_nav, +// type_enum_auto, func_decl, memoized_call entries etc. +// +// The arch module name is derived from the arch family: wasm32/wasm64 → +// "wasm", x86/x86_64 → "x86", etc. We find the matching nav in +// Target.zig's namespace by trying progressively shorter prefixes. +// +// The nav resolution order was determined by tracing the Zig compiler's +// demand-driven evaluation in loadComptimePtrInner. We replicate the +// exact same order to produce matching IP indices. +// +// Ported from the side effects of Sema/comptime_ptr_access.zig +// loadComptimePtrInner → ensureNavResolved → analyzeNavVal chain. +static void triggerArchModuleCascade( + uint32_t target_ns, uint32_t target_file_idx) { + if (!s_target_cpu_arch_name || !s_module_ip) + return; + + // Find the arch module nav by trying progressively shorter prefixes + // of the arch name. E.g. "wasm32" → "wasm32" (no) → ... → "wasm" (yes). + char name[64]; + snprintf(name, sizeof(name), "%s", s_target_cpu_arch_name); + + uint32_t arch_module_nav = UINT32_MAX; + for (size_t len = strlen(name); len > 0; len--) { + name[len] = '\0'; + arch_module_nav = findNavInNamespace(target_ns, name); + if (arch_module_nav != UINT32_MAX) + break; + } + if (arch_module_nav == UINT32_MAX) + return; + + // $685: Resolve the arch module nav → @import("Target/<arch>.zig"). + // Creates type_struct for the arch module root. + (void)ensureNavValUpToDate(arch_module_nav); + const Nav* arch_n = ipGetNav(arch_module_nav); + if (arch_n->resolved_type == IP_INDEX_NONE) + return; + InternPoolIndex arch_struct = arch_n->resolved_type; + uint32_t arch_ns = findNamespaceForType(arch_struct); + if (arch_ns == UINT32_MAX) + return; + + // $686: ptr_nav for the arch module nav (e.g. "wasm" in Target.zig). + InternPoolIndex arch_ptr_ty = internPtrConst(IP_INDEX_TYPE_TYPE); + (void)internNavPtr(arch_ptr_ty, arch_module_nav); + + // Resolve navs in the arch module in the order the Zig compiler's + // demand-driven evaluation resolves them during loadComptimePtrInner. + // Order determined by tracing the Zig compiler (see cascade trace). + + // $687-$688: "cpu" struct in arch module. + uint32_t cpu_nav = findNavInNamespace(arch_ns, "cpu"); + if (cpu_nav != UINT32_MAX) + (void)resolveNavRef(cpu_nav); + + // $689: "std" (private import in arch module). + uint32_t std_priv = findNavInNamespace(arch_ns, "std"); + if (std_priv != UINT32_MAX) + (void)resolveNavRef(std_priv); + + // $690: "CpuModel" (private const = std.Target.Cpu.Model). + uint32_t cpumodel = findNavInNamespace(arch_ns, "CpuModel"); + if (cpumodel != UINT32_MAX) + (void)resolveNavRef(cpumodel); + + // $691: ptr_nav for the target's default CPU model constant. + // The Zig compiler resolves exactly one CPU model during the + // cCallingConvention cascade (demand-driven from loadComptimePtrInner + // dereferencing cpu.model which is *const CpuModel). + // Find it in the cpu struct's namespace using s_target_cpu_model_name. + if (cpu_nav != UINT32_MAX && s_target_cpu_model_name) { + const Nav* cpu_n = ipGetNav(cpu_nav); + if (cpu_n->resolved_type != IP_INDEX_NONE) { + uint32_t cpu_ns = findNamespaceForType(cpu_n->resolved_type); + if (cpu_ns != UINT32_MAX) { + uint32_t model_nav + = findNavInNamespace(cpu_ns, s_target_cpu_model_name); + if (model_nav != UINT32_MAX) + (void)resolveNavRef(model_nav); + } + } + } + + // $692: "CpuFeature" (private const = std.Target.Cpu.Feature). + uint32_t cpufeat = findNavInNamespace(arch_ns, "CpuFeature"); + if (cpufeat != UINT32_MAX) { + (void)resolveNavRef(cpufeat); + + // $693-$696: FeatureSetFns — a function in std.Target.Cpu.Feature. + // (Not in Feature.Set — FeatureSetFns is at Feature's level.) + const Nav* cf = ipGetNav(cpufeat); + if (cf->resolved_type != IP_INDEX_NONE) { + uint32_t feat_ns = findNamespaceForType(cf->resolved_type); + if (feat_ns != UINT32_MAX) { + uint32_t fsf_nav + = findNavInNamespace(feat_ns, "FeatureSetFns"); + if (fsf_nav != UINT32_MAX) + (void)resolveNavRef(fsf_nav); + } + } + } + + // $697-$698: "Feature" enum in arch module. + uint32_t feature = findNavInNamespace(arch_ns, "Feature"); + if (feature != UINT32_MAX) + (void)resolveNavRef(feature); + + // $706: "featureSet" function in arch module. + uint32_t fset = findNavInNamespace(arch_ns, "featureSet"); + if (fset != UINT32_MAX) + (void)resolveNavRef(fset); + + (void)target_file_idx; +} + // --- resolveExportPreamble --- // Create IP entries produced when the Zig compiler processes the @export // statement in neghf2.zig's comptime block. This triggers resolution of @@ -5278,6 +5787,22 @@ static void resolveExportPreamble(void) { (void)internNavPtr(ccc_ptr, ccc_nav); } } + + // $675+: Evaluate CallingConvention.c to create call frame entries + // and comptime evaluation entries. In the Zig compiler, this happens + // when @export resolves callconv(.c), which triggers evaluation of + // CallingConvention.c = builtin.target.cCallingConvention().? + // The evaluation cascades through field access, function call, and + // produces IP entries for the call frame setup. + if (cc_type != IP_INDEX_NONE) { + uint32_t cc_ns_inner = findNamespaceForType(cc_type); + if (cc_ns_inner != UINT32_MAX) { + uint32_t c_nav = findNavInNamespace(cc_ns_inner, "c"); + if (c_nav != UINT32_MAX) { + (void)ensureNavValUpToDate(c_nav); + } + } + } } // --- resolveBuiltinDeclTypes --- @@ -5435,6 +5960,12 @@ static void resolveStartComptimePreamble(void) { InternPoolIndex zig_backend_int = internTypedInt(IP_INDEX_U64_TYPE, 4); (void)internEnumTag(cb_enum_ip, zig_backend_int); + // Store target CPU arch name for comptime evaluator (e.g. + // builtin.target.cpu.arch resolution in cCallingConvention). + // Determined from the same target info that sets zig_backend. + s_target_cpu_arch_name = "wasm32"; + s_target_cpu_model_name = "lime1"; + // --- $169-$170: type_pointer + ptr_nav for zig_backend access --- // When start.zig accesses builtin.zig_backend, the Zig compiler // creates a ptr_nav for the zig_backend Nav in the compiler-generated @@ -5597,6 +6128,54 @@ static const char* findDeclImportPathFromZir( // Given module source_dir and import_path, compute full file path. // Returns true if resolution succeeded. +// Normalize a path in-place by resolving ".." components. +// E.g. "/a/b/c/../d.zig" → "/a/b/d.zig". +static void normalizePath(char* path) { + // Split into components, resolve "..", then rejoin. + // We work in-place since the result is always <= input length. + char* parts[128]; + uint32_t nparts = 0; + bool absolute = (path[0] == '/'); + + // Tokenize by '/'. + char* p = path; + while (*p) { + while (*p == '/') + p++; + if (*p == '\0') + break; + char* start = p; + while (*p && *p != '/') + p++; + if (*p) { + *p = '\0'; + p++; + } + if (strcmp(start, ".") == 0) + continue; + if (strcmp(start, "..") == 0) { + if (nparts > 0) + nparts--; + continue; + } + if (nparts < 128) + parts[nparts++] = start; + } + + // Rebuild path. + char* out = path; + if (absolute) + *out++ = '/'; + for (uint32_t i = 0; i < nparts; i++) { + if (i > 0) + *out++ = '/'; + size_t len = strlen(parts[i]); + memmove(out, parts[i], len); + out += len; + } + *out = '\0'; +} + static bool resolveImportPath(const char* source_dir, const char* import_path, char* out_full, size_t out_size) { const char* rel = import_path; @@ -5606,6 +6185,7 @@ static bool resolveImportPath(const char* source_dir, const char* import_path, // Relative or absolute paths: resolve from source_dir. if (import_path[0] == '.' || import_path[0] == '/') { snprintf(out_full, out_size, "%s/%s", source_dir, rel); + normalizePath(out_full); return true; } @@ -5614,6 +6194,7 @@ static bool resolveImportPath(const char* source_dir, const char* import_path, const char* dot = strrchr(import_path, '.'); if (dot && strcmp(dot, ".zig") == 0) { snprintf(out_full, out_size, "%s/%s", source_dir, import_path); + normalizePath(out_full); return true; } @@ -5786,6 +6367,8 @@ static void resetModuleTracking(void) { s_builtin_file_idx = UINT32_MAX; s_cg_builtin_nav = UINT32_MAX; s_global_module_root = NULL; + s_target_cpu_arch_name = NULL; + s_target_cpu_model_name = NULL; ipResetNavs(); memset(s_file_root_type, 0, sizeof(s_file_root_type)); memset(s_file_namespace, 0, sizeof(s_file_namespace)); @@ -6566,8 +7149,14 @@ static AirInstRef comptimeFieldCall(Sema* sema, SemaBlock* block, // Find the type's namespace. If the object IS a type (typeOf == // type), use it directly. Otherwise use the object's type. + // For pointer types, dereference to get the pointee type's namespace + // (e.g. *const Target → Target namespace for method lookup). InternPoolIndex ns_type = (obj_type == IP_INDEX_TYPE_TYPE) ? obj_ip : obj_type; + if (ns_type != IP_INDEX_NONE + && sema->ip->items[ns_type].tag == IP_KEY_PTR_TYPE) { + ns_type = sema->ip->items[ns_type].data.ptr_type.child; + } uint32_t ns_idx = findNamespaceForType(ns_type); if (ns_idx == UINT32_MAX) return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); @@ -6638,12 +7227,21 @@ static AirInstRef comptimeFieldCall(Sema* sema, SemaBlock* block, uint32_t param_count = 0; { ZirInstTag pb_tag = fn_zir->inst_tags[param_block_inst]; + const uint32_t* pb_body = NULL; + uint32_t pb_body_len = 0; if (pb_tag == ZIR_INST_BLOCK || pb_tag == ZIR_INST_BLOCK_COMPTIME || pb_tag == ZIR_INST_BLOCK_INLINE) { uint32_t pb_payload = fn_zir->inst_datas[param_block_inst].pl_node.payload_index; - uint32_t pb_body_len = fn_zir->extra[pb_payload]; - const uint32_t* pb_body = &fn_zir->extra[pb_payload + 1]; + pb_body_len = fn_zir->extra[pb_payload]; + pb_body = &fn_zir->extra[pb_payload + 1]; + } else if (pb_tag == ZIR_INST_DECLARATION) { + // Ported from Zir.getParamBody: for declaration param blocks, + // the params are in the declaration's value_body. + getValueBodyFromZir( + fn_zir, param_block_inst, &pb_body, &pb_body_len); + } + if (pb_body) { for (uint32_t pi = 0; pi < pb_body_len && pi < 16; pi++) { ZirInstTag ptag = fn_zir->inst_tags[pb_body[pi]]; if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME @@ -6667,11 +7265,53 @@ static AirInstRef comptimeFieldCall(Sema* sema, SemaBlock* block, fn_zir->inst_len * sizeof(AirInstRef)); } + // --- Call frame setup for comptime function calls --- + // Ported from Sema.zig analyzeCall comptime path. + // Creates: *const param_type, ptr_uav, *mut return_type, undef, + // ptr_comptime_alloc. These must be created before body evaluation + // to match the Zig compiler's IP entry order. + { + // Get function type to extract return type. + const Nav* fn_nav_resolved = ipGetNav(fn_nav_idx); + InternPoolIndex fn_decl_ip = fn_nav_resolved->resolved_type; + if (fn_decl_ip != IP_INDEX_NONE + && sema->ip->items[fn_decl_ip].tag == IP_KEY_FUNC) { + InternPoolIndex fn_type_ip + = sema->ip->items[fn_decl_ip].data.func_decl.ty; + if (fn_type_ip != IP_INDEX_NONE + && sema->ip->items[fn_type_ip].tag == IP_KEY_FUNC_TYPE) { + InternPoolIndex ret_ty + = sema->ip->items[fn_type_ip].data.func_type.return_type; + // Set inner sema's return type so zirRetPtr/zirRetType + // produce the correct pointer type during body evaluation. + fn_sema.fn_ret_ty = ret_ty; + // Create *const param_type (pointer to the parameter). + InternPoolIndex param_ptr_ty = internPtrConst(obj_type); + // Create ptr_uav wrapping the parameter value. + (void)internPtrUav(param_ptr_ty, obj_ip); + // Create *mut return_type for the return value alloc. + InternPoolIndex ret_mut_ptr = internPtrMutable(ret_ty); + // Create undef(return_type). + (void)internUndef(ret_ty); + // Create ptr_comptime_alloc for return value storage. + (void)internPtrComptimeAlloc(ret_mut_ptr, 0); + } + } + } + // Map parameter instructions to argument values. // For field_call, the first param is the object (self). // Ported from Sema.zig analyzeCall line 7773-7776. if (param_count > 0) { + // Transfer CT tracking from the outer sema to the inner sema. + // This allows the inner function body to resolve field accesses + // on tracked comptime values (e.g. target pointer). + uint32_t ct_val; + uint8_t ct_tag = ctLookup(sema, AIR_REF_TO_IP(obj_ref), &ct_val); instMapPut(&fn_sema.inst_map, param_insts[0], obj_ref); + if (ct_tag != CT_TAG_NONE) { + ctTrack(&fn_sema, AIR_REF_TO_IP(obj_ref), ct_tag, ct_val); + } } // Evaluate the function body. @@ -8514,10 +9154,9 @@ static AirInstRef zirCall( sema->code = dbg_code; } - // Upstream: need_debug_scope = !block.isComptime() && ... - // When comptime (or all args comptime), use BLOCK; otherwise - // DBG_INLINE_BLOCK. - bool need_debug_scope = !block->is_comptime; + // Upstream: need_debug_scope = !block.isComptime() && !block.is_typeof + // && !block.ownerModule().strip + bool need_debug_scope = !block->is_comptime && !sema->strip; AirInstTag block_tag = need_debug_scope ? AIR_INST_DBG_INLINE_BLOCK : AIR_INST_BLOCK; @@ -8633,7 +9272,8 @@ static AirInstRef zirCall( } } } - if (!child_block.is_comptime && !arg_comptime_only) { + if (!child_block.is_comptime && !sema->strip + && !arg_comptime_only) { uint32_t param_name_idx; if (ptag == ZIR_INST_PARAM_ANYTYPE) { // str_tok: name is at str_tok.start. @@ -10203,6 +10843,107 @@ static AirInstRef zirFieldValComptime( return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); } + // Comptime field access on target pointer: target.cpu, target.cpu.arch. + // When the body of cCallingConvention evaluates target.cpu.arch, + // the Zig compiler creates ptr_type, ptr_nav, ptr_field, and enum_tag + // entries. Ported from Sema.zig fieldVal / fieldPtr comptime path. + if (ct_tag == CT_TAG_TARGET_PTR && strcmp(field_name, "cpu") == 0) { + // Access .cpu on the comptime target pointer. + // ct_val = Target struct type IP index. + InternPoolIndex target_ty = ct_val; + + // Find the "cpu" field in Target struct. + uint32_t cpu_fidx = findStructFieldIndexByType(target_ty, "cpu"); + if (cpu_fidx != UINT32_MAX) { + InternPoolIndex cpu_type + = resolveFieldTypeByIP(target_ty, cpu_fidx); + if (cpu_type != IP_INDEX_NONE) { + // Create *const Cpu pointer type ($680). + InternPoolIndex const_cpu_ptr = internPtrConst(cpu_type); + // Create *mut Target pointer type ($681). + InternPoolIndex mut_tgt_ptr = internPtrMutable(target_ty); + // Create ptr_nav for the target ($682). + // Use the CG builtin "target" nav with *mut Target type. + uint32_t tgt_nav + = findNavInNamespace(s_cg_builtin_ns_idx, "target"); + if (tgt_nav != UINT32_MAX) { + InternPoolIndex nav_ptr + = internNavPtr(mut_tgt_ptr, tgt_nav); + // Create ptr_field ($683). + InternPoolIndex pf + = internPtrField(const_cpu_ptr, nav_ptr, cpu_fidx); + ctTrack(sema, pf, CT_TAG_STRUCT_FIELD_PTR, cpu_type); + return AIR_REF_FROM_IP(pf); + } + } + } + } + + if (ct_tag == CT_TAG_STRUCT_FIELD_PTR && strcmp(field_name, "arch") == 0 + && s_target_cpu_arch_name != NULL) { + // Access .arch on the Cpu struct (from target.cpu.arch). + // ct_val = Cpu struct type IP index. + InternPoolIndex cpu_type = ct_val; + uint32_t cpu_ns = findNamespaceForType(cpu_type); + if (cpu_ns != UINT32_MAX) { + // Find the Arch enum type in Cpu namespace. + uint32_t arch_nav = findNavInNamespace(cpu_ns, "Arch"); + if (arch_nav != UINT32_MAX) { + const Nav* arch_n = ipGetNav(arch_nav); + InternPoolIndex arch_ty = arch_n->resolved_type; + if (arch_ty != IP_INDEX_NONE) { + // Find the target arch field in the Arch enum ZIR. + const Zir* arch_zir = NULL; + uint32_t enum_inst + = findEnumDeclForNav(arch_nav, &arch_zir); + if (enum_inst != UINT32_MAX) { + uint32_t arch_fidx = findEnumFieldByName( + arch_zir, enum_inst, s_target_cpu_arch_name); + if (arch_fidx != UINT32_MAX) { + InternPoolIndex int_val = getEnumFieldIntVal( + arch_zir, enum_inst, arch_fidx); + if (int_val != IP_INDEX_NONE) { + InternPoolIndex tag + = internEnumTag(arch_ty, int_val); + // Trigger arch module cascade ($685+). + // Must happen here (after enum_tag, before + // switch body creates enum_literals). + uint32_t tfi + = findFileByPathSuffix("/std/Target.zig"); + if (tfi != UINT32_MAX) { + triggerArchModuleCascade( + s_file_namespace[tfi], tfi); + } + return AIR_REF_FROM_IP(tag); + } + } + } + } + } + } + } + + // General namespace lookup: when the base object is a struct/union type, + // look up the field name as a declaration in its namespace. + // This handles chains like std.Target.Cpu.Model where each step + // resolves a type declaration in the previous type's namespace. + // Ported from Sema.zig fieldVal for types → analyzeNavRefInner path. + if (obj_ip < sema->ip->items_len && obj_ip != IP_INDEX_VOID_VALUE) { + InternPoolKeyTag kt = sema->ip->items[obj_ip].tag; + if (kt == IP_KEY_STRUCT_TYPE || kt == IP_KEY_UNION_TYPE + || kt == IP_KEY_ENUM_TYPE) { + uint32_t base_ns = findNamespaceForType(obj_ip); + if (base_ns != UINT32_MAX) { + uint32_t member_nav = findNavInNamespace(base_ns, field_name); + if (member_nav != UINT32_MAX) { + InternPoolIndex val = ensureNavValUpToDate(member_nav); + if (val != IP_INDEX_NONE) + return AIR_REF_FROM_IP(val); + } + } + } + } + // Handle cross-module struct type resolution: // std.math.F80 → register the F80 struct (fraction: u64, exp: u16). // Ported from lib/std/math.zig F80 definition. @@ -10235,6 +10976,29 @@ static AirInstRef zirFieldValComptime( uint32_t field_nav = findNavInNamespace(s_cg_builtin_ns_idx, field_name); if (field_nav != UINT32_MAX) { + // For "target", return the ptr_nav (pointer to the + // target value) rather than the type. This enables + // comptime method calls like + // target.cCallingConvention() where the evaluator + // needs a pointer, not a type. + // Track it so the comptime evaluator can resolve + // field accesses on the target value. + if (strcmp(field_name, "target") == 0) { + const Nav* tn = ipGetNav(field_nav); + if (tn->resolved_type != IP_INDEX_NONE) { + InternPoolIndex ptr_ty + = internPtrConst(tn->resolved_type); + InternPoolKey pk; + memset(&pk, 0, sizeof(pk)); + pk.tag = IP_KEY_PTR_NAV; + pk.data.ptr_nav.ty = ptr_ty; + pk.data.ptr_nav.nav = field_nav; + InternPoolIndex pn = ipIntern(sema->ip, pk); + ctTrack(sema, pn, CT_TAG_TARGET_PTR, + tn->resolved_type); + return AIR_REF_FROM_IP(pn); + } + } InternPoolIndex val = ensureNavValUpToDate(field_nav); if (val != IP_INDEX_NONE) return AIR_REF_FROM_IP(val); @@ -10277,6 +11041,61 @@ static AirInstRef zirFieldPtr(Sema* sema, SemaBlock* block, uint32_t inst) { AirInstRef obj = resolveInst(sema, obj_ref); + // Cross-module comptime pointer resolution. + // When the object is from a cross-module import (decl_ref of + // @import), load the imported module's ZIR and resolve the field. + // This handles patterns like: + // @import("builtin").target → ptr_nav(target) + // Ported from zirFieldValComptime cross-module path, adapted for + // pointer (field_ptr) context. + if (AIR_REF_IS_IP(obj) && block->is_comptime) { + InternPoolIndex obj_ip = AIR_REF_TO_IP(obj); + if (obj_ip == IP_INDEX_VOID_VALUE && obj_ref >= ZIR_REF_START_INDEX + && sema->source_dir) { + uint32_t obj_inst = obj_ref - ZIR_REF_START_INDEX; + ZirInstTag obj_tag = sema->code.inst_tags[obj_inst]; + if (obj_tag == ZIR_INST_DECL_VAL || obj_tag == ZIR_INST_DECL_REF) { + uint32_t decl_name_idx + = sema->code.inst_datas[obj_inst].str_tok.start; + const char* import_path + = findDeclImportPath(sema, decl_name_idx); + if (import_path && strcmp(import_path, "builtin") == 0 + && s_cg_builtin_ns_idx != UINT32_MAX) { + uint32_t field_nav + = findNavInNamespace(s_cg_builtin_ns_idx, field_name); + if (field_nav != UINT32_MAX) { + if (strcmp(field_name, "target") == 0) { + const Nav* tn = ipGetNav(field_nav); + if (tn->resolved_type != IP_INDEX_NONE) { + InternPoolIndex ptr_ty + = internPtrConst(tn->resolved_type); + InternPoolKey pk; + memset(&pk, 0, sizeof(pk)); + pk.tag = IP_KEY_PTR_NAV; + pk.data.ptr_nav.ty = ptr_ty; + pk.data.ptr_nav.nav = field_nav; + InternPoolIndex pn = ipIntern(sema->ip, pk); + ctTrack(sema, pn, CT_TAG_TARGET_PTR, + tn->resolved_type); + return AIR_REF_FROM_IP(pn); + } + } + InternPoolIndex val = ensureNavValUpToDate(field_nav); + if (val != IP_INDEX_NONE) { + InternPoolIndex ptr_ty = internPtrConst(val); + InternPoolKey pk; + memset(&pk, 0, sizeof(pk)); + pk.tag = IP_KEY_PTR_NAV; + pk.data.ptr_nav.ty = ptr_ty; + pk.data.ptr_nav.nav = field_nav; + return AIR_REF_FROM_IP(ipIntern(sema->ip, pk)); + } + } + } + } + } + } + // Comptime: if the operand IS a struct type (its type is `type`), // look up the field in the type's namespace. This handles patterns // like @import("builtin").target where the import resolves to a @@ -10312,6 +11131,55 @@ static AirInstRef zirFieldPtr(Sema* sema, SemaBlock* block, uint32_t inst) { } } + // Comptime struct field access through tracked pointers. + // When the base pointer is a tracked comptime value (e.g. target + // pointer), create ptr_field IP entries and track the result. + // Ported from Sema.zig fieldPtr → comptime pointer path. + if (AIR_REF_IS_IP(obj) && block->is_comptime) { + InternPoolIndex obj_ip = AIR_REF_TO_IP(obj); + uint32_t ct_val; + uint8_t ct_tag = ctLookup(sema, obj_ip, &ct_val); + if (ct_tag == CT_TAG_TARGET_PTR || ct_tag == CT_TAG_STRUCT_FIELD_PTR) { + // Get the pointee struct type from the pointer type. + InternPoolIndex ptr_type = ipTypeOf(sema->ip, obj_ip); + if (ptr_type != IP_INDEX_NONE + && sema->ip->items[ptr_type].tag == IP_KEY_PTR_TYPE) { + InternPoolIndex struct_type_ip + = sema->ip->items[ptr_type].data.ptr_type.child; + // Look up the field index from ZIR. + uint32_t fidx + = findStructFieldIndexByType(struct_type_ip, field_name); + if (fidx != UINT32_MAX) { + // Look up the field type by finding the struct's + // ZIR and resolving the field type reference. + // For now, find the namespace's file, parse the + // struct_decl, and get the field's type. + uint32_t ns = findNamespaceForType(struct_type_ip); + InternPoolIndex field_type = IP_INDEX_NONE; + if (ns != UINT32_MAX) { + uint32_t fid = s_namespaces[ns].file_idx; + if (fid < s_num_loaded_modules + && s_loaded_modules[fid].has_zir) { + field_type = resolveStructFieldType( + &s_loaded_modules[fid].zir, 0, fidx, ns, fid); + } + } + if (field_type != IP_INDEX_NONE) { + // Create *const field_type pointer. + InternPoolIndex fld_ptr_ty + = internPtrConst(field_type); + // Create ptr_field entry. + InternPoolIndex pf + = internPtrField(fld_ptr_ty, obj_ip, fidx); + // Track so subsequent accesses can resolve. + ctTrack(sema, pf, CT_TAG_STRUCT_FIELD_PTR, field_type); + return AIR_REF_FROM_IP(pf); + } + } + } + } + } + // 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); @@ -10977,16 +11845,11 @@ static AirInstRef zirMinMax(Sema* sema, SemaBlock* block, uint32_t inst) { // --- zirRetPtr --- // Ported from src/Sema.zig zirRetPtr. -static AirInstRef zirRetPtr(Sema* sema, SemaBlock* block) { +static AirInstRef zirRetPtr(const Sema* sema, SemaBlock* block) { TypeIndex ret_ty = sema->fn_ret_ty; if (ret_ty == TYPE_NONE) ret_ty = IP_INDEX_VOID_TYPE; - InternPoolKey pk; - memset(&pk, 0, sizeof(pk)); - pk.tag = IP_KEY_PTR_TYPE; - pk.data.ptr_type.child = ret_ty; - pk.data.ptr_type.flags = 0; - TypeIndex ptr_ty = ipIntern(sema->ip, pk); + TypeIndex ptr_ty = internPtrMutable(ret_ty); AirInstData d; memset(&d, 0, sizeof(d)); d.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); @@ -11943,7 +12806,6 @@ static bool analyzeBodyInner( return true; uint32_t inst = body[i]; ZirInstTag inst_tag = sema->code.inst_tags[inst]; - switch (inst_tag) { // Instructions that don't produce a ref and don't go into the @@ -12035,15 +12897,13 @@ static bool analyzeBodyInner( if (import_path) { if (strcmp(import_path, "builtin") == 0) { // @import("builtin") — compiler-generated - // builtin module. Look up the nav in the - // current file's namespace. - 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; - } + // builtin module. Don't resolve to the struct + // type here — leave as VOID_VALUE so that + // field_val's cross-module builtin path + // (zirFieldValComptime lines 10405-10414) can + // look up field declarations in the CG builtin + // namespace directly. Resolving to the struct + // type would prevent that path from firing. } else if (sema->source_dir) { char import_full[1024]; if (resolveImportPath(sema->source_dir, import_path, diff --git a/stage0/sema.h b/stage0/sema.h @@ -284,6 +284,10 @@ typedef struct Sema { // When true, test declarations are analyzed (test blocks become // analyzeable functions). Set by the caller before semaAnalyze. bool is_test; + // When true, debug statements (dbg_stmt, dbg_var_val, etc.) and + // debug inline blocks are suppressed. Matches Zig's + // block.ownerModule().strip. Set by the caller before semaAnalyze. + bool strip; // File index into s_loaded_modules[] for this sema's module. // Set by analyzeNavValC / semaAnalyze. UINT32_MAX = unknown. uint32_t file_idx; diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig @@ -75,6 +75,8 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) defer sc.semaDeinit(&c_sema); c_sema.source_dir = source_dir_path.ptr; c_sema.module_root = module_root_path.ptr; + // Pre-generated AIR uses ReleaseSmall (strip=true), so match it. + c_sema.strip = true; // Set root_fqn and module_prefix so C sema FQNs match Zig's. // lib/std/ files: "std.{prefix}.func" (std.zig compiled as root) // other files: "{stem}.func" (standalone compilation)