commit a35da223fbc2d0067b093bfc28bbcd7c18ec59ec (tree)
parent c69e558131e5459f8b61019dcd555c38b73d69cf
Author: Motiejus <motiejus@jakstys.lt>
Date: Tue, 3 Mar 2026 08:30:41 +0000
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 <noreply@anthropic.com>
Diffstat:
5 files changed, 82 insertions(+), 1 deletion(-)
diff --git 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
@@ -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
@@ -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
@@ -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
@@ -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;