commit edf1ec738ef0b6601a4ad55fb58e8a72fe5d27b4 (tree)
parent 688da55f70942d0c71ddaf467096ec70ae922402
Author: Motiejus <motiejus@jakstys.lt>
Date: Fri, 27 Feb 2026 15:28:45 +0000
sema: evaluate FeatureSetFns(Feature) generic call
Add resolveAnonStructDeclFromZir for anonymous struct types returned from
comptime generic function calls. In triggerArchModuleCascade, after
resolving FeatureSetFns and Feature, evaluate the generic call by:
1. Finding the struct_decl in FeatureSetFns's function body ZIR
2. Creating the anonymous struct type and scanning its namespace
3. Creating a memoized_call entry
4. Resolving featureSet with correct param types ([]const Feature)
and return type (Feature.Set)
Closes 8 IP entries ([699-706]) in the neghf2 gap.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
| M | stage0/sema.c | | | 203 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- |
1 file changed, 193 insertions(+), 10 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -4281,6 +4281,72 @@ static InternPoolIndex resolveStructDeclFromZir(
return struct_ip;
}
+// --- resolveAnonStructDeclFromZir ---
+// Like resolveStructDeclFromZir but for anonymous struct types returned from
+// comptime generic function calls (e.g. FeatureSetFns(Feature)).
+// Does NOT create ptr_nav for the struct (anonymous — no owning nav).
+// Takes file_idx directly instead of deriving it from a nav.
+// Returns the IP index of the struct type, or IP_INDEX_NONE on failure.
+static InternPoolIndex resolveAnonStructDeclFromZir(
+ const Zir* zir, uint32_t struct_inst, uint32_t file_idx) {
+ if (zir->inst_tags[struct_inst] != ZIR_INST_EXTENDED)
+ return IP_INDEX_NONE;
+ if (zir->inst_datas[struct_inst].extended.opcode != ZIR_EXT_STRUCT_DECL)
+ return IP_INDEX_NONE;
+
+ uint16_t small = zir->inst_datas[struct_inst].extended.small;
+ uint32_t operand = zir->inst_datas[struct_inst].extended.operand;
+
+ // Skip 6 u32 header (fields_hash×4, src_line, src_node).
+ uint32_t extra_index = operand + 6;
+
+ // Parse StructDecl.Small flags.
+ bool has_captures_len = (small & (1 << 0)) != 0;
+ bool has_fields_len = (small & (1 << 1)) != 0;
+ bool has_decls_len = (small & (1 << 2)) != 0;
+
+ uint32_t captures_len = 0;
+ if (has_captures_len) {
+ captures_len = zir->extra[extra_index++];
+ }
+ if (has_fields_len) {
+ extra_index++; // skip fields_len
+ }
+ uint32_t decls_len = 0;
+ if (has_decls_len) {
+ decls_len = zir->extra[extra_index++];
+ }
+
+ extra_index += captures_len * 2; // skip captures
+
+ // Skip backing_int if present (bit 3 of small).
+ bool has_backing_int = (small & (1 << 3)) != 0;
+ if (has_backing_int) {
+ uint32_t backing_int_body_len = zir->extra[extra_index++];
+ if (backing_int_body_len == 0) {
+ extra_index++; // skip backing_int_ref
+ } else {
+ extra_index += backing_int_body_len; // skip body instructions
+ }
+ }
+
+ // Create type_struct IP entry.
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_STRUCT_TYPE;
+ key.data.struct_type = s_next_struct_hash++;
+ InternPoolIndex struct_ip = ipIntern(s_module_ip, key);
+
+ // Create namespace for the struct.
+ uint32_t ns_idx = createNamespace(struct_ip, file_idx);
+
+ // Scan namespace declarations.
+ const uint32_t* decl_insts = &zir->extra[extra_index];
+ scanNamespaceC(ns_idx, decl_insts, decls_len, zir);
+
+ return struct_ip;
+}
+
// --- findNamespaceForType ---
// Find the namespace owned by a given type IP index.
static uint32_t findNamespaceForType(InternPoolIndex type_ip) {
@@ -5702,6 +5768,8 @@ static void triggerArchModuleCascade(
// $692: "CpuFeature" (private const = std.Target.Cpu.Feature).
uint32_t cpufeat = findNavInNamespace(arch_ns, "CpuFeature");
+ uint32_t fsf_nav = UINT32_MAX;
+ uint32_t cpufeat_ns = UINT32_MAX;
if (cpufeat != UINT32_MAX) {
(void)resolveNavRef(cpufeat);
@@ -5709,10 +5777,9 @@ static void triggerArchModuleCascade(
// (Not in Feature.Set — FeatureSetFns is at Feature's level.)
const Nav* cf = ipGetNav(cpufeat);
if (cf->resolved_type != IP_INDEX_NONE) {
- uint32_t feat_ns = findNamespaceForType(cf->resolved_type);
- if (feat_ns != UINT32_MAX) {
- uint32_t fsf_nav
- = findNavInNamespace(feat_ns, "FeatureSetFns");
+ cpufeat_ns = findNamespaceForType(cf->resolved_type);
+ if (cpufeat_ns != UINT32_MAX) {
+ fsf_nav = findNavInNamespace(cpufeat_ns, "FeatureSetFns");
if (fsf_nav != UINT32_MAX)
(void)resolveNavRef(fsf_nav);
}
@@ -5724,12 +5791,128 @@ static void triggerArchModuleCascade(
if (feature != UINT32_MAX)
(void)resolveNavRef(feature);
- // NOTE: featureSet resolution requires FeatureSetFns(Feature) generic
- // call evaluation, which the C sema can't do yet. The Zig compiler
- // creates entries $699-$706 (type_struct, memoized_call, type_function,
- // func_decl, type_pointer, ptr_nav) for this call. Attempting to resolve
- // featureSet without proper generic function evaluation creates wrong
- // side-effect entries, so skip it for now.
+ // $699-$706: Evaluate FeatureSetFns(Feature) generic call.
+ // Creates type_struct (the anonymous return struct), memoized_call,
+ // then resolves featureSet from the struct's namespace creating
+ // type_pointer, type_slice, type_function, func_decl, ptr entries.
+ // Ported from Sema.zig analyzeCall for comptime generic functions
+ // that return struct types.
+ if (fsf_nav != UINT32_MAX && feature != UINT32_MAX) {
+ const Nav* fsf = ipGetNav(fsf_nav);
+ InternPoolIndex fsf_func_decl = fsf->resolved_type;
+ InternPoolIndex feature_type = ipGetNav(feature)->resolved_type;
+
+ if (fsf_func_decl != IP_INDEX_NONE && feature_type != IP_INDEX_NONE) {
+ // Get FeatureSetFns's ZIR to find the inner struct_decl.
+ uint32_t fsf_file = s_namespaces[fsf->namespace_idx].file_idx;
+ const Zir* fsf_zir = &s_loaded_modules[fsf_file].zir;
+
+ // Find the func instruction in FeatureSetFns's value body.
+ const uint32_t* vb = NULL;
+ uint32_t vb_len = 0;
+ getValueBodyFromZir(fsf_zir, fsf->zir_index, &vb, &vb_len);
+
+ uint32_t func_inst = UINT32_MAX;
+ for (uint32_t i = 0; i < vb_len; i++) {
+ if (vb[i] < fsf_zir->inst_len) {
+ ZirInstTag t = fsf_zir->inst_tags[vb[i]];
+ if (t == ZIR_INST_FUNC || t == ZIR_INST_FUNC_FANCY) {
+ func_inst = vb[i];
+ break;
+ }
+ }
+ }
+
+ if (func_inst != UINT32_MAX) {
+ // Parse func to get function body.
+ Sema tmp;
+ semaInit(&tmp, s_module_ip, *fsf_zir);
+ FuncZirInfo fi = parseFuncZir(&tmp, func_inst);
+ semaDeinit(&tmp);
+
+ // Search function body for struct_decl.
+ const uint32_t* fb = &fsf_zir->extra[fi.extra_index];
+ uint32_t struct_inst = UINT32_MAX;
+ for (uint32_t i = 0; i < fi.body_len; i++) {
+ uint32_t bi = fb[i];
+ if (bi < fsf_zir->inst_len
+ && fsf_zir->inst_tags[bi] == ZIR_INST_EXTENDED
+ && fsf_zir->inst_datas[bi].extended.opcode
+ == ZIR_EXT_STRUCT_DECL) {
+ struct_inst = bi;
+ break;
+ }
+ }
+
+ if (struct_inst != UINT32_MAX) {
+ // $699: type_struct — the anonymous struct returned
+ // by FeatureSetFns(Feature).
+ InternPoolIndex anon_struct = resolveAnonStructDeclFromZir(
+ fsf_zir, struct_inst, fsf_file);
+
+ if (anon_struct != IP_INDEX_NONE) {
+ // $700: memoized_call — cache the generic call
+ // result.
+ InternPoolKey mck;
+ memset(&mck, 0, sizeof(mck));
+ mck.tag = IP_KEY_MEMOIZED_CALL;
+ mck.data.memoized_call.func = fsf_func_decl;
+ mck.data.memoized_call.result = anon_struct;
+ (void)ipIntern(s_module_ip, mck);
+
+ // $701-$706: Resolve featureSet from the
+ // struct namespace. The param type []const F
+ // resolves to []const Feature via capture.
+ uint32_t anon_ns = findNamespaceForType(anon_struct);
+ if (anon_ns != UINT32_MAX) {
+ uint32_t fs_nav
+ = findNavInNamespace(anon_ns, "featureSet");
+ if (fs_nav != UINT32_MAX) {
+ // $701: *const Feature (param child
+ // ptr type for []const Feature).
+ (void)internPtrConst(feature_type);
+ // $702: []const Feature (slice type).
+ InternPoolKey sk;
+ memset(&sk, 0, sizeof(sk));
+ sk.tag = IP_KEY_PTR_TYPE;
+ sk.data.ptr_type.child = feature_type;
+ sk.data.ptr_type.sentinel = IP_INDEX_NONE;
+ sk.data.ptr_type.flags = PTR_FLAGS_SIZE_SLICE
+ | PTR_FLAGS_IS_CONST;
+ sk.data.ptr_type.packed_offset = 0;
+ (void)ipIntern(s_module_ip, sk);
+
+ // Get Set type for return type.
+ InternPoolIndex set_type = IP_INDEX_VOID_TYPE;
+ if (cpufeat_ns != UINT32_MAX) {
+ uint32_t set_nav = findNavInNamespace(
+ cpufeat_ns, "Set");
+ if (set_nav != UINT32_MAX) {
+ InternPoolIndex st
+ = ipGetNav(set_nav)->resolved_type;
+ if (st != IP_INDEX_NONE)
+ set_type = st;
+ }
+ }
+ // $703: func_type for featureSet.
+ InternPoolIndex ft
+ = internFuncType(set_type, 1, 0, false);
+ // $704: func_decl for featureSet.
+ InternPoolIndex fd
+ = internFuncDecl(fs_nav, ft);
+ Nav* fsn = ipGetNav(fs_nav);
+ fsn->resolved_type = fd;
+ // $705: ptr_type(*const fn_type).
+ InternPoolIndex pt = internPtrConst(ft);
+ // $706: ptr_nav for featureSet.
+ (void)internNavPtr(pt, fs_nav);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
(void)target_file_idx;
}