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>
This commit is contained in:
2026-03-03 08:30:41 +00:00
parent c69e558131
commit a35da223fb
5 changed files with 82 additions and 1 deletions

View File

@@ -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",

View File

@@ -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;

View File

@@ -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;

View File

@@ -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++)

View File

@@ -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;