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:
| M | stage0/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, ¶m_body, ¶m_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, ¶m_body, ¶m_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