zig

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

commit c69e558131e5459f8b61019dcd555c38b73d69cf (tree)
parent 8b4d45bf3004f9a70d2f8abeb6fb000ba5782fa5
Author: Motiejus <motiejus@jakstys.lt>
Date:   Tue,  3 Mar 2026 08:04:04 +0000

sema: extract semaAnalyzeCall from zirCall

Refactor to match upstream Sema.zig structure where zirCall handles
callee resolution and analyzeCall handles the actual call analysis.
The new semaAnalyzeCall function takes a CalleeResolution struct that
bundles cross-module state. No functional changes.

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

Diffstat:
Mstage0/sema.c | 2982++++++++++++++++++++++++++++++++++++++++---------------------------------------
1 file changed, 1506 insertions(+), 1476 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -5617,1596 +5617,1626 @@ static AirInstRef evalComptimeTypeCall(Sema* sema, uint32_t func_inst, return result; } -// zirCall: handle call/field_call ZIR instruction for inline functions. -// Ported from src/Sema.zig zirCall / analyzeCall (inline-only subset). -// For inline functions from the same module, analyzes the function body -// in a child block and emits dbg_inline_block. -static AirInstRef zirCall( - Sema* sema, SemaBlock* block, uint32_t inst, bool is_field_call) { - if (sema->has_compile_errors) - return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; - - // Parse Call/FieldCall extra data. - uint32_t flags = sema->code.extra[payload_index]; - uint32_t args_len = flags >> 5; // bits 5..31 = args_len - uint32_t callee_ref; - uint32_t arg_data_start; - uint32_t callee_name_idx = 0; // string_bytes index for decl name - - if (is_field_call) { - // FieldCall: {flags, obj_ptr, field_name_start} - callee_ref = sema->code.extra[payload_index + 1]; // obj_ptr - uint32_t field_name_start = sema->code.extra[payload_index + 2]; - callee_name_idx = field_name_start; - arg_data_start = payload_index + 3; +// semaAnalyzeCall: analyze a resolved call (inline or runtime dispatch). +// Ported from src/Sema.zig analyzeCall. +// Called by zirCall after callee resolution. +typedef struct { + Zir saved_code; + Zir import_zir; + Ast import_ast; + char import_source_dir[1024]; + InternPoolIndex struct_ret_type; + bool is_cross_module; +} CalleeResolution; - // Comptime field_call: evaluate function body at comptime. - // Ported from Sema.zig analyzeCall comptime inline path. - if (block->is_comptime && callee_name_idx != 0) { - AirInstRef ct_result = comptimeFieldCall(sema, block, callee_ref, - callee_name_idx, args_len, arg_data_start); - if (ct_result != AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)) - return ct_result; - // Fall through to runtime path if comptime eval failed. +static AirInstRef semaAnalyzeCall(Sema* sema, SemaBlock* block, uint32_t inst, + uint32_t func_inst, uint32_t callee_name_idx, uint32_t args_len, + uint32_t arg_data_start, CalleeResolution* cr) { + FuncZirInfo func_info = parseFuncZir(sema, func_inst); + if (func_info.body_len == 0) { + if (cr->is_cross_module) { + sema->code = cr->saved_code; + zirDeinit(&cr->import_zir); + astDeinit(&cr->import_ast); } - } else { - // Call: {flags, callee} - callee_ref = sema->code.extra[payload_index + 1]; - arg_data_start = payload_index + 2; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - // For non-field calls, resolve the callee name from decl_val/decl_ref. - if (!is_field_call && callee_ref >= ZIR_REF_START_INDEX) { - uint32_t callee_inst = callee_ref - ZIR_REF_START_INDEX; - ZirInstTag callee_tag = sema->code.inst_tags[callee_inst]; - if (callee_tag == ZIR_INST_DECL_VAL - || callee_tag == ZIR_INST_DECL_REF) { - callee_name_idx = sema->code.inst_datas[callee_inst].str_tok.start; + // Type-returning functions (return `type`): evaluate via comptime + // evaluation. Ported from src/Sema.zig analyzeCall comptime path. + // Detect type-returning functions by resolving the return type ref. + // For plain Func with ret_ty_body_len==1, extra[ret_ty_ref_pos] is + // a pre-interned Ref (e.g. IP_INDEX_TYPE_TYPE). + // For FuncFancy with has_ret_ty_body, extra[ret_ty_ref_pos] is a + // ZIR instruction index; resolve it via break_inline's operand. + // Ported from Sema.zig:7247 comptimeOnlySema check. + if (func_info.ret_ty_body_len >= 1) { + uint32_t ret_ref = sema->code.extra[func_info.ret_ty_ref_pos]; + // For has_ret_ty_body (FuncFancy): the body's last instruction + // is break_inline whose operand is the actual return type ref. + // Ported from Zir.getFnInfo (Zir.zig:4929): when ret_ty_body + // is present, evaluate the body to get the return type. + if (ret_ref >= ZIR_REF_START_INDEX) { + uint32_t last_inst = sema->code.extra[func_info.ret_ty_ref_pos + + func_info.ret_ty_body_len - 1]; + if (last_inst < sema->code.inst_len + && sema->code.inst_tags[last_inst] == ZIR_INST_BREAK_INLINE) { + ret_ref = sema->code.inst_datas[last_inst].break_data.operand; + } + } + if (ret_ref == IP_INDEX_TYPE_TYPE) { + // Resolve args in the caller's ZIR context first. + AirInstRef ct_arg_refs[16]; + memset(ct_arg_refs, 0, sizeof(ct_arg_refs)); + assert(args_len <= 16); + { + Zir fn_zir = sema->code; + if (cr->is_cross_module) + sema->code = cr->saved_code; + uint32_t prev_end = args_len; + for (uint32_t a = 0; a < args_len; a++) { + uint32_t end_off = sema->code.extra[arg_data_start + a]; + uint32_t body_start = arg_data_start + prev_end; + uint32_t body_len = end_off - prev_end; + if (body_len > 1) { + (void)analyzeBodyInner(sema, block, + &sema->code.extra[body_start], body_len - 1); + } + if (body_len >= 1) { + uint32_t li + = sema->code.extra[body_start + body_len - 1]; + ZirInstRef op + = sema->code.inst_datas[li].break_data.operand; + ct_arg_refs[a] = resolveInst(sema, op); + } else { + ct_arg_refs[a] = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + prev_end = end_off; + } + if (cr->is_cross_module) + sema->code = fn_zir; + } + AirInstRef honest_result = evalComptimeTypeCall(sema, func_inst, + &func_info, ct_arg_refs, args_len, + cr->is_cross_module ? cr->import_source_dir : NULL); + if (cr->is_cross_module) { + sema->code = cr->saved_code; + zirDeinit(&cr->import_zir); + astDeinit(&cr->import_ast); + } + if (AIR_REF_IS_IP(honest_result) + && AIR_REF_TO_IP(honest_result) != IP_INDEX_VOID_VALUE) + return honest_result; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } } - // Find the inline function's ZIR instruction. - uint32_t func_inst = findDeclFuncInst(sema, callee_name_idx); - // Fallback: during cross-module inline expansion, sema->decl_names - // contains indices from the main module's string table, but - // callee_name_idx is from the inlined module's string table. - // Search the current ZIR directly by name. - if (func_inst == UINT32_MAX && callee_name_idx != 0) { - const char* cn - = (const char*)&sema->code.string_bytes[callee_name_idx]; - func_inst = findFuncInstInZir(&sema->code, cn); + // Determine which parameters are comptime (from callee's ZIR). + // Ported from src/Sema.zig lines 7104-7123: generate args to comptime + // params in comptime block. + bool is_ct_param[16]; + memset(is_ct_param, 0, sizeof(is_ct_param)); + bool is_generic = false; + // Track whether each param has a generic type (refers to previous + // comptime params). Ported from Zir.Inst.Param.Type.is_generic. + bool has_generic_type[16]; + memset(has_generic_type, 0, sizeof(has_generic_type)); + { + uint32_t early_pb_inst + = sema->code + .extra[sema->code.inst_datas[func_inst].pl_node.payload_index + + func_info.param_block_pi]; + const uint32_t* early_pb; + uint32_t early_pb_len; + getParamBody(sema, early_pb_inst, &early_pb, &early_pb_len); + uint32_t pi = 0; + for (uint32_t p = 0; p < early_pb_len; p++) { + ZirInstTag ptag = sema->code.inst_tags[early_pb[p]]; + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + if (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + is_ct_param[pi] = true; + } + // Ported from upstream: anytype and comptime params both + // make the function generic. Used for dummy alloc emission + // in the non-inline CALL path (Sema.zig:7394-7399). + if (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE) { + is_generic = true; + } + // Check if param type is generic (refers to previous params). + // Ported from Zir.Inst.Param: extra[payload+1] bit 31. + if (ptag == ZIR_INST_PARAM + || ptag == ZIR_INST_PARAM_COMPTIME) { + uint32_t ppl = sema->code.inst_datas[early_pb[p]] + .pl_tok.payload_index; + uint32_t type_raw = sema->code.extra[ppl + 1]; + if ((type_raw >> 31) & 1) + has_generic_type[pi] = true; + } + pi++; + } + } } - // 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; - InternPoolIndex struct_ret_type = IP_INDEX_VOID_TYPE; - char import_source_dir[1024]; - memset(&import_ast, 0, sizeof(import_ast)); - memset(&import_zir, 0, sizeof(import_zir)); - memset(&saved_code, 0, sizeof(saved_code)); - import_source_dir[0] = 0; - // For non-field calls to imported function aliases: - // e.g. `const isNan = std.math.isNan;` then `isNan(x)`. - // The declaration value body follows the pattern: - // decl_val("std") + field_val("math") + field_val("isNan") + - // break_inline - // Follow the import chain to resolve the function. - if (func_inst == UINT32_MAX && !is_field_call && callee_name_idx != 0 - && sema->source_dir) { - // Get the declaration's value body. - uint32_t decl_inst_val = UINT32_MAX; - for (uint32_t i = 0; i < sema->num_decls; i++) { - if (sema->decl_names[i] == callee_name_idx) { - decl_inst_val = sema->decl_insts[i]; - break; + // 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 + // Arg 0 body: args_body[args_len .. end_offsets[0]] + // Arg N body: args_body[end_offsets[N-1] .. end_offsets[N]] + 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 (cr->is_cross_module) + sema->code = cr->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 = sema->code.extra[arg_data_start + a]; + uint32_t arg_body_start = arg_data_start + prev_end; + uint32_t arg_body_len = arg_end_off - prev_end; + // Each arg body should end with a break_inline whose + // operand is the argument ref. + assert(arg_body_len >= 1); + // Ported from src/Sema.zig lines 7104-7123: + // Generate args to comptime params in comptime block. + bool saved_is_comptime = block->is_comptime; + if (is_ct_param[a]) + block->is_comptime = true; + // Analyze preceding instructions in the arg body so that + // the break_inline operand is mapped. For simple args + // (e.g. param refs) arg_body_len==1 and this is a no-op. + if (arg_body_len > 1) { + (void)analyzeBodyInner(sema, block, + &sema->code.extra[arg_body_start], arg_body_len - 1); } + block->is_comptime = saved_is_comptime; + uint32_t last_inst_idx + = sema->code.extra[arg_body_start + arg_body_len - 1]; + ZirInstTag last_tag = sema->code.inst_tags[last_inst_idx]; + assert(last_tag == ZIR_INST_BREAK_INLINE); + ZirInstRef arg_operand + = sema->code.inst_datas[last_inst_idx].break_data.operand; + arg_refs[a] = resolveInst(sema, arg_operand); + // Ported from Sema.zig:7394-7399: emit dummy alloc for + // non-comptime runtime params of generic functions, + // interleaved with arg evaluation (not batched after). + if (is_generic && !is_ct_param[a] && !func_info.is_inline + && !block->is_comptime) { + TypeIndex arg_ty = semaTypeOf(sema, arg_refs[a]); + AirInstData dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.ty.ty_ref = AIR_REF_FROM_IP(arg_ty); + semaAddInstAsIndex(sema, AIR_INST_ALLOC, dummy); + } + prev_end = arg_end_off; } - // Fallback: search current ZIR by name (cross-module inline). - if (decl_inst_val == UINT32_MAX) { - const char* cn - = (const char*)&sema->code.string_bytes[callee_name_idx]; - decl_inst_val = findDeclInstByNameInZir(&sema->code, cn); - } - if (decl_inst_val != UINT32_MAX) { - const uint32_t* vb; - uint32_t vb_len; - getParamBody(sema, decl_inst_val, &vb, &vb_len); - // Parse the chain: import/decl_val, field_val(s), break_inline. - const char* base_import = NULL; - const char* fields[8]; - uint32_t nfields = 0; - for (uint32_t vi = 0; vi < vb_len; vi++) { - ZirInstTag vtag = sema->code.inst_tags[vb[vi]]; - if (vtag == ZIR_INST_IMPORT && !base_import) { - uint32_t vpl - = sema->code.inst_datas[vb[vi]].pl_tok.payload_index; - uint32_t path_idx = sema->code.extra[vpl + 1]; - base_import - = (const char*)&sema->code.string_bytes[path_idx]; - } else if ((vtag == ZIR_INST_DECL_VAL - || vtag == ZIR_INST_DECL_REF) - && !base_import) { - uint32_t ref_idx - = sema->code.inst_datas[vb[vi]].str_tok.start; - base_import = findDeclImportPath(sema, ref_idx); - } else if (vtag == ZIR_INST_FIELD_VAL && base_import - && nfields < 8) { - uint32_t vpl - = sema->code.inst_datas[vb[vi]].pl_node.payload_index; - uint32_t fs = sema->code.extra[vpl + 1]; - fields[nfields++] - = (const char*)&sema->code.string_bytes[fs]; - } - } - // Resolve the chain: load base import, then follow field - // accesses through sub-imports until we find the function. - if (base_import && nfields >= 2) { - // nfields >= 2: e.g. fields[0]="math", fields[1]="isNan" - // Load base import (e.g. std.zig). - Ast base_ast; - memset(&base_ast, 0, sizeof(base_ast)); - Zir base_zir = loadStdImportZir(sema, - sema->zcu->comp->config.module_root, sema->source_dir, - base_import, &base_ast); - if (base_zir.inst_len > 0) { - // Follow intermediate fields (all but last). - char cur_dir[1024]; - computeSourceDir(sema->zcu->comp->config.module_root, - sema->source_dir, base_import, cur_dir, - sizeof(cur_dir)); - Zir cur_zir = base_zir; - Ast cur_ast = base_ast; - for (uint32_t fi = 0; fi + 1 < nfields; fi++) { - const char* sub_import - = findDeclImportPathInZir(&cur_zir, fields[fi]); - if (!sub_import) { - // Not a sub-import; function might be here. - func_inst - = findFuncInstInZir(&cur_zir, fields[fi]); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = cur_zir; - import_zir = cur_zir; - import_ast = cur_ast; - is_cross_module = true; - snprintf(import_source_dir, - sizeof(import_source_dir), "%s", cur_dir); - } else { - zirDeinit(&cur_zir); - astDeinit(&cur_ast); - } - break; - } - // Load the sub-module. - Ast next_ast; - memset(&next_ast, 0, sizeof(next_ast)); - Zir next_zir = loadImportZir( - sema, cur_dir, sub_import, &next_ast); - // Compute new source dir BEFORE freeing cur_zir, - // since sub_import points into cur_zir string bytes. - char new_dir[1024]; - computeSourceDir(NULL, cur_dir, sub_import, new_dir, - sizeof(new_dir)); - zirDeinit(&cur_zir); - astDeinit(&cur_ast); - if (next_zir.inst_len == 0) - break; - memcpy(cur_dir, new_dir, sizeof(cur_dir)); - cur_zir = next_zir; - cur_ast = next_ast; - } - // The last field is the function name. - if (!is_cross_module && cur_zir.inst_len > 0) { - const char* fn_name = fields[nfields - 1]; - // Check if the last intermediate resolved to - // another import. - const char* fn_import - = findDeclImportPathInZir(&cur_zir, fn_name); - if (fn_import) { - Ast fn_ast; - memset(&fn_ast, 0, sizeof(fn_ast)); - Zir fn_zir = loadImportZir( - sema, cur_dir, fn_import, &fn_ast); - // Compute source dir BEFORE freeing cur_zir, - // since fn_import points into cur_zir string - // bytes. - char fn_src_dir[1024]; - computeSourceDir(NULL, cur_dir, fn_import, - fn_src_dir, sizeof(fn_src_dir)); - zirDeinit(&cur_zir); - astDeinit(&cur_ast); - if (fn_zir.inst_len > 0) { - // Try direct lookup first, then follow - // re-exports (e.g. scalbn -> ldexp). - func_inst = findFuncInModuleZir(sema, - fn_src_dir, &fn_zir, &fn_ast, fn_name, - import_source_dir, - sizeof(import_source_dir)); - if (func_inst != UINT32_MAX) { - // If findFuncInModuleZir found it - // directly (no re-export), it did - // not write import_source_dir. - // Set it to fn_src_dir as fallback. - if (import_source_dir[0] == '\0') - snprintf(import_source_dir, - sizeof(import_source_dir), "%s", - fn_src_dir); - saved_code = sema->code; - sema->code = fn_zir; - import_zir = fn_zir; - import_ast = fn_ast; - is_cross_module = true; - } else { - zirDeinit(&fn_zir); - astDeinit(&fn_ast); - } - } + if (cr->is_cross_module) + sema->code = arg_code; + } + + // Parse the inline function's parameter body. + uint32_t param_block_inst + = sema->code + .extra[sema->code.inst_datas[func_inst].pl_node.payload_index + + func_info.param_block_pi]; + const uint32_t* param_body; + uint32_t param_body_len; + getParamBody(sema, param_block_inst, &param_body, &param_body_len); + + // 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); + // For struct-returning functions found via findFuncInStructInZir, + // the return type is the enclosing struct type. + if (ret_ty == IP_INDEX_VOID_TYPE + && cr->struct_ret_type != IP_INDEX_VOID_TYPE) { + ret_ty = cr->struct_ret_type; + } + // Resolve multi-instruction return type body honestly. + // Ported from Sema.zig:7437-7444: when ret_ty_body.len > 0, + // evaluate the body via resolveInlineBody to produce the type. + // The inst_map already has param→arg bindings from the generic + // param evaluation loop above, so @TypeOf(a), Complex(T), etc. + // resolve through the normal ZIR dispatch. + if (ret_ty == IP_INDEX_VOID_TYPE && func_info.ret_ty_body_len > 1) { + const uint32_t* rtb = &sema->code.extra[func_info.ret_ty_ref_pos]; + instMapEnsureSpaceForBody( + &sema->inst_map, rtb, func_info.ret_ty_body_len); + + SemaBlock ct_block; + semaBlockInit(&ct_block, sema, block); + ct_block.is_comptime = true; + + uint32_t saved_cbi = sema->comptime_break_inst; + sema->comptime_break_inst = UINT32_MAX; + (void)analyzeBodyInner( + sema, &ct_block, rtb, func_info.ret_ty_body_len); + + if (sema->comptime_break_inst != UINT32_MAX) { + ZirInstRef op = sema->code.inst_datas[sema->comptime_break_inst] + .break_data.operand; + AirInstRef resolved = resolveInst(sema, op); + if (AIR_REF_IS_IP(resolved)) + ret_ty = AIR_REF_TO_IP(resolved); + } + semaBlockDeinit(&ct_block); + sema->comptime_break_inst = saved_cbi; + } + // Handle case where return type is a param ref (e.g. `fn absv(comptime + // ST: type, a: ST) ST`). The ret_ty_ref points to a param instruction; + // map it to the corresponding argument value. + if (ret_ty == IP_INDEX_VOID_TYPE && args_len > 0 + && func_info.ret_ty_body_len == 1) { + ZirInstRef ret_ty_ref = sema->code.extra[func_info.ret_ty_ref_pos]; + if (ret_ty_ref >= ZIR_REF_START_INDEX) { + uint32_t ret_ty_inst = ret_ty_ref - ZIR_REF_START_INDEX; + // Match the return type inst against param body insts. + uint32_t pi = 0; + for (uint32_t p = 0; p < param_body_len; p++) { + ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + if (param_body[p] == ret_ty_inst && pi < args_len) { + // Return type ref matches this param. + // For comptime type params, the arg value IS + // the type (an IP ref). For runtime params + // (including anytype), use semaTypeOf to get + // the argument's type. + if (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + ret_ty = AIR_REF_TO_IP(arg_refs[pi]); } else { - func_inst = findFuncInstInZir(&cur_zir, fn_name); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = cur_zir; - import_zir = cur_zir; - import_ast = cur_ast; - is_cross_module = true; - snprintf(import_source_dir, - sizeof(import_source_dir), "%s", cur_dir); - } else { - zirDeinit(&cur_zir); - astDeinit(&cur_ast); - } + ret_ty = semaTypeOf(sema, arg_refs[pi]); } + break; } - } - } else if (base_import && nfields == 1) { - // Single-level: e.g. const panic = common.panic; - // fields[0] = "panic" - Ast base_ast; - memset(&base_ast, 0, sizeof(base_ast)); - Zir base_zir = loadStdImportZir(sema, - sema->zcu->comp->config.module_root, sema->source_dir, - base_import, &base_ast); - if (base_zir.inst_len > 0) { - const char* field_name = fields[0]; - func_inst = findFuncInstInZir(&base_zir, field_name); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = base_zir; - import_zir = base_zir; - import_ast = base_ast; - is_cross_module = true; - computeSourceDir(sema->zcu->comp->config.module_root, - sema->source_dir, base_import, import_source_dir, - sizeof(import_source_dir)); - } else { - zirDeinit(&base_zir); - astDeinit(&base_ast); - } + pi++; } } } } - 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, sema->source_dir, import_path, &import_ast); - if (import_zir.inst_len > 0) { - computeSourceDir(sema->zcu->comp->config.module_root, - sema->source_dir, import_path, import_source_dir, - sizeof(import_source_dir)); - func_inst = findFuncInModuleZir(sema, import_source_dir, - &import_zir, &import_ast, field_name, - import_source_dir, sizeof(import_source_dir)); - if (func_inst != UINT32_MAX) { - // Swap to imported module's ZIR. - saved_code = sema->code; - sema->code = import_zir; - is_cross_module = true; - } - } - } - // Follow chained import: if direct import failed, check - // if the obj decl follows decl_val(X) + field_val(X, "Y") - // pattern and resolve the chain. - // E.g. "math" → decl_val("std") + field_val(%, "math") - // → "std" → @import("std") → load std.zig - // → "math" in std.zig → @import("math.zig") - // → load math.zig → find callee there - if (!is_cross_module) { - const char* chain_import = NULL; - const char* chain_field = NULL; - if (findDeclImportFieldVal( - sema, obj_name_idx, &chain_import, &chain_field) - && chain_field) { - // chain_import is the base import (possibly "std") - // chain_field is the field name in that import - // (e.g. "math"). - // Load the base import, then find chain_field in - // it (which may itself be an @import). - Ast base_ast; - memset(&base_ast, 0, sizeof(base_ast)); - Zir base_zir = loadStdImportZir(sema, - sema->zcu->comp->config.module_root, sema->source_dir, - chain_import, &base_ast); - if (base_zir.inst_len > 0) { - // Find chain_field in base module; if it's - // also an @import, follow it. - const char* sub_import_ptr - = findDeclImportPathInZir(&base_zir, chain_field); - if (sub_import_ptr) { - // Copy sub_import before freeing base_zir, - // since it points into base_zir.string_bytes. - char sub_import[1024]; - snprintf(sub_import, sizeof(sub_import), "%s", - sub_import_ptr); - // Compute source_dir for the base import. - char base_dir[1024]; - computeSourceDir( - sema->zcu->comp->config.module_root, - sema->source_dir, chain_import, base_dir, - sizeof(base_dir)); - import_zir = loadImportZir( - sema, base_dir, sub_import, &import_ast); - zirDeinit(&base_zir); - astDeinit(&base_ast); - if (import_zir.inst_len > 0) { - const char* field_name - = (const char*)&sema->code - .string_bytes[callee_name_idx]; - computeSourceDir(NULL, base_dir, sub_import, - import_source_dir, - sizeof(import_source_dir)); - func_inst = findFuncInModuleZir(sema, - import_source_dir, &import_zir, - &import_ast, field_name, import_source_dir, - sizeof(import_source_dir)); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = import_zir; - is_cross_module = true; + // Non-inline functions: emit CALL instruction instead of inlining. + // Ported from Sema.zig analyzeCall (non-inline path, lines 7575-7601). + // Upstream (Sema.zig:7247): if the return type is comptime-only + // (e.g. comptime_int, type), the block is set to comptime, making + // is_inline_call = true. Upstream (Sema.zig:7482): + // is_inline_call = block.isComptime() or func_type.isGeneric() + // or inline_requested; + bool is_comptime_only_ret + = (ret_ty == IP_INDEX_TYPE_TYPE || ret_ty == IP_INDEX_COMPTIME_INT_TYPE + || ret_ty == IP_INDEX_COMPTIME_FLOAT_TYPE + || ret_ty == IP_INDEX_ENUM_LITERAL_TYPE); + // Upstream (Sema.zig:7482): + // is_inline_call = block.isComptime() or inline_requested; + // Generic functions are NOT auto-inlined — they get monomorphized + // and called at runtime (like regular functions minus comptime params). + bool is_inline_call + = func_info.is_inline || block->is_comptime || is_comptime_only_ret; + if (!is_inline_call) { + // Dummy allocs for generic runtime params are now emitted + // interleaved in the arg evaluation loop above (Fix A). + + // Count runtime params (skip comptime type params) and coerce + // arguments to callee's parameter types. + // Ported from src/Sema.zig analyzeCall (Sema.zig:7575-7601). + uint32_t runtime_args_count = 0; + AirInstRef runtime_arg_refs[16]; + uint32_t pi = 0; + for (uint32_t p = 0; p < param_body_len; p++) { + ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_ANYTYPE) { + if (pi < args_len) { + AirInstRef arg = arg_refs[pi]; + // For typed params, coerce arg to the param type. + // Param extra: {name(u32), type(u32={body_len:31, + // is_generic:1}), body_inst...} + if (ptag == ZIR_INST_PARAM) { + uint32_t param_pl + = sema->code.inst_datas[param_body[p]] + .pl_tok.payload_index; + uint32_t type_raw = sema->code.extra[param_pl + 1]; + uint32_t type_body_len = type_raw & 0x7FFFFFFF; + if (type_body_len >= 1) { + // The type body contains ZIR instruction + // indices. The last instruction should be a + // break_inline whose operand is the type ref. + uint32_t last_type_inst + = sema->code + .extra[param_pl + 2 + type_body_len - 1]; + if (last_type_inst < sema->code.inst_len + && sema->code.inst_tags[last_type_inst] + == ZIR_INST_BREAK_INLINE) { + ZirInstRef type_ref + = sema->code.inst_datas[last_type_inst] + .break_data.operand; + if (type_ref < ZIR_REF_START_INDEX) { + TypeIndex param_ty = type_ref; + arg = semaCoerce( + sema, block, param_ty, arg); } } - } else { - // chain_field is the function directly. - const char* field_name - = (const char*)&sema->code - .string_bytes[callee_name_idx]; - func_inst - = findFuncInstInZir(&base_zir, field_name); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = base_zir; - import_zir = base_zir; - import_ast = base_ast; - is_cross_module = true; - computeSourceDir( - sema->zcu->comp->config.module_root, - sema->source_dir, chain_import, - import_source_dir, - sizeof(import_source_dir)); - } else { - zirDeinit(&base_zir); - astDeinit(&base_ast); - } } } + runtime_arg_refs[runtime_args_count++] = arg; } + pi++; + } else if (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + pi++; } } - // Handle FIELD_PTR/FIELD_VAL callee_ref for 3-level import - // chains like std.meta.Int(...). - // callee_ref → FIELD_PTR(lhs=DECL_VAL("std"), field="meta") - // callee_name_idx → "Int" - if (!is_cross_module && callee_ref >= ZIR_REF_START_INDEX) { - uint32_t ci = callee_ref - ZIR_REF_START_INDEX; - ZirInstTag ctag2 = sema->code.inst_tags[ci]; - if (ctag2 == ZIR_INST_FIELD_PTR || ctag2 == ZIR_INST_FIELD_VAL) { - uint32_t pi = sema->code.inst_datas[ci].pl_node.payload_index; - uint32_t lhs_ref = sema->code.extra[pi]; - uint32_t mid_str = sema->code.extra[pi + 1]; - if (lhs_ref >= ZIR_REF_START_INDEX) { - uint32_t li = lhs_ref - ZIR_REF_START_INDEX; - ZirInstTag lt = sema->code.inst_tags[li]; - if (lt == ZIR_INST_DECL_REF || lt == ZIR_INST_DECL_VAL) { - uint32_t base_name_idx - = sema->code.inst_datas[li].str_tok.start; - const char* base_import - = findDeclImportPath(sema, base_name_idx); - // Function name is the field in the - // outer field_val (e.g. "floatMantissaBits" - // from math.floatMantissaBits). - const char* fn_field - = (const char*)&sema->code.string_bytes[mid_str]; - - // If the lhs is not a direct import, - // try alias resolution. E.g. for - // `const math = std.math;`, resolve - // through the alias chain. - const char* alias_import = NULL; - const char* alias_field = NULL; - if (!base_import - && findDeclImportFieldVal(sema, base_name_idx, - &alias_import, &alias_field)) { - // alias_import = std import path - // alias_field = "math" - // Load std module, find sub-module - Ast alias_ast; - memset(&alias_ast, 0, sizeof(alias_ast)); - Zir alias_zir = loadStdImportZir(sema, - sema->zcu->comp->config.module_root, - sema->source_dir, alias_import, &alias_ast); - if (alias_zir.inst_len > 0) { - const char* sub_path = findDeclImportPathInZir( - &alias_zir, alias_field); - if (sub_path) { - char alias_dir[1024]; - computeSourceDir( - sema->zcu->comp->config.module_root, - sema->source_dir, alias_import, - alias_dir, sizeof(alias_dir)); - import_zir = loadImportZir(sema, alias_dir, - sub_path, &import_ast); - zirDeinit(&alias_zir); - astDeinit(&alias_ast); - if (import_zir.inst_len > 0) { - computeSourceDir(NULL, alias_dir, - sub_path, import_source_dir, - sizeof(import_source_dir)); - func_inst = findFuncInModuleZir(sema, - import_source_dir, &import_zir, - &import_ast, fn_field, - import_source_dir, - sizeof(import_source_dir)); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = import_zir; - is_cross_module = true; - } - } - } else { - zirDeinit(&alias_zir); - astDeinit(&alias_ast); - } - } - } - if (!is_cross_module && base_import) { - const char* mid_field = fn_field; - Ast base_ast; - memset(&base_ast, 0, sizeof(base_ast)); - Zir base_zir = loadStdImportZir(sema, - sema->zcu->comp->config.module_root, - sema->source_dir, base_import, &base_ast); - if (base_zir.inst_len > 0) { - const char* sub_import_ptr - = findDeclImportPathInZir( - &base_zir, mid_field); - if (sub_import_ptr) { - // Copy before freeing base_zir. - char sub_import[1024]; - snprintf(sub_import, sizeof(sub_import), - "%s", sub_import_ptr); - char base_dir[1024]; - computeSourceDir( - sema->zcu->comp->config.module_root, - sema->source_dir, base_import, - base_dir, sizeof(base_dir)); - import_zir = loadImportZir(sema, base_dir, - sub_import, &import_ast); - zirDeinit(&base_zir); - astDeinit(&base_ast); - if (import_zir.inst_len > 0) { - const char* fn_name = callee_name_idx - ? (const char*)&sema->code - .string_bytes - [callee_name_idx] - : fn_field; - computeSourceDir(NULL, base_dir, - sub_import, import_source_dir, - sizeof(import_source_dir)); - func_inst = findFuncInModuleZir(sema, - import_source_dir, &import_zir, - &import_ast, fn_name, - import_source_dir, - sizeof(import_source_dir)); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = import_zir; - is_cross_module = true; - } - } - } else { - zirDeinit(&base_zir); - astDeinit(&base_ast); - } - } - } - // 4-level chain: callee_ref = field_val( - // field_val(decl_val("std"), "math"), "F80") - // callee_name = "fromFloat" - // Handles: std.math.F80.fromFloat(...) - } else if (lt == ZIR_INST_FIELD_VAL - || lt == ZIR_INST_FIELD_PTR) { - uint32_t lp - = sema->code.inst_datas[li].pl_node.payload_index; - uint32_t lhs2_ref = sema->code.extra[lp]; - uint32_t mid2_str = sema->code.extra[lp + 1]; - if (lhs2_ref >= ZIR_REF_START_INDEX) { - uint32_t l2i = lhs2_ref - ZIR_REF_START_INDEX; - ZirInstTag l2t = sema->code.inst_tags[l2i]; - if (l2t == ZIR_INST_DECL_REF - || l2t == ZIR_INST_DECL_VAL) { - uint32_t bn - = sema->code.inst_datas[l2i].str_tok.start; - const char* base_imp - = findDeclImportPath(sema, bn); - const char* sub_field - = (const char*)&sema->code - .string_bytes[mid2_str]; - const char* struct_nm - = (const char*)&sema->code - .string_bytes[mid_str]; - const char* fn_nm - = (const char*)&sema->code - .string_bytes[callee_name_idx]; - if (base_imp) { - Ast b_ast; - memset(&b_ast, 0, sizeof(b_ast)); - Zir b_zir = loadStdImportZir(sema, - sema->zcu->comp->config.module_root, - sema->source_dir, base_imp, &b_ast); - if (b_zir.inst_len > 0) { - const char* sp_ptr - = findDeclImportPathInZir( - &b_zir, sub_field); - if (sp_ptr) { - // Copy before freeing b_zir. - char sp[1024]; - snprintf( - sp, sizeof(sp), "%s", sp_ptr); - char bd[1024]; - computeSourceDir( - sema->zcu->comp->config - .module_root, - sema->source_dir, base_imp, bd, - sizeof(bd)); - import_zir = loadImportZir( - sema, bd, sp, &import_ast); - zirDeinit(&b_zir); - astDeinit(&b_ast); - if (import_zir.inst_len > 0) { - func_inst - = findFuncInStructInZir( - &import_zir, struct_nm, - fn_nm); - if (func_inst != UINT32_MAX) { - // Register the struct - // type for field access. - struct_ret_type - = registerStructTypeFromZir( - sema, &import_zir, - struct_nm); - saved_code = sema->code; - sema->code = import_zir; - is_cross_module = true; - computeSourceDir(NULL, bd, - sp, import_source_dir, - sizeof( - import_source_dir)); - } - } - } else { - zirDeinit(&b_zir); - astDeinit(&b_ast); - } - } - } - } - } - } + // Use the callee's existing func_decl. Ported from upstream + // Sema.zig analyzeCall: func_val is the resolved callee, not + // a newly-created func entry. Look up the callee's nav by name + // in the file namespace, then use its existing func_decl. + InternPoolIndex func_val_ip = IP_INDEX_NONE; + { + uint32_t callee_nav = UINT32_MAX; + if (callee_name_idx != 0 + && sema->zcu->root_file_idx != UINT32_MAX) { + const char* cname + = (const char*)&sema->code.string_bytes[callee_name_idx]; + uint32_t rns + = sema->zcu->file_namespaces[sema->zcu->root_file_idx]; + callee_nav = findNavInNamespace(sema, rns, cname); + } + if (callee_nav != UINT32_MAX) { + const Nav* cn = ipGetNav(sema->ip, callee_nav); + InternPoolIndex resolved = cn->resolved_type; + // If resolved points to a func_decl, use it directly. + // If it points to a func_type, construct the func_decl + // key and look it up. + if (resolved != IP_INDEX_NONE + && sema->ip->items[resolved].tag == IP_KEY_FUNC) { + func_val_ip = resolved; + } else if (resolved != IP_INDEX_NONE) { + InternPoolKey fv_key; + memset(&fv_key, 0, sizeof(fv_key)); + fv_key.tag = IP_KEY_FUNC; + fv_key.data.func_decl.owner_nav = callee_nav; + fv_key.data.func_decl.ty = resolved; + func_val_ip = ipIntern(sema->ip, fv_key); } + } else { + // Fallback: create with func_inst as nav. + InternPoolKey ft_key; + memset(&ft_key, 0, sizeof(ft_key)); + ft_key.tag = IP_KEY_FUNC_TYPE; + ft_key.data.func_type.return_type = ret_ty; + ft_key.data.func_type.param_count = runtime_args_count; + InternPoolIndex func_type_ip = ipForceIntern(sema->ip, ft_key); + InternPoolKey fv_key; + memset(&fv_key, 0, sizeof(fv_key)); + fv_key.tag = IP_KEY_FUNC; + fv_key.data.func_decl.owner_nav = func_inst; + fv_key.data.func_decl.ty = func_type_ip; + func_val_ip = ipIntern(sema->ip, fv_key); } } - } - // For non-field calls: if local lookup fails, check if the callee - // declaration follows the import(...).field_val pattern - // (e.g. `const absv = @import("./absv.zig").absv;`). - if (func_inst == UINT32_MAX && !is_field_call && sema->source_dir - && callee_name_idx != 0) { - const char* import_path = NULL; - const char* field_name = NULL; - if (findDeclImportFieldVal( - sema, callee_name_idx, &import_path, &field_name)) { - import_zir = loadImportZir( - sema, sema->source_dir, import_path, &import_ast); - if (import_zir.inst_len > 0) { - computeSourceDir(sema->zcu->comp->config.module_root, - sema->source_dir, import_path, import_source_dir, - sizeof(import_source_dir)); - func_inst = findFuncInModuleZir(sema, import_source_dir, - &import_zir, &import_ast, field_name, import_source_dir, - sizeof(import_source_dir)); - if (func_inst != UINT32_MAX) { - saved_code = sema->code; - sema->code = import_zir; - is_cross_module = true; - } - } + // Emit CALL extra: {args_len, arg_refs[0..args_len]}. + uint32_t call_extra = semaAddExtra(sema, runtime_args_count); + for (uint32_t a = 0; a < runtime_args_count; a++) + semaAddExtra(sema, runtime_arg_refs[a]); + + // Ported from Sema.zig:7480: AstGen ensures a dbg_stmt + // always precedes a call instruction. Use the caller's ZIR + // for cross-module calls. Note: the general dbg_stmt at the + // top of zirCall (before arg evaluation) may have been + // followed by arg instructions, so emit another one here + // to directly precede the call. zirDbgStmt de-duplicates + // when the previous block instruction is already a dbg_stmt. + if (cr->is_cross_module) { + Zir temp = sema->code; + sema->code = cr->saved_code; + zirDbgStmt(sema, block, inst - 1); + sema->code = temp; + } else { + zirDbgStmt(sema, block, inst - 1); } - } - if (func_inst == UINT32_MAX) { - // Can't resolve callee; return void. - return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } + // Emit CALL instruction. + AirInstData call_data; + memset(&call_data, 0, sizeof(call_data)); + call_data.pl_op.operand = AIR_REF_FROM_IP(func_val_ip); + call_data.pl_op.payload = call_extra; + AirInstRef call_ref = semaAddInst(block, AIR_INST_CALL, call_data); - 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); + // Register CALL return type for semaTypeOf. + if (sema->num_calls < 16) { + sema->call_air_insts[sema->num_calls] = AIR_REF_TO_INST(call_ref); + sema->call_ret_types[sema->num_calls] = ret_ty; + sema->num_calls++; } - return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - // Type-returning functions (return `type`): evaluate via comptime - // evaluation. Ported from src/Sema.zig analyzeCall comptime path. - // Detect type-returning functions by resolving the return type ref. - // For plain Func with ret_ty_body_len==1, extra[ret_ty_ref_pos] is - // a pre-interned Ref (e.g. IP_INDEX_TYPE_TYPE). - // For FuncFancy with has_ret_ty_body, extra[ret_ty_ref_pos] is a - // ZIR instruction index; resolve it via break_inline's operand. - // Ported from Sema.zig:7247 comptimeOnlySema check. - if (func_info.ret_ty_body_len >= 1) { - uint32_t ret_ref = sema->code.extra[func_info.ret_ty_ref_pos]; - // For has_ret_ty_body (FuncFancy): the body's last instruction - // is break_inline whose operand is the actual return type ref. - // Ported from Zir.getFnInfo (Zir.zig:4929): when ret_ty_body - // is present, evaluate the body to get the return type. - if (ret_ref >= ZIR_REF_START_INDEX) { - uint32_t last_inst = sema->code.extra[func_info.ret_ty_ref_pos - + func_info.ret_ty_body_len - 1]; - if (last_inst < sema->code.inst_len - && sema->code.inst_tags[last_inst] == ZIR_INST_BREAK_INLINE) { - ret_ref = sema->code.inst_datas[last_inst].break_data.operand; + // Trigger separate body analysis of the callee function. + // Ported from Sema.zig analyzeCall: ensureFuncBodyAnalysisQueued. + // For generic functions, pass call args so comptime params are + // mapped to their resolved values (monomorphization). + // For cross-module calls, only analyze generic (monomorphized) + // function bodies — non-generic cross-module functions belong to + // their own module's AIR, not the current file's. + if (!cr->is_cross_module || is_generic) { + uint32_t body_name_idx = callee_name_idx; + if (cr->is_cross_module && callee_name_idx != 0) { + // callee_name_idx is from the pre-swap ZIR's string table. + // Find the same name in the imported ZIR's string table. + const char* name = (const char*)&cr->saved_code + .string_bytes[callee_name_idx]; + body_name_idx = findStringInZirBytes(&sema->code, name); } - } - if (ret_ref == IP_INDEX_TYPE_TYPE) { - // Resolve args in the caller's ZIR context first. - AirInstRef ct_arg_refs[16]; - memset(ct_arg_refs, 0, sizeof(ct_arg_refs)); - assert(args_len <= 16); - { - Zir fn_zir = sema->code; - if (is_cross_module) - sema->code = saved_code; - uint32_t prev_end = args_len; - for (uint32_t a = 0; a < args_len; a++) { - uint32_t end_off = sema->code.extra[arg_data_start + a]; - uint32_t body_start = arg_data_start + prev_end; - uint32_t body_len = end_off - prev_end; - if (body_len > 1) { - (void)analyzeBodyInner(sema, block, - &sema->code.extra[body_start], body_len - 1); - } - if (body_len >= 1) { - uint32_t li - = sema->code.extra[body_start + body_len - 1]; - ZirInstRef op - = sema->code.inst_datas[li].break_data.operand; - ct_arg_refs[a] = resolveInst(sema, op); - } else { - ct_arg_refs[a] = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - prev_end = end_off; - } - if (is_cross_module) - sema->code = fn_zir; - } - AirInstRef honest_result = evalComptimeTypeCall(sema, func_inst, - &func_info, ct_arg_refs, args_len, - is_cross_module ? import_source_dir : NULL); - if (is_cross_module) { - sema->code = saved_code; - zirDeinit(&import_zir); - astDeinit(&import_ast); + // Compute call-site arg types before entering body analysis + // (which saves/resets AIR state, making semaTypeOf unavailable). + TypeIndex arg_type_buf[16]; + const TypeIndex* arg_types_ptr = NULL; + if (is_generic && args_len > 0) { + for (uint32_t ai = 0; ai < args_len && ai < 16; ai++) + arg_type_buf[ai] = semaTypeOf(sema, arg_refs[ai]); + arg_types_ptr = arg_type_buf; } - if (AIR_REF_IS_IP(honest_result) - && AIR_REF_TO_IP(honest_result) != IP_INDEX_VOID_VALUE) - return honest_result; - return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + analyzeFuncBodyAndRecord(sema, block, func_inst, body_name_idx, + is_generic ? arg_refs : NULL, is_generic ? args_len : 0, + arg_types_ptr, func_val_ip); } - } - // Determine which parameters are comptime (from callee's ZIR). - // Ported from src/Sema.zig lines 7104-7123: generate args to comptime - // params in comptime block. - bool is_ct_param[16]; - memset(is_ct_param, 0, sizeof(is_ct_param)); - bool is_generic = false; - // Track whether each param has a generic type (refers to previous - // comptime params). Ported from Zir.Inst.Param.Type.is_generic. - bool has_generic_type[16]; - memset(has_generic_type, 0, sizeof(has_generic_type)); - { - uint32_t early_pb_inst - = sema->code - .extra[sema->code.inst_datas[func_inst].pl_node.payload_index - + func_info.param_block_pi]; - const uint32_t* early_pb; - uint32_t early_pb_len; - getParamBody(sema, early_pb_inst, &early_pb, &early_pb_len); - uint32_t pi = 0; - for (uint32_t p = 0; p < early_pb_len; p++) { - ZirInstTag ptag = sema->code.inst_tags[early_pb[p]]; - if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { - if (ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { - is_ct_param[pi] = true; - } - // Ported from upstream: anytype and comptime params both - // make the function generic. Used for dummy alloc emission - // in the non-inline CALL path (Sema.zig:7394-7399). - if (ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE) { - is_generic = true; - } - // Check if param type is generic (refers to previous params). - // Ported from Zir.Inst.Param: extra[payload+1] bit 31. - if (ptag == ZIR_INST_PARAM - || ptag == ZIR_INST_PARAM_COMPTIME) { - uint32_t ppl = sema->code.inst_datas[early_pb[p]] - .pl_tok.payload_index; - uint32_t type_raw = sema->code.extra[ppl + 1]; - if ((type_raw >> 31) & 1) - has_generic_type[pi] = true; - } - pi++; - } + // Clean up cross-module state. + if (cr->is_cross_module) { + sema->code = cr->saved_code; + zirDeinit(&cr->import_zir); + astDeinit(&cr->import_ast); } + return call_ref; } - // 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 - // Arg 0 body: args_body[args_len .. end_offsets[0]] - // Arg N body: args_body[end_offsets[N-1] .. end_offsets[N]] - 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 + // Upstream (Sema.zig:7247): if the return type is comptime-only, + // temporarily make the block comptime. This is deferred-restored + // in upstream; we restore it at the end of zirCall. + bool saved_is_comptime = block->is_comptime; + // Upstream (Sema.zig:7247): only for NON-inline functions. + // For inline functions, they're already going to be inlined, so + // the comptime_reason is not set for the comptime-only ret case. + if (is_comptime_only_ret && !func_info.is_inline && !block->is_comptime) + block->is_comptime = true; + + // Memoization: check if we've seen this function with the same + // comptime args before. + // Ported from src/Sema.zig line 7712: want_memoize = block.isComptime(). + bool all_comptime = block->is_comptime && args_len > 0; + if (all_comptime) { for (uint32_t a = 0; a < args_len; a++) { - uint32_t arg_end_off = sema->code.extra[arg_data_start + a]; - uint32_t arg_body_start = arg_data_start + prev_end; - uint32_t arg_body_len = arg_end_off - prev_end; - // Each arg body should end with a break_inline whose - // operand is the argument ref. - assert(arg_body_len >= 1); - // Ported from src/Sema.zig lines 7104-7123: - // Generate args to comptime params in comptime block. - bool saved_is_comptime = block->is_comptime; - if (is_ct_param[a]) - block->is_comptime = true; - // Analyze preceding instructions in the arg body so that - // the break_inline operand is mapped. For simple args - // (e.g. param refs) arg_body_len==1 and this is a no-op. - if (arg_body_len > 1) { - (void)analyzeBodyInner(sema, block, - &sema->code.extra[arg_body_start], arg_body_len - 1); + if (!AIR_REF_IS_IP(arg_refs[a])) { + all_comptime = false; + break; } - block->is_comptime = saved_is_comptime; - uint32_t last_inst_idx - = sema->code.extra[arg_body_start + arg_body_len - 1]; - ZirInstTag last_tag = sema->code.inst_tags[last_inst_idx]; - assert(last_tag == ZIR_INST_BREAK_INLINE); - ZirInstRef arg_operand - = sema->code.inst_datas[last_inst_idx].break_data.operand; - arg_refs[a] = resolveInst(sema, arg_operand); - // Ported from Sema.zig:7394-7399: emit dummy alloc for - // non-comptime runtime params of generic functions, - // interleaved with arg evaluation (not batched after). - if (is_generic && !is_ct_param[a] && !func_info.is_inline - && !block->is_comptime) { - TypeIndex arg_ty = semaTypeOf(sema, arg_refs[a]); - AirInstData dummy; - memset(&dummy, 0, sizeof(dummy)); - dummy.ty.ty_ref = AIR_REF_FROM_IP(arg_ty); - semaAddInstAsIndex(sema, AIR_INST_ALLOC, dummy); + } + } + if (all_comptime) { + for (uint32_t mi = 0; mi < sema->num_memo; mi++) { + if (sema->memo_func_inst[mi] != func_inst) + continue; + if (sema->memo_args_len[mi] != args_len) + continue; + bool match = true; + for (uint32_t a = 0; a < args_len && a < 4; a++) { + if (sema->memo_args[mi][a] != arg_refs[a]) { + match = false; + break; + } + } + if (match) { + if (cr->is_cross_module) { + sema->code = cr->saved_code; + zirDeinit(&cr->import_zir); + astDeinit(&cr->import_ast); + } + block->is_comptime = saved_is_comptime; + return sema->memo_result[mi]; } - prev_end = arg_end_off; } - if (is_cross_module) - sema->code = arg_code; } - // Parse the inline function's parameter body. - uint32_t param_block_inst - = sema->code - .extra[sema->code.inst_datas[func_inst].pl_node.payload_index - + func_info.param_block_pi]; - const uint32_t* param_body; - uint32_t param_body_len; - getParamBody(sema, param_block_inst, &param_body, &param_body_len); - - // 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); - // For struct-returning functions found via findFuncInStructInZir, - // the return type is the enclosing struct type. - if (ret_ty == IP_INDEX_VOID_TYPE - && struct_ret_type != IP_INDEX_VOID_TYPE) { - ret_ty = struct_ret_type; + // Ported from src/Sema.zig line 7480: emit dbg_stmt for inline + // calls, just before the inline block. Use the caller's ZIR + // for cross-module calls. + if (inst > 0 && !block->is_comptime) { + Zir dbg_code = sema->code; + if (cr->is_cross_module) + sema->code = cr->saved_code; + zirDbgStmt(sema, block, inst - 1); + if (cr->is_cross_module) + sema->code = dbg_code; } - // Resolve multi-instruction return type body honestly. - // Ported from Sema.zig:7437-7444: when ret_ty_body.len > 0, - // evaluate the body via resolveInlineBody to produce the type. - // The inst_map already has param→arg bindings from the generic - // param evaluation loop above, so @TypeOf(a), Complex(T), etc. - // resolve through the normal ZIR dispatch. - if (ret_ty == IP_INDEX_VOID_TYPE && func_info.ret_ty_body_len > 1) { - const uint32_t* rtb = &sema->code.extra[func_info.ret_ty_ref_pos]; - instMapEnsureSpaceForBody( - &sema->inst_map, rtb, func_info.ret_ty_body_len); - SemaBlock ct_block; - semaBlockInit(&ct_block, sema, block); - ct_block.is_comptime = true; + // 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; - uint32_t saved_cbi = sema->comptime_break_inst; - sema->comptime_break_inst = UINT32_MAX; - (void)analyzeBodyInner( - sema, &ct_block, rtb, func_info.ret_ty_body_len); + // Save block instruction count for rollback if comptime return. + uint32_t saved_block_inst_len = block->instructions_len; - if (sema->comptime_break_inst != UINT32_MAX) { - ZirInstRef op = sema->code.inst_datas[sema->comptime_break_inst] - .break_data.operand; - AirInstRef resolved = resolveInst(sema, op); - if (AIR_REF_IS_IP(resolved)) - ret_ty = AIR_REF_TO_IP(resolved); - } - semaBlockDeinit(&ct_block); - sema->comptime_break_inst = saved_cbi; - } - // Handle case where return type is a param ref (e.g. `fn absv(comptime - // ST: type, a: ST) ST`). The ret_ty_ref points to a param instruction; - // map it to the corresponding argument value. - if (ret_ty == IP_INDEX_VOID_TYPE && args_len > 0 - && func_info.ret_ty_body_len == 1) { - ZirInstRef ret_ty_ref = sema->code.extra[func_info.ret_ty_ref_pos]; - if (ret_ty_ref >= ZIR_REF_START_INDEX) { - uint32_t ret_ty_inst = ret_ty_ref - ZIR_REF_START_INDEX; - // Match the return type inst against param body insts. - uint32_t pi = 0; - for (uint32_t p = 0; p < param_body_len; p++) { - ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; - if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { - if (param_body[p] == ret_ty_inst && pi < args_len) { - // Return type ref matches this param. - // For comptime type params, the arg value IS - // the type (an IP ref). For runtime params - // (including anytype), use semaTypeOf to get - // the argument's type. - if (ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { - ret_ty = AIR_REF_TO_IP(arg_refs[pi]); - } else { - ret_ty = semaTypeOf(sema, arg_refs[pi]); - } - break; - } - pi++; - } + // Reserve the block instruction (data filled later). + uint32_t block_inst_idx = semaAddInstAsIndex(sema, block_tag, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); + // Set up child block for inlining. + SemaBlockMerges merges; + memset(&merges, 0, sizeof(merges)); + merges.block_inst = block_inst_idx; + + // Use the callee's existing func_decl for the dbg_inline_block's + // func field. Ported from upstream Sema.zig line 7788: + // .func = func_val.?.toIntern(), + // The callee's func_decl was already created by ensureNavValUpToDate + // during inline function resolution. Find it by looking up the nav + // and reconstructing the func_decl key. + InternPoolIndex func_ip = IP_INDEX_NONE; + if (need_debug_scope) { + // Find the callee's nav by matching zir_index. + uint32_t callee_nav = UINT32_MAX; + for (uint32_t ni = 0; ni < ipNavCount(sema->ip); ni++) { + const Nav* n = ipGetNav(sema->ip, ni); + if (n->zir_index == func_inst + && n->resolved_type != IP_INDEX_NONE) { + callee_nav = ni; + break; } } + if (callee_nav != UINT32_MAX) { + const Nav* cn = ipGetNav(sema->ip, callee_nav); + InternPoolKey func_key; + memset(&func_key, 0, sizeof(func_key)); + func_key.tag = IP_KEY_FUNC; + func_key.data.func_decl.owner_nav = callee_nav; + func_key.data.func_decl.ty = cn->resolved_type; + func_ip = ipIntern(sema->ip, func_key); + } } - // Non-inline functions: emit CALL instruction instead of inlining. - // Ported from Sema.zig analyzeCall (non-inline path, lines 7575-7601). - // Upstream (Sema.zig:7247): if the return type is comptime-only - // (e.g. comptime_int, type), the block is set to comptime, making - // is_inline_call = true. Upstream (Sema.zig:7482): - // is_inline_call = block.isComptime() or func_type.isGeneric() - // or inline_requested; - bool is_comptime_only_ret - = (ret_ty == IP_INDEX_TYPE_TYPE || ret_ty == IP_INDEX_COMPTIME_INT_TYPE - || ret_ty == IP_INDEX_COMPTIME_FLOAT_TYPE - || ret_ty == IP_INDEX_ENUM_LITERAL_TYPE); - // Upstream (Sema.zig:7482): - // is_inline_call = block.isComptime() or inline_requested; - // Generic functions are NOT auto-inlined — they get monomorphized - // and called at runtime (like regular functions minus comptime params). - bool is_inline_call - = func_info.is_inline || block->is_comptime || is_comptime_only_ret; - if (!is_inline_call) { - // Dummy allocs for generic runtime params are now emitted - // interleaved in the arg evaluation loop above (Fix A). + SemaBlockInlining inlining; + memset(&inlining, 0, sizeof(inlining)); + inlining.call_block = block; + inlining.func = func_ip; + inlining.merges = merges; - // Count runtime params (skip comptime type params) and coerce - // arguments to callee's parameter types. - // Ported from src/Sema.zig analyzeCall (Sema.zig:7575-7601). - uint32_t runtime_args_count = 0; - AirInstRef runtime_arg_refs[16]; - uint32_t pi = 0; - for (uint32_t p = 0; p < param_body_len; p++) { - ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; - if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_ANYTYPE) { - if (pi < args_len) { - AirInstRef arg = arg_refs[pi]; - // For typed params, coerce arg to the param type. - // Param extra: {name(u32), type(u32={body_len:31, - // is_generic:1}), body_inst...} - if (ptag == ZIR_INST_PARAM) { - uint32_t param_pl - = sema->code.inst_datas[param_body[p]] - .pl_tok.payload_index; - uint32_t type_raw = sema->code.extra[param_pl + 1]; - uint32_t type_body_len = type_raw & 0x7FFFFFFF; - if (type_body_len >= 1) { - // The type body contains ZIR instruction - // indices. The last instruction should be a - // break_inline whose operand is the type ref. - uint32_t last_type_inst - = sema->code - .extra[param_pl + 2 + type_body_len - 1]; - if (last_type_inst < sema->code.inst_len - && sema->code.inst_tags[last_type_inst] - == ZIR_INST_BREAK_INLINE) { - ZirInstRef type_ref - = sema->code.inst_datas[last_type_inst] - .break_data.operand; - if (type_ref < ZIR_REF_START_INDEX) { - TypeIndex param_ty = type_ref; - arg = semaCoerce( - sema, block, param_ty, arg); - } + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = block->is_comptime; + child_block.want_safety = false; + child_block.want_safety_set = true; + child_block.inlining = &inlining; + // Upstream creates child_block directly (not via makeSubBlock), + // so need_debug_scope is null (not inherited from parent). + child_block.need_debug_scope = NULL; + + // Save and clear inst_map for the inline call scope. + // Ported from Sema.zig:7749-7764: each inline call gets a fresh inst_map. + InstMap saved_inst_map = sema->inst_map; + memset(&sema->inst_map, 0, sizeof(sema->inst_map)); + + // Map param ZIR instructions to the argument values. + instMapEnsureSpaceForBody(&sema->inst_map, param_body, param_body_len); + uint32_t param_idx = 0; + for (uint32_t p = 0; p < param_body_len; p++) { + ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + if (param_idx >= args_len) { + sema->has_compile_errors = true; + free(sema->inst_map.items); + sema->inst_map = saved_inst_map; + block->is_comptime = saved_is_comptime; + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + instMapPut(&sema->inst_map, param_body[p], arg_refs[param_idx]); + + // Emit dbg_arg_inline for params whose resolved type is + // not comptime-only. Ported from addDbgVar: + // if (try val_ty.comptimeOnlySema(pt)) return; + // Check (a): arg value IS a type (key in TYPE range). + // Check (b): for comptime params, the DECLARED param type + // is comptime-only (comptime_int, comptime_float, etc.). + // Upstream coerces args to param types before typeOf; + // the C sema doesn't, so we check the declared type. + { + bool arg_comptime_only = false; + if (AIR_REF_IS_IP(arg_refs[param_idx])) { + InternPoolIndex ip = AIR_REF_TO_IP(arg_refs[param_idx]); + InternPoolKey key = sema->ip->items[ip]; + // (a) arg is a type value. + arg_comptime_only = (key.tag >= IP_KEY_INT_TYPE + && key.tag <= IP_KEY_INFERRED_ERROR_SET_TYPE); + if (!arg_comptime_only) { + arg_comptime_only = (ip == IP_INDEX_VOID_TYPE + || ip == IP_INDEX_BOOL_TYPE + || ip == IP_INDEX_TYPE_TYPE + || ip == IP_INDEX_COMPTIME_INT_TYPE + || ip == IP_INDEX_COMPTIME_FLOAT_TYPE + || ip == IP_INDEX_F16_TYPE + || ip == IP_INDEX_F32_TYPE + || ip == IP_INDEX_F64_TYPE + || ip == IP_INDEX_F80_TYPE + || ip == IP_INDEX_F128_TYPE); + } + } + // (b) declared param type is comptime-only. + if (!arg_comptime_only + && (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME)) { + uint32_t ppl = sema->code.inst_datas[param_body[p]] + .pl_tok.payload_index; + uint32_t type_raw = sema->code.extra[ppl + 1]; + uint32_t tbody_len = type_raw & 0x7FFFFFFF; + if (tbody_len == 1) { + uint32_t ti = sema->code.extra[ppl + 2]; + if (ti < sema->code.inst_len + && sema->code.inst_tags[ti] + == ZIR_INST_BREAK_INLINE) { + ZirInstRef tref + = sema->code.inst_datas[ti].break_data.operand; + if (tref < ZIR_REF_START_INDEX) { + arg_comptime_only + = (tref == IP_INDEX_COMPTIME_INT_TYPE + || tref == IP_INDEX_COMPTIME_FLOAT_TYPE + || tref == IP_INDEX_ENUM_LITERAL_TYPE); } } } - runtime_arg_refs[runtime_args_count++] = arg; } - pi++; - } else if (ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { - pi++; - } + 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. + param_name_idx = sema->code.inst_datas[param_body[p]] + .str_tok.start; + } else { + // pl_tok: name is extra[payload_index + 0]. + uint32_t param_payload + = sema->code.inst_datas[param_body[p]] + .pl_tok.payload_index; + param_name_idx = sema->code.extra[param_payload]; + } + const char* param_name = (const char*)&sema->code + .string_bytes[param_name_idx]; + uint32_t name_nts = semaAppendAirString(sema, param_name); + AirInstData data; + memset(&data, 0, sizeof(data)); + data.pl_op.operand = arg_refs[param_idx]; + data.pl_op.payload = name_nts; + (void)semaAddInst( + &child_block, AIR_INST_DBG_ARG_INLINE, data); + } + } // end scope for arg_is_type + param_idx++; } + } - // Use the callee's existing func_decl. Ported from upstream - // Sema.zig analyzeCall: func_val is the resolved callee, not - // a newly-created func entry. Look up the callee's nav by name - // in the file namespace, then use its existing func_decl. - InternPoolIndex func_val_ip = IP_INDEX_NONE; - { - uint32_t callee_nav = UINT32_MAX; - if (callee_name_idx != 0 - && sema->zcu->root_file_idx != UINT32_MAX) { - const char* cname - = (const char*)&sema->code.string_bytes[callee_name_idx]; - uint32_t rns - = sema->zcu->file_namespaces[sema->zcu->root_file_idx]; - callee_nav = findNavInNamespace(sema, rns, cname); - } - if (callee_nav != UINT32_MAX) { - const Nav* cn = ipGetNav(sema->ip, callee_nav); - InternPoolIndex resolved = cn->resolved_type; - // If resolved points to a func_decl, use it directly. - // If it points to a func_type, construct the func_decl - // key and look it up. - if (resolved != IP_INDEX_NONE - && sema->ip->items[resolved].tag == IP_KEY_FUNC) { - func_val_ip = resolved; - } else if (resolved != IP_INDEX_NONE) { - InternPoolKey fv_key; - memset(&fv_key, 0, sizeof(fv_key)); - fv_key.tag = IP_KEY_FUNC; - fv_key.data.func_decl.owner_nav = callee_nav; - fv_key.data.func_decl.ty = resolved; - func_val_ip = ipIntern(sema->ip, fv_key); - } - } else { - // Fallback: create with func_inst as nav. - InternPoolKey ft_key; - memset(&ft_key, 0, sizeof(ft_key)); - ft_key.tag = IP_KEY_FUNC_TYPE; - ft_key.data.func_type.return_type = ret_ty; - ft_key.data.func_type.param_count = runtime_args_count; - InternPoolIndex func_type_ip = ipForceIntern(sema->ip, ft_key); - InternPoolKey fv_key; - memset(&fv_key, 0, sizeof(fv_key)); - fv_key.tag = IP_KEY_FUNC; - fv_key.data.func_decl.owner_nav = func_inst; - fv_key.data.func_decl.ty = func_type_ip; - func_val_ip = ipIntern(sema->ip, fv_key); - } + // Save and set the return type for the inline function. + TypeIndex saved_fn_ret_ty = sema->fn_ret_ty; + sema->fn_ret_ty = ret_ty; + + // For cross-module calls, populate decl table from imported ZIR + // so that decl_val/decl_ref can resolve the imported module's + // declarations (e.g. `const std = @import("std")`). + // Also set source_dir to the imported module's directory so that + // nested cross-module calls resolve relative imports correctly. + uint32_t saved_decl_names[64]; + uint32_t saved_decl_insts[64]; + uint32_t saved_num_decls = 0; + const char* saved_source_dir = NULL; + if (cr->is_cross_module) { + saved_num_decls = sema->num_decls; + memcpy(saved_decl_names, sema->decl_names, + saved_num_decls * sizeof(uint32_t)); + memcpy(saved_decl_insts, sema->decl_insts, + saved_num_decls * sizeof(uint32_t)); + populateDeclTableFromZir(sema, &cr->import_zir); + if (cr->import_source_dir[0]) { + saved_source_dir = sema->source_dir; + size_t len = strlen(cr->import_source_dir) + 1; + char* dir_copy = (char*)malloc(len); + assert(dir_copy); + memcpy(dir_copy, cr->import_source_dir, len); + sema->source_dir = dir_copy; } + } - // Emit CALL extra: {args_len, arg_refs[0..args_len]}. - uint32_t call_extra = semaAddExtra(sema, runtime_args_count); - for (uint32_t a = 0; a < runtime_args_count; a++) - semaAddExtra(sema, runtime_arg_refs[a]); + // Analyze the inline function body. + const uint32_t* func_body = &sema->code.extra[func_info.extra_index]; - // Ported from Sema.zig:7480: AstGen ensures a dbg_stmt - // always precedes a call instruction. Use the caller's ZIR - // for cross-module calls. Note: the general dbg_stmt at the - // top of zirCall (before arg evaluation) may have been - // followed by arg instructions, so emit another one here - // to directly precede the call. zirDbgStmt de-duplicates - // when the previous block instruction is already a dbg_stmt. - if (is_cross_module) { - Zir temp = sema->code; - sema->code = saved_code; - zirDbgStmt(sema, block, inst - 1); - sema->code = temp; - } else { - zirDbgStmt(sema, block, inst - 1); + (void)analyzeBodyInner(sema, &child_block, func_body, func_info.body_len); + + // Restore decl table and source_dir for cross-module calls. + if (cr->is_cross_module) { + memcpy(sema->decl_names, saved_decl_names, + saved_num_decls * sizeof(uint32_t)); + memcpy(sema->decl_insts, saved_decl_insts, + saved_num_decls * sizeof(uint32_t)); + sema->num_decls = saved_num_decls; + if (saved_source_dir) { + free((char*)sema->source_dir); + sema->source_dir = saved_source_dir; } + } - // Emit CALL instruction. - AirInstData call_data; - memset(&call_data, 0, sizeof(call_data)); - call_data.pl_op.operand = AIR_REF_FROM_IP(func_val_ip); - call_data.pl_op.payload = call_extra; - AirInstRef call_ref = semaAddInst(block, AIR_INST_CALL, call_data); + sema->fn_ret_ty = saved_fn_ret_ty; - // Register CALL return type for semaTypeOf. - if (sema->num_calls < 16) { - sema->call_air_insts[sema->num_calls] = AIR_REF_TO_INST(call_ref); - sema->call_ret_types[sema->num_calls] = ret_ty; - sema->num_calls++; - } + // ComptimeReturn: the inline function returned at comptime. + // Upstream Sema.zig:7872: error.ComptimeReturn => break :result + // inlining.comptime_result + // Upstream does NOT roll back the AIR — the block instruction and + // all body instructions (including nested dead blocks from + // comptime inline calls) remain in the AIR array. + if (inlining.comptime_returned) { + AirInstRef ct_result = inlining.comptime_result; + block->instructions_len = saved_block_inst_len; - // Trigger separate body analysis of the callee function. - // Ported from Sema.zig analyzeCall: ensureFuncBodyAnalysisQueued. - // For generic functions, pass call args so comptime params are - // mapped to their resolved values (monomorphization). - // For cross-module calls, only analyze generic (monomorphized) - // function bodies — non-generic cross-module functions belong to - // their own module's AIR, not the current file's. - if (!is_cross_module || is_generic) { - uint32_t body_name_idx = callee_name_idx; - if (is_cross_module && callee_name_idx != 0) { - // callee_name_idx is from the pre-swap ZIR's string table. - // Find the same name in the imported ZIR's string table. - const char* name - = (const char*)&saved_code.string_bytes[callee_name_idx]; - body_name_idx = findStringInZirBytes(&sema->code, name); - } - // Compute call-site arg types before entering body analysis - // (which saves/resets AIR state, making semaTypeOf unavailable). - TypeIndex arg_type_buf[16]; - const TypeIndex* arg_types_ptr = NULL; - if (is_generic && args_len > 0) { - for (uint32_t ai = 0; ai < args_len && ai < 16; ai++) - arg_type_buf[ai] = semaTypeOf(sema, arg_refs[ai]); - arg_types_ptr = arg_type_buf; + // Cache comptime results for memoization. + if (all_comptime && AIR_REF_IS_IP(ct_result)) { + if (sema->num_memo < 32) { + uint32_t mi = sema->num_memo++; + sema->memo_func_inst[mi] = func_inst; + sema->memo_args_len[mi] = args_len; + for (uint32_t a = 0; a < args_len && a < 4; a++) + sema->memo_args[mi][a] = arg_refs[a]; + sema->memo_result[mi] = ct_result; } - analyzeFuncBodyAndRecord(sema, block, func_inst, body_name_idx, - is_generic ? arg_refs : NULL, is_generic ? args_len : 0, - arg_types_ptr, func_val_ip); } - // Clean up cross-module state. - if (is_cross_module) { - sema->code = saved_code; - zirDeinit(&import_zir); - astDeinit(&import_ast); + free(inlining.merges.results); + free(inlining.merges.br_list); + semaBlockDeinit(&child_block); + free(sema->inst_map.items); + sema->inst_map = saved_inst_map; + if (cr->is_cross_module) { + sema->code = cr->saved_code; + zirDeinit(&cr->import_zir); + astDeinit(&cr->import_ast); } - return call_ref; + block->is_comptime = saved_is_comptime; + return ct_result; } - // Upstream (Sema.zig:7247): if the return type is comptime-only, - // temporarily make the block comptime. This is deferred-restored - // in upstream; we restore it at the end of zirCall. - bool saved_is_comptime = block->is_comptime; - // Upstream (Sema.zig:7247): only for NON-inline functions. - // For inline functions, they're already going to be inlined, so - // the comptime_reason is not set for the comptime-only ret case. - if (is_comptime_only_ret && !func_info.is_inline && !block->is_comptime) - block->is_comptime = true; + // Resolve the inline call block, ported from resolveAnalyzedBlock. + AirInstRef result_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + if (inlining.merges.results_len > 0) + result_ref = inlining.merges.results[0]; - // Memoization: check if we've seen this function with the same - // comptime args before. - // Ported from src/Sema.zig line 7712: want_memoize = block.isComptime(). - bool all_comptime = block->is_comptime && args_len > 0; - if (all_comptime) { - for (uint32_t a = 0; a < args_len; a++) { - if (!AIR_REF_IS_IP(arg_refs[a])) { - all_comptime = false; - break; - } + // Cache comptime results for memoization. + if (all_comptime && AIR_REF_IS_IP(result_ref)) { + if (sema->num_memo < 32) { + uint32_t mi = sema->num_memo++; + sema->memo_func_inst[mi] = func_inst; + sema->memo_args_len[mi] = args_len; + for (uint32_t a = 0; a < args_len && a < 4; a++) + sema->memo_args[mi][a] = arg_refs[a]; + sema->memo_result[mi] = result_ref; } } - if (all_comptime) { - for (uint32_t mi = 0; mi < sema->num_memo; mi++) { - if (sema->memo_func_inst[mi] != func_inst) - continue; - if (sema->memo_args_len[mi] != args_len) - continue; - bool match = true; - for (uint32_t a = 0; a < args_len && a < 4; a++) { - if (sema->memo_args[mi][a] != arg_refs[a]) { - match = false; - break; + + // Ported from src/Sema.zig resolveAnalyzedBlock. + AirInstRef call_result; + + if (inlining.merges.results_len == 0) { + // Noreturn block. + if (child_block.instructions_len == 0) { + // Body analysis produced nothing (unhandled ZIR instructions). + // Return void to avoid crash; AIR comparison will catch the error. + call_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } else if (block_tag == AIR_INST_BLOCK) { + // Copy instructions directly to parent block. + for (uint32_t i = 0; i < child_block.instructions_len; i++) { + if (block->instructions_len >= block->instructions_cap) { + uint32_t nc = block->instructions_cap * 2; + semaBlockGrowInstructions(block, nc); } + block->instructions[block->instructions_len++] + = child_block.instructions[i]; } - if (match) { - if (is_cross_module) { - sema->code = saved_code; - zirDeinit(&import_zir); - astDeinit(&import_ast); + call_result = AIR_REF_FROM_INST( + child_block.instructions[child_block.instructions_len - 1]); + } else { + // dbg_inline_block: create block with noreturn type. + uint32_t es = semaAddExtra(sema, inlining.func); + semaAddExtra(sema, child_block.instructions_len); + for (uint32_t i = 0; i < child_block.instructions_len; i++) + semaAddExtra(sema, child_block.instructions[i]); + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_NORETURN_TYPE); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; + if (block->instructions_len >= block->instructions_cap) { + uint32_t nc = block->instructions_cap * 2; + semaBlockGrowInstructions(block, nc); + } + block->instructions[block->instructions_len++] = block_inst_idx; + call_result = AIR_REF_FROM_INST(block_inst_idx); + } + } else if (inlining.merges.results_len == 1) { + // Single result. Try to elide block when !need_debug_scope. + bool elided = false; + if (!need_debug_scope && child_block.instructions_len > 0) { + uint32_t last + = child_block.instructions[child_block.instructions_len - 1]; + if (sema->air_inst_tags[last] == AIR_INST_BR + && sema->air_inst_datas[last].br.block_inst + == block_inst_idx) { + // Elide: copy instructions except trailing break. + for (uint32_t i = 0; i < child_block.instructions_len - 1; + i++) { + if (block->instructions_len >= block->instructions_cap) { + uint32_t nc = block->instructions_cap * 2; + semaBlockGrowInstructions(block, nc); + } + block->instructions[block->instructions_len++] + = child_block.instructions[i]; } - block->is_comptime = saved_is_comptime; - return sema->memo_result[mi]; + elided = true; } } - } - - // Ported from src/Sema.zig line 7480: emit dbg_stmt for inline - // calls, just before the inline block. Use the caller's ZIR - // for cross-module calls. - if (inst > 0 && !block->is_comptime) { - Zir dbg_code = sema->code; - if (is_cross_module) - sema->code = saved_code; - zirDbgStmt(sema, block, inst - 1); - if (is_cross_module) - sema->code = dbg_code; - } - - // 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; - - // Save block instruction count for rollback if comptime return. - uint32_t saved_block_inst_len = block->instructions_len; - - // Reserve the block instruction (data filled later). - uint32_t block_inst_idx = semaAddInstAsIndex(sema, block_tag, - (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); - // Set up child block for inlining. - SemaBlockMerges merges; - memset(&merges, 0, sizeof(merges)); - merges.block_inst = block_inst_idx; - - // Use the callee's existing func_decl for the dbg_inline_block's - // func field. Ported from upstream Sema.zig line 7788: - // .func = func_val.?.toIntern(), - // The callee's func_decl was already created by ensureNavValUpToDate - // during inline function resolution. Find it by looking up the nav - // and reconstructing the func_decl key. - InternPoolIndex func_ip = IP_INDEX_NONE; - if (need_debug_scope) { - // Find the callee's nav by matching zir_index. - uint32_t callee_nav = UINT32_MAX; - for (uint32_t ni = 0; ni < ipNavCount(sema->ip); ni++) { - const Nav* n = ipGetNav(sema->ip, ni); - if (n->zir_index == func_inst - && n->resolved_type != IP_INDEX_NONE) { - callee_nav = ni; - break; + if (!elided && AIR_REF_IS_IP(result_ref)) { + // Comptime-known result: create block with void type, + // rewrite break to void_value. + // Ported from resolveAnalyzedBlock lines 6031-6062. + uint32_t es; + if (need_debug_scope) { + es = semaAddExtra(sema, inlining.func); + semaAddExtra(sema, child_block.instructions_len); + } else { + es = semaAddExtra(sema, child_block.instructions_len); + } + for (uint32_t i = 0; i < child_block.instructions_len; i++) + semaAddExtra(sema, child_block.instructions[i]); + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; + // Rewrite break operand to void_value. + if (inlining.merges.br_list_len > 0) { + uint32_t br_inst = inlining.merges.br_list[0]; + sema->air_inst_datas[br_inst].br.operand + = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + } + if (block->instructions_len >= block->instructions_cap) { + uint32_t nc = block->instructions_cap * 2; + semaBlockGrowInstructions(block, nc); + } + block->instructions[block->instructions_len++] = block_inst_idx; + call_result = result_ref; + } else if (!elided) { + // Runtime result: create block with result type. + TypeIndex rty = semaTypeOf(sema, result_ref); + uint32_t es; + if (need_debug_scope) { + es = semaAddExtra(sema, inlining.func); + semaAddExtra(sema, child_block.instructions_len); + } else { + es = semaAddExtra(sema, child_block.instructions_len); + } + for (uint32_t i = 0; i < child_block.instructions_len; i++) + semaAddExtra(sema, child_block.instructions[i]); + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(rty); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; + if (block->instructions_len >= block->instructions_cap) { + uint32_t nc = block->instructions_cap * 2; + semaBlockGrowInstructions(block, nc); } + block->instructions[block->instructions_len++] = block_inst_idx; + call_result = AIR_REF_FROM_INST(block_inst_idx); + } else { + call_result = result_ref; } - if (callee_nav != UINT32_MAX) { - const Nav* cn = ipGetNav(sema->ip, callee_nav); - InternPoolKey func_key; - memset(&func_key, 0, sizeof(func_key)); - func_key.tag = IP_KEY_FUNC; - func_key.data.func_decl.owner_nav = callee_nav; - func_key.data.func_decl.ty = cn->resolved_type; - func_ip = ipIntern(sema->ip, func_key); + } else { + // Multiple results: create block with result type. + TypeIndex rty = semaTypeOf(sema, result_ref); + uint32_t es; + if (need_debug_scope) { + es = semaAddExtra(sema, inlining.func); + semaAddExtra(sema, child_block.instructions_len); + } else { + es = semaAddExtra(sema, child_block.instructions_len); + } + for (uint32_t i = 0; i < child_block.instructions_len; i++) + semaAddExtra(sema, child_block.instructions[i]); + sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref + = AIR_REF_FROM_IP(rty); + sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; + if (block->instructions_len >= block->instructions_cap) { + uint32_t nc = block->instructions_cap * 2; + semaBlockGrowInstructions(block, nc); } + block->instructions[block->instructions_len++] = block_inst_idx; + call_result = AIR_REF_FROM_INST(block_inst_idx); } - SemaBlockInlining inlining; - memset(&inlining, 0, sizeof(inlining)); - inlining.call_block = block; - inlining.func = func_ip; - inlining.merges = merges; + // Clean up. + free(inlining.merges.results); + free(inlining.merges.br_list); + semaBlockDeinit(&child_block); + free(sema->inst_map.items); + sema->inst_map = saved_inst_map; - SemaBlock child_block; - semaBlockInit(&child_block, sema, block); - child_block.is_comptime = block->is_comptime; - child_block.want_safety = false; - child_block.want_safety_set = true; - child_block.inlining = &inlining; - // Upstream creates child_block directly (not via makeSubBlock), - // so need_debug_scope is null (not inherited from parent). - child_block.need_debug_scope = NULL; + // Restore original ZIR and free imported module data. + if (cr->is_cross_module) { + sema->code = cr->saved_code; + zirDeinit(&cr->import_zir); + astDeinit(&cr->import_ast); + } - // Save and clear inst_map for the inline call scope. - // Ported from Sema.zig:7749-7764: each inline call gets a fresh inst_map. - InstMap saved_inst_map = sema->inst_map; - memset(&sema->inst_map, 0, sizeof(sema->inst_map)); + block->is_comptime = saved_is_comptime; + return call_result; +} - // Map param ZIR instructions to the argument values. - instMapEnsureSpaceForBody(&sema->inst_map, param_body, param_body_len); - uint32_t param_idx = 0; - for (uint32_t p = 0; p < param_body_len; p++) { - ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; - if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { - if (param_idx >= args_len) { - sema->has_compile_errors = true; - free(sema->inst_map.items); - sema->inst_map = saved_inst_map; - block->is_comptime = saved_is_comptime; - return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - instMapPut(&sema->inst_map, param_body[p], arg_refs[param_idx]); +// zirCall: handle call/field_call ZIR instruction for inline functions. +// Ported from src/Sema.zig zirCall / analyzeCall (inline-only subset). +// For inline functions from the same module, analyzes the function body +// in a child block and emits dbg_inline_block. +static AirInstRef zirCall( + Sema* sema, SemaBlock* block, uint32_t inst, bool is_field_call) { + if (sema->has_compile_errors) + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; - // Emit dbg_arg_inline for params whose resolved type is - // not comptime-only. Ported from addDbgVar: - // if (try val_ty.comptimeOnlySema(pt)) return; - // Check (a): arg value IS a type (key in TYPE range). - // Check (b): for comptime params, the DECLARED param type - // is comptime-only (comptime_int, comptime_float, etc.). - // Upstream coerces args to param types before typeOf; - // the C sema doesn't, so we check the declared type. - { - bool arg_comptime_only = false; - if (AIR_REF_IS_IP(arg_refs[param_idx])) { - InternPoolIndex ip = AIR_REF_TO_IP(arg_refs[param_idx]); - InternPoolKey key = sema->ip->items[ip]; - // (a) arg is a type value. - arg_comptime_only = (key.tag >= IP_KEY_INT_TYPE - && key.tag <= IP_KEY_INFERRED_ERROR_SET_TYPE); - if (!arg_comptime_only) { - arg_comptime_only = (ip == IP_INDEX_VOID_TYPE - || ip == IP_INDEX_BOOL_TYPE - || ip == IP_INDEX_TYPE_TYPE - || ip == IP_INDEX_COMPTIME_INT_TYPE - || ip == IP_INDEX_COMPTIME_FLOAT_TYPE - || ip == IP_INDEX_F16_TYPE - || ip == IP_INDEX_F32_TYPE - || ip == IP_INDEX_F64_TYPE - || ip == IP_INDEX_F80_TYPE - || ip == IP_INDEX_F128_TYPE); - } - } - // (b) declared param type is comptime-only. - if (!arg_comptime_only - && (ptag == ZIR_INST_PARAM_COMPTIME - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME)) { - uint32_t ppl = sema->code.inst_datas[param_body[p]] - .pl_tok.payload_index; - uint32_t type_raw = sema->code.extra[ppl + 1]; - uint32_t tbody_len = type_raw & 0x7FFFFFFF; - if (tbody_len == 1) { - uint32_t ti = sema->code.extra[ppl + 2]; - if (ti < sema->code.inst_len - && sema->code.inst_tags[ti] - == ZIR_INST_BREAK_INLINE) { - ZirInstRef tref - = sema->code.inst_datas[ti].break_data.operand; - if (tref < ZIR_REF_START_INDEX) { - arg_comptime_only - = (tref == IP_INDEX_COMPTIME_INT_TYPE - || tref == IP_INDEX_COMPTIME_FLOAT_TYPE - || tref == IP_INDEX_ENUM_LITERAL_TYPE); - } - } - } - } - 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. - param_name_idx = sema->code.inst_datas[param_body[p]] - .str_tok.start; - } else { - // pl_tok: name is extra[payload_index + 0]. - uint32_t param_payload - = sema->code.inst_datas[param_body[p]] - .pl_tok.payload_index; - param_name_idx = sema->code.extra[param_payload]; - } - const char* param_name = (const char*)&sema->code - .string_bytes[param_name_idx]; - uint32_t name_nts = semaAppendAirString(sema, param_name); - AirInstData data; - memset(&data, 0, sizeof(data)); - data.pl_op.operand = arg_refs[param_idx]; - data.pl_op.payload = name_nts; - (void)semaAddInst( - &child_block, AIR_INST_DBG_ARG_INLINE, data); - } - } // end scope for arg_is_type - param_idx++; - } - } + // Parse Call/FieldCall extra data. + uint32_t flags = sema->code.extra[payload_index]; + uint32_t args_len = flags >> 5; // bits 5..31 = args_len + uint32_t callee_ref; + uint32_t arg_data_start; + uint32_t callee_name_idx = 0; // string_bytes index for decl name - // Save and set the return type for the inline function. - TypeIndex saved_fn_ret_ty = sema->fn_ret_ty; - sema->fn_ret_ty = ret_ty; + if (is_field_call) { + // FieldCall: {flags, obj_ptr, field_name_start} + callee_ref = sema->code.extra[payload_index + 1]; // obj_ptr + uint32_t field_name_start = sema->code.extra[payload_index + 2]; + callee_name_idx = field_name_start; + arg_data_start = payload_index + 3; - // For cross-module calls, populate decl table from imported ZIR - // so that decl_val/decl_ref can resolve the imported module's - // declarations (e.g. `const std = @import("std")`). - // Also set source_dir to the imported module's directory so that - // nested cross-module calls resolve relative imports correctly. - uint32_t saved_decl_names[64]; - uint32_t saved_decl_insts[64]; - uint32_t saved_num_decls = 0; - const char* saved_source_dir = NULL; - if (is_cross_module) { - saved_num_decls = sema->num_decls; - memcpy(saved_decl_names, sema->decl_names, - saved_num_decls * sizeof(uint32_t)); - memcpy(saved_decl_insts, sema->decl_insts, - saved_num_decls * sizeof(uint32_t)); - populateDeclTableFromZir(sema, &import_zir); - if (import_source_dir[0]) { - saved_source_dir = sema->source_dir; - size_t len = strlen(import_source_dir) + 1; - char* dir_copy = (char*)malloc(len); - assert(dir_copy); - memcpy(dir_copy, import_source_dir, len); - sema->source_dir = dir_copy; + // Comptime field_call: evaluate function body at comptime. + // Ported from Sema.zig analyzeCall comptime inline path. + if (block->is_comptime && callee_name_idx != 0) { + AirInstRef ct_result = comptimeFieldCall(sema, block, callee_ref, + callee_name_idx, args_len, arg_data_start); + if (ct_result != AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)) + return ct_result; + // Fall through to runtime path if comptime eval failed. } + } else { + // Call: {flags, callee} + callee_ref = sema->code.extra[payload_index + 1]; + arg_data_start = payload_index + 2; } - // Analyze the inline function body. - const uint32_t* func_body = &sema->code.extra[func_info.extra_index]; - - (void)analyzeBodyInner(sema, &child_block, func_body, func_info.body_len); - - // Restore decl table and source_dir for cross-module calls. - if (is_cross_module) { - memcpy(sema->decl_names, saved_decl_names, - saved_num_decls * sizeof(uint32_t)); - memcpy(sema->decl_insts, saved_decl_insts, - saved_num_decls * sizeof(uint32_t)); - sema->num_decls = saved_num_decls; - if (saved_source_dir) { - free((char*)sema->source_dir); - sema->source_dir = saved_source_dir; + // For non-field calls, resolve the callee name from decl_val/decl_ref. + if (!is_field_call && callee_ref >= ZIR_REF_START_INDEX) { + uint32_t callee_inst = callee_ref - ZIR_REF_START_INDEX; + ZirInstTag callee_tag = sema->code.inst_tags[callee_inst]; + if (callee_tag == ZIR_INST_DECL_VAL + || callee_tag == ZIR_INST_DECL_REF) { + callee_name_idx = sema->code.inst_datas[callee_inst].str_tok.start; } } - sema->fn_ret_ty = saved_fn_ret_ty; - - // ComptimeReturn: the inline function returned at comptime. - // Upstream Sema.zig:7872: error.ComptimeReturn => break :result - // inlining.comptime_result - // Upstream does NOT roll back the AIR — the block instruction and - // all body instructions (including nested dead blocks from - // comptime inline calls) remain in the AIR array. - if (inlining.comptime_returned) { - AirInstRef ct_result = inlining.comptime_result; - block->instructions_len = saved_block_inst_len; + // Find the inline function's ZIR instruction. + uint32_t func_inst = findDeclFuncInst(sema, callee_name_idx); + // Fallback: during cross-module inline expansion, sema->decl_names + // contains indices from the main module's string table, but + // callee_name_idx is from the inlined module's string table. + // Search the current ZIR directly by name. + if (func_inst == UINT32_MAX && callee_name_idx != 0) { + const char* cn + = (const char*)&sema->code.string_bytes[callee_name_idx]; + func_inst = findFuncInstInZir(&sema->code, cn); + } + // 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; + InternPoolIndex struct_ret_type = IP_INDEX_VOID_TYPE; + char import_source_dir[1024]; + memset(&import_ast, 0, sizeof(import_ast)); + memset(&import_zir, 0, sizeof(import_zir)); + memset(&saved_code, 0, sizeof(saved_code)); + import_source_dir[0] = 0; - // Cache comptime results for memoization. - if (all_comptime && AIR_REF_IS_IP(ct_result)) { - if (sema->num_memo < 32) { - uint32_t mi = sema->num_memo++; - sema->memo_func_inst[mi] = func_inst; - sema->memo_args_len[mi] = args_len; - for (uint32_t a = 0; a < args_len && a < 4; a++) - sema->memo_args[mi][a] = arg_refs[a]; - sema->memo_result[mi] = ct_result; + // For non-field calls to imported function aliases: + // e.g. `const isNan = std.math.isNan;` then `isNan(x)`. + // The declaration value body follows the pattern: + // decl_val("std") + field_val("math") + field_val("isNan") + + // break_inline + // Follow the import chain to resolve the function. + if (func_inst == UINT32_MAX && !is_field_call && callee_name_idx != 0 + && sema->source_dir) { + // Get the declaration's value body. + uint32_t decl_inst_val = UINT32_MAX; + for (uint32_t i = 0; i < sema->num_decls; i++) { + if (sema->decl_names[i] == callee_name_idx) { + decl_inst_val = sema->decl_insts[i]; + break; + } + } + // Fallback: search current ZIR by name (cross-module inline). + if (decl_inst_val == UINT32_MAX) { + const char* cn + = (const char*)&sema->code.string_bytes[callee_name_idx]; + decl_inst_val = findDeclInstByNameInZir(&sema->code, cn); + } + if (decl_inst_val != UINT32_MAX) { + const uint32_t* vb; + uint32_t vb_len; + getParamBody(sema, decl_inst_val, &vb, &vb_len); + // Parse the chain: import/decl_val, field_val(s), break_inline. + const char* base_import = NULL; + const char* fields[8]; + uint32_t nfields = 0; + for (uint32_t vi = 0; vi < vb_len; vi++) { + ZirInstTag vtag = sema->code.inst_tags[vb[vi]]; + if (vtag == ZIR_INST_IMPORT && !base_import) { + uint32_t vpl + = sema->code.inst_datas[vb[vi]].pl_tok.payload_index; + uint32_t path_idx = sema->code.extra[vpl + 1]; + base_import + = (const char*)&sema->code.string_bytes[path_idx]; + } else if ((vtag == ZIR_INST_DECL_VAL + || vtag == ZIR_INST_DECL_REF) + && !base_import) { + uint32_t ref_idx + = sema->code.inst_datas[vb[vi]].str_tok.start; + base_import = findDeclImportPath(sema, ref_idx); + } else if (vtag == ZIR_INST_FIELD_VAL && base_import + && nfields < 8) { + uint32_t vpl + = sema->code.inst_datas[vb[vi]].pl_node.payload_index; + uint32_t fs = sema->code.extra[vpl + 1]; + fields[nfields++] + = (const char*)&sema->code.string_bytes[fs]; + } + } + // Resolve the chain: load base import, then follow field + // accesses through sub-imports until we find the function. + if (base_import && nfields >= 2) { + // nfields >= 2: e.g. fields[0]="math", fields[1]="isNan" + // Load base import (e.g. std.zig). + Ast base_ast; + memset(&base_ast, 0, sizeof(base_ast)); + Zir base_zir = loadStdImportZir(sema, + sema->zcu->comp->config.module_root, sema->source_dir, + base_import, &base_ast); + if (base_zir.inst_len > 0) { + // Follow intermediate fields (all but last). + char cur_dir[1024]; + computeSourceDir(sema->zcu->comp->config.module_root, + sema->source_dir, base_import, cur_dir, + sizeof(cur_dir)); + Zir cur_zir = base_zir; + Ast cur_ast = base_ast; + for (uint32_t fi = 0; fi + 1 < nfields; fi++) { + const char* sub_import + = findDeclImportPathInZir(&cur_zir, fields[fi]); + if (!sub_import) { + // Not a sub-import; function might be here. + func_inst + = findFuncInstInZir(&cur_zir, fields[fi]); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = cur_zir; + import_zir = cur_zir; + import_ast = cur_ast; + is_cross_module = true; + snprintf(import_source_dir, + sizeof(import_source_dir), "%s", cur_dir); + } else { + zirDeinit(&cur_zir); + astDeinit(&cur_ast); + } + break; + } + // Load the sub-module. + Ast next_ast; + memset(&next_ast, 0, sizeof(next_ast)); + Zir next_zir = loadImportZir( + sema, cur_dir, sub_import, &next_ast); + // Compute new source dir BEFORE freeing cur_zir, + // since sub_import points into cur_zir string bytes. + char new_dir[1024]; + computeSourceDir(NULL, cur_dir, sub_import, new_dir, + sizeof(new_dir)); + zirDeinit(&cur_zir); + astDeinit(&cur_ast); + if (next_zir.inst_len == 0) + break; + memcpy(cur_dir, new_dir, sizeof(cur_dir)); + cur_zir = next_zir; + cur_ast = next_ast; + } + // The last field is the function name. + if (!is_cross_module && cur_zir.inst_len > 0) { + const char* fn_name = fields[nfields - 1]; + // Check if the last intermediate resolved to + // another import. + const char* fn_import + = findDeclImportPathInZir(&cur_zir, fn_name); + if (fn_import) { + Ast fn_ast; + memset(&fn_ast, 0, sizeof(fn_ast)); + Zir fn_zir = loadImportZir( + sema, cur_dir, fn_import, &fn_ast); + // Compute source dir BEFORE freeing cur_zir, + // since fn_import points into cur_zir string + // bytes. + char fn_src_dir[1024]; + computeSourceDir(NULL, cur_dir, fn_import, + fn_src_dir, sizeof(fn_src_dir)); + zirDeinit(&cur_zir); + astDeinit(&cur_ast); + if (fn_zir.inst_len > 0) { + // Try direct lookup first, then follow + // re-exports (e.g. scalbn -> ldexp). + func_inst = findFuncInModuleZir(sema, + fn_src_dir, &fn_zir, &fn_ast, fn_name, + import_source_dir, + sizeof(import_source_dir)); + if (func_inst != UINT32_MAX) { + // If findFuncInModuleZir found it + // directly (no re-export), it did + // not write import_source_dir. + // Set it to fn_src_dir as fallback. + if (import_source_dir[0] == '\0') + snprintf(import_source_dir, + sizeof(import_source_dir), "%s", + fn_src_dir); + saved_code = sema->code; + sema->code = fn_zir; + import_zir = fn_zir; + import_ast = fn_ast; + is_cross_module = true; + } else { + zirDeinit(&fn_zir); + astDeinit(&fn_ast); + } + } + } else { + func_inst = findFuncInstInZir(&cur_zir, fn_name); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = cur_zir; + import_zir = cur_zir; + import_ast = cur_ast; + is_cross_module = true; + snprintf(import_source_dir, + sizeof(import_source_dir), "%s", cur_dir); + } else { + zirDeinit(&cur_zir); + astDeinit(&cur_ast); + } + } + } + } + } else if (base_import && nfields == 1) { + // Single-level: e.g. const panic = common.panic; + // fields[0] = "panic" + Ast base_ast; + memset(&base_ast, 0, sizeof(base_ast)); + Zir base_zir = loadStdImportZir(sema, + sema->zcu->comp->config.module_root, sema->source_dir, + base_import, &base_ast); + if (base_zir.inst_len > 0) { + const char* field_name = fields[0]; + func_inst = findFuncInstInZir(&base_zir, field_name); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = base_zir; + import_zir = base_zir; + import_ast = base_ast; + is_cross_module = true; + computeSourceDir(sema->zcu->comp->config.module_root, + sema->source_dir, base_import, import_source_dir, + sizeof(import_source_dir)); + } else { + zirDeinit(&base_zir); + astDeinit(&base_ast); + } + } + } + } + } + + 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, sema->source_dir, import_path, &import_ast); + if (import_zir.inst_len > 0) { + computeSourceDir(sema->zcu->comp->config.module_root, + sema->source_dir, import_path, import_source_dir, + sizeof(import_source_dir)); + func_inst = findFuncInModuleZir(sema, import_source_dir, + &import_zir, &import_ast, field_name, + import_source_dir, sizeof(import_source_dir)); + if (func_inst != UINT32_MAX) { + // Swap to imported module's ZIR. + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } + // Follow chained import: if direct import failed, check + // if the obj decl follows decl_val(X) + field_val(X, "Y") + // pattern and resolve the chain. + // E.g. "math" → decl_val("std") + field_val(%, "math") + // → "std" → @import("std") → load std.zig + // → "math" in std.zig → @import("math.zig") + // → load math.zig → find callee there + if (!is_cross_module) { + const char* chain_import = NULL; + const char* chain_field = NULL; + if (findDeclImportFieldVal( + sema, obj_name_idx, &chain_import, &chain_field) + && chain_field) { + // chain_import is the base import (possibly "std") + // chain_field is the field name in that import + // (e.g. "math"). + // Load the base import, then find chain_field in + // it (which may itself be an @import). + Ast base_ast; + memset(&base_ast, 0, sizeof(base_ast)); + Zir base_zir = loadStdImportZir(sema, + sema->zcu->comp->config.module_root, sema->source_dir, + chain_import, &base_ast); + if (base_zir.inst_len > 0) { + // Find chain_field in base module; if it's + // also an @import, follow it. + const char* sub_import_ptr + = findDeclImportPathInZir(&base_zir, chain_field); + if (sub_import_ptr) { + // Copy sub_import before freeing base_zir, + // since it points into base_zir.string_bytes. + char sub_import[1024]; + snprintf(sub_import, sizeof(sub_import), "%s", + sub_import_ptr); + // Compute source_dir for the base import. + char base_dir[1024]; + computeSourceDir( + sema->zcu->comp->config.module_root, + sema->source_dir, chain_import, base_dir, + sizeof(base_dir)); + import_zir = loadImportZir( + sema, base_dir, sub_import, &import_ast); + zirDeinit(&base_zir); + astDeinit(&base_ast); + if (import_zir.inst_len > 0) { + const char* field_name + = (const char*)&sema->code + .string_bytes[callee_name_idx]; + computeSourceDir(NULL, base_dir, sub_import, + import_source_dir, + sizeof(import_source_dir)); + func_inst = findFuncInModuleZir(sema, + import_source_dir, &import_zir, + &import_ast, field_name, import_source_dir, + sizeof(import_source_dir)); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } else { + // chain_field is the function directly. + const char* field_name + = (const char*)&sema->code + .string_bytes[callee_name_idx]; + func_inst + = findFuncInstInZir(&base_zir, field_name); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = base_zir; + import_zir = base_zir; + import_ast = base_ast; + is_cross_module = true; + computeSourceDir( + sema->zcu->comp->config.module_root, + sema->source_dir, chain_import, + import_source_dir, + sizeof(import_source_dir)); + } else { + zirDeinit(&base_zir); + astDeinit(&base_ast); + } + } + } + } + } + } + // Handle FIELD_PTR/FIELD_VAL callee_ref for 3-level import + // chains like std.meta.Int(...). + // callee_ref → FIELD_PTR(lhs=DECL_VAL("std"), field="meta") + // callee_name_idx → "Int" + if (!is_cross_module && callee_ref >= ZIR_REF_START_INDEX) { + uint32_t ci = callee_ref - ZIR_REF_START_INDEX; + ZirInstTag ctag2 = sema->code.inst_tags[ci]; + if (ctag2 == ZIR_INST_FIELD_PTR || ctag2 == ZIR_INST_FIELD_VAL) { + uint32_t pi = sema->code.inst_datas[ci].pl_node.payload_index; + uint32_t lhs_ref = sema->code.extra[pi]; + uint32_t mid_str = sema->code.extra[pi + 1]; + if (lhs_ref >= ZIR_REF_START_INDEX) { + uint32_t li = lhs_ref - ZIR_REF_START_INDEX; + ZirInstTag lt = sema->code.inst_tags[li]; + if (lt == ZIR_INST_DECL_REF || lt == ZIR_INST_DECL_VAL) { + uint32_t base_name_idx + = sema->code.inst_datas[li].str_tok.start; + const char* base_import + = findDeclImportPath(sema, base_name_idx); + // Function name is the field in the + // outer field_val (e.g. "floatMantissaBits" + // from math.floatMantissaBits). + const char* fn_field + = (const char*)&sema->code.string_bytes[mid_str]; + + // If the lhs is not a direct import, + // try alias resolution. E.g. for + // `const math = std.math;`, resolve + // through the alias chain. + const char* alias_import = NULL; + const char* alias_field = NULL; + if (!base_import + && findDeclImportFieldVal(sema, base_name_idx, + &alias_import, &alias_field)) { + // alias_import = std import path + // alias_field = "math" + // Load std module, find sub-module + Ast alias_ast; + memset(&alias_ast, 0, sizeof(alias_ast)); + Zir alias_zir = loadStdImportZir(sema, + sema->zcu->comp->config.module_root, + sema->source_dir, alias_import, &alias_ast); + if (alias_zir.inst_len > 0) { + const char* sub_path = findDeclImportPathInZir( + &alias_zir, alias_field); + if (sub_path) { + char alias_dir[1024]; + computeSourceDir( + sema->zcu->comp->config.module_root, + sema->source_dir, alias_import, + alias_dir, sizeof(alias_dir)); + import_zir = loadImportZir(sema, alias_dir, + sub_path, &import_ast); + zirDeinit(&alias_zir); + astDeinit(&alias_ast); + if (import_zir.inst_len > 0) { + computeSourceDir(NULL, alias_dir, + sub_path, import_source_dir, + sizeof(import_source_dir)); + func_inst = findFuncInModuleZir(sema, + import_source_dir, &import_zir, + &import_ast, fn_field, + import_source_dir, + sizeof(import_source_dir)); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } else { + zirDeinit(&alias_zir); + astDeinit(&alias_ast); + } + } + } + + if (!is_cross_module && base_import) { + const char* mid_field = fn_field; + Ast base_ast; + memset(&base_ast, 0, sizeof(base_ast)); + Zir base_zir = loadStdImportZir(sema, + sema->zcu->comp->config.module_root, + sema->source_dir, base_import, &base_ast); + if (base_zir.inst_len > 0) { + const char* sub_import_ptr + = findDeclImportPathInZir( + &base_zir, mid_field); + if (sub_import_ptr) { + // Copy before freeing base_zir. + char sub_import[1024]; + snprintf(sub_import, sizeof(sub_import), + "%s", sub_import_ptr); + char base_dir[1024]; + computeSourceDir( + sema->zcu->comp->config.module_root, + sema->source_dir, base_import, + base_dir, sizeof(base_dir)); + import_zir = loadImportZir(sema, base_dir, + sub_import, &import_ast); + zirDeinit(&base_zir); + astDeinit(&base_ast); + if (import_zir.inst_len > 0) { + const char* fn_name = callee_name_idx + ? (const char*)&sema->code + .string_bytes + [callee_name_idx] + : fn_field; + computeSourceDir(NULL, base_dir, + sub_import, import_source_dir, + sizeof(import_source_dir)); + func_inst = findFuncInModuleZir(sema, + import_source_dir, &import_zir, + &import_ast, fn_name, + import_source_dir, + sizeof(import_source_dir)); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + } + } + } else { + zirDeinit(&base_zir); + astDeinit(&base_ast); + } + } + } + // 4-level chain: callee_ref = field_val( + // field_val(decl_val("std"), "math"), "F80") + // callee_name = "fromFloat" + // Handles: std.math.F80.fromFloat(...) + } else if (lt == ZIR_INST_FIELD_VAL + || lt == ZIR_INST_FIELD_PTR) { + uint32_t lp + = sema->code.inst_datas[li].pl_node.payload_index; + uint32_t lhs2_ref = sema->code.extra[lp]; + uint32_t mid2_str = sema->code.extra[lp + 1]; + if (lhs2_ref >= ZIR_REF_START_INDEX) { + uint32_t l2i = lhs2_ref - ZIR_REF_START_INDEX; + ZirInstTag l2t = sema->code.inst_tags[l2i]; + if (l2t == ZIR_INST_DECL_REF + || l2t == ZIR_INST_DECL_VAL) { + uint32_t bn + = sema->code.inst_datas[l2i].str_tok.start; + const char* base_imp + = findDeclImportPath(sema, bn); + const char* sub_field + = (const char*)&sema->code + .string_bytes[mid2_str]; + const char* struct_nm + = (const char*)&sema->code + .string_bytes[mid_str]; + const char* fn_nm + = (const char*)&sema->code + .string_bytes[callee_name_idx]; + if (base_imp) { + Ast b_ast; + memset(&b_ast, 0, sizeof(b_ast)); + Zir b_zir = loadStdImportZir(sema, + sema->zcu->comp->config.module_root, + sema->source_dir, base_imp, &b_ast); + if (b_zir.inst_len > 0) { + const char* sp_ptr + = findDeclImportPathInZir( + &b_zir, sub_field); + if (sp_ptr) { + // Copy before freeing b_zir. + char sp[1024]; + snprintf( + sp, sizeof(sp), "%s", sp_ptr); + char bd[1024]; + computeSourceDir( + sema->zcu->comp->config + .module_root, + sema->source_dir, base_imp, bd, + sizeof(bd)); + import_zir = loadImportZir( + sema, bd, sp, &import_ast); + zirDeinit(&b_zir); + astDeinit(&b_ast); + if (import_zir.inst_len > 0) { + func_inst + = findFuncInStructInZir( + &import_zir, struct_nm, + fn_nm); + if (func_inst != UINT32_MAX) { + // Register the struct + // type for field access. + struct_ret_type + = registerStructTypeFromZir( + sema, &import_zir, + struct_nm); + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; + computeSourceDir(NULL, bd, + sp, import_source_dir, + sizeof( + import_source_dir)); + } + } + } else { + zirDeinit(&b_zir); + astDeinit(&b_ast); + } + } + } + } + } + } + } } } - - free(inlining.merges.results); - free(inlining.merges.br_list); - semaBlockDeinit(&child_block); - free(sema->inst_map.items); - sema->inst_map = saved_inst_map; - if (is_cross_module) { - sema->code = saved_code; - zirDeinit(&import_zir); - astDeinit(&import_ast); - } - block->is_comptime = saved_is_comptime; - return ct_result; - } - - // Resolve the inline call block, ported from resolveAnalyzedBlock. - AirInstRef result_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - if (inlining.merges.results_len > 0) - result_ref = inlining.merges.results[0]; - - // Cache comptime results for memoization. - if (all_comptime && AIR_REF_IS_IP(result_ref)) { - if (sema->num_memo < 32) { - uint32_t mi = sema->num_memo++; - sema->memo_func_inst[mi] = func_inst; - sema->memo_args_len[mi] = args_len; - for (uint32_t a = 0; a < args_len && a < 4; a++) - sema->memo_args[mi][a] = arg_refs[a]; - sema->memo_result[mi] = result_ref; - } } - // Ported from src/Sema.zig resolveAnalyzedBlock. - AirInstRef call_result; - - if (inlining.merges.results_len == 0) { - // Noreturn block. - if (child_block.instructions_len == 0) { - // Body analysis produced nothing (unhandled ZIR instructions). - // Return void to avoid crash; AIR comparison will catch the error. - call_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } else if (block_tag == AIR_INST_BLOCK) { - // Copy instructions directly to parent block. - for (uint32_t i = 0; i < child_block.instructions_len; i++) { - if (block->instructions_len >= block->instructions_cap) { - uint32_t nc = block->instructions_cap * 2; - semaBlockGrowInstructions(block, nc); - } - block->instructions[block->instructions_len++] - = child_block.instructions[i]; - } - call_result = AIR_REF_FROM_INST( - child_block.instructions[child_block.instructions_len - 1]); - } else { - // dbg_inline_block: create block with noreturn type. - uint32_t es = semaAddExtra(sema, inlining.func); - semaAddExtra(sema, child_block.instructions_len); - for (uint32_t i = 0; i < child_block.instructions_len; i++) - semaAddExtra(sema, child_block.instructions[i]); - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(IP_INDEX_NORETURN_TYPE); - sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; - if (block->instructions_len >= block->instructions_cap) { - uint32_t nc = block->instructions_cap * 2; - semaBlockGrowInstructions(block, nc); - } - block->instructions[block->instructions_len++] = block_inst_idx; - call_result = AIR_REF_FROM_INST(block_inst_idx); - } - } else if (inlining.merges.results_len == 1) { - // Single result. Try to elide block when !need_debug_scope. - bool elided = false; - if (!need_debug_scope && child_block.instructions_len > 0) { - uint32_t last - = child_block.instructions[child_block.instructions_len - 1]; - if (sema->air_inst_tags[last] == AIR_INST_BR - && sema->air_inst_datas[last].br.block_inst - == block_inst_idx) { - // Elide: copy instructions except trailing break. - for (uint32_t i = 0; i < child_block.instructions_len - 1; - i++) { - if (block->instructions_len >= block->instructions_cap) { - uint32_t nc = block->instructions_cap * 2; - semaBlockGrowInstructions(block, nc); - } - block->instructions[block->instructions_len++] - = child_block.instructions[i]; + // For non-field calls: if local lookup fails, check if the callee + // declaration follows the import(...).field_val pattern + // (e.g. `const absv = @import("./absv.zig").absv;`). + if (func_inst == UINT32_MAX && !is_field_call && sema->source_dir + && callee_name_idx != 0) { + const char* import_path = NULL; + const char* field_name = NULL; + if (findDeclImportFieldVal( + sema, callee_name_idx, &import_path, &field_name)) { + import_zir = loadImportZir( + sema, sema->source_dir, import_path, &import_ast); + if (import_zir.inst_len > 0) { + computeSourceDir(sema->zcu->comp->config.module_root, + sema->source_dir, import_path, import_source_dir, + sizeof(import_source_dir)); + func_inst = findFuncInModuleZir(sema, import_source_dir, + &import_zir, &import_ast, field_name, import_source_dir, + sizeof(import_source_dir)); + if (func_inst != UINT32_MAX) { + saved_code = sema->code; + sema->code = import_zir; + is_cross_module = true; } - elided = true; - } - } - if (!elided && AIR_REF_IS_IP(result_ref)) { - // Comptime-known result: create block with void type, - // rewrite break to void_value. - // Ported from resolveAnalyzedBlock lines 6031-6062. - uint32_t es; - if (need_debug_scope) { - es = semaAddExtra(sema, inlining.func); - semaAddExtra(sema, child_block.instructions_len); - } else { - es = semaAddExtra(sema, child_block.instructions_len); - } - for (uint32_t i = 0; i < child_block.instructions_len; i++) - semaAddExtra(sema, child_block.instructions[i]); - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; - // Rewrite break operand to void_value. - if (inlining.merges.br_list_len > 0) { - uint32_t br_inst = inlining.merges.br_list[0]; - sema->air_inst_datas[br_inst].br.operand - = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - } - if (block->instructions_len >= block->instructions_cap) { - uint32_t nc = block->instructions_cap * 2; - semaBlockGrowInstructions(block, nc); - } - block->instructions[block->instructions_len++] = block_inst_idx; - call_result = result_ref; - } else if (!elided) { - // Runtime result: create block with result type. - TypeIndex rty = semaTypeOf(sema, result_ref); - uint32_t es; - if (need_debug_scope) { - es = semaAddExtra(sema, inlining.func); - semaAddExtra(sema, child_block.instructions_len); - } else { - es = semaAddExtra(sema, child_block.instructions_len); - } - for (uint32_t i = 0; i < child_block.instructions_len; i++) - semaAddExtra(sema, child_block.instructions[i]); - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(rty); - sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; - if (block->instructions_len >= block->instructions_cap) { - uint32_t nc = block->instructions_cap * 2; - semaBlockGrowInstructions(block, nc); } - block->instructions[block->instructions_len++] = block_inst_idx; - call_result = AIR_REF_FROM_INST(block_inst_idx); - } else { - call_result = result_ref; - } - } else { - // Multiple results: create block with result type. - TypeIndex rty = semaTypeOf(sema, result_ref); - uint32_t es; - if (need_debug_scope) { - es = semaAddExtra(sema, inlining.func); - semaAddExtra(sema, child_block.instructions_len); - } else { - es = semaAddExtra(sema, child_block.instructions_len); - } - for (uint32_t i = 0; i < child_block.instructions_len; i++) - semaAddExtra(sema, child_block.instructions[i]); - sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref - = AIR_REF_FROM_IP(rty); - sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; - if (block->instructions_len >= block->instructions_cap) { - uint32_t nc = block->instructions_cap * 2; - semaBlockGrowInstructions(block, nc); } - block->instructions[block->instructions_len++] = block_inst_idx; - call_result = AIR_REF_FROM_INST(block_inst_idx); } - // Clean up. - free(inlining.merges.results); - free(inlining.merges.br_list); - semaBlockDeinit(&child_block); - free(sema->inst_map.items); - sema->inst_map = saved_inst_map; - - // Restore original ZIR and free imported module data. - if (is_cross_module) { - sema->code = saved_code; - zirDeinit(&import_zir); - astDeinit(&import_ast); + if (func_inst == UINT32_MAX) { + // Can't resolve callee; return void. + return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - block->is_comptime = saved_is_comptime; - return call_result; + CalleeResolution cr; + memset(&cr, 0, sizeof(cr)); + cr.struct_ret_type = struct_ret_type; + cr.is_cross_module = is_cross_module; + if (is_cross_module) { + cr.saved_code = saved_code; + cr.import_zir = import_zir; + cr.import_ast = import_ast; + memcpy(cr.import_source_dir, import_source_dir, + sizeof(cr.import_source_dir)); + } + return semaAnalyzeCall(sema, block, inst, func_inst, callee_name_idx, + args_len, arg_data_start, &cr); } // analyzeFuncBodyAndRecord: analyze a function body in fresh AIR context