From a35da223fbc2d0067b093bfc28bbcd7c18ec59ec Mon Sep 17 00:00:00 2001 From: Motiejus Date: Tue, 3 Mar 2026 08:30:41 +0000 Subject: [PATCH] sema: add func_instance support for generic function monomorphization Port IP_KEY_FUNC_INSTANCE from upstream InternPool.getFuncInstance. When a generic non-inline function is called, create a monomorphized func_type (runtime params only) and a func_instance entry referencing the generic owner's func_decl. This matches the Zig compiler's IP entry sequence for generic instantiations. Bumps num_passing from 84 to 85. Co-Authored-By: Claude Opus 4.6 --- stage0/corpus.zig | 2 +- stage0/intern_pool.c | 11 +++++++ stage0/intern_pool.h | 6 ++++ stage0/sema.c | 57 ++++++++++++++++++++++++++++++++++++ stage0/verbose_intern_pool.c | 7 +++++ 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/stage0/corpus.zig b/stage0/corpus.zig index 3341d77bb1..cc2a48133c 100644 --- a/stage0/corpus.zig +++ b/stage0/corpus.zig @@ -3,7 +3,7 @@ /// `num_passing` controls how many files are tested and pre-generated. /// Both build.zig and stages_test.zig import this file. /// To enable more tests: just increment `num_passing`. -pub const num_passing: usize = 84; +pub const num_passing: usize = 85; pub const files = [_][]const u8{ "stage0/sema_tests/empty.zig", diff --git a/stage0/intern_pool.c b/stage0/intern_pool.c index e5cdf0ff23..a558a9364d 100644 --- a/stage0/intern_pool.c +++ b/stage0/intern_pool.c @@ -80,6 +80,10 @@ static uint32_t ipHashKey(const InternPoolKey* key) { wyhash_update_u32(&h, key->data.func_decl.owner_nav); wyhash_update_u32(&h, key->data.func_decl.ty); break; + case IP_KEY_FUNC_INSTANCE: + wyhash_update_u32(&h, key->data.func_instance.generic_owner); + wyhash_update_u32(&h, key->data.func_instance.ty); + break; case IP_KEY_MEMOIZED_CALL: wyhash_update_u32(&h, key->data.memoized_call.func); wyhash_update_u32(&h, key->data.memoized_call.result); @@ -223,6 +227,10 @@ static bool ipKeysEqual(const InternPoolKey* a, const InternPoolKey* b) { case IP_KEY_FUNC: return a->data.func_decl.owner_nav == b->data.func_decl.owner_nav && a->data.func_decl.ty == b->data.func_decl.ty; + case IP_KEY_FUNC_INSTANCE: + return a->data.func_instance.generic_owner + == b->data.func_instance.generic_owner + && a->data.func_instance.ty == b->data.func_instance.ty; case IP_KEY_MEMOIZED_CALL: return a->data.memoized_call.func == b->data.memoized_call.func && a->data.memoized_call.result == b->data.memoized_call.result; @@ -982,6 +990,9 @@ InternPoolIndex ipTypeOf(const InternPool* ip, InternPoolIndex index) { case IP_KEY_FUNC: return key.data.func_decl.ty; + case IP_KEY_FUNC_INSTANCE: + return key.data.func_instance.ty; + case IP_KEY_MEMOIZED_CALL: return IP_INDEX_VOID_TYPE; diff --git a/stage0/intern_pool.h b/stage0/intern_pool.h index 8d532ce789..fa55b2ffea 100644 --- a/stage0/intern_pool.h +++ b/stage0/intern_pool.h @@ -320,6 +320,7 @@ typedef enum { IP_KEY_OPT_NULL, IP_KEY_REPEATED, IP_KEY_INT_U16, + IP_KEY_FUNC_INSTANCE, } InternPoolKeyTag; // --- InternPoolKey (tagged union) --- @@ -351,6 +352,11 @@ typedef struct { uint32_t owner_nav; // Nav index InternPoolIndex ty; // function type IP index } func_decl; + struct { + InternPoolIndex generic_owner; // func_decl IP index + InternPoolIndex ty; // monomorphized function type IP index + uint32_t owner_nav; // Nav index + } func_instance; TypedInt int_val; InternPoolIndex err; InternPoolIndex error_union; diff --git a/stage0/sema.c b/stage0/sema.c index 03038e3c47..90d1580d0f 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -5933,6 +5933,7 @@ static AirInstRef semaAnalyzeCall(Sema* sema, SemaBlock* block, uint32_t inst, // Ported from src/Sema.zig analyzeCall (Sema.zig:7575-7601). uint32_t runtime_args_count = 0; AirInstRef runtime_arg_refs[16]; + TypeIndex runtime_param_types[16]; uint32_t pi = 0; for (uint32_t p = 0; p < param_body_len; p++) { ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; @@ -5969,6 +5970,8 @@ static AirInstRef semaAnalyzeCall(Sema* sema, SemaBlock* block, uint32_t inst, } } } + runtime_param_types[runtime_args_count] + = semaTypeOf(sema, arg); runtime_arg_refs[runtime_args_count++] = arg; } pi++; @@ -6027,6 +6030,60 @@ static AirInstRef semaAnalyzeCall(Sema* sema, SemaBlock* block, uint32_t inst, } } + // For generic functions, create a func_instance entry. + // Ported from Sema.zig:7549 / InternPool.getFuncInstance. + // The monomorphized function has its own func_type (runtime + // params only) and a func_instance referencing the generic owner. + if (is_generic && func_val_ip != IP_INDEX_NONE) { + InternPoolIndex generic_owner_ip = func_val_ip; + InternPoolIndex generic_owner_ty + = sema->ip->items[generic_owner_ip].data.func_decl.ty; + uint8_t owner_cc + = sema->ip->items[generic_owner_ty].data.func_type.cc; + + // Create monomorphized func_type with runtime params only. + InternPoolKey mono_ft; + memset(&mono_ft, 0, sizeof(mono_ft)); + mono_ft.tag = IP_KEY_FUNC_TYPE; + mono_ft.data.func_type.return_type = ret_ty; + mono_ft.data.func_type.param_count = runtime_args_count; + mono_ft.data.func_type.cc = owner_cc; + for (uint32_t ri = 0; + ri < runtime_args_count && ri < FUNC_TYPE_MAX_PARAMS; ri++) + mono_ft.data.func_type.param_types[ri] + = runtime_param_types[ri]; + InternPoolIndex mono_ft_ip = (runtime_args_count > 0) + ? ipIntern(sema->ip, mono_ft) + : ipForceIntern(sema->ip, mono_ft); + + // Pre-compute the func_instance IP index (next slot). + InternPoolIndex fi_ip = sema->ip->items_len; + + // Create nav for the func_instance. Ported from + // InternPool.finishFuncInstance. + uint32_t owner_nav_idx + = sema->ip->items[generic_owner_ip].data.func_decl.owner_nav; + const Nav* owner_nav = ipGetNav(sema->ip, owner_nav_idx); + const char* owner_name + = (const char*)&sema->ip->string_bytes[owner_nav->name]; + char anon_name[256]; + snprintf(anon_name, sizeof(anon_name), "%s__anon_%u", owner_name, + fi_ip); + uint32_t anon_str = ipGetOrPutString(sema->ip, anon_name); + uint32_t fi_nav = ipCreateDeclNav(sema->ip, anon_str, anon_str, + owner_nav->zir_index, owner_nav->namespace_idx, + owner_nav->is_pub, owner_nav->is_const); + + // Intern the func_instance entry. + InternPoolKey fi_key; + memset(&fi_key, 0, sizeof(fi_key)); + fi_key.tag = IP_KEY_FUNC_INSTANCE; + fi_key.data.func_instance.generic_owner = generic_owner_ip; + fi_key.data.func_instance.ty = mono_ft_ip; + fi_key.data.func_instance.owner_nav = fi_nav; + func_val_ip = ipForceIntern(sema->ip, fi_key); + } + // 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++) diff --git a/stage0/verbose_intern_pool.c b/stage0/verbose_intern_pool.c index 523db7b3d2..b56aea2962 100644 --- a/stage0/verbose_intern_pool.c +++ b/stage0/verbose_intern_pool.c @@ -98,6 +98,8 @@ static const char* ipKeyTagName(InternPoolKeyTag tag) { return "repeated"; case IP_KEY_INT_U16: return "int_u16"; + case IP_KEY_FUNC_INSTANCE: + return "func_instance"; } return "?"; } @@ -140,6 +142,11 @@ void verboseIpPrint(FILE* out, const InternPool* ip) { fprintf(out, " nav=%u ty=%u", key.data.func_decl.owner_nav, key.data.func_decl.ty); break; + case IP_KEY_FUNC_INSTANCE: + fprintf(out, " generic_owner=%u ty=%u nav=%u", + key.data.func_instance.generic_owner, + key.data.func_instance.ty, key.data.func_instance.owner_nav); + break; case IP_KEY_ENUM_LITERAL: fprintf(out, " str_idx=%u", key.data.enum_literal); break;