commit c2b0d0242e00077b4a58a8b320fe6ec26753508a (tree)
parent 97285975d522e86338d6e9c38bed3a0f4a4e9dad
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date: Fri, 20 Feb 2026 16:29:06 +0000
sema: enable neghf2.zig with cross-module comptime evaluation
Implement comptime evaluation of fneg's body from common.zig:
- block_comptime handler: evaluate body, map break_inline result
- typeof/typeof_builtin: resolve to param type for anytype
- type_info: extract float bits from type
- field_val: comptime field access on type_info results
- reify/struct_init/decl_literal: reconstruct int type from bits
- as_node/bitcast: handle comptime-resolved type refs
- dbg_var_val: skip comptime-only values (types, comptime_int)
- int_comptime handler for cross-module comptime integer literals
neghf2.zig corpus test now passes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
2 files changed, 454 insertions(+), 19 deletions(-)
diff --git a/stage0/sema.c b/stage0/sema.c
@@ -128,10 +128,13 @@ static void instMapPut(InstMap* map, uint32_t zir_inst, AirInstRef ref) {
// --- Comptime tracker helpers ---
// Track comptime type-info values for cross-module inline evaluation.
-// tag: 0=none, 1=type_info(type), 2=float_info(bits)
+// tag: 0=none, 1=type_info(type), 2=float_info(bits),
+// 3=int_info(signedness<<16|bits), 4=reify_int(int_info_ip)
#define CT_TAG_NONE 0
#define CT_TAG_TYPE_INFO 1
#define CT_TAG_FLOAT_INFO 2
+#define CT_TAG_INT_INFO 3
+#define CT_TAG_REIFY_INT 4
static void ctTrack(Sema* sema, InternPoolIndex ip_idx,
uint8_t tag_val, uint32_t val) {
@@ -257,6 +260,9 @@ static uint32_t addAirExtra(Sema* sema, uint32_t value) {
return idx;
}
+// --- Forward declarations ---
+static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref);
+
// --- ZIR instruction handlers ---
// Ported from src/Sema.zig instruction handlers.
@@ -326,6 +332,17 @@ static void zirDbgVar(
ZirInstRef operand_ref = sema->code.inst_datas[inst].str_op.operand;
AirInstRef operand = resolveInst(sema, operand_ref);
+ // Skip comptime-only values: types have no runtime representation.
+ // Ported from src/Sema.zig addDbgVar: comptimeOnlySema /
+ // hasRuntimeBitsSema checks.
+ TypeIndex val_ty = semaTypeOf(sema, operand);
+ if (val_ty == IP_INDEX_TYPE_TYPE
+ || val_ty == IP_INDEX_COMPTIME_INT_TYPE
+ || val_ty == IP_INDEX_COMPTIME_FLOAT_TYPE
+ || val_ty == IP_INDEX_ENUM_LITERAL_TYPE) {
+ return;
+ }
+
const char* name
= (const char*)&sema->code.string_bytes[str_idx];
uint32_t name_nts = semaAppendAirString(sema, name);
@@ -558,6 +575,12 @@ static AirInstRef semaCoerce(
return ref;
if (src_ty == IP_INDEX_COMPTIME_INT_TYPE)
return coerceIntRef(sema, ref, target_ty);
+ // Comptime int→int coercion: re-intern with target type.
+ if (AIR_REF_IS_IP(ref)
+ && sema->ip->items[src_ty].tag == IP_KEY_INT_TYPE
+ && sema->ip->items[target_ty].tag == IP_KEY_INT_TYPE) {
+ return coerceIntRef(sema, ref, target_ty);
+ }
// Runtime int→int coercion: emit intcast.
if (AIR_REF_IS_INST(ref)
&& sema->ip->items[src_ty].tag == IP_KEY_INT_TYPE
@@ -730,6 +753,37 @@ static AirInstRef zirByteSwap(Sema* sema, SemaBlock* block, uint32_t inst) {
return blockAddInst(block, AIR_INST_BYTE_SWAP, data);
}
+// isComptimeInt: check if an AIR ref is a comptime integer value.
+// Returns true and sets *value if the ref is a comptime int.
+static bool isComptimeInt(
+ const Sema* sema, AirInstRef ref, int64_t* value) {
+ if (!AIR_REF_IS_IP(ref))
+ return false;
+ InternPoolIndex ip_idx = AIR_REF_TO_IP(ref);
+ if (ip_idx >= sema->ip->items_len)
+ return false;
+ InternPoolKey key = sema->ip->items[ip_idx];
+ if (key.tag != IP_KEY_INT)
+ return false;
+ if (key.data.int_val.is_negative)
+ *value = -(int64_t)key.data.int_val.value;
+ else
+ *value = (int64_t)key.data.int_val.value;
+ return true;
+}
+
+// internComptimeInt: intern a comptime integer value with given type.
+static AirInstRef internComptimeInt(
+ Sema* sema, TypeIndex ty, uint64_t value) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT;
+ key.data.int_val.ty = ty;
+ key.data.int_val.value = value;
+ key.data.int_val.is_negative = false;
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
+}
+
// zirArithmetic: handle add/sub ZIR instructions.
// Ported from src/Sema.zig zirArithmetic.
static AirInstRef zirArithmetic(
@@ -740,6 +794,40 @@ static AirInstRef zirArithmetic(
ZirInstRef zir_rhs = sema->code.extra[payload_index + 1];
AirInstRef lhs = resolveInst(sema, zir_lhs);
AirInstRef rhs = resolveInst(sema, zir_rhs);
+
+ // Comptime folding: if both operands are comptime integers,
+ // compute the result at comptime.
+ int64_t lhs_val;
+ int64_t rhs_val;
+ if (isComptimeInt(sema, lhs, &lhs_val)
+ && isComptimeInt(sema, rhs, &rhs_val)) {
+ int64_t result;
+ TypeIndex lhs_ty = semaTypeOf(sema, lhs);
+ TypeIndex rhs_ty = semaTypeOf(sema, rhs);
+ TypeIndex result_ty = (lhs_ty == IP_INDEX_COMPTIME_INT_TYPE)
+ ? rhs_ty : lhs_ty;
+ if (result_ty == IP_INDEX_COMPTIME_INT_TYPE)
+ result_ty = IP_INDEX_COMPTIME_INT_TYPE;
+ switch (air_tag) {
+ case AIR_INST_ADD:
+ case AIR_INST_ADD_WRAP:
+ result = lhs_val + rhs_val;
+ break;
+ case AIR_INST_SUB:
+ case AIR_INST_SUB_WRAP:
+ result = lhs_val - rhs_val;
+ break;
+ case AIR_INST_MUL:
+ case AIR_INST_MUL_WRAP:
+ result = lhs_val * rhs_val;
+ break;
+ default:
+ goto emit_runtime;
+ }
+ return internComptimeInt(sema, result_ty, (uint64_t)result);
+ }
+
+emit_runtime:;
TypeIndex peer_ty = resolvePeerType(sema, lhs, rhs);
lhs = semaCoerce(sema, block, peer_ty, lhs);
rhs = semaCoerce(sema, block, peer_ty, rhs);
@@ -874,6 +962,12 @@ static void zirTypeofLog2IntType(Sema* sema, uint32_t inst) {
ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand;
AirInstRef operand = resolveInst(sema, operand_ref);
TypeIndex operand_ty = semaTypeOf(sema, operand);
+ // Handle comptime_int: upstream returns comptime_int directly.
+ if (operand_ty == IP_INDEX_COMPTIME_INT_TYPE) {
+ instMapPut(&sema->inst_map, inst,
+ AIR_REF_FROM_IP(IP_INDEX_COMPTIME_INT_TYPE));
+ return;
+ }
assert(sema->ip->items[operand_ty].tag == IP_KEY_INT_TYPE);
uint16_t bits = sema->ip->items[operand_ty].data.int_type.bits;
// Compute ceil(log2(bits)): count bits needed to represent 0..bits-1.
@@ -921,6 +1015,22 @@ static AirInstRef zirShl(
ZirInstRef rhs_ref = sema->code.extra[payload_index + 1];
AirInstRef lhs = resolveInst(sema, lhs_ref);
AirInstRef rhs = resolveInst(sema, rhs_ref);
+
+ // Comptime folding for shift operations.
+ int64_t lhs_val;
+ int64_t rhs_val;
+ if (isComptimeInt(sema, lhs, &lhs_val)
+ && isComptimeInt(sema, rhs, &rhs_val)) {
+ TypeIndex lhs_ty = semaTypeOf(sema, lhs);
+ uint64_t result;
+ if (air_tag == AIR_INST_SHL) {
+ result = (uint64_t)lhs_val << (uint32_t)rhs_val;
+ } else {
+ result = (uint64_t)lhs_val >> (uint32_t)rhs_val;
+ }
+ return internComptimeInt(sema, lhs_ty, result);
+ }
+
AirInstData data;
memset(&data, 0, sizeof(data));
data.bin_op.lhs = lhs;
@@ -1486,11 +1596,21 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst,
// Emit dbg_arg_inline for each param.
if (!child_block.is_comptime) {
- uint32_t param_payload
- = sema->code.inst_datas[param_body[p]]
- .pl_tok.payload_index;
- uint32_t param_name_idx
- = sema->code.extra[param_payload];
+ uint32_t param_name_idx;
+ if (ptag == ZIR_INST_PARAM_ANYTYPE
+ || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) {
+ // 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];
@@ -2093,10 +2213,11 @@ static AirInstRef zirFieldValComptime(Sema* sema, uint32_t inst) {
&& strcmp(field_name, "bits") == 0) {
// Access .bits on a float_info result.
// ct_val is the bits count.
+ // The .bits field has type u16 in std.builtin.Type.Float.
InternPoolKey key;
memset(&key, 0, sizeof(key));
key.tag = IP_KEY_INT;
- key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE;
+ key.data.int_val.ty = IP_INDEX_U16_TYPE;
key.data.int_val.value = ct_val;
key.data.int_val.is_negative = false;
return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
@@ -2126,6 +2247,184 @@ static InternPoolIndex resolveUnsignedIntType(
}
}
+// zirStructInitComptime: handle struct_init in comptime context.
+// For @Type(.{.int = .{.signedness = .unsigned, .bits = N}}):
+// - inner struct: extract signedness and bits, track as CT_TAG_INT_INFO
+// - outer struct: extract field name (.int), track as CT_TAG_REIFY_INT
+static AirInstRef zirStructInitComptime(Sema* sema, uint32_t inst) {
+ uint32_t payload_index
+ = sema->code.inst_datas[inst].pl_node.payload_index;
+ // StructInit payload: abs_node, abs_line, fields_len, then Items.
+ uint32_t fields_len = sema->code.extra[payload_index + 2];
+ const uint32_t* items = &sema->code.extra[payload_index + 3];
+
+ // Check if this is the inner int struct (signedness + bits).
+ // Look at field names from the struct_init_field_type instructions.
+ uint32_t signedness = 0;
+ uint32_t bits_val = 0;
+ bool has_signedness = false;
+ bool has_bits = false;
+ const char* outer_field_name = NULL;
+ InternPoolIndex inner_ip = IP_INDEX_NONE;
+
+ for (uint32_t f = 0; f < fields_len; f++) {
+ uint32_t field_type_inst = items[f * 2];
+ ZirInstRef init_ref = items[f * 2 + 1];
+
+ // Get field name from struct_init_field_type instruction.
+ uint32_t ft_payload
+ = sema->code.inst_datas[field_type_inst].pl_node.payload_index;
+ uint32_t name_start = sema->code.extra[ft_payload + 1];
+ const char* name
+ = (const char*)&sema->code.string_bytes[name_start];
+
+ AirInstRef init_air = resolveInst(sema, init_ref);
+
+ if (strcmp(name, "signedness") == 0) {
+ has_signedness = true;
+ // The init should resolve to 0 (unsigned) or 1 (signed).
+ // It comes from decl_literal(.unsigned) or
+ // a comptime int.
+ if (AIR_REF_IS_IP(init_air)) {
+ InternPoolIndex ip_idx = AIR_REF_TO_IP(init_air);
+ InternPoolKey key_val = sema->ip->items[ip_idx];
+ if (key_val.tag == IP_KEY_INT) {
+ signedness = (uint32_t)key_val.data.int_val.value;
+ }
+ }
+ } else if (strcmp(name, "bits") == 0) {
+ has_bits = true;
+ if (AIR_REF_IS_IP(init_air)) {
+ InternPoolIndex ip_idx = AIR_REF_TO_IP(init_air);
+ InternPoolKey key_val = sema->ip->items[ip_idx];
+ if (key_val.tag == IP_KEY_INT) {
+ bits_val = (uint32_t)key_val.data.int_val.value;
+ }
+ }
+ } else if (strcmp(name, "int") == 0) {
+ outer_field_name = name;
+ if (AIR_REF_IS_IP(init_air)) {
+ inner_ip = AIR_REF_TO_IP(init_air);
+ }
+ }
+ }
+
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT;
+ key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE;
+ key.data.int_val.is_negative = false;
+
+ if (has_signedness && has_bits) {
+ // Inner struct: .{.signedness = S, .bits = N}
+ key.data.int_val.value = 0x51F70000
+ | ((uint64_t)signedness << 16) | bits_val;
+ InternPoolIndex marker = ipIntern(sema->ip, key);
+ ctTrack(sema, marker, CT_TAG_INT_INFO,
+ (signedness << 16) | bits_val);
+ return AIR_REF_FROM_IP(marker);
+ }
+
+ if (outer_field_name != NULL
+ && strcmp(outer_field_name, "int") == 0) {
+ // Outer struct: .{.int = <inner>}
+ key.data.int_val.value = 0x52E10000 + inner_ip;
+ InternPoolIndex marker = ipIntern(sema->ip, key);
+ ctTrack(sema, marker, CT_TAG_REIFY_INT, inner_ip);
+ return AIR_REF_FROM_IP(marker);
+ }
+
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+}
+
+// zirReifyComptime: handle @Type(...) in comptime context.
+// Looks up the comptime-tracked argument to determine the type.
+static AirInstRef zirReifyComptime(Sema* sema, uint32_t inst) {
+ uint16_t opcode = sema->code.inst_datas[inst].extended.opcode;
+ (void)opcode;
+ uint32_t operand_extra = sema->code.inst_datas[inst].extended.operand;
+ // Reify payload: node, operand_ref, src_line.
+ ZirInstRef operand_ref = sema->code.extra[operand_extra + 1];
+ AirInstRef operand = resolveInst(sema, operand_ref);
+
+ if (!AIR_REF_IS_IP(operand))
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
+
+ InternPoolIndex op_ip = AIR_REF_TO_IP(operand);
+ uint32_t ct_val;
+ uint8_t ct_tag = ctLookup(sema, op_ip, &ct_val);
+
+ if (ct_tag == CT_TAG_REIFY_INT) {
+ // The operand is .{.int = <inner>}.
+ // Look up the inner int_info.
+ uint32_t inner_val;
+ uint8_t inner_tag = ctLookup(sema, ct_val, &inner_val);
+ if (inner_tag == CT_TAG_INT_INFO) {
+ uint32_t s = (inner_val >> 16) & 0xFFFF;
+ uint32_t b = inner_val & 0xFFFF;
+ if (s == 0) {
+ return AIR_REF_FROM_IP(
+ resolveUnsignedIntType(sema->ip, b));
+ }
+ // Signed int type.
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT_TYPE;
+ key.data.int_type.signedness = 1;
+ key.data.int_type.bits = (uint16_t)b;
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
+ }
+ } else if (ct_tag == CT_TAG_INT_INFO) {
+ // Direct int_info (no outer wrapping).
+ uint32_t s = (ct_val >> 16) & 0xFFFF;
+ uint32_t b = ct_val & 0xFFFF;
+ if (s == 0) {
+ return AIR_REF_FROM_IP(
+ resolveUnsignedIntType(sema->ip, b));
+ }
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT_TYPE;
+ key.data.int_type.signedness = 1;
+ key.data.int_type.bits = (uint16_t)b;
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
+ }
+
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
+}
+
+// zirDeclLiteralComptime: handle decl_literal in comptime context.
+// For enum literals like .unsigned, resolve to the enum value.
+static AirInstRef zirDeclLiteralComptime(Sema* sema, uint32_t inst) {
+ uint32_t payload_index
+ = sema->code.inst_datas[inst].pl_node.payload_index;
+ // Field payload: lhs(Ref), field_name_start(u32).
+ uint32_t field_name_start = sema->code.extra[payload_index + 1];
+ const char* field_name
+ = (const char*)&sema->code.string_bytes[field_name_start];
+
+ // For std.builtin.Signedness enum: unsigned=0, signed=1.
+ if (strcmp(field_name, "unsigned") == 0) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT;
+ key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE;
+ key.data.int_val.value = 0;
+ key.data.int_val.is_negative = false;
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
+ } else if (strcmp(field_name, "signed") == 0) {
+ InternPoolKey key;
+ memset(&key, 0, sizeof(key));
+ key.tag = IP_KEY_INT;
+ key.data.int_val.ty = IP_INDEX_COMPTIME_INT_TYPE;
+ key.data.int_val.value = 1;
+ key.data.int_val.is_negative = false;
+ return AIR_REF_FROM_IP(ipIntern(sema->ip, key));
+ }
+
+ return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+}
+
// --- analyzeBodyInner ---
// Ported from src/Sema.zig analyzeBodyInner.
// Main dispatch loop: iterates over ZIR instructions in a body and
@@ -2292,12 +2591,15 @@ static bool analyzeBodyInner(
// extended: handle extended opcodes.
case ZIR_INST_EXTENDED: {
uint16_t opcode = sema->code.inst_datas[inst].extended.opcode;
+ AirInstRef air_ref;
if (opcode == ZIR_EXT_STRUCT_DECL) {
zirStructDecl(sema, block, inst);
+ air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
+ } else if (opcode == ZIR_EXT_REIFY) {
+ air_ref = zirReifyComptime(sema, inst);
+ } else {
+ air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
}
- // Map the extended instruction to void; full type
- // machinery is not yet implemented.
- AirInstRef air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
instMapPut(&sema->inst_map, inst, air_ref);
i++;
continue;
@@ -2608,17 +2910,150 @@ static bool analyzeBodyInner(
i++;
continue;
- // @TypeOf(operand) — handled by default (void) for now.
- // zirTypeof is used for cross-module comptime eval.
+ // @TypeOf(operand): resolve the type of the operand.
+ case ZIR_INST_TYPEOF:
+ instMapPut(&sema->inst_map, inst, zirTypeof(sema, inst));
+ i++;
+ continue;
- // @TypeOf builtin with block body — handled by default (void)
- // for now. Cross-module comptime eval uses this inline.
+ // @TypeOf builtin with block body: analyze body, then typeof.
+ // Uses a child block and saves/restores AIR state.
+ case ZIR_INST_TYPEOF_BUILTIN: {
+ ZirInstData data = sema->code.inst_datas[inst];
+ uint32_t payload_index = data.pl_node.payload_index;
+ uint32_t inner_body_len = sema->code.extra[payload_index];
+ const uint32_t* inner_body
+ = &sema->code.extra[payload_index + 1];
+
+ SemaBlock ct_block;
+ semaBlockInit(&ct_block, sema, block);
+ ct_block.is_comptime = true;
+ ct_block.is_typeof = true;
+ ct_block.inlining = block->inlining;
+
+ uint32_t saved_air_len = sema->air_inst_len;
+ uint32_t saved_extra_len = sema->air_extra_len;
+
+ bool completed = analyzeBodyInner(
+ sema, &ct_block, inner_body, inner_body_len);
+
+ sema->air_inst_len = saved_air_len;
+ sema->air_extra_len = saved_extra_len;
+
+ AirInstRef result;
+ if (!completed) {
+ uint32_t break_inst = sema->comptime_break_inst;
+ ZirInstData break_data
+ = sema->code.inst_datas[break_inst];
+ ZirInstRef operand = break_data.break_data.operand;
+ if (operand == ZIR_REF_NONE) {
+ result = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
+ } else {
+ AirInstRef resolved = resolveInst(sema, operand);
+ TypeIndex ty = semaTypeOf(sema, resolved);
+ result = AIR_REF_FROM_IP(ty);
+ }
+ } else {
+ result = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE);
+ }
+ semaBlockDeinit(&ct_block);
+ instMapPut(&sema->inst_map, inst, result);
+ i++;
+ continue;
+ }
+
+ // block_comptime: like block_inline but with extra reason word.
+ // Creates a temporary child block and saves/restores AIR state
+ // so comptime-internal instructions are not counted.
+ case ZIR_INST_BLOCK_COMPTIME: {
+ ZirInstData data = sema->code.inst_datas[inst];
+ uint32_t payload_index = data.pl_node.payload_index;
+ // Payload: reason (1 word), body_len (1 word), body...
+ uint32_t inner_body_len
+ = sema->code.extra[payload_index + 1];
+ const uint32_t* inner_body
+ = &sema->code.extra[payload_index + 2];
- // @typeInfo(type) — handled by default (void) for now.
- // zirTypeInfoComptime is used for cross-module comptime eval.
+ SemaBlock ct_block;
+ semaBlockInit(&ct_block, sema, block);
+ ct_block.is_comptime = true;
+ ct_block.inlining = block->inlining;
- // field_val — field access: handled by default (void) for now.
- // zirFieldValComptime is used for cross-module comptime eval.
+ // Save AIR state so comptime instructions are discarded.
+ uint32_t saved_air_len = sema->air_inst_len;
+ uint32_t saved_extra_len = sema->air_extra_len;
+
+ bool completed = analyzeBodyInner(
+ sema, &ct_block, inner_body, inner_body_len);
+
+ // Restore AIR state (discard comptime instructions).
+ sema->air_inst_len = saved_air_len;
+ sema->air_extra_len = saved_extra_len;
+
+ if (!completed) {
+ uint32_t break_inst = sema->comptime_break_inst;
+ ZirInstData break_data
+ = sema->code.inst_datas[break_inst];
+ ZirInstRef operand = break_data.break_data.operand;
+ AirInstRef result;
+ if (operand == ZIR_REF_NONE) {
+ result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE);
+ } else {
+ result = resolveInst(sema, operand);
+ }
+ instMapPut(&sema->inst_map, inst, result);
+ }
+ semaBlockDeinit(&ct_block);
+ i++;
+ continue;
+ }
+
+ // @typeInfo(type): comptime type info extraction.
+ case ZIR_INST_TYPE_INFO:
+ instMapPut(&sema->inst_map, inst,
+ zirTypeInfoComptime(sema, inst));
+ i++;
+ continue;
+
+ // field_val: comptime field access on type_info results.
+ case ZIR_INST_FIELD_VAL:
+ instMapPut(&sema->inst_map, inst,
+ zirFieldValComptime(sema, inst));
+ i++;
+ continue;
+
+ // Validation-only struct init instructions: no-op.
+ case ZIR_INST_VALIDATE_STRUCT_INIT_TY:
+ case ZIR_INST_VALIDATE_STRUCT_INIT_RESULT_TY:
+ case ZIR_INST_VALIDATE_PTR_STRUCT_INIT:
+ case ZIR_INST_STRUCT_INIT_FIELD_TYPE:
+ case ZIR_INST_STRUCT_INIT_FIELD_PTR:
+ instMapPut(&sema->inst_map, inst,
+ AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE));
+ i++;
+ continue;
+
+ // struct_init: comptime struct initialization.
+ case ZIR_INST_STRUCT_INIT:
+ instMapPut(&sema->inst_map, inst,
+ zirStructInitComptime(sema, inst));
+ i++;
+ continue;
+
+ // decl_literal: resolve enum/decl literals at comptime.
+ case ZIR_INST_DECL_LITERAL:
+ case ZIR_INST_DECL_LITERAL_NO_COERCE:
+ instMapPut(&sema->inst_map, inst,
+ zirDeclLiteralComptime(sema, inst));
+ i++;
+ continue;
+
+ // str: string literal — map to void in comptime context.
+ case ZIR_INST_STR:
+ instMapPut(&sema->inst_map, inst,
+ AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE));
+ i++;
+ continue;
// For all other instructions, produce a void mapping and skip.
// As handlers are implemented, they will replace this default.
diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig
@@ -102,7 +102,7 @@ const corpus_files = .{
"../lib/std/crypto/codecs.zig", // 165
"../lib/std/os/uefi/tables/table_header.zig", // 214
"../lib/std/zig/llvm.zig", // 247
- //"../lib/compiler_rt/neghf2.zig", // 265 -- cross-module ZIR loading works; needs comptime eval (reify, struct_init)
+ "../lib/compiler_rt/neghf2.zig", // 265 -- cross-module ZIR loading works; needs comptime eval (reify, struct_init)
//"../lib/compiler_rt/negxf2.zig", // 265 -- @export+func_fancy handled; body analysis incomplete
//"../lib/compiler_rt/absvdi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete
//"../lib/compiler_rt/absvsi2.zig", // 311 -- @export+func_fancy handled; body analysis incomplete