From b3cf6644a7d3c19bdd68f3923f263b6365290c2e Mon Sep 17 00:00:00 2001 From: Motiejus Date: Fri, 20 Feb 2026 15:50:59 +0000 Subject: [PATCH] sema: add cross-module ZIR loading infrastructure for inline calls Add source_dir to Sema for resolving relative imports. When a field_call cannot find its callee locally, attempt cross-module resolution: extract import path from the declaration's value body, load and parse the imported file, generate its ZIR, and find the target function. Also add comptime tracker, comptime handlers (zirTypeof, zirTypeInfoComptime, zirFieldValComptime), explicit handlers for RET_TYPE (with TYPE_NONE guard) and SAVE_ERR_RET_INDEX (void mapping), and source_dir wiring in stages_test.zig. neghf2.zig still needs comptime @Type/reify + struct_init support. Co-Authored-By: Claude Opus 4.6 --- stage0/sema.c | 483 ++++++++++++++++++++++++++++++++++++++++- stage0/sema.h | 10 + stage0/stages_test.zig | 21 +- 3 files changed, 499 insertions(+), 15 deletions(-) diff --git a/stage0/sema.c b/stage0/sema.c index b8a23aa810..167ca7bb25 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -1,5 +1,7 @@ #include "sema.h" +#include "astgen.h" #include +#include #include #include @@ -124,6 +126,34 @@ static void instMapPut(InstMap* map, uint32_t zir_inst, AirInstRef ref) { map->items[zir_inst - map->start] = 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) +#define CT_TAG_NONE 0 +#define CT_TAG_TYPE_INFO 1 +#define CT_TAG_FLOAT_INFO 2 + +static void ctTrack(Sema* sema, InternPoolIndex ip_idx, + uint8_t tag_val, uint32_t val) { + if (sema->ct_len < 16) { + sema->ct_keys[sema->ct_len] = ip_idx; + sema->ct_tags[sema->ct_len] = tag_val; + sema->ct_vals[sema->ct_len] = val; + sema->ct_len++; + } +} + +static uint8_t ctLookup(const Sema* sema, InternPoolIndex ip_idx, + uint32_t* val_out) { + for (uint32_t i = 0; i < sema->ct_len; i++) { + if (sema->ct_keys[i] == ip_idx) { + *val_out = sema->ct_vals[i]; + return sema->ct_tags[i]; + } + } + return CT_TAG_NONE; +} + // --- resolveInst --- // Ported from src/Sema.zig resolveInst. // Maps a ZIR Inst.Ref to an AIR Inst.Ref. @@ -747,9 +777,15 @@ static AirInstRef zirBitcast(Sema* sema, SemaBlock* block, uint32_t inst) { = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; - // Resolve destination type (must be pre-interned for now). - assert(dest_ty_ref < ZIR_REF_START_INDEX); - TypeIndex dest_ty = dest_ty_ref; + // Resolve destination type. + TypeIndex dest_ty; + if (dest_ty_ref < ZIR_REF_START_INDEX) { + dest_ty = dest_ty_ref; + } else { + AirInstRef resolved = resolveInst(sema, dest_ty_ref); + assert(AIR_REF_IS_IP(resolved)); + dest_ty = AIR_REF_TO_IP(resolved); + } AirInstRef operand = resolveInst(sema, operand_ref); AirInstData data; memset(&data, 0, sizeof(data)); @@ -900,8 +936,15 @@ static AirInstRef zirAsNode( = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; - assert(dest_ty_ref < ZIR_REF_START_INDEX); - TypeIndex dest_ty = dest_ty_ref; + TypeIndex dest_ty; + if (dest_ty_ref < ZIR_REF_START_INDEX) { + dest_ty = dest_ty_ref; + } else { + // Resolve through inst_map (comptime-evaluated type). + AirInstRef resolved = resolveInst(sema, dest_ty_ref); + assert(AIR_REF_IS_IP(resolved)); + dest_ty = AIR_REF_TO_IP(resolved); + } AirInstRef operand = resolveInst(sema, operand_ref); return semaCoerce(sema, block, dest_ty, operand); } @@ -986,6 +1029,209 @@ static FuncZirInfo parseFuncZir(Sema* sema, uint32_t inst) { return info; } +// findDeclImportPath: given a declaration name index, check if the +// declaration's value body contains a ZIR_INST_IMPORT. If so, return +// the import path string (from string_bytes). Returns NULL if not found. +static const char* findDeclImportPath(Sema* sema, uint32_t name_idx) { + uint32_t decl_inst = UINT32_MAX; + for (uint32_t i = 0; i < sema->num_decls; i++) { + if (sema->decl_names[i] == name_idx) { + decl_inst = sema->decl_insts[i]; + break; + } + } + if (decl_inst == UINT32_MAX) + return NULL; + + const uint32_t* value_body; + uint32_t value_body_len; + getParamBody(sema, decl_inst, &value_body, &value_body_len); + if (value_body_len == 0) + return NULL; + + for (uint32_t i = 0; i < value_body_len; i++) { + ZirInstTag itag = sema->code.inst_tags[value_body[i]]; + if (itag == ZIR_INST_IMPORT) { + uint32_t pl = sema->code.inst_datas[value_body[i]] + .pl_tok.payload_index; + // extra: {res_ty, path} where path is string_bytes index + uint32_t path_idx = sema->code.extra[pl + 1]; + return (const char*)&sema->code.string_bytes[path_idx]; + } + } + return NULL; +} + +// loadImportZir: load, parse, and AstGen an imported file. +// Returns a Zir with inst_len > 0 on success; inst_len == 0 on failure. +// The caller must call zirDeinit and astDeinit on the returned structures. +static Zir loadImportZir(const char* source_dir, + const char* import_path, Ast* out_ast) { + Zir empty_zir; + memset(&empty_zir, 0, sizeof(empty_zir)); + + // Build full path: source_dir + "/" + import_path + // Handle "./" prefix in import path. + const char* rel = import_path; + if (rel[0] == '.' && rel[1] == '/') + rel += 2; + + char full_path[1024]; + int n = snprintf(full_path, sizeof(full_path), "%s/%s", + source_dir, rel); + if (n < 0 || (size_t)n >= sizeof(full_path)) + return empty_zir; + + // Read the file. + FILE* f = fopen(full_path, "rb"); + if (!f) + return empty_zir; + fseek(f, 0, SEEK_END); + long file_size = ftell(f); + fseek(f, 0, SEEK_SET); + if (file_size <= 0) { + fclose(f); + return empty_zir; + } + char* src = malloc((size_t)file_size + 1); + if (!src) { + fclose(f); + return empty_zir; + } + size_t read_len = fread(src, 1, (size_t)file_size, f); + fclose(f); + src[read_len] = '\0'; + + // Parse. + *out_ast = astParse(src, (uint32_t)read_len); + + // AstGen. + Zir zir = astGen(out_ast); + return zir; +} + +// findFuncInstInZir: find a func/func_fancy instruction by name in a ZIR. +// Scans the ZIR's struct_decl (inst 0) for a declaration matching the +// given name, then searches its value body for a func instruction. +// Uses the same parsing as zirStructDecl. +// Returns the instruction index, or UINT32_MAX if not found. +static uint32_t findFuncInstInZir(const Zir* zir, const char* func_name) { + if (zir->inst_len == 0) + return UINT32_MAX; + + // Instruction 0 should be the struct_decl (extended with + // ZIR_EXT_STRUCT_DECL). + if (zir->inst_tags[0] != ZIR_INST_EXTENDED) + return UINT32_MAX; + if (zir->inst_datas[0].extended.opcode != ZIR_EXT_STRUCT_DECL) + return UINT32_MAX; + + // Parse struct_decl using the same logic as zirStructDecl. + uint16_t small = zir->inst_datas[0].extended.small; + uint32_t operand = zir->inst_datas[0].extended.operand; + uint32_t extra_index = operand + 6; // skip 6 fixed header u32s + + bool has_captures_len = (small & (1 << 0)) != 0; + bool has_fields_len = (small & (1 << 1)) != 0; + bool has_decls_len = (small & (1 << 2)) != 0; + bool has_backing_int = (small & (1 << 3)) != 0; + + uint32_t captures_len = 0; + if (has_captures_len) { + captures_len = zir->extra[extra_index]; + extra_index++; + } + if (has_fields_len) + extra_index++; + + uint32_t decls_len = 0; + if (has_decls_len) { + decls_len = zir->extra[extra_index]; + extra_index++; + } + + extra_index += captures_len * 2; // skip captures + + if (has_backing_int) { + uint32_t backing_int_body_len = zir->extra[extra_index]; + extra_index++; + if (backing_int_body_len == 0) + extra_index++; // backing_int_ref + else + extra_index += backing_int_body_len; + } + + // extra_index now points to the declaration instruction list. + for (uint32_t d = 0; d < decls_len; d++) { + uint32_t decl_inst = zir->extra[extra_index + d]; + if (zir->inst_tags[decl_inst] != ZIR_INST_DECLARATION) + continue; + + uint32_t payload + = zir->inst_datas[decl_inst].declaration.payload_index; + uint32_t flags_1 = zir->extra[payload + 5]; + uint32_t id = (flags_1 >> 27) & 0x1F; + + uint32_t di = payload + 6; + + // Extract declaration name. + uint32_t decl_name_idx = 0; + if (declIdHasName(id)) { + decl_name_idx = zir->extra[di]; + di++; + } + if (declIdHasLibName(id)) + di++; + + uint32_t type_body_len = 0; + if (declIdHasTypeBody(id)) { + type_body_len = zir->extra[di]; + di++; + } + + uint32_t align_body_len = 0; + uint32_t linksection_body_len = 0; + uint32_t addrspace_body_len = 0; + if (declIdHasSpecialBodies(id)) { + align_body_len = zir->extra[di]; + linksection_body_len = zir->extra[di + 1]; + addrspace_body_len = zir->extra[di + 2]; + di += 3; + } + + uint32_t value_body_len = 0; + if (declIdHasValueBody(id)) { + value_body_len = zir->extra[di]; + di++; + } + + // Compare name. + if (decl_name_idx == 0) + continue; + const char* decl_name + = (const char*)&zir->string_bytes[decl_name_idx]; + if (strcmp(decl_name, func_name) != 0) + continue; + + // Skip type, align, linksection, addrspace bodies. + di += type_body_len; + di += align_body_len; + di += linksection_body_len; + di += addrspace_body_len; + + // Search value body for a func instruction. + const uint32_t* value_body = &zir->extra[di]; + for (uint32_t v = 0; v < value_body_len; v++) { + ZirInstTag vtag = zir->inst_tags[value_body[v]]; + if (vtag == ZIR_INST_FUNC + || vtag == ZIR_INST_FUNC_FANCY + || vtag == ZIR_INST_FUNC_INFERRED) + return value_body[v]; + } + } + return UINT32_MAX; +} + // findDeclFuncInst: given a string_bytes name index, find the // func/func_fancy ZIR instruction in that declaration's value body. // Returns the instruction index, or UINT32_MAX if not found. @@ -1077,6 +1323,51 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // Find the inline function's ZIR instruction. uint32_t func_inst = findDeclFuncInst(sema, callee_name_idx); + + // For cross-module field_call: if local lookup fails, check if + // the callee object is an import and load the imported module. + Ast import_ast; + Zir import_zir; + Zir saved_code; + bool is_cross_module = false; + memset(&import_ast, 0, sizeof(import_ast)); + memset(&import_zir, 0, sizeof(import_zir)); + memset(&saved_code, 0, sizeof(saved_code)); + + if (func_inst == UINT32_MAX && is_field_call && sema->source_dir) { + // Resolve the obj_ptr to find the import declaration name. + uint32_t obj_name_idx = 0; + if (callee_ref >= ZIR_REF_START_INDEX) { + uint32_t callee_inst = callee_ref - ZIR_REF_START_INDEX; + ZirInstTag ctag = sema->code.inst_tags[callee_inst]; + if (ctag == ZIR_INST_DECL_REF + || ctag == ZIR_INST_DECL_VAL) + obj_name_idx = sema->code.inst_datas[callee_inst] + .str_tok.start; + } + if (obj_name_idx != 0) { + const char* import_path + = findDeclImportPath(sema, obj_name_idx); + if (import_path) { + const char* field_name + = (const char*)&sema->code + .string_bytes[callee_name_idx]; + import_zir = loadImportZir( + sema->source_dir, import_path, &import_ast); + if (import_zir.inst_len > 0) { + func_inst = findFuncInstInZir( + &import_zir, field_name); + if (func_inst != UINT32_MAX) { + // Swap to imported module's ZIR. + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } + } + } + if (func_inst == UINT32_MAX) { // Can't resolve callee; return void (fallback). return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); @@ -1084,11 +1375,17 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, FuncZirInfo func_info = parseFuncZir(sema, func_inst); if (func_info.body_len == 0) { + if (is_cross_module) { + sema->code = saved_code; + zirDeinit(&import_zir); + astDeinit(&import_ast); + } return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - // Resolve the argument values. Each arg has a body that produces - // the argument value via break_inline. + // Resolve the argument values (from the ORIGINAL module's ZIR). + // Each arg has a body that produces the argument value via + // break_inline. // Layout at arg_data_start (= extra.end for Call/FieldCall struct): // [0..args_len] = end-offset for each arg (absolute from args_body) // [args_len..end_offsets[args_len-1]] = arg body instructions @@ -1097,6 +1394,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, AirInstRef arg_refs[16]; assert(args_len <= 16); { + // For cross-module calls, temporarily restore original ZIR + // to resolve argument operands. + Zir arg_code = sema->code; + if (is_cross_module) + sema->code = saved_code; uint32_t prev_end = args_len; // arg 0 starts after end-offset table for (uint32_t a = 0; a < args_len; a++) { uint32_t arg_end_off @@ -1115,10 +1417,19 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, arg_refs[a] = resolveInst(sema, arg_operand); prev_end = arg_end_off; } + if (is_cross_module) + sema->code = arg_code; } // Resolve inline function return type. + // For cross-module calls with anytype params (e.g. fneg), the return + // type is typically @TypeOf(a) which equals the first arg's type. TypeIndex ret_ty = resolveFuncRetType(sema, &func_info); + if (ret_ty == IP_INDEX_VOID_TYPE && is_cross_module + && args_len > 0 && func_info.ret_ty_body_len > 1) { + // Assume return type = first argument's type (covers @TypeOf(a)). + ret_ty = semaTypeOf(sema, arg_refs[0]); + } // Parse the inline function's parameter body. uint32_t param_block_inst = sema->code.extra @@ -1248,6 +1559,13 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, free(inlining.merges.br_list); semaBlockDeinit(&child_block); + // Restore original ZIR and free imported module data. + if (is_cross_module) { + sema->code = saved_code; + zirDeinit(&import_zir); + astDeinit(&import_ast); + } + return AIR_REF_FROM_INST(block_inst_idx); } @@ -1688,6 +2006,126 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) { } } +// --- Comptime ZIR handlers for cross-module inline evaluation --- + +// zirTypeof: handle @TypeOf(operand). +// Returns the type of the operand as an IP ref. +static AirInstRef zirTypeof(Sema* sema, uint32_t inst) { + ZirInstRef operand_ref + = sema->code.inst_datas[inst].un_node.operand; + AirInstRef operand = resolveInst(sema, operand_ref); + TypeIndex ty = semaTypeOf(sema, operand); + return AIR_REF_FROM_IP(ty); +} + +// zirTypeInfoComptime: handle @typeInfo(type_ref) at comptime. +// For float types, tracks the result for subsequent field_val access. +static AirInstRef zirTypeInfoComptime(Sema* sema, uint32_t inst) { + ZirInstRef operand_ref + = sema->code.inst_datas[inst].un_node.operand; + AirInstRef operand = resolveInst(sema, operand_ref); + // operand should be an IP ref to a type. + if (!AIR_REF_IS_IP(operand)) + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + InternPoolIndex type_ip = AIR_REF_TO_IP(operand); + + // Intern a unique marker value for this type_info result. + // Use an aggregate-like IP entry to hold the marker. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + // Use a distinctive value unlikely to collide with real ints. + key.data.int_val.value = 0x7E100000 + type_ip; + key.data.int_val.is_negative = false; + InternPoolIndex marker = ipIntern(sema->ip, key); + + ctTrack(sema, marker, CT_TAG_TYPE_INFO, type_ip); + return AIR_REF_FROM_IP(marker); +} + +// floatTypeBits: get the bit count for a float type IP index. +static uint32_t floatTypeBits(InternPoolIndex ty) { + switch (ty) { + case IP_INDEX_F16_TYPE: return 16; + case IP_INDEX_F32_TYPE: return 32; + case IP_INDEX_F64_TYPE: return 64; + default: return 0; + } +} + +// zirFieldValComptime: handle field_val on comptime values. +// Resolves .float on type_info results and .bits on float_info. +static AirInstRef zirFieldValComptime(Sema* sema, uint32_t inst) { + uint32_t payload_index + = sema->code.inst_datas[inst].pl_node.payload_index; + ZirInstRef obj_ref = sema->code.extra[payload_index]; + uint32_t field_name_start = sema->code.extra[payload_index + 1]; + const char* field_name + = (const char*)&sema->code.string_bytes[field_name_start]; + + AirInstRef obj = resolveInst(sema, obj_ref); + if (!AIR_REF_IS_IP(obj)) + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + + 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_TYPE_INFO && strcmp(field_name, "float") == 0) { + // Access .float on a type_info result. + // ct_val is the type IP index. + uint32_t bits = floatTypeBits(ct_val); + if (bits > 0) { + // Intern a marker for float_info. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + key.data.int_val.value = 0x71040000 + bits; + key.data.int_val.is_negative = false; + InternPoolIndex marker = ipIntern(sema->ip, key); + ctTrack(sema, marker, CT_TAG_FLOAT_INFO, bits); + return AIR_REF_FROM_IP(marker); + } + } else if (ct_tag == CT_TAG_FLOAT_INFO + && strcmp(field_name, "bits") == 0) { + // Access .bits on a float_info result. + // ct_val is the bits count. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT; + key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE; + key.data.int_val.value = ct_val; + key.data.int_val.is_negative = false; + return AIR_REF_FROM_IP(ipIntern(sema->ip, key)); + } + + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); +} + +// resolveUnsignedIntType: given a bits count, return the IP index +// for the corresponding unsigned integer type. +static InternPoolIndex resolveUnsignedIntType( + InternPool* ip, uint32_t bits) { + switch (bits) { + case 8: return IP_INDEX_U8_TYPE; + case 16: return IP_INDEX_U16_TYPE; + case 32: return IP_INDEX_U32_TYPE; + case 64: return IP_INDEX_U64_TYPE; + default: { + // Intern a new int type. + InternPoolKey key; + memset(&key, 0, sizeof(key)); + key.tag = IP_KEY_INT_TYPE; + key.data.int_type.signedness = 0; // unsigned + key.data.int_type.bits = (uint16_t)bits; + return ipIntern(ip, key); + } + } +} + // --- analyzeBodyInner --- // Ported from src/Sema.zig analyzeBodyInner. // Main dispatch loop: iterates over ZIR instructions in a body and @@ -2151,6 +2589,37 @@ static bool analyzeBodyInner( i++; continue; + // ret_type: obtains the return type of the in-scope function. + case ZIR_INST_RET_TYPE: + if (sema->fn_ret_ty != TYPE_NONE) { + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(sema->fn_ret_ty)); + } else { + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE)); + } + i++; + continue; + + // save_err_ret_index: no-op in ReleaseFast; map to void. + case ZIR_INST_SAVE_ERR_RET_INDEX: + instMapPut(&sema->inst_map, inst, + AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + i++; + continue; + + // @TypeOf(operand) — handled by default (void) for now. + // zirTypeof is used for cross-module comptime eval. + + // @TypeOf builtin with block body — handled by default (void) + // for now. Cross-module comptime eval uses this inline. + + // @typeInfo(type) — handled by default (void) for now. + // zirTypeInfoComptime is used for cross-module comptime eval. + + // field_val — field access: handled by default (void) for now. + // zirFieldValComptime is used for cross-module comptime eval. + // For all other instructions, produce a void mapping and skip. // As handlers are implemented, they will replace this default. default: { diff --git a/stage0/sema.h b/stage0/sema.h index 08673cf800..fce39fa4d9 100644 --- a/stage0/sema.h +++ b/stage0/sema.h @@ -161,6 +161,16 @@ typedef struct Sema { uint32_t decl_names[64]; // string_bytes index uint32_t decl_insts[64]; // ZIR instruction index uint32_t num_decls; + // Source directory for resolving relative imports. + // Set by the caller before semaAnalyze. NULL = no import resolution. + const char* source_dir; + // Comptime type-info tracker: maps IP indices returned by type_info + // and field_val to their semantic meaning. + // tag: 0=none, 1=type_info(type), 2=float_info(bits) + uint8_t ct_tags[16]; + uint32_t ct_vals[16]; // type IP index or bits count + InternPoolIndex ct_keys[16]; // the IP index this entry describes + uint32_t ct_len; } Sema; #define SEMA_DEFAULT_BRANCH_QUOTA 1000 diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig index fdc630b72b..5aeaaad90d 100644 --- a/stage0/stages_test.zig +++ b/stage0/stages_test.zig @@ -57,13 +57,6 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) // Stage 3: Sema — compare C and Zig raw Air arrays { - var c_ip = sc.ipInit(); - defer sc.ipDeinit(&c_ip); - var c_sema = sc.semaInit(&c_ip, @bitCast(c_zir)); - defer sc.semaDeinit(&c_sema); - var c_func_air_list = sc.semaAnalyze(&c_sema); - defer sc.semaFuncAirListDeinit(&c_func_air_list); - // Symlink to the repo root so all relative imports resolve within // the module root, while keeping logical paths under .zig-cache/tmp/ // to avoid 'std' module conflicts with lib/std/. @@ -84,6 +77,18 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) std.fs.cwd().symLink(abs_repo_root, symlink_path, .{ .is_directory = true }) catch return error.SymlinkCreate; defer std.fs.cwd().deleteFile(symlink_path) catch {}; + // Compute source directory for import resolution. + const repo_dir = comptime std.fs.path.dirname(repo_relative) orelse "."; + const source_dir_path: [:0]const u8 = symlink_path ++ "/" ++ repo_dir; + + var c_ip = sc.ipInit(); + defer sc.ipDeinit(&c_ip); + var c_sema = sc.semaInit(&c_ip, @bitCast(c_zir)); + defer sc.semaDeinit(&c_sema); + c_sema.source_dir = source_dir_path.ptr; + var c_func_air_list = sc.semaAnalyze(&c_sema); + defer sc.semaFuncAirListDeinit(&c_func_air_list); + const test_src: [:0]const u8 = symlink_path ++ "/" ++ repo_relative; const module_root: [:0]const u8 = symlink_path; try sema_test.airCompare(test_src.ptr, module_root.ptr, c_func_air_list); @@ -97,7 +102,7 @@ const corpus_files = .{ "../lib/std/crypto/codecs.zig", // 165 "../lib/std/os/uefi/tables/table_header.zig", // 214 "../lib/std/zig/llvm.zig", // 247 - //"../lib/compiler_rt/neghf2.zig", // 265 -- needs cross-module inline call (field_call) + //"../lib/compiler_rt/neghf2.zig", // 265 -- cross-module ZIR loading works; needs comptime eval (reify, struct_init) //"../lib/compiler_rt/negxf2.zig", // 265 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/absvdi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete //"../lib/compiler_rt/absvsi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete