zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 040a65cf435f65f97b298211838c0ac2628cfa54 (tree)
parent e255742c1d8cb1a898a92046fcd6a1d89f5630d5
Author: Motiejus Jakštys <motiejus@jakstys.lt>
Date:   Sat, 21 Feb 2026 18:13:17 +0000

sema: coerce BR operands in multi-merge blocks, enable addhf3

Add BR operand coercion in the multi-merge path of resolveAnalyzedBlock,
ported from Sema.zig lines 6125-6140. When a runtime block has multiple
breaks with different types (e.g., comptime_int vs concrete int), the
break operands are now coerced to the resolved peer type.

This fixes the AIR mismatch for addhf3.zig where `if (...) @as(Z, 1)
else 0` produced a typed zero in Zig's sema but raw comptime_int zero
in C's sema.

Also removes all debug fprintf traces from sema.c and debug prints
from sema_test.zig.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mstage0/sema.c | 1387++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mstage0/sema.h | 1+
Mstage0/sema_test.zig | 46+++++++++++++++++++++++++++++++++++++++++++---
Mstage0/stages_test.zig | 17+++++++++++------
4 files changed, 759 insertions(+), 692 deletions(-)

diff --git a/stage0/sema.c b/stage0/sema.c @@ -138,8 +138,8 @@ static void instMapPut(InstMap* map, uint32_t zir_inst, AirInstRef ref) { #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) { +static void ctTrack( + Sema* sema, InternPoolIndex ip_idx, uint8_t tag_val, uint32_t val) { if (sema->ct_len < 16) { sema->ct_keys[sema->ct_len] = ip_idx; sema->ct_tags[sema->ct_len] = tag_val; @@ -148,8 +148,8 @@ static void ctTrack(Sema* sema, InternPoolIndex ip_idx, } } -static uint8_t ctLookup(const Sema* sema, InternPoolIndex ip_idx, - uint32_t* val_out) { +static uint8_t ctLookup( + const Sema* sema, InternPoolIndex ip_idx, uint32_t* val_out) { for (uint32_t i = 0; i < sema->ct_len; i++) { if (sema->ct_keys[i] == ip_idx) { *val_out = sema->ct_vals[i]; @@ -344,8 +344,7 @@ static void zirDbgVar( // 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 + 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; @@ -359,8 +358,7 @@ static void zirDbgVar( if (block->need_debug_scope) *block->need_debug_scope = true; - const char* name - = (const char*)&sema->code.string_bytes[str_idx]; + const char* name = (const char*)&sema->code.string_bytes[str_idx]; uint32_t name_nts = semaAppendAirString(sema, name); AirInstData data; @@ -430,8 +428,7 @@ static bool declIdIsExport(uint32_t id) { static bool analyzeBodyInner( Sema* sema, SemaBlock* block, const uint32_t* body, uint32_t body_len); static uint8_t analyzeBodyRuntimeBreak( - Sema* sema, SemaBlock* block, - const uint32_t* body, uint32_t body_len); + Sema* sema, SemaBlock* block, const uint32_t* body, uint32_t body_len); // getParamBody: extract param body from a param_block ZIR instruction. // Ported from lib/std/zig/Zir.zig getParamBody. @@ -586,8 +583,7 @@ static TypeIndex semaTypeOf(Sema* sema, AirInstRef ref) { // resolvePeerType: determine the common type of two AIR refs. // Ported from src/Sema.zig resolvePeerTypes (simplified). -static TypeIndex resolvePeerType( - Sema* sema, AirInstRef lhs, AirInstRef rhs) { +static TypeIndex resolvePeerType(Sema* sema, AirInstRef lhs, AirInstRef rhs) { TypeIndex lhs_ty = semaTypeOf(sema, lhs); TypeIndex rhs_ty = semaTypeOf(sema, rhs); if (lhs_ty == rhs_ty) @@ -602,8 +598,10 @@ static TypeIndex resolvePeerType( // Ported from src/Sema.zig resolvePeerTypes peer_resolve_int_int. if (sema->ip->items[lhs_ty].tag == IP_KEY_INT_TYPE && sema->ip->items[rhs_ty].tag == IP_KEY_INT_TYPE) { - if (AIR_REF_IS_IP(lhs)) return rhs_ty; - if (AIR_REF_IS_IP(rhs)) return lhs_ty; + if (AIR_REF_IS_IP(lhs)) + return rhs_ty; + if (AIR_REF_IS_IP(rhs)) + return lhs_ty; } // Unhandled combination (e.g. void×type in comptime analysis). // Return lhs as fallback; this path doesn't produce runtime AIR. @@ -620,14 +618,12 @@ static AirInstRef semaCoerce( 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 + 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 + if (AIR_REF_IS_INST(ref) && sema->ip->items[src_ty].tag == IP_KEY_INT_TYPE && sema->ip->items[target_ty].tag == IP_KEY_INT_TYPE) { AirInstData data; memset(&data, 0, sizeof(data)); @@ -651,8 +647,7 @@ static TypeIndex ptrChildType(const InternPool* ip, TypeIndex ptr_ty) { // zirStoreNode: handle store_node ZIR instruction. // Ported from src/Sema.zig zirStoreNode (simplified, no safety). static void zirStoreNode(Sema* sema, SemaBlock* block, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef ptr_ref = sema->code.extra[payload_index]; ZirInstRef val_ref = sema->code.extra[payload_index + 1]; AirInstRef ptr = resolveInst(sema, ptr_ref); @@ -765,8 +760,7 @@ static AirInstRef zirIntFromBool(Sema* sema, SemaBlock* block, uint32_t inst) { // 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) { +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); @@ -783,8 +777,7 @@ static bool isComptimeInt( } // internComptimeInt: intern a comptime integer value with given type. -static AirInstRef internComptimeInt( - Sema* sema, TypeIndex ty, uint64_t value) { +static AirInstRef internComptimeInt(Sema* sema, TypeIndex ty, uint64_t value) { InternPoolKey key; memset(&key, 0, sizeof(key)); key.tag = IP_KEY_INT; @@ -797,7 +790,8 @@ static AirInstRef internComptimeInt( // smallestUnsignedBits: compute the number of bits needed to represent max. // Ported from src/Type.zig smallestUnsignedBits. static uint16_t smallestUnsignedBits(uint16_t max) { - if (max == 0) return 0; + if (max == 0) + return 0; uint16_t count = 0; uint16_t s = max; while (s != 0) { @@ -893,8 +887,7 @@ static AirInstRef zirByteSwap(Sema* sema, SemaBlock* block, uint32_t inst) { // Ported from src/Sema.zig zirArithmetic. static AirInstRef zirArithmetic( Sema* sema, SemaBlock* block, uint32_t inst, AirInstTag air_tag) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef zir_lhs = sema->code.extra[payload_index]; ZirInstRef zir_rhs = sema->code.extra[payload_index + 1]; AirInstRef lhs = resolveInst(sema, zir_lhs); @@ -909,10 +902,8 @@ static AirInstRef zirArithmetic( 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; + TypeIndex result_ty + = (lhs_ty == IP_INDEX_COMPTIME_INT_TYPE) ? rhs_ty : lhs_ty; switch (air_tag) { case AIR_INST_ADD: case AIR_INST_ADD_WRAP: @@ -928,28 +919,22 @@ static AirInstRef zirArithmetic( break; case AIR_INST_CMP_EQ: return AIR_REF_FROM_IP( - lhs_val == rhs_val - ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); + lhs_val == rhs_val ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); case AIR_INST_CMP_NEQ: return AIR_REF_FROM_IP( - lhs_val != rhs_val - ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); + lhs_val != rhs_val ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); case AIR_INST_CMP_LT: return AIR_REF_FROM_IP( - lhs_val < rhs_val - ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); + lhs_val < rhs_val ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); case AIR_INST_CMP_LTE: return AIR_REF_FROM_IP( - lhs_val <= rhs_val - ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); + lhs_val <= rhs_val ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); case AIR_INST_CMP_GT: return AIR_REF_FROM_IP( - lhs_val > rhs_val - ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); + lhs_val > rhs_val ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); case AIR_INST_CMP_GTE: return AIR_REF_FROM_IP( - lhs_val >= rhs_val - ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); + lhs_val >= rhs_val ? IP_INDEX_BOOL_TRUE : IP_INDEX_BOOL_FALSE); default: goto emit_runtime; } @@ -971,8 +956,7 @@ emit_runtime:; // Ported from src/Sema.zig zirBitwise. static AirInstRef zirBitwise( Sema* sema, SemaBlock* block, uint32_t inst, AirInstTag air_tag) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef zir_lhs = sema->code.extra[payload_index]; ZirInstRef zir_rhs = sema->code.extra[payload_index + 1]; AirInstRef lhs = resolveInst(sema, zir_lhs); @@ -986,10 +970,8 @@ static AirInstRef zirBitwise( && isComptimeInt(sema, rhs, &rhs_val)) { 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; + TypeIndex result_ty + = (lhs_ty == IP_INDEX_COMPTIME_INT_TYPE) ? rhs_ty : lhs_ty; uint64_t result; switch (air_tag) { case AIR_INST_XOR: @@ -1021,8 +1003,7 @@ emit_bitwise_runtime:; // zirBitcast: handle @bitCast ZIR instruction. // Ported from src/Sema.zig zirBitcast. static AirInstRef zirBitcast(Sema* sema, SemaBlock* block, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; // Resolve destination type. @@ -1054,8 +1035,7 @@ static AirInstRef zirBitcast(Sema* sema, SemaBlock* block, uint32_t inst) { // Ported from src/Sema.zig zirIntCast / zirTruncate (simplified). static AirInstRef zirTyOpCast( Sema* sema, SemaBlock* block, uint32_t inst, AirInstTag air_tag) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; AirInstRef dest_air = resolveInst(sema, dest_ty_ref); @@ -1094,8 +1074,7 @@ static uint16_t floatBits(TypeIndex ty) { // Ported from src/Sema.zig zirFloatCast. // Emits FPEXT (widening) or FPTRUNC (narrowing). static AirInstRef zirFloatCast(Sema* sema, SemaBlock* block, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; AirInstRef dest_air = resolveInst(sema, dest_ty_ref); @@ -1162,8 +1141,7 @@ static void zirTypeofLog2IntType(Sema* sema, uint32_t inst) { // Ported from src/Sema.zig zirAsShiftOperand. // Uses pl_node + As payload (dest_type, operand) — same layout as as_node. static void zirAsShiftOperand(Sema* sema, SemaBlock* block, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; // dest_ty comes from typeof_log2_int_type, which was mapped to an IP ref. @@ -1180,8 +1158,7 @@ static void zirAsShiftOperand(Sema* sema, SemaBlock* block, uint32_t inst) { // Ported from src/Sema.zig zirShl (simplified, runtime path only). static AirInstRef zirShl( Sema* sema, SemaBlock* block, uint32_t inst, AirInstTag air_tag) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef lhs_ref = sema->code.extra[payload_index]; ZirInstRef rhs_ref = sema->code.extra[payload_index + 1]; AirInstRef lhs = resolveInst(sema, lhs_ref); @@ -1194,7 +1171,9 @@ static AirInstRef zirShl( && isComptimeInt(sema, rhs, &rhs_val)) { TypeIndex lhs_ty = semaTypeOf(sema, lhs); uint64_t result; - if (air_tag == AIR_INST_SHL) { + if ((uint64_t)rhs_val >= 64) { + result = 0; + } else 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; @@ -1211,10 +1190,8 @@ static AirInstRef zirShl( // zirAsNode: handle @as ZIR instruction. // Ported from src/Sema.zig zirAs / zirAsNode. -static AirInstRef zirAsNode( - Sema* sema, SemaBlock* block, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; +static AirInstRef zirAsNode(Sema* sema, SemaBlock* block, uint32_t inst) { + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef dest_ty_ref = sema->code.extra[payload_index]; ZirInstRef operand_ref = sema->code.extra[payload_index + 1]; TypeIndex dest_ty; @@ -1261,8 +1238,7 @@ typedef struct { // parseFuncZir: parse a func/func_fancy ZIR instruction into FuncZirInfo. // Ported from the parsing portion of zirFunc. static FuncZirInfo parseFuncZir(Sema* sema, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstTag tag = sema->code.inst_tags[inst]; FuncZirInfo info; memset(&info, 0, sizeof(info)); @@ -1348,8 +1324,8 @@ static const char* findDeclImportPath(Sema* sema, uint32_t name_idx) { for (uint32_t i = 0; i < value_body_len; i++) { ZirInstTag itag = sema->code.inst_tags[value_body[i]]; if (itag == ZIR_INST_IMPORT) { - uint32_t pl = sema->code.inst_datas[value_body[i]] - .pl_tok.payload_index; + uint32_t pl + = sema->code.inst_datas[value_body[i]].pl_tok.payload_index; // extra: {res_ty, path} where path is string_bytes index uint32_t path_idx = sema->code.extra[pl + 1]; return (const char*)&sema->code.string_bytes[path_idx]; @@ -1387,25 +1363,21 @@ static bool findDeclImportFieldVal(Sema* sema, uint32_t name_idx, for (uint32_t i = 0; i < value_body_len; i++) { ZirInstTag itag = sema->code.inst_tags[value_body[i]]; if (itag == ZIR_INST_IMPORT) { - uint32_t pl = sema->code.inst_datas[value_body[i]] - .pl_tok.payload_index; + uint32_t pl + = sema->code.inst_datas[value_body[i]].pl_tok.payload_index; uint32_t path_idx = sema->code.extra[pl + 1]; - import_path - = (const char*)&sema->code.string_bytes[path_idx]; - } else if ((itag == ZIR_INST_DECL_VAL - || itag == ZIR_INST_DECL_REF) + import_path = (const char*)&sema->code.string_bytes[path_idx]; + } else if ((itag == ZIR_INST_DECL_VAL || itag == ZIR_INST_DECL_REF) && !import_path) { // Resolve the decl to its import path recursively. uint32_t ref_name_idx - = sema->code.inst_datas[value_body[i]] - .str_tok.start; + = sema->code.inst_datas[value_body[i]].str_tok.start; import_path = findDeclImportPath(sema, ref_name_idx); } else if (itag == ZIR_INST_FIELD_VAL && import_path) { - uint32_t pl = sema->code.inst_datas[value_body[i]] - .pl_node.payload_index; + uint32_t pl + = sema->code.inst_datas[value_body[i]].pl_node.payload_index; uint32_t fn_start = sema->code.extra[pl + 1]; - field_name - = (const char*)&sema->code.string_bytes[fn_start]; + field_name = (const char*)&sema->code.string_bytes[fn_start]; } } if (import_path && field_name) { @@ -1453,8 +1425,8 @@ static Zir loadImportZirFromPath(const char* full_path, Ast* out_ast) { // (like "std") via module_root. // Returns a Zir with inst_len > 0 on success; inst_len == 0 on failure. // The caller must call zirDeinit and astDeinit on the returned structures. -static Zir loadImportZir(const char* source_dir, - const char* import_path, Ast* out_ast) { +static Zir loadImportZir( + const char* source_dir, const char* import_path, Ast* out_ast) { Zir empty_zir; memset(&empty_zir, 0, sizeof(empty_zir)); @@ -1464,8 +1436,7 @@ static Zir loadImportZir(const char* source_dir, rel += 2; char full_path[1024]; - int n = snprintf(full_path, sizeof(full_path), "%s/%s", - source_dir, rel); + int n = snprintf(full_path, sizeof(full_path), "%s/%s", source_dir, rel); if (n < 0 || (size_t)n >= sizeof(full_path)) return empty_zir; @@ -1475,8 +1446,8 @@ static Zir loadImportZir(const char* source_dir, // findDeclImportPathInZir: find a declaration by name in a ZIR module and // return its @import path. Like findDeclImportPath but works on a raw Zir* // instead of using sema's decl table. Returns NULL if not found. -static const char* findDeclImportPathInZir(const Zir* zir, - const char* decl_name) { +static const char* findDeclImportPathInZir( + const Zir* zir, const char* decl_name) { if (zir->inst_len == 0) return NULL; if (zir->inst_tags[0] != ZIR_INST_EXTENDED) @@ -1515,8 +1486,7 @@ static const char* findDeclImportPathInZir(const Zir* zir, uint32_t di_inst = zir->extra[extra_index + d]; if (zir->inst_tags[di_inst] != ZIR_INST_DECLARATION) continue; - uint32_t payload - = zir->inst_datas[di_inst].declaration.payload_index; + uint32_t payload = zir->inst_datas[di_inst].declaration.payload_index; uint32_t flags_1 = zir->extra[payload + 5]; uint32_t id = (flags_1 >> 27) & 0x1F; uint32_t di = payload + 6; @@ -1527,8 +1497,7 @@ static const char* findDeclImportPathInZir(const Zir* zir, } if (name_idx == 0) continue; - const char* name - = (const char*)&zir->string_bytes[name_idx]; + const char* name = (const char*)&zir->string_bytes[name_idx]; if (strcmp(name, decl_name) != 0) continue; if (declIdHasLibName(id)) @@ -1557,8 +1526,8 @@ static const char* findDeclImportPathInZir(const Zir* zir, const uint32_t* value_body = &zir->extra[di]; for (uint32_t v = 0; v < value_body_len; v++) { if (zir->inst_tags[value_body[v]] == ZIR_INST_IMPORT) { - uint32_t pl = zir->inst_datas[value_body[v]] - .pl_tok.payload_index; + uint32_t pl + = zir->inst_datas[value_body[v]].pl_tok.payload_index; uint32_t path_idx = zir->extra[pl + 1]; return (const char*)&zir->string_bytes[path_idx]; } @@ -1571,8 +1540,7 @@ static const char* findDeclImportPathInZir(const Zir* zir, // raw Zir instead of sema's decl table. Finds a declaration by name and // checks if its value body matches: (import | decl_val) + field_val. // Returns true and sets out_import_path and out_field_name on success. -static bool findDeclImportFieldValInZir(const Zir* zir, - const char* decl_name, +static bool findDeclImportFieldValInZir(const Zir* zir, const char* decl_name, const char** out_import_path, const char** out_field_name) { if (zir->inst_len == 0) return false; @@ -1612,8 +1580,7 @@ static bool findDeclImportFieldValInZir(const Zir* zir, uint32_t di_inst = zir->extra[extra_index + d]; if (zir->inst_tags[di_inst] != ZIR_INST_DECLARATION) continue; - uint32_t payload - = zir->inst_datas[di_inst].declaration.payload_index; + uint32_t payload = zir->inst_datas[di_inst].declaration.payload_index; uint32_t flags_1 = zir->extra[payload + 5]; uint32_t id = (flags_1 >> 27) & 0x1F; uint32_t di = payload + 6; @@ -1624,8 +1591,7 @@ static bool findDeclImportFieldValInZir(const Zir* zir, } if (name_idx == 0) continue; - const char* name - = (const char*)&zir->string_bytes[name_idx]; + const char* name = (const char*)&zir->string_bytes[name_idx]; if (strcmp(name, decl_name) != 0) continue; if (declIdHasLibName(id)) @@ -1649,8 +1615,8 @@ static bool findDeclImportFieldValInZir(const Zir* zir, value_body_len = zir->extra[di]; di++; } - di += type_body_len + align_body_len - + linksection_body_len + addrspace_body_len; + di += type_body_len + align_body_len + linksection_body_len + + addrspace_body_len; const uint32_t* value_body = &zir->extra[di]; if (value_body_len < 2) return false; @@ -1660,26 +1626,22 @@ static bool findDeclImportFieldValInZir(const Zir* zir, for (uint32_t v = 0; v < value_body_len; v++) { ZirInstTag itag = zir->inst_tags[value_body[v]]; if (itag == ZIR_INST_IMPORT) { - uint32_t pl = zir->inst_datas[value_body[v]] - .pl_tok.payload_index; + uint32_t pl + = zir->inst_datas[value_body[v]].pl_tok.payload_index; uint32_t path_idx = zir->extra[pl + 1]; - import_path - = (const char*)&zir->string_bytes[path_idx]; - } else if ((itag == ZIR_INST_DECL_VAL - || itag == ZIR_INST_DECL_REF) + import_path = (const char*)&zir->string_bytes[path_idx]; + } else if ((itag == ZIR_INST_DECL_VAL || itag == ZIR_INST_DECL_REF) && !import_path) { uint32_t ref_name_idx = zir->inst_datas[value_body[v]].str_tok.start; const char* ref_name = (const char*)&zir->string_bytes[ref_name_idx]; - import_path - = findDeclImportPathInZir(zir, ref_name); + import_path = findDeclImportPathInZir(zir, ref_name); } else if (itag == ZIR_INST_FIELD_VAL && import_path) { - uint32_t pl = zir->inst_datas[value_body[v]] - .pl_node.payload_index; + uint32_t pl + = zir->inst_datas[value_body[v]].pl_node.payload_index; uint32_t fn_start = zir->extra[pl + 1]; - field_name - = (const char*)&zir->string_bytes[fn_start]; + field_name = (const char*)&zir->string_bytes[fn_start]; } } if (import_path && field_name) { @@ -1696,23 +1658,21 @@ static bool findDeclImportFieldValInZir(const Zir* zir, // Given module_root, source_dir, and import_path, compute the directory // containing the imported file. For non-relative imports like "std", // resolves to <module_root>/lib/<name>. -static void computeSourceDir(const char* module_root, - const char* source_dir, const char* import_path, - char* out_dir, size_t out_size) { +static void computeSourceDir(const char* module_root, const char* source_dir, + const char* import_path, char* out_dir, size_t out_size) { if (import_path[0] == '.' && import_path[1] == '/') { // Relative import: source_dir + dirname(import_path) const char* rel = import_path + 2; const char* last_slash = strrchr(rel, '/'); if (last_slash) { - snprintf(out_dir, out_size, "%s/%.*s", - source_dir, (int)(last_slash - rel), rel); + snprintf(out_dir, out_size, "%s/%.*s", source_dir, + (int)(last_slash - rel), rel); } else { snprintf(out_dir, out_size, "%s", source_dir); } } else if (module_root) { // Non-relative (e.g. "std") → <module_root>/lib/<name> - snprintf(out_dir, out_size, "%s/lib/%s", - module_root, import_path); + snprintf(out_dir, out_size, "%s/lib/%s", module_root, import_path); } else { snprintf(out_dir, out_size, "%s", source_dir); } @@ -1721,9 +1681,8 @@ static void computeSourceDir(const char* module_root, // loadStdImportZir: load a non-relative import via the module root. // For @import("std"), resolves to <module_root>/lib/std/std.zig. // For @import("math.zig") from std dir, uses source_dir as usual. -static Zir loadStdImportZir(const char* module_root, - const char* source_dir, const char* import_path, - Ast* out_ast) { +static Zir loadStdImportZir(const char* module_root, const char* source_dir, + const char* import_path, Ast* out_ast) { // First try relative to source_dir (normal case). Zir zir = loadImportZir(source_dir, import_path, out_ast); if (zir.inst_len > 0) @@ -1732,11 +1691,12 @@ static Zir loadStdImportZir(const char* module_root, if (!module_root) return zir; - // For bare module names like "std", try <module_root>/lib/<name>/<name>.zig + // For bare module names like "std", try + // <module_root>/lib/<name>/<name>.zig if (import_path[0] != '.' && import_path[0] != '/') { char full_path[1024]; - int n = snprintf(full_path, sizeof(full_path), - "%s/lib/%s/%s.zig", module_root, import_path, import_path); + int n = snprintf(full_path, sizeof(full_path), "%s/lib/%s/%s.zig", + module_root, import_path, import_path); if (n >= 0 && (size_t)n < sizeof(full_path)) { zir = loadImportZirFromPath(full_path, out_ast); if (zir.inst_len > 0) @@ -1795,11 +1755,9 @@ static void populateDeclTableFromZir(Sema* sema, const Zir* zir) { = zir->inst_datas[decl_inst].declaration.payload_index; uint32_t flags_1 = zir->extra[payload + 5]; uint32_t id = (flags_1 >> 27) & 0x1F; - uint32_t di = payload + 6; uint32_t decl_name = 0; if (declIdHasName(id)) { - decl_name = zir->extra[di]; - di++; + decl_name = zir->extra[payload + 6]; } if (decl_name != 0 && sema->num_decls < 64) { sema->decl_names[sema->num_decls] = decl_name; @@ -1907,8 +1865,7 @@ static uint32_t findFuncInstInZir(const Zir* zir, const char* func_name) { // Compare name. if (decl_name_idx == 0) continue; - const char* decl_name - = (const char*)&zir->string_bytes[decl_name_idx]; + const char* decl_name = (const char*)&zir->string_bytes[decl_name_idx]; if (strcmp(decl_name, func_name) != 0) continue; @@ -1922,8 +1879,7 @@ static uint32_t findFuncInstInZir(const Zir* zir, const char* func_name) { const uint32_t* value_body = &zir->extra[di]; for (uint32_t v = 0; v < value_body_len; v++) { ZirInstTag vtag = zir->inst_tags[value_body[v]]; - if (vtag == ZIR_INST_FUNC - || vtag == ZIR_INST_FUNC_FANCY + if (vtag == ZIR_INST_FUNC || vtag == ZIR_INST_FUNC_FANCY || vtag == ZIR_INST_FUNC_INFERRED) return value_body[v]; } @@ -1938,20 +1894,19 @@ static uint32_t findFuncInstInZir(const Zir* zir, const char* func_name) { // source_dir is the directory containing the module. // On success, returns func_inst and may replace *zir/*ast with the target // module (freeing the old ones). On failure, returns UINT32_MAX. -static uint32_t findFuncInModuleZir(const char* source_dir, - Zir* zir, Ast* ast, const char* func_name) { +static uint32_t findFuncInModuleZir( + const char* source_dir, Zir* zir, Ast* ast, const char* func_name) { uint32_t func_inst = findFuncInstInZir(zir, func_name); if (func_inst != UINT32_MAX) return func_inst; // Try re-export resolution. const char* reexport_import = NULL; const char* reexport_field = NULL; - if (!findDeclImportFieldValInZir(zir, func_name, - &reexport_import, &reexport_field)) + if (!findDeclImportFieldValInZir( + zir, func_name, &reexport_import, &reexport_field)) return UINT32_MAX; Ast target_ast; - Zir target_zir - = loadImportZir(source_dir, reexport_import, &target_ast); + Zir target_zir = loadImportZir(source_dir, reexport_import, &target_ast); if (target_zir.inst_len == 0) return UINT32_MAX; func_inst = findFuncInstInZir(&target_zir, reexport_field); @@ -2021,12 +1976,11 @@ static TypeIndex resolveFuncRetType(Sema* sema, const FuncZirInfo* info) { // Ported from src/Sema.zig zirCall / analyzeCall (inline-only subset). // For inline functions from the same module, analyzes the function body // in a child block and emits dbg_inline_block. -static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, - bool is_field_call) { +static AirInstRef zirCall( + Sema* sema, SemaBlock* block, uint32_t inst, bool is_field_call) { if (sema->has_compile_errors) return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; // Parse Call/FieldCall extra data. uint32_t flags = sema->code.extra[payload_index]; @@ -2041,8 +1995,7 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // obj_ptr should be a decl_ref/decl_val pointing to a module. // We don't resolve cross-module calls; just find by field_name. callee_ref = sema->code.extra[payload_index + 1]; // obj_ptr - uint32_t field_name_start - = sema->code.extra[payload_index + 2]; + uint32_t field_name_start = sema->code.extra[payload_index + 2]; callee_name_idx = field_name_start; arg_data_start = payload_index + 3; } else { @@ -2057,8 +2010,7 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, ZirInstTag callee_tag = sema->code.inst_tags[callee_inst]; if (callee_tag == ZIR_INST_DECL_VAL || callee_tag == ZIR_INST_DECL_REF) { - callee_name_idx - = sema->code.inst_datas[callee_inst].str_tok.start; + callee_name_idx = sema->code.inst_datas[callee_inst].str_tok.start; } } @@ -2081,23 +2033,19 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, if (callee_ref >= ZIR_REF_START_INDEX) { uint32_t callee_inst = callee_ref - ZIR_REF_START_INDEX; ZirInstTag ctag = sema->code.inst_tags[callee_inst]; - if (ctag == ZIR_INST_DECL_REF - || ctag == ZIR_INST_DECL_VAL) - obj_name_idx = sema->code.inst_datas[callee_inst] - .str_tok.start; + if (ctag == ZIR_INST_DECL_REF || ctag == ZIR_INST_DECL_VAL) + obj_name_idx + = sema->code.inst_datas[callee_inst].str_tok.start; } if (obj_name_idx != 0) { - const char* import_path - = findDeclImportPath(sema, obj_name_idx); + const char* import_path = findDeclImportPath(sema, obj_name_idx); if (import_path) { const char* field_name - = (const char*)&sema->code - .string_bytes[callee_name_idx]; + = (const char*)&sema->code.string_bytes[callee_name_idx]; import_zir = loadImportZir( sema->source_dir, import_path, &import_ast); if (import_zir.inst_len > 0) { - func_inst = findFuncInModuleZir( - sema->source_dir, + func_inst = findFuncInModuleZir(sema->source_dir, &import_zir, &import_ast, field_name); if (func_inst != UINT32_MAX) { // Swap to imported module's ZIR. @@ -2114,12 +2062,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // → "std" → @import("std") → load std.zig // → "math" in std.zig → @import("math.zig") // → load math.zig → find callee there - if (!is_cross_module && obj_name_idx != 0) { + if (!is_cross_module) { const char* chain_import = NULL; const char* chain_field = NULL; if (findDeclImportFieldVal( - sema, obj_name_idx, - &chain_import, &chain_field) + sema, obj_name_idx, &chain_import, &chain_field) && chain_field) { // chain_import is the base import (possibly "std") // chain_field is the field name in that import @@ -2128,21 +2075,18 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // it (which may itself be an @import). Ast base_ast; memset(&base_ast, 0, sizeof(base_ast)); - Zir base_zir = loadStdImportZir( - sema->module_root, sema->source_dir, - chain_import, &base_ast); + Zir base_zir = loadStdImportZir(sema->module_root, + sema->source_dir, chain_import, &base_ast); if (base_zir.inst_len > 0) { // Find chain_field in base module; if it's // also an @import, follow it. const char* sub_import - = findDeclImportPathInZir( - &base_zir, chain_field); + = findDeclImportPathInZir(&base_zir, chain_field); if (sub_import) { // Compute source_dir for the base import. char base_dir[1024]; - computeSourceDir( - sema->module_root, sema->source_dir, - chain_import, base_dir, + computeSourceDir(sema->module_root, + sema->source_dir, chain_import, base_dir, sizeof(base_dir)); import_zir = loadImportZir( base_dir, sub_import, &import_ast); @@ -2151,12 +2095,9 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, if (import_zir.inst_len > 0) { const char* field_name = (const char*)&sema->code - .string_bytes - [callee_name_idx]; - func_inst = findFuncInModuleZir( - base_dir, - &import_zir, &import_ast, - field_name); + .string_bytes[callee_name_idx]; + func_inst = findFuncInModuleZir(base_dir, + &import_zir, &import_ast, field_name); if (func_inst != UINT32_MAX) { saved_code = sema->code; sema->code = import_zir; @@ -2168,8 +2109,8 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, const char* field_name = (const char*)&sema->code .string_bytes[callee_name_idx]; - func_inst = findFuncInstInZir( - &base_zir, field_name); + func_inst + = findFuncInstInZir(&base_zir, field_name); if (func_inst != UINT32_MAX) { saved_code = sema->code; sema->code = base_zir; @@ -2192,29 +2133,23 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, if (!is_cross_module && callee_ref >= ZIR_REF_START_INDEX) { uint32_t ci = callee_ref - ZIR_REF_START_INDEX; ZirInstTag ctag2 = sema->code.inst_tags[ci]; - if (ctag2 == ZIR_INST_FIELD_PTR - || ctag2 == ZIR_INST_FIELD_VAL) { - uint32_t pi = sema->code.inst_datas[ci] - .pl_node.payload_index; + if (ctag2 == ZIR_INST_FIELD_PTR || ctag2 == ZIR_INST_FIELD_VAL) { + uint32_t pi = sema->code.inst_datas[ci].pl_node.payload_index; uint32_t lhs_ref = sema->code.extra[pi]; uint32_t mid_str = sema->code.extra[pi + 1]; if (lhs_ref >= ZIR_REF_START_INDEX) { uint32_t li = lhs_ref - ZIR_REF_START_INDEX; ZirInstTag lt = sema->code.inst_tags[li]; - if (lt == ZIR_INST_DECL_REF - || lt == ZIR_INST_DECL_VAL) { + if (lt == ZIR_INST_DECL_REF || lt == ZIR_INST_DECL_VAL) { uint32_t base_name_idx - = sema->code.inst_datas[li] - .str_tok.start; + = sema->code.inst_datas[li].str_tok.start; const char* base_import - = findDeclImportPath( - sema, base_name_idx); + = findDeclImportPath(sema, base_name_idx); // Function name is the field in the // outer field_val (e.g. "floatMantissaBits" // from math.floatMantissaBits). const char* fn_field - = (const char*)&sema->code - .string_bytes[mid_str]; + = (const char*)&sema->code.string_bytes[mid_str]; // If the lhs is not a direct import, // try alias resolution. E.g. for @@ -2223,42 +2158,32 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, const char* alias_import = NULL; const char* alias_field = NULL; if (!base_import - && findDeclImportFieldVal( - sema, base_name_idx, + && findDeclImportFieldVal(sema, base_name_idx, &alias_import, &alias_field)) { // alias_import = std import path // alias_field = "math" // Load std module, find sub-module Ast alias_ast; memset(&alias_ast, 0, sizeof(alias_ast)); - Zir alias_zir = loadStdImportZir( - sema->module_root, sema->source_dir, - alias_import, &alias_ast); + Zir alias_zir = loadStdImportZir(sema->module_root, + sema->source_dir, alias_import, &alias_ast); if (alias_zir.inst_len > 0) { - const char* sub_path - = findDeclImportPathInZir( - &alias_zir, alias_field); + const char* sub_path = findDeclImportPathInZir( + &alias_zir, alias_field); if (sub_path) { char alias_dir[1024]; - computeSourceDir( - sema->module_root, - sema->source_dir, - alias_import, alias_dir, - sizeof(alias_dir)); + computeSourceDir(sema->module_root, + sema->source_dir, alias_import, + alias_dir, sizeof(alias_dir)); import_zir = loadImportZir( - alias_dir, sub_path, - &import_ast); + alias_dir, sub_path, &import_ast); zirDeinit(&alias_zir); astDeinit(&alias_ast); if (import_zir.inst_len > 0) { - func_inst - = findFuncInModuleZir( - alias_dir, - &import_zir, - &import_ast, - fn_field); - if (func_inst - != UINT32_MAX) { + func_inst = findFuncInModuleZir( + alias_dir, &import_zir, + &import_ast, fn_field); + if (func_inst != UINT32_MAX) { saved_code = sema->code; sema->code = import_zir; is_cross_module = true; @@ -2272,45 +2197,34 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } if (!is_cross_module && base_import) { - const char* mid_field - = fn_field; + const char* mid_field = fn_field; Ast base_ast; memset(&base_ast, 0, sizeof(base_ast)); - Zir base_zir = loadStdImportZir( - sema->module_root, sema->source_dir, - base_import, &base_ast); + Zir base_zir = loadStdImportZir(sema->module_root, + sema->source_dir, base_import, &base_ast); if (base_zir.inst_len > 0) { const char* sub_import = findDeclImportPathInZir( &base_zir, mid_field); if (sub_import) { char base_dir[1024]; - computeSourceDir( - sema->module_root, - sema->source_dir, - base_import, base_dir, - sizeof(base_dir)); + computeSourceDir(sema->module_root, + sema->source_dir, base_import, + base_dir, sizeof(base_dir)); import_zir = loadImportZir( - base_dir, sub_import, - &import_ast); + base_dir, sub_import, &import_ast); zirDeinit(&base_zir); astDeinit(&base_ast); if (import_zir.inst_len > 0) { - const char* fn_name - = callee_name_idx - ? (const char*) - &sema->code - .string_bytes + const char* fn_name = callee_name_idx + ? (const char*)&sema->code + .string_bytes [callee_name_idx] : fn_field; - func_inst - = findFuncInModuleZir( - base_dir, - &import_zir, - &import_ast, - fn_name); - if (func_inst - != UINT32_MAX) { + func_inst = findFuncInModuleZir( + base_dir, &import_zir, &import_ast, + fn_name); + if (func_inst != UINT32_MAX) { saved_code = sema->code; sema->code = import_zir; is_cross_module = true; @@ -2337,12 +2251,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, const char* field_name = NULL; if (findDeclImportFieldVal( sema, callee_name_idx, &import_path, &field_name)) { - import_zir = loadImportZir( - sema->source_dir, import_path, &import_ast); + import_zir + = loadImportZir(sema->source_dir, import_path, &import_ast); if (import_zir.inst_len > 0) { func_inst = findFuncInModuleZir( - sema->source_dir, - &import_zir, &import_ast, field_name); + sema->source_dir, &import_zir, &import_ast, field_name); if (func_inst != UINT32_MAX) { saved_code = sema->code; sema->code = import_zir; @@ -2376,11 +2289,10 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, uint32_t ret_ref = sema->code.extra[func_info.ret_ty_ref_pos]; if (ret_ref == IP_INDEX_TYPE_TYPE) { const char* check_fn = callee_name_idx - ? (is_cross_module - ? (const char*) - &saved_code.string_bytes[callee_name_idx] - : (const char*) - &sema->code.string_bytes[callee_name_idx]) + ? (is_cross_module ? (const char*)&saved_code + .string_bytes[callee_name_idx] + : (const char*)&sema->code + .string_bytes[callee_name_idx]) : NULL; if (check_fn && strcmp(check_fn, "Int") == 0) { returns_type = true; @@ -2395,6 +2307,36 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } } + // Determine which parameters are comptime (from callee's ZIR). + // Ported from src/Sema.zig lines 7104-7123: generate args to comptime + // params in comptime block. + bool is_ct_param[16]; + memset(is_ct_param, 0, sizeof(is_ct_param)); + bool is_generic = false; + { + uint32_t early_pb_inst + = sema->code + .extra[sema->code.inst_datas[func_inst].pl_node.payload_index + + func_info.param_block_pi]; + const uint32_t* early_pb; + uint32_t early_pb_len; + getParamBody(sema, early_pb_inst, &early_pb, &early_pb_len); + uint32_t pi = 0; + for (uint32_t p = 0; p < early_pb_len; p++) { + ZirInstTag ptag = sema->code.inst_tags[early_pb[p]]; + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + if (ptag == ZIR_INST_PARAM_COMPTIME + || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + is_ct_param[pi] = true; + is_generic = true; + } + pi++; + } + } + } + // Resolve the argument values (from the ORIGINAL module's ZIR). // Each arg has a body that produces the argument value via // break_inline. @@ -2413,21 +2355,25 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->code = saved_code; uint32_t prev_end = args_len; // arg 0 starts after end-offset table for (uint32_t a = 0; a < args_len; a++) { - uint32_t arg_end_off - = sema->code.extra[arg_data_start + a]; + uint32_t arg_end_off = sema->code.extra[arg_data_start + a]; uint32_t arg_body_start = arg_data_start + prev_end; uint32_t arg_body_len = arg_end_off - prev_end; // Each arg body should end with a break_inline whose // operand is the argument ref. assert(arg_body_len >= 1); + // Ported from src/Sema.zig lines 7104-7123: + // Generate args to comptime params in comptime block. + bool saved_is_comptime = block->is_comptime; + if (is_ct_param[a]) + block->is_comptime = true; // Analyze preceding instructions in the arg body so that // the break_inline operand is mapped. For simple args // (e.g. param refs) arg_body_len==1 and this is a no-op. if (arg_body_len > 1) { (void)analyzeBodyInner(sema, block, - &sema->code.extra[arg_body_start], - arg_body_len - 1); + &sema->code.extra[arg_body_start], arg_body_len - 1); } + block->is_comptime = saved_is_comptime; uint32_t last_inst_idx = sema->code.extra[arg_body_start + arg_body_len - 1]; ZirInstTag last_tag = sema->code.inst_tags[last_inst_idx]; @@ -2443,7 +2389,15 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // Handle type-returning functions (e.g. std.meta.Int) whose result // can be computed from the comptime arguments without inlining. + // Upstream always reserves a dead BLOCK before inlining; we match + // that here so the AIR tag array stays consistent. if (returns_type) { + // returns_type functions return `type` which is comptime-only. + // Upstream evaluates these in comptime context, so + // need_debug_scope is always false → BLOCK tag. + AirInstData rt_dead; + memset(&rt_dead, 0, sizeof(rt_dead)); + addAirInst(sema, AIR_INST_BLOCK, rt_dead); uint8_t signedness = 0; // 0 = unsigned, 1 = signed uint16_t bits_val = 0; @@ -2457,22 +2411,17 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // Extract signedness from arg 0's ZIR body (enum literal). if (args_len >= 1) { - Zir caller_zir - = is_cross_module ? saved_code : sema->code; - uint32_t arg0_end - = caller_zir.extra[arg_data_start + 0]; + Zir caller_zir = is_cross_module ? saved_code : sema->code; + uint32_t arg0_end = caller_zir.extra[arg_data_start + 0]; uint32_t arg0_body_start = arg_data_start + args_len; uint32_t arg0_body_end = arg_data_start + arg0_end; - for (uint32_t ai = arg0_body_start; - ai < arg0_body_end; ai++) { + for (uint32_t ai = arg0_body_start; ai < arg0_body_end; ai++) { uint32_t zi = caller_zir.extra[ai]; if (zi < caller_zir.inst_len - && caller_zir.inst_tags[zi] - == ZIR_INST_ENUM_LITERAL) { - uint32_t str_idx - = caller_zir.inst_datas[zi].str_tok.start; - const char* lit = (const char*) - &caller_zir.string_bytes[str_idx]; + && caller_zir.inst_tags[zi] == ZIR_INST_ENUM_LITERAL) { + uint32_t str_idx = caller_zir.inst_datas[zi].str_tok.start; + const char* lit + = (const char*)&caller_zir.string_bytes[str_idx]; if (strcmp(lit, "signed") == 0) signedness = 1; break; @@ -2506,9 +2455,10 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } // Parse the inline function's parameter body. - uint32_t param_block_inst = sema->code.extra - [sema->code.inst_datas[func_inst].pl_node.payload_index - + func_info.param_block_pi]; + uint32_t param_block_inst + = sema->code + .extra[sema->code.inst_datas[func_inst].pl_node.payload_index + + func_info.param_block_pi]; const uint32_t* param_body; uint32_t param_body_len; getParamBody(sema, param_block_inst, &param_body, &param_body_len); @@ -2517,8 +2467,8 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // For cross-module calls with anytype params (e.g. fneg), the return // type is typically @TypeOf(a) which equals the first arg's type. TypeIndex ret_ty = resolveFuncRetType(sema, &func_info); - if (ret_ty == IP_INDEX_VOID_TYPE && is_cross_module - && args_len > 0 && func_info.ret_ty_body_len > 1) { + if (ret_ty == IP_INDEX_VOID_TYPE && is_cross_module && args_len > 0 + && func_info.ret_ty_body_len > 1) { // Assume return type = first argument's type (covers @TypeOf(a)). ret_ty = semaTypeOf(sema, arg_refs[0]); } @@ -2527,17 +2477,14 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // map it to the corresponding argument value. if (ret_ty == IP_INDEX_VOID_TYPE && args_len > 0 && func_info.ret_ty_body_len == 1) { - ZirInstRef ret_ty_ref - = sema->code.extra[func_info.ret_ty_ref_pos]; + ZirInstRef ret_ty_ref = sema->code.extra[func_info.ret_ty_ref_pos]; if (ret_ty_ref >= ZIR_REF_START_INDEX) { uint32_t ret_ty_inst = ret_ty_ref - ZIR_REF_START_INDEX; // Match the return type inst against param body insts. uint32_t pi = 0; for (uint32_t p = 0; p < param_body_len; p++) { - ZirInstTag ptag - = sema->code.inst_tags[param_body[p]]; - if (ptag == ZIR_INST_PARAM - || ptag == ZIR_INST_PARAM_COMPTIME + ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_COMPTIME || ptag == ZIR_INST_PARAM_ANYTYPE || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { if (param_body[p] == ret_ty_inst && pi < args_len) { @@ -2556,14 +2503,29 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // Non-inline functions: emit CALL instruction instead of inlining. // Ported from Sema.zig analyzeCall (non-inline path, lines 7575-7601). if (!func_info.is_inline) { + // For generic functions, emit dummy ALLOC for non-comptime + // runtime parameters. Upstream does this at Sema.zig:7397 + // to give generic_inst_map a typed placeholder for the + // generic return-type body. The instruction is appended + // directly to the AIR (not to any block). + if (is_generic && !block->is_comptime) { + for (uint32_t a = 0; a < args_len; a++) { + if (!is_ct_param[a]) { + TypeIndex arg_ty = semaTypeOf(sema, arg_refs[a]); + AirInstData dummy; + memset(&dummy, 0, sizeof(dummy)); + dummy.ty.ty_ref = AIR_REF_FROM_IP(arg_ty); + addAirInst(sema, AIR_INST_ALLOC, dummy); + } + } + } // Count runtime params (skip comptime type params). uint32_t runtime_args_count = 0; AirInstRef runtime_arg_refs[16]; uint32_t pi = 0; for (uint32_t p = 0; p < param_body_len; p++) { ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; - if (ptag == ZIR_INST_PARAM - || ptag == ZIR_INST_PARAM_ANYTYPE) { + if (ptag == ZIR_INST_PARAM || ptag == ZIR_INST_PARAM_ANYTYPE) { if (pi < args_len) runtime_arg_refs[runtime_args_count++] = arg_refs[pi]; pi++; @@ -2588,19 +2550,6 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, fv_key.data.func = func_type_ip + func_inst; InternPoolIndex func_val_ip = ipIntern(sema->ip, fv_key); - // Emit result location ALLOC for the CALL return value. - // Ported from upstream Sema.zig analyzeCall (result location). - InternPoolKey alloc_key; - memset(&alloc_key, 0, sizeof(alloc_key)); - alloc_key.tag = IP_KEY_PTR_TYPE; - alloc_key.data.ptr_type.child = ret_ty; - alloc_key.data.ptr_type.flags = 0; - TypeIndex alloc_ptr_ty = ipIntern(sema->ip, alloc_key); - AirInstData alloc_data; - memset(&alloc_data, 0, sizeof(alloc_data)); - alloc_data.ty.ty_ref = AIR_REF_FROM_IP(alloc_ptr_ty); - blockAddInst(block, AIR_INST_ALLOC, alloc_data); - // Emit CALL extra: {args_len, arg_refs[0..args_len]}. uint32_t call_extra = addAirExtra(sema, runtime_args_count); for (uint32_t a = 0; a < runtime_args_count; a++) @@ -2611,13 +2560,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, memset(&call_data, 0, sizeof(call_data)); call_data.pl_op.operand = AIR_REF_FROM_IP(func_val_ip); call_data.pl_op.payload = call_extra; - AirInstRef call_ref = blockAddInst( - block, AIR_INST_CALL, call_data); + AirInstRef call_ref = blockAddInst(block, AIR_INST_CALL, call_data); // Register CALL return type for semaTypeOf. if (sema->num_calls < 16) { - sema->call_air_insts[sema->num_calls] - = AIR_REF_TO_INST(call_ref); + sema->call_air_insts[sema->num_calls] = AIR_REF_TO_INST(call_ref); sema->call_ret_types[sema->num_calls] = ret_ty; sema->num_calls++; } @@ -2632,37 +2579,37 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } // Memoization: check if we've seen this function with the same - // comptime args before. We memoize aggressively (even in - // non-comptime blocks) because we can't fully resolve all comptime - // declarations. This compensates for missing features. - // TODO: switch to upstream matching (block->is_comptime && args_len>0) - // once namespace function calls are properly resolved. - bool all_comptime = (args_len > 0); - for (uint32_t a = 0; a < args_len && all_comptime; a++) { - if (!AIR_REF_IS_IP(arg_refs[a])) - all_comptime = false; + // comptime args before. + // Ported from src/Sema.zig line 7712: want_memoize = block.isComptime(). + bool all_comptime = block->is_comptime && args_len > 0; + if (all_comptime) { + for (uint32_t a = 0; a < args_len; a++) { + if (!AIR_REF_IS_IP(arg_refs[a])) { + all_comptime = false; + break; + } + } } if (all_comptime) { - for (uint32_t ci = 0; ci < sema->num_memo; ci++) { - if (sema->memo_func_inst[ci] != func_inst) + for (uint32_t mi = 0; mi < sema->num_memo; mi++) { + if (sema->memo_func_inst[mi] != func_inst) continue; - if (sema->memo_args_len[ci] != args_len) + if (sema->memo_args_len[mi] != args_len) continue; bool match = true; - for (uint32_t a = 0; a < args_len; a++) { - if (sema->memo_args[ci][a] != arg_refs[a]) { + for (uint32_t a = 0; a < args_len && a < 4; a++) { + if (sema->memo_args[mi][a] != arg_refs[a]) { match = false; break; } } if (match) { - // Cached result found — skip inline expansion. if (is_cross_module) { sema->code = saved_code; zirDeinit(&import_zir); astDeinit(&import_ast); } - return sema->memo_result[ci]; + return sema->memo_result[mi]; } } } @@ -2671,15 +2618,12 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, // When comptime (or all args comptime), use BLOCK; otherwise // DBG_INLINE_BLOCK. bool need_debug_scope = !block->is_comptime; - AirInstTag block_tag = need_debug_scope - ? AIR_INST_DBG_INLINE_BLOCK - : AIR_INST_BLOCK; + AirInstTag block_tag + = need_debug_scope ? AIR_INST_DBG_INLINE_BLOCK : AIR_INST_BLOCK; // Reserve the block instruction (data filled later). - uint32_t block_inst_idx = addAirInst(sema, - block_tag, - (AirInstData){ .ty_pl = { .ty_ref = 0, .payload = 0 } }); - + uint32_t block_inst_idx = addAirInst(sema, block_tag, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); // Set up child block for inlining. SemaBlockMerges merges; @@ -2712,8 +2656,7 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, child_block.need_debug_scope = NULL; // Map param ZIR instructions to the argument values. - instMapEnsureSpaceForBody( - &sema->inst_map, param_body, param_body_len); + instMapEnsureSpaceForBody(&sema->inst_map, param_body, param_body_len); uint32_t param_idx = 0; for (uint32_t p = 0; p < param_body_len; p++) { ZirInstTag ptag = sema->code.inst_tags[param_body[p]]; @@ -2724,35 +2667,28 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->has_compile_errors = true; return AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - instMapPut( - &sema->inst_map, param_body[p], arg_refs[param_idx]); + instMapPut(&sema->inst_map, param_body[p], arg_refs[param_idx]); // Emit dbg_arg_inline for runtime params only. // Comptime params (param_comptime, param_anytype_comptime) // are skipped because their types are comptime-only. - if (!child_block.is_comptime - && ptag != ZIR_INST_PARAM_COMPTIME + if (!child_block.is_comptime && ptag != ZIR_INST_PARAM_COMPTIME && ptag != ZIR_INST_PARAM_ANYTYPE_COMPTIME) { uint32_t param_name_idx; - if (ptag == ZIR_INST_PARAM_ANYTYPE - || ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME) { + if (ptag == ZIR_INST_PARAM_ANYTYPE) { // str_tok: name is at str_tok.start. param_name_idx - = sema->code.inst_datas[param_body[p]] - .str_tok.start; + = 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]; + param_name_idx = sema->code.extra[param_payload]; } const char* param_name - = (const char*)&sema->code - .string_bytes[param_name_idx]; - uint32_t name_nts - = semaAppendAirString(sema, param_name); + = (const char*)&sema->code.string_bytes[param_name_idx]; + uint32_t name_nts = semaAppendAirString(sema, param_name); AirInstData data; memset(&data, 0, sizeof(data)); data.pl_op.operand = arg_refs[param_idx]; @@ -2784,11 +2720,9 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } // Analyze the inline function body. - const uint32_t* func_body - = &sema->code.extra[func_info.extra_index]; + const uint32_t* func_body = &sema->code.extra[func_info.extra_index]; - (void)analyzeBodyInner( - sema, &child_block, func_body, func_info.body_len); + (void)analyzeBodyInner(sema, &child_block, func_body, func_info.body_len); // Restore decl table for cross-module calls. if (is_cross_module) { @@ -2801,6 +2735,36 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->fn_ret_ty = saved_fn_ret_ty; + // ComptimeReturn: the inline function returned at comptime. + // Skip block finalization — no AIR instructions emitted. + // Roll back AIR state to discard the reserved block and any body + // instructions (they are dead). Ported from src/Sema.zig line 7872. + if (inlining.comptime_returned) { + AirInstRef ct_result = inlining.comptime_result; + + // Cache comptime results for memoization. + if (all_comptime && AIR_REF_IS_IP(ct_result)) { + if (sema->num_memo < 32) { + uint32_t mi = sema->num_memo++; + sema->memo_func_inst[mi] = func_inst; + sema->memo_args_len[mi] = args_len; + for (uint32_t a = 0; a < args_len && a < 4; a++) + sema->memo_args[mi][a] = arg_refs[a]; + sema->memo_result[mi] = ct_result; + } + } + + free(inlining.merges.results); + free(inlining.merges.br_list); + semaBlockDeinit(&child_block); + if (is_cross_module) { + sema->code = saved_code; + zirDeinit(&import_zir); + astDeinit(&import_ast); + } + return ct_result; + } + // Resolve the inline call block, ported from resolveAnalyzedBlock. AirInstRef result_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); if (inlining.merges.results_len > 0) @@ -2828,16 +2792,15 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, for (uint32_t i = 0; i < child_block.instructions_len; i++) { if (block->instructions_len >= block->instructions_cap) { uint32_t nc = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, nc * sizeof(uint32_t)); + block->instructions + = realloc(block->instructions, nc * sizeof(uint32_t)); block->instructions_cap = nc; } block->instructions[block->instructions_len++] = child_block.instructions[i]; } call_result = AIR_REF_FROM_INST( - child_block - .instructions[child_block.instructions_len - 1]); + child_block.instructions[child_block.instructions_len - 1]); } else { // dbg_inline_block: create block with noreturn type. uint32_t es = addAirExtra(sema, inlining.func); @@ -2849,38 +2812,34 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; if (block->instructions_len >= block->instructions_cap) { uint32_t nc = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, nc * sizeof(uint32_t)); + block->instructions + = realloc(block->instructions, nc * sizeof(uint32_t)); block->instructions_cap = nc; } - block->instructions[block->instructions_len++] - = block_inst_idx; + block->instructions[block->instructions_len++] = block_inst_idx; call_result = AIR_REF_FROM_INST(block_inst_idx); } } else if (inlining.merges.results_len == 1) { // Single result. Try to elide block when !need_debug_scope. bool elided = false; if (!need_debug_scope && child_block.instructions_len > 0) { - uint32_t last = child_block.instructions[ - child_block.instructions_len - 1]; + uint32_t last + = child_block.instructions[child_block.instructions_len - 1]; if (sema->air_inst_tags[last] == AIR_INST_BR && sema->air_inst_datas[last].br.block_inst == block_inst_idx) { // Elide: copy instructions except trailing break. - for (uint32_t i = 0; - i < child_block.instructions_len - 1; i++) { - if (block->instructions_len - >= block->instructions_cap) { + for (uint32_t i = 0; i < child_block.instructions_len - 1; + i++) { + if (block->instructions_len >= block->instructions_cap) { uint32_t nc = block->instructions_cap * 2; block->instructions = realloc( - block->instructions, - nc * sizeof(uint32_t)); + block->instructions, nc * sizeof(uint32_t)); block->instructions_cap = nc; } block->instructions[block->instructions_len++] = child_block.instructions[i]; } - call_result = result_ref; elided = true; } } @@ -2908,12 +2867,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, } if (block->instructions_len >= block->instructions_cap) { uint32_t nc = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, nc * sizeof(uint32_t)); + block->instructions + = realloc(block->instructions, nc * sizeof(uint32_t)); block->instructions_cap = nc; } - block->instructions[block->instructions_len++] - = block_inst_idx; + block->instructions[block->instructions_len++] = block_inst_idx; call_result = result_ref; } else if (!elided) { // Runtime result: create block with result type. @@ -2932,12 +2890,11 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; if (block->instructions_len >= block->instructions_cap) { uint32_t nc = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, nc * sizeof(uint32_t)); + block->instructions + = realloc(block->instructions, nc * sizeof(uint32_t)); block->instructions_cap = nc; } - block->instructions[block->instructions_len++] - = block_inst_idx; + block->instructions[block->instructions_len++] = block_inst_idx; call_result = AIR_REF_FROM_INST(block_inst_idx); } else { call_result = result_ref; @@ -2959,8 +2916,8 @@ static AirInstRef zirCall(Sema* sema, SemaBlock* block, uint32_t inst, sema->air_inst_datas[block_inst_idx].ty_pl.payload = es; if (block->instructions_len >= block->instructions_cap) { uint32_t nc = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, nc * sizeof(uint32_t)); + block->instructions + = realloc(block->instructions, nc * sizeof(uint32_t)); block->instructions_cap = nc; } block->instructions[block->instructions_len++] = block_inst_idx; @@ -3108,14 +3065,14 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) { sema->fn_ret_ty = AIR_REF_TO_IP(ret_air); } else if (ret_ty_body_len == 2) { // Two-instruction return type body: [ptr_type, break_inline]. - uint32_t ret_body_start = ret_ty_ref_pos; // set by both func and func_fancy paths + uint32_t ret_body_start + = ret_ty_ref_pos; // set by both func and func_fancy paths uint32_t first_inst = sema->code.extra[ret_body_start]; ZirInstTag first_tag = sema->code.inst_tags[first_inst]; if (first_tag == ZIR_INST_PTR_TYPE) { uint8_t zir_flags = sema->code.inst_datas[first_inst].ptr_type.flags; - uint8_t zir_size - = sema->code.inst_datas[first_inst].ptr_type.size; + uint8_t zir_size = sema->code.inst_datas[first_inst].ptr_type.size; uint32_t pi = sema->code.inst_datas[first_inst].ptr_type.payload_index; ZirInstRef elem_ty_ref = sema->code.extra[pi]; @@ -3210,12 +3167,14 @@ static void zirFunc(Sema* sema, SemaBlock* block, uint32_t inst) { = sema->code.inst_datas[first_inst].ptr_type.size; uint32_t pi = sema->code.inst_datas[first_inst].ptr_type.payload_index; - ZirInstRef elem_ty_ref = sema->code.extra[pi]; // PtrType.elem_type + ZirInstRef elem_ty_ref + = sema->code.extra[pi]; // PtrType.elem_type AirInstRef elem_air = resolveInst(sema, elem_ty_ref); TypeIndex elem_ty = AIR_REF_TO_IP(elem_air); // Build IP PtrType key. uint32_t ip_flags = (uint32_t)zir_size & PTR_FLAGS_SIZE_MASK; - if (!(zir_flags & 0x02)) // ZIR bit 1 = is_mutable; !is_mutable → const + if (!(zir_flags + & 0x02)) // ZIR bit 1 = is_mutable; !is_mutable → const ip_flags |= PTR_FLAGS_IS_CONST; InternPoolKey key; memset(&key, 0, sizeof(key)); @@ -3430,10 +3389,8 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) { const uint32_t* value_body = &sema->code.extra[di]; bool is_func = false; for (uint32_t vi = 0; vi < value_body_len; vi++) { - ZirInstTag vtag - = sema->code.inst_tags[value_body[vi]]; - if (vtag == ZIR_INST_FUNC - || vtag == ZIR_INST_FUNC_FANCY) { + ZirInstTag vtag = sema->code.inst_tags[value_body[vi]]; + if (vtag == ZIR_INST_FUNC || vtag == ZIR_INST_FUNC_FANCY) { is_func = true; break; } @@ -3461,8 +3418,7 @@ static void zirStructDecl(Sema* sema, SemaBlock* block, uint32_t inst) { // zirTypeof: handle @TypeOf(operand). // Returns the type of the operand as an IP ref. static AirInstRef zirTypeof(Sema* sema, uint32_t inst) { - ZirInstRef operand_ref - = sema->code.inst_datas[inst].un_node.operand; + ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef operand = resolveInst(sema, operand_ref); TypeIndex ty = semaTypeOf(sema, operand); return AIR_REF_FROM_IP(ty); @@ -3475,8 +3431,7 @@ static AirInstRef zirTypeof(Sema* sema, uint32_t inst) { // Returns the result value from the matched break instruction. static AirInstRef zirSwitchBlockComptime( Sema* sema, SemaBlock* block, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef operand_ref = sema->code.extra[payload_index]; uint32_t bits = sema->code.extra[payload_index + 1]; @@ -3561,8 +3516,7 @@ static AirInstRef zirSwitchBlockComptime( && AIR_REF_TO_IP(operand) == AIR_REF_TO_IP(item)) match = true; else if (isComptimeInt(sema, operand, &op_val) - && isComptimeInt(sema, item, &item_val) - && op_val == item_val) + && isComptimeInt(sema, item, &item_val) && op_val == item_val) match = true; if (match) { // Match found. Analyze the body via resolveBlockBody. @@ -3579,8 +3533,7 @@ static AirInstRef zirSwitchBlockComptime( // debug undefined pattern. AirInstData block_data; memset(&block_data, 0xaa, sizeof(block_data)); - uint32_t block_inst - = addAirInst(sema, AIR_INST_BLOCK, block_data); + uint32_t block_inst = addAirInst(sema, AIR_INST_BLOCK, block_data); // Set up a label so break can find this block. SemaBlockLabel label; @@ -3588,21 +3541,19 @@ static AirInstRef zirSwitchBlockComptime( label.zir_block = inst; label.merges.block_inst = block_inst; - // Non-comptime child block (matches Zig line 11942-11959). - // The break handler will add a BR instruction. + // Child block inherits comptime from parent (matches Zig + // line 11942-11959: comptime_reason is inherited). + // In comptime context, the break handler records the + // result without emitting a BR, matching upstream where + // only the dead BLOCK remains. SemaBlock child_block; semaBlockInit(&child_block, sema, block); - child_block.is_comptime = false; + child_block.is_comptime = block->is_comptime; child_block.inlining = block->inlining; child_block.label = &label; - (void)analyzeBodyInner( - sema, &child_block, body, body_len); + (void)analyzeBodyInner(sema, &child_block, body, body_len); - // resolveAnalyzedBlock elides the block: the single break - // is the last instruction in child_block, targeting our - // block_inst, so instructions are not added to parent. - // Both BLOCK and BR remain as dead AIR instructions. AirInstRef result; if (label.merges.results_len > 0) result = label.merges.results[0]; @@ -3636,8 +3587,7 @@ static AirInstRef zirSwitchBlockComptime( // Reserve BLOCK instruction (same as scalar case above). AirInstData block_data; memset(&block_data, 0xaa, sizeof(block_data)); - uint32_t block_inst - = addAirInst(sema, AIR_INST_BLOCK, block_data); + uint32_t block_inst = addAirInst(sema, AIR_INST_BLOCK, block_data); SemaBlockLabel label; memset(&label, 0, sizeof(label)); @@ -3646,12 +3596,11 @@ static AirInstRef zirSwitchBlockComptime( SemaBlock child_block; semaBlockInit(&child_block, sema, block); - child_block.is_comptime = false; + child_block.is_comptime = block->is_comptime; child_block.inlining = block->inlining; child_block.label = &label; - (void)analyzeBodyInner( - sema, &child_block, body, else_body_len); + (void)analyzeBodyInner(sema, &child_block, body, else_body_len); AirInstRef result; if (label.merges.results_len > 0) @@ -3671,8 +3620,7 @@ static AirInstRef zirSwitchBlockComptime( // zirTypeInfoComptime: handle @typeInfo(type_ref) at comptime. // For float types, tracks the result for subsequent field_val access. static AirInstRef zirTypeInfoComptime(Sema* sema, uint32_t inst) { - ZirInstRef operand_ref - = sema->code.inst_datas[inst].un_node.operand; + ZirInstRef operand_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef operand = resolveInst(sema, operand_ref); // operand should be an IP ref to a type. if (!AIR_REF_IS_IP(operand)) @@ -3698,12 +3646,18 @@ static AirInstRef zirTypeInfoComptime(Sema* sema, uint32_t inst) { // floatTypeBits: get the bit count for a float type IP index. static uint32_t floatTypeBits(InternPoolIndex ty) { switch (ty) { - case IP_INDEX_F16_TYPE: return 16; - case IP_INDEX_F32_TYPE: return 32; - case IP_INDEX_F64_TYPE: return 64; - case IP_INDEX_F80_TYPE: return 80; - case IP_INDEX_F128_TYPE: return 128; - default: return 0; + case IP_INDEX_F16_TYPE: + return 16; + case IP_INDEX_F32_TYPE: + return 32; + case IP_INDEX_F64_TYPE: + return 64; + case IP_INDEX_F80_TYPE: + return 80; + case IP_INDEX_F128_TYPE: + return 128; + default: + return 0; } } @@ -3744,8 +3698,7 @@ static AirInstRef zirBitSizeOf(Sema* sema, uint32_t inst) { // zirFieldValComptime: handle field_val on comptime values. // Resolves .float on type_info results and .bits on float_info. static AirInstRef zirFieldValComptime(Sema* sema, uint32_t inst) { - uint32_t payload_index - = sema->code.inst_datas[inst].pl_node.payload_index; + uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; ZirInstRef obj_ref = sema->code.extra[payload_index]; uint32_t field_name_start = sema->code.extra[payload_index + 1]; const char* field_name @@ -3794,13 +3747,16 @@ static AirInstRef zirFieldValComptime(Sema* sema, uint32_t inst) { // resolveUnsignedIntType: given a bits count, return the IP index // for the corresponding unsigned integer type. -static InternPoolIndex resolveUnsignedIntType( - InternPool* ip, uint32_t bits) { +static InternPoolIndex resolveUnsignedIntType(InternPool* ip, uint32_t bits) { switch (bits) { - case 8: return IP_INDEX_U8_TYPE; - case 16: return IP_INDEX_U16_TYPE; - case 32: return IP_INDEX_U32_TYPE; - case 64: return IP_INDEX_U64_TYPE; + case 8: + return IP_INDEX_U8_TYPE; + case 16: + return IP_INDEX_U16_TYPE; + case 32: + return IP_INDEX_U32_TYPE; + case 64: + return IP_INDEX_U64_TYPE; default: { // Intern a new int type. InternPoolKey key; @@ -3818,8 +3774,7 @@ static InternPoolIndex resolveUnsignedIntType( // - 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; + 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]; @@ -3841,8 +3796,7 @@ static AirInstRef zirStructInitComptime(Sema* sema, uint32_t inst) { 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]; + const char* name = (const char*)&sema->code.string_bytes[name_start]; AirInstRef init_air = resolveInst(sema, init_ref); @@ -3883,16 +3837,14 @@ static AirInstRef zirStructInitComptime(Sema* sema, uint32_t inst) { if (has_signedness && has_bits) { // Inner struct: .{.signedness = S, .bits = N} - key.data.int_val.value = 0x51F70000 - | ((uint64_t)signedness << 16) | bits_val; + 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); + 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) { + 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); @@ -3929,8 +3881,7 @@ static AirInstRef zirReifyComptime(Sema* sema, uint32_t inst) { 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)); + return AIR_REF_FROM_IP(resolveUnsignedIntType(sema->ip, b)); } // Signed int type. InternPoolKey key; @@ -3945,8 +3896,7 @@ static AirInstRef zirReifyComptime(Sema* sema, uint32_t inst) { 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)); + return AIR_REF_FROM_IP(resolveUnsignedIntType(sema->ip, b)); } InternPoolKey key; memset(&key, 0, sizeof(key)); @@ -3962,8 +3912,7 @@ static AirInstRef zirReifyComptime(Sema* sema, uint32_t inst) { // 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; + 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 @@ -3993,15 +3942,14 @@ static AirInstRef zirDeclLiteralComptime(Sema* sema, uint32_t inst) { // --- mergesAppend --- // Append a result and br instruction index to a SemaBlockMerges. -static void mergesAppend(SemaBlockMerges* merges, - AirInstRef result, uint32_t br_inst) { +static void mergesAppend( + SemaBlockMerges* merges, AirInstRef result, uint32_t br_inst) { if (merges->results_len >= merges->results_cap) { uint32_t new_cap = (merges->results_cap == 0) ? 4 : merges->results_cap * 2; merges->results = realloc(merges->results, new_cap * sizeof(AirInstRef)); - merges->br_list - = realloc(merges->br_list, new_cap * sizeof(uint32_t)); + merges->br_list = realloc(merges->br_list, new_cap * sizeof(uint32_t)); if (!merges->results || !merges->br_list) exit(1); merges->results_cap = new_cap; @@ -4081,27 +4029,30 @@ static bool analyzeBodyInner( operand = semaCoerce(sema, block, sema->fn_ret_ty, operand); if (block->inlining) { - // Inlining: rewrite ret as br to the inline block. // Ported from src/Sema.zig analyzeRet (inlining path). SemaBlockInlining* inl = block->inlining; + // Comptime inlining: signal ComptimeReturn instead of + // emitting a BR. Ported from Sema.zig lines 18955-18964. + if (block->is_comptime) { + inl->comptime_result = operand; + inl->comptime_returned = true; + return false; + } + // Runtime inlining: rewrite ret as br to the inline block. AirInstData br_data; memset(&br_data, 0, sizeof(br_data)); br_data.br.block_inst = inl->merges.block_inst; br_data.br.operand = operand; - AirInstRef br_ref - = blockAddInst(block, AIR_INST_BR, br_data); + AirInstRef br_ref = blockAddInst(block, AIR_INST_BR, br_data); // Record merge result and br instruction. if (inl->merges.results_len >= inl->merges.results_cap) { - uint32_t new_cap - = (inl->merges.results_cap == 0) + uint32_t new_cap = (inl->merges.results_cap == 0) ? 4 : inl->merges.results_cap * 2; inl->merges.results = realloc( - inl->merges.results, - new_cap * sizeof(AirInstRef)); + inl->merges.results, new_cap * sizeof(AirInstRef)); inl->merges.br_list = realloc( - inl->merges.br_list, - new_cap * sizeof(uint32_t)); + inl->merges.br_list, new_cap * sizeof(uint32_t)); if (!inl->merges.results || !inl->merges.br_list) exit(1); inl->merges.results_cap = new_cap; @@ -4167,21 +4118,21 @@ static bool analyzeBodyInner( // when the callee is a decl_val/decl_ref. case ZIR_INST_DECL_VAL: case ZIR_INST_DECL_REF: - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + instMapPut( + &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); i++; continue; // call / field_call: function call. // Handles inline function calls from the same module. case ZIR_INST_CALL: - instMapPut(&sema->inst_map, inst, - zirCall(sema, block, inst, false)); + instMapPut( + &sema->inst_map, inst, zirCall(sema, block, inst, false)); i++; continue; case ZIR_INST_FIELD_CALL: - instMapPut(&sema->inst_map, inst, - zirCall(sema, block, inst, true)); + instMapPut( + &sema->inst_map, inst, zirCall(sema, block, inst, true)); i++; continue; @@ -4205,16 +4156,13 @@ static bool analyzeBodyInner( // Extra: {src_node, operand (ZIR ref)}. uint32_t payload_index = sema->code.inst_datas[inst].extended.operand; - ZirInstRef hint_ref - = sema->code.extra[payload_index + 1]; + ZirInstRef hint_ref = sema->code.extra[payload_index + 1]; AirInstRef resolved = resolveInst(sema, hint_ref); - if (AIR_REF_IS_IP(resolved) - && sema->branch_hint < 0) { - InternPoolKey key = ipIndexToKey( - sema->ip, AIR_REF_TO_IP(resolved)); + if (AIR_REF_IS_IP(resolved) && sema->branch_hint < 0) { + InternPoolKey key + = ipIndexToKey(sema->ip, AIR_REF_TO_IP(resolved)); if (key.tag == IP_KEY_INT) { - sema->branch_hint - = (int8_t)key.data.int_val.value; + sema->branch_hint = (int8_t)key.data.int_val.value; } } air_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); @@ -4222,7 +4170,8 @@ static bool analyzeBodyInner( // Ported from src/Sema.zig zirInplaceArithResultTy. // operand is the ZIR ref of the loaded LHS value. // For non-pointer types, the result type is the LHS type. - uint32_t operand = sema->code.inst_datas[inst].extended.operand; + uint32_t operand + = sema->code.inst_datas[inst].extended.operand; AirInstRef lhs = resolveInst(sema, operand); TypeIndex lhs_ty = semaTypeOf(sema, lhs); air_ref = AIR_REF_FROM_IP(lhs_ty); @@ -4247,9 +4196,9 @@ static bool analyzeBodyInner( // this block_inline, requiring a post-hoc BLOCK wrapper // for correct lexical scoping. // Ported from src/Sema.zig block_inline need_debug_scope. - bool need_debug_scope = false; + volatile bool need_debug_scope = false; bool* saved_need_debug_scope = block->need_debug_scope; - block->need_debug_scope = &need_debug_scope; + block->need_debug_scope = (bool*)&need_debug_scope; uint32_t block_index = block->instructions_len; bool completed @@ -4267,53 +4216,42 @@ static bool analyzeBodyInner( = block->instructions_len - block_index; // Reserve an AIR block instruction. - uint32_t blk_inst = addAirInst(sema, - AIR_INST_BLOCK, - (AirInstData){ - .ty_pl = { .ty_ref = 0, .payload = 0 } - }); + uint32_t blk_inst = addAirInst(sema, AIR_INST_BLOCK, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); // Add a BR to exit the block with void_value. AirInstData br_data; memset(&br_data, 0, sizeof(br_data)); br_data.br.block_inst = blk_inst; - br_data.br.operand - = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); - uint32_t br_inst = addAirInst( - sema, AIR_INST_BR, br_data); + br_data.br.operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); + uint32_t br_inst = addAirInst(sema, AIR_INST_BR, br_data); // Write block extra: body_len2, then body insts. uint32_t body_len2 = new_insts_count + 1; // +1 for BR - uint32_t extra_start - = addAirExtra(sema, body_len2); - for (uint32_t ci = block_index; - ci < block->instructions_len; ci++) { - addAirExtra(sema, - block->instructions[ci]); + uint32_t extra_start = addAirExtra(sema, body_len2); + for (uint32_t ci = block_index; ci < block->instructions_len; + ci++) { + addAirExtra(sema, block->instructions[ci]); } addAirExtra(sema, br_inst); // Patch the BLOCK instruction data. sema->air_inst_datas[blk_inst].ty_pl.ty_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); - sema->air_inst_datas[blk_inst].ty_pl.payload - = extra_start; + sema->air_inst_datas[blk_inst].ty_pl.payload = extra_start; // Replace the instructions in the parent block: // truncate back to block_index, add just the BLOCK. block->instructions_len = block_index; - if (block->instructions_len - >= block->instructions_cap) { - uint32_t new_cap - = block->instructions_cap * 2; + if (block->instructions_len >= block->instructions_cap) { + uint32_t new_cap = block->instructions_cap * 2; block->instructions = realloc( - block->instructions, - new_cap * sizeof(uint32_t)); - if (!block->instructions) exit(1); + block->instructions, new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); block->instructions_cap = new_cap; } - block->instructions[block->instructions_len++] - = blk_inst; + block->instructions[block->instructions_len++] = blk_inst; } if (!completed) { @@ -4323,8 +4261,7 @@ static bool analyzeBodyInner( uint32_t break_inst_zir = sema->comptime_break_inst; ZirInstData break_data_zir = sema->code.inst_datas[break_inst_zir]; - ZirInstRef operand - = break_data_zir.break_data.operand; + ZirInstRef operand = break_data_zir.break_data.operand; AirInstRef result; if (operand == ZIR_REF_NONE) { result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); @@ -4353,24 +4290,20 @@ static bool analyzeBodyInner( = sema->code.inst_datas[inst].pl_node.payload_index; uint32_t exported_ref = sema->code.extra[payload_index]; if (exported_ref >= ZIR_REF_START_INDEX) { - uint32_t exported_inst - = exported_ref - ZIR_REF_START_INDEX; + uint32_t exported_inst = exported_ref - ZIR_REF_START_INDEX; ZirInstTag etag = sema->code.inst_tags[exported_inst]; - if (etag == ZIR_INST_DECL_REF - || etag == ZIR_INST_DECL_VAL) { + if (etag == ZIR_INST_DECL_REF || etag == ZIR_INST_DECL_VAL) { uint32_t name_idx - = sema->code.inst_datas[exported_inst] - .str_tok.start; + = sema->code.inst_datas[exported_inst].str_tok.start; if (sema->num_exported_decl_names < MAX_EXPORTED_DECL_NAMES) { - s_exported_decl_names - [sema->num_exported_decl_names++] = name_idx; + s_exported_decl_names[sema->num_exported_decl_names++] + = name_idx; } } } instMapPut( - &sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); i++; continue; } @@ -4478,8 +4411,7 @@ static bool analyzeBodyInner( // @bitCast. case ZIR_INST_BITCAST: - instMapPut(&sema->inst_map, inst, - zirBitcast(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirBitcast(sema, block, inst)); i++; continue; @@ -4513,8 +4445,7 @@ static bool analyzeBodyInner( // @floatCast. case ZIR_INST_FLOAT_CAST: - instMapPut(&sema->inst_map, inst, - zirFloatCast(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirFloatCast(sema, block, inst)); i++; continue; @@ -4544,8 +4475,7 @@ static bool analyzeBodyInner( // @as. case ZIR_INST_AS_NODE: - instMapPut(&sema->inst_map, inst, - zirAsNode(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirAsNode(sema, block, inst)); i++; continue; @@ -4557,35 +4487,31 @@ static bool analyzeBodyInner( // Load (pointer dereference). case ZIR_INST_LOAD: - instMapPut(&sema->inst_map, inst, - zirLoad(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirLoad(sema, block, inst)); i++; continue; // Unary operations. case ZIR_INST_NEGATE: - instMapPut(&sema->inst_map, inst, - zirNegate(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirNegate(sema, block, inst)); i++; continue; case ZIR_INST_BIT_NOT: - instMapPut(&sema->inst_map, inst, - zirBitNot(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirBitNot(sema, block, inst)); i++; continue; case ZIR_INST_BOOL_NOT: - instMapPut(&sema->inst_map, inst, - zirBoolNot(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirBoolNot(sema, block, inst)); i++; continue; case ZIR_INST_INT_FROM_BOOL: - instMapPut(&sema->inst_map, inst, - zirIntFromBool(sema, block, inst)); + instMapPut( + &sema->inst_map, inst, zirIntFromBool(sema, block, inst)); i++; continue; case ZIR_INST_NEGATE_WRAP: - instMapPut(&sema->inst_map, inst, - zirNegateWrap(sema, block, inst)); + instMapPut( + &sema->inst_map, inst, zirNegateWrap(sema, block, inst)); i++; continue; @@ -4608,8 +4534,7 @@ static bool analyzeBodyInner( // @byteSwap. case ZIR_INST_BYTE_SWAP: - instMapPut(&sema->inst_map, inst, - zirByteSwap(sema, block, inst)); + instMapPut(&sema->inst_map, inst, zirByteSwap(sema, block, inst)); i++; continue; @@ -4619,11 +4544,33 @@ static bool analyzeBodyInner( i++; continue; + // ret_ptr: get pointer to return value location. + // When inlining, emits ALLOC; otherwise emits ret_ptr. + // Ported from src/Sema.zig zirRetPtr. + case ZIR_INST_RET_PTR: { + TypeIndex ret_ty = sema->fn_ret_ty; + if (ret_ty == TYPE_NONE) + ret_ty = IP_INDEX_VOID_TYPE; + InternPoolKey pk; + memset(&pk, 0, sizeof(pk)); + pk.tag = IP_KEY_PTR_TYPE; + pk.data.ptr_type.child = ret_ty; + pk.data.ptr_type.flags = 0; + TypeIndex ptr_ty = ipIntern(sema->ip, pk); + AirInstData d; + memset(&d, 0, sizeof(d)); + d.ty.ty_ref = AIR_REF_FROM_IP(ptr_ty); + AirInstRef ref = blockAddInst(block, AIR_INST_ALLOC, d); + instMapPut(&sema->inst_map, inst, ref); + i++; + continue; + } + // ret_type: obtains the return type of the in-scope function. case ZIR_INST_RET_TYPE: if (sema->fn_ret_ty != TYPE_NONE) { - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(sema->fn_ret_ty)); + instMapPut( + &sema->inst_map, inst, AIR_REF_FROM_IP(sema->fn_ret_ty)); } else { instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE)); @@ -4633,8 +4580,8 @@ static bool analyzeBodyInner( // save_err_ret_index: no-op in ReleaseFast; map to void. case ZIR_INST_SAVE_ERR_RET_INDEX: - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); + instMapPut( + &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); i++; continue; @@ -4652,8 +4599,7 @@ static bool analyzeBodyInner( } TypeIndex ptr_ty = AIR_REF_TO_IP(resolved); TypeIndex elem_ty = ptrChildType(sema->ip, ptr_ty); - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_IP(elem_ty)); + instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(elem_ty)); i++; continue; } @@ -4670,8 +4616,7 @@ static bool analyzeBodyInner( 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]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; SemaBlock ct_block; semaBlockInit(&ct_block, sema, block); @@ -4691,8 +4636,7 @@ static bool analyzeBodyInner( AirInstRef result; if (!completed) { uint32_t break_inst = sema->comptime_break_inst; - ZirInstData break_data - = sema->code.inst_datas[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); @@ -4717,10 +4661,8 @@ static bool analyzeBodyInner( 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]; + uint32_t inner_body_len = sema->code.extra[payload_index + 1]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 2]; SemaBlock ct_block; semaBlockInit(&ct_block, sema, block); @@ -4732,8 +4674,7 @@ static bool analyzeBodyInner( if (!completed) { uint32_t break_inst = sema->comptime_break_inst; - ZirInstData break_data - = sema->code.inst_datas[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) { @@ -4750,15 +4691,13 @@ static bool analyzeBodyInner( // @typeInfo(type): comptime type info extraction. case ZIR_INST_TYPE_INFO: - instMapPut(&sema->inst_map, inst, - zirTypeInfoComptime(sema, inst)); + instMapPut(&sema->inst_map, inst, zirTypeInfoComptime(sema, inst)); i++; continue; // @bitSizeOf(type): comptime bit size extraction. case ZIR_INST_BIT_SIZE_OF: - instMapPut(&sema->inst_map, inst, - zirBitSizeOf(sema, inst)); + instMapPut(&sema->inst_map, inst, zirBitSizeOf(sema, inst)); i++; continue; @@ -4771,8 +4710,7 @@ static bool analyzeBodyInner( // field_val: comptime field access on type_info results. case ZIR_INST_FIELD_VAL: - instMapPut(&sema->inst_map, inst, - zirFieldValComptime(sema, inst)); + instMapPut(&sema->inst_map, inst, zirFieldValComptime(sema, inst)); i++; continue; @@ -4782,30 +4720,30 @@ static bool analyzeBodyInner( 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)); + 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)); + 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)); + 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)); + instMapPut( + &sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE)); i++; continue; @@ -4813,8 +4751,7 @@ static bool analyzeBodyInner( // Ported from src/Sema.zig zirAllocMut. // Creates AIR_INST_ALLOC with pointer type *T. case ZIR_INST_ALLOC_MUT: { - ZirInstRef type_ref - = sema->code.inst_datas[inst].un_node.operand; + ZirInstRef type_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef resolved = resolveInst(sema, type_ref); if (!AIR_REF_IS_IP(resolved)) { i++; @@ -4875,14 +4812,12 @@ static bool analyzeBodyInner( memset(&data, 0, sizeof(data)); data.bin_op.lhs = ptr; data.bin_op.rhs = val; - AirInstRef store_ref - = blockAddInst(block, AIR_INST_STORE, data); + AirInstRef store_ref = blockAddInst(block, AIR_INST_STORE, data); // Track the store for the inferred alloc. uint32_t ptr_inst = AIR_REF_TO_INST(ptr); for (uint32_t k = 0; k < sema->num_ia; k++) { if (sema->ia_air[k] == ptr_inst) { - sema->ia_store[k] - = AIR_REF_TO_INST(store_ref); + sema->ia_store[k] = AIR_REF_TO_INST(store_ref); break; } } @@ -4894,8 +4829,7 @@ static bool analyzeBodyInner( // Ported from src/Sema.zig zirResolveInferredAlloc. // Patches INFERRED_ALLOC → ALLOC with the resolved pointer type. case ZIR_INST_RESOLVE_INFERRED_ALLOC: { - ZirInstRef alloc_ref - = sema->code.inst_datas[inst].un_node.operand; + ZirInstRef alloc_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef ptr = resolveInst(sema, alloc_ref); uint32_t ptr_inst = AIR_REF_TO_INST(ptr); // Find tracked inferred alloc. @@ -4916,8 +4850,7 @@ static bool analyzeBodyInner( // Get the store's RHS type. assert(sema->ia_store[ia_idx] != UINT32_MAX); uint32_t store_inst = sema->ia_store[ia_idx]; - AirInstRef rhs - = sema->air_inst_datas[store_inst].bin_op.rhs; + AirInstRef rhs = sema->air_inst_datas[store_inst].bin_op.rhs; TypeIndex elem_ty = semaTypeOf(sema, rhs); // Create pointer type *T (mutable, size one). InternPoolKey key; @@ -4965,17 +4898,79 @@ static bool analyzeBodyInner( // block: runtime block. // Ported from src/Sema.zig zirBlock / resolveAnalyzedBlock. + // In Zig, comptime .block redirects to .block_inline (no AIR + // BLOCK pre-allocated). We handle that here by checking + // is_comptime BEFORE allocating the AIR instruction. case ZIR_INST_BLOCK: { ZirInstData bdata = sema->code.inst_datas[inst]; uint32_t payload_index = bdata.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]; + const uint32_t* inner_body = &sema->code.extra[payload_index + 1]; + + // Comptime block: handle inline (no AIR block allocated). + // Ported from Sema.zig line 1719-1721: comptime .block + // redirects to .block_inline, and resolveBlockBody 5906. + if (block->is_comptime) { + SemaBlockLabel label; + memset(&label, 0, sizeof(label)); + label.zir_block = inst; + // No merges.block_inst needed (comptime breaks + // don't emit BR). + + SemaBlock child_block; + semaBlockInit(&child_block, sema, block); + child_block.is_comptime = true; + child_block.want_safety = block->want_safety; + child_block.want_safety_set = block->want_safety_set; + child_block.inlining = block->inlining; + child_block.label = &label; + + bool body_completed = analyzeBodyInner( + sema, &child_block, inner_body, inner_body_len); + + // Copy any child instructions to parent block. + for (uint32_t ci = 0; ci < child_block.instructions_len; + ci++) { + if (block->instructions_len >= block->instructions_cap) { + uint32_t new_cap = block->instructions_cap * 2; + block->instructions = realloc( + block->instructions, new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); + block->instructions_cap = new_cap; + } + block->instructions[block->instructions_len++] + = child_block.instructions[ci]; + } - // Reserve an AIR block instruction (data filled later). - uint32_t block_inst_idx = addAirInst(sema, - AIR_INST_BLOCK, - (AirInstData){ .ty_pl = { .ty_ref = 0, .payload = 0 } }); + AirInstRef result; + if (!body_completed && label.merges.results_len == 0) { + // ComptimeReturn or break to outer block: + // propagate upward. + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return false; + } else if (!body_completed) { + // Break targeted this block. + result = label.merges.results[0]; + } else { + // Body completed normally (no break). + result = AIR_REF_FROM_IP(IP_INDEX_UNREACHABLE_VALUE); + } + + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + instMapPut(&sema->inst_map, inst, result); + i++; + continue; + } + + // Runtime block: reserve an AIR block instruction + // (data filled later). + uint32_t block_inst_idx = addAirInst(sema, AIR_INST_BLOCK, + (AirInstData) { .ty_pl = { .ty_ref = 0, .payload = 0 } }); // Set up label so break instructions can find this block. SemaBlockLabel label; @@ -4997,42 +4992,44 @@ static bool analyzeBodyInner( bool need_debug_scope = false; child_block.need_debug_scope = &need_debug_scope; - (void)analyzeBodyInner( + bool body_completed = analyzeBodyInner( sema, &child_block, inner_body, inner_body_len); // Resolve the block: write extra data and patch type. // Ported from src/Sema.zig resolveAnalyzedBlock. + (void)body_completed; AirInstRef block_result; if (label.merges.results_len == 0) { // No breaks (noreturn block): copy instructions to // parent. If need_debug_scope, forward to parent. + // Ported from Sema.zig line 1978: isNoReturn check + // causes break from analyzeBodyInner loop — remaining + // ZIR instructions in this body are NOT processed. if (need_debug_scope && block->need_debug_scope) *block->need_debug_scope = true; - for (uint32_t ci = 0; - ci < child_block.instructions_len; ci++) { - if (block->instructions_len - >= block->instructions_cap) { + for (uint32_t ci = 0; ci < child_block.instructions_len; + ci++) { + if (block->instructions_len >= block->instructions_cap) { uint32_t new_cap = block->instructions_cap * 2; block->instructions = realloc( - block->instructions, - new_cap * sizeof(uint32_t)); - if (!block->instructions) exit(1); + block->instructions, new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); block->instructions_cap = new_cap; } block->instructions[block->instructions_len++] = child_block.instructions[ci]; } - block_result = AIR_REF_FROM_INST( - child_block.instructions - [child_block.instructions_len - 1]); + free(label.merges.results); + free(label.merges.br_list); + semaBlockDeinit(&child_block); + return false; } else if (label.merges.results_len == 1) { // Single break: check if last instruction is the // break. Only elide if need_debug_scope is false. // Ported from src/Sema.zig resolveAnalyzedBlock. - uint32_t last_inst_idx - = child_block.instructions_len - 1; - uint32_t last_inst - = child_block.instructions[last_inst_idx]; + uint32_t last_inst_idx = child_block.instructions_len - 1; + uint32_t last_inst = child_block.instructions[last_inst_idx]; bool elide = false; if (!need_debug_scope && sema->air_inst_tags[last_inst] == AIR_INST_BR @@ -5046,12 +5043,11 @@ static bool analyzeBodyInner( for (uint32_t ci = 0; ci < last_inst_idx; ci++) { if (block->instructions_len >= block->instructions_cap) { - uint32_t new_cap - = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, + uint32_t new_cap = block->instructions_cap * 2; + block->instructions = realloc(block->instructions, new_cap * sizeof(uint32_t)); - if (!block->instructions) exit(1); + if (!block->instructions) + exit(1); block->instructions_cap = new_cap; } block->instructions[block->instructions_len++] @@ -5062,25 +5058,22 @@ static bool analyzeBodyInner( // Need runtime block. Since result is // comptime-known (void), use void block type. // Emit the block instruction in the parent. - if (block->instructions_len - >= block->instructions_cap) { + if (block->instructions_len >= block->instructions_cap) { uint32_t new_cap = block->instructions_cap * 2; block->instructions = realloc( - block->instructions, - new_cap * sizeof(uint32_t)); - if (!block->instructions) exit(1); + block->instructions, new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); block->instructions_cap = new_cap; } block->instructions[block->instructions_len++] = block_inst_idx; uint32_t extra_start - = addAirExtra(sema, - child_block.instructions_len); - for (uint32_t ci = 0; - ci < child_block.instructions_len; ci++) { - addAirExtra(sema, - child_block.instructions[ci]); + = addAirExtra(sema, child_block.instructions_len); + for (uint32_t ci = 0; ci < child_block.instructions_len; + ci++) { + addAirExtra(sema, child_block.instructions[ci]); } sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref = AIR_REF_FROM_IP(IP_INDEX_VOID_TYPE); @@ -5088,8 +5081,7 @@ static bool analyzeBodyInner( = extra_start; // Rewrite the break operand to void. - sema->air_inst_datas - [label.merges.br_list[0]].br.operand + sema->air_inst_datas[label.merges.br_list[0]].br.operand = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); block_result = label.merges.results[0]; @@ -5097,35 +5089,81 @@ static bool analyzeBodyInner( } else { // Multiple breaks: need full runtime block with peer // type resolution. - if (block->instructions_len - >= block->instructions_cap) { + if (block->instructions_len >= block->instructions_cap) { uint32_t new_cap = block->instructions_cap * 2; block->instructions = realloc( - block->instructions, - new_cap * sizeof(uint32_t)); - if (!block->instructions) exit(1); + block->instructions, new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); block->instructions_cap = new_cap; } block->instructions[block->instructions_len++] = block_inst_idx; - TypeIndex resolved_ty = resolvePeerType(sema, - label.merges.results[0], - label.merges.results[1]); + TypeIndex resolved_ty = resolvePeerType( + sema, label.merges.results[0], label.merges.results[1]); uint32_t extra_start - = addAirExtra(sema, - child_block.instructions_len); - for (uint32_t ci = 0; - ci < child_block.instructions_len; ci++) { - addAirExtra(sema, - child_block.instructions[ci]); + = addAirExtra(sema, child_block.instructions_len); + for (uint32_t ci = 0; ci < child_block.instructions_len; + ci++) { + addAirExtra(sema, child_block.instructions[ci]); } sema->air_inst_datas[block_inst_idx].ty_pl.ty_ref = AIR_REF_FROM_IP(resolved_ty); sema->air_inst_datas[block_inst_idx].ty_pl.payload = extra_start; + // Coerce BR operands to the resolved peer type. + // Ported from src/Sema.zig resolveAnalyzedBlock + // lines 6125-6140. + for (uint32_t mi = 0; mi < label.merges.results_len; mi++) { + uint32_t br = label.merges.br_list[mi]; + AirInstRef br_operand + = sema->air_inst_datas[br].br.operand; + TypeIndex br_ty = semaTypeOf(sema, br_operand); + if (br_ty == resolved_ty) + continue; + // Coerce. For constant→constant (e.g. + // comptime_int→concrete int), this produces + // no new AIR instructions. + SemaBlock coerce_block; + semaBlockInit(&coerce_block, sema, block); + coerce_block.is_comptime = false; + coerce_block.want_safety = block->want_safety; + coerce_block.want_safety_set = block->want_safety_set; + coerce_block.inlining = block->inlining; + AirInstRef coerced = semaCoerce( + sema, &coerce_block, resolved_ty, br_operand); + if (coerce_block.instructions_len == 0) { + sema->air_inst_datas[br].br.operand = coerced; + } else { + // Runtime coercion: wrap BR in + // a sub-block. Ported from + // Sema.zig lines 6141-6170. + assert(coerce_block + .instructions[coerce_block.instructions_len + - 1] + == AIR_REF_TO_INST(coerced)); + uint32_t sub_bl = coerce_block.instructions_len + 1; + uint32_t sub_br_idx = addAirInst(sema, AIR_INST_BR, + (AirInstData) { + .br = { .block_inst = label.merges.block_inst, + .operand = coerced } }); + uint32_t sub_extra = addAirExtra(sema, sub_bl); + for (uint32_t si = 0; + si < coerce_block.instructions_len; si++) { + addAirExtra(sema, coerce_block.instructions[si]); + } + addAirExtra(sema, sub_br_idx); + sema->air_inst_tags[br] = AIR_INST_BLOCK; + sema->air_inst_datas[br].ty_pl.ty_ref + = AIR_REF_FROM_IP(resolved_ty); + sema->air_inst_datas[br].ty_pl.payload = sub_extra; + } + semaBlockDeinit(&coerce_block); + } + block_result = AIR_REF_FROM_INST(block_inst_idx); } @@ -5145,13 +5183,9 @@ static bool analyzeBodyInner( = (sema->code.inst_tags[inst] == ZIR_INST_BOOL_BR_OR); uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef lhs_ref - = sema->code.extra[payload_index]; - uint32_t rhs_body_len - = sema->code.extra[payload_index + 1]; - const uint32_t* rhs_body - = &sema->code.extra[payload_index + 2]; - + ZirInstRef lhs_ref = sema->code.extra[payload_index]; + uint32_t rhs_body_len = sema->code.extra[payload_index + 1]; + const uint32_t* rhs_body = &sema->code.extra[payload_index + 2]; AirInstRef lhs = resolveInst(sema, lhs_ref); lhs = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, lhs); @@ -5162,8 +5196,7 @@ static bool analyzeBodyInner( i++; continue; } - if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE) - && !is_bool_or) { + if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE) && !is_bool_or) { instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)); i++; @@ -5172,8 +5205,8 @@ static bool analyzeBodyInner( // Comptime-known LHS, evaluate RHS body on parent block. if (lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE) || lhs == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) { - bool completed = analyzeBodyInner( - sema, block, rhs_body, rhs_body_len); + bool completed + = analyzeBodyInner(sema, block, rhs_body, rhs_body_len); AirInstRef rhs_result; if (!completed) { uint32_t bi = sema->comptime_break_inst; @@ -5183,19 +5216,18 @@ static bool analyzeBodyInner( } else { rhs_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - rhs_result = semaCoerce( - sema, block, IP_INDEX_BOOL_TYPE, rhs_result); + rhs_result + = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, rhs_result); instMapPut(&sema->inst_map, inst, rhs_result); i++; continue; } // Runtime: emit BLOCK(bool) + COND_BR with then/else bodies. - uint32_t block_inst_idx = addAirInst(sema, - AIR_INST_BLOCK, - (AirInstData){ .ty_pl = { - .ty_ref = AIR_REF_FROM_IP(IP_INDEX_BOOL_TYPE), - .payload = 0 } }); + uint32_t block_inst_idx = addAirInst(sema, AIR_INST_BLOCK, + (AirInstData) { + .ty_pl = { .ty_ref = AIR_REF_FROM_IP(IP_INDEX_BOOL_TYPE), + .payload = 0 } }); SemaBlockLabel label; memset(&label, 0, sizeof(label)); @@ -5204,7 +5236,8 @@ static bool analyzeBodyInner( SemaBlock child_block; semaBlockInit(&child_block, sema, block); - child_block.is_comptime = false; + // Inherit comptime from parent (matches Zig Sema.zig). + child_block.is_comptime = block->is_comptime; child_block.want_safety = block->want_safety; child_block.want_safety_set = block->want_safety_set; child_block.inlining = block->inlining; @@ -5229,10 +5262,8 @@ static bool analyzeBodyInner( else_block.label = child_block.label; else_block.need_debug_scope = NULL; - SemaBlock* lhs_block - = is_bool_or ? &then_block : &else_block; - SemaBlock* rhs_block - = is_bool_or ? &else_block : &then_block; + SemaBlock* lhs_block = is_bool_or ? &then_block : &else_block; + SemaBlock* rhs_block = is_bool_or ? &else_block : &then_block; // LHS block: emit BR with short-circuit result. AirInstRef lhs_result = is_bool_or @@ -5245,8 +5276,8 @@ static bool analyzeBodyInner( br_data.br.operand = lhs_result; AirInstRef br_ref = blockAddInst(lhs_block, AIR_INST_BR, br_data); - mergesAppend(&label.merges, - lhs_result, AIR_REF_TO_INST(br_ref)); + mergesAppend( + &label.merges, lhs_result, AIR_REF_TO_INST(br_ref)); } // Save/restore branch hint across RHS body. @@ -5254,8 +5285,8 @@ static bool analyzeBodyInner( sema->branch_hint = -1; // RHS block: evaluate body (ends with break_inline). - bool rhs_completed = analyzeBodyInner( - sema, rhs_block, rhs_body, rhs_body_len); + bool rhs_completed + = analyzeBodyInner(sema, rhs_block, rhs_body, rhs_body_len); AirInstRef rhs_result; if (!rhs_completed) { uint32_t bi = sema->comptime_break_inst; @@ -5265,8 +5296,8 @@ static bool analyzeBodyInner( } else { rhs_result = AIR_REF_FROM_IP(IP_INDEX_VOID_VALUE); } - rhs_result = semaCoerce( - sema, rhs_block, IP_INDEX_BOOL_TYPE, rhs_result); + rhs_result + = semaCoerce(sema, rhs_block, IP_INDEX_BOOL_TYPE, rhs_result); // Emit BR for RHS result. { @@ -5276,12 +5307,12 @@ static bool analyzeBodyInner( br_data.br.operand = rhs_result; AirInstRef br_ref = blockAddInst(rhs_block, AIR_INST_BR, br_data); - mergesAppend(&label.merges, - rhs_result, AIR_REF_TO_INST(br_ref)); + mergesAppend( + &label.merges, rhs_result, AIR_REF_TO_INST(br_ref)); } - uint8_t rhs_hint = (sema->branch_hint >= 0) - ? (uint8_t)sema->branch_hint : 0; + uint8_t rhs_hint + = (sema->branch_hint >= 0) ? (uint8_t)sema->branch_hint : 0; sema->branch_hint = parent_hint; // Emit COND_BR in child_block. @@ -5301,12 +5332,10 @@ static bool analyzeBodyInner( branch_hints_packed |= (1u << 7); // else_cov = poi addAirExtra(sema, branch_hints_packed); - for (uint32_t ti = 0; - ti < then_block.instructions_len; ti++) { + for (uint32_t ti = 0; ti < then_block.instructions_len; ti++) { addAirExtra(sema, then_block.instructions[ti]); } - for (uint32_t ei = 0; - ei < else_block.instructions_len; ei++) { + for (uint32_t ei = 0; ei < else_block.instructions_len; ei++) { addAirExtra(sema, else_block.instructions[ei]); } @@ -5314,26 +5343,23 @@ static bool analyzeBodyInner( memset(&cond_data, 0, sizeof(cond_data)); cond_data.pl_op.operand = lhs; cond_data.pl_op.payload = extra_start; - (void)blockAddInst( - &child_block, AIR_INST_COND_BR, cond_data); + (void)blockAddInst(&child_block, AIR_INST_COND_BR, cond_data); // Resolve the block (same as zirBlock resolveAnalyzedBlock). // bool_br always has 2 merges (lhs + rhs BR). if (block->instructions_len >= block->instructions_cap) { uint32_t new_cap = block->instructions_cap * 2; - block->instructions = realloc( - block->instructions, - new_cap * sizeof(uint32_t)); - if (!block->instructions) exit(1); + block->instructions + = realloc(block->instructions, new_cap * sizeof(uint32_t)); + if (!block->instructions) + exit(1); block->instructions_cap = new_cap; } - block->instructions[block->instructions_len++] - = block_inst_idx; + block->instructions[block->instructions_len++] = block_inst_idx; uint32_t blk_extra_start = addAirExtra(sema, child_block.instructions_len); - for (uint32_t ci = 0; - ci < child_block.instructions_len; ci++) { + for (uint32_t ci = 0; ci < child_block.instructions_len; ci++) { addAirExtra(sema, child_block.instructions[ci]); } sema->air_inst_datas[block_inst_idx].ty_pl.payload @@ -5344,8 +5370,8 @@ static bool analyzeBodyInner( semaBlockDeinit(&else_block); semaBlockDeinit(&then_block); semaBlockDeinit(&child_block); - instMapPut(&sema->inst_map, inst, - AIR_REF_FROM_INST(block_inst_idx)); + instMapPut( + &sema->inst_map, inst, AIR_REF_FROM_INST(block_inst_idx)); i++; continue; } @@ -5355,28 +5381,24 @@ static bool analyzeBodyInner( case ZIR_INST_CONDBR: { uint32_t payload_index = sema->code.inst_datas[inst].pl_node.payload_index; - ZirInstRef condition_ref - = sema->code.extra[payload_index]; - uint32_t then_body_len - = sema->code.extra[payload_index + 1]; - uint32_t else_body_len - = sema->code.extra[payload_index + 2]; - const uint32_t* then_body - = &sema->code.extra[payload_index + 3]; + ZirInstRef condition_ref = sema->code.extra[payload_index]; + uint32_t then_body_len = sema->code.extra[payload_index + 1]; + uint32_t else_body_len = sema->code.extra[payload_index + 2]; + const uint32_t* then_body = &sema->code.extra[payload_index + 3]; const uint32_t* else_body = &sema->code.extra[payload_index + 3 + then_body_len]; - AirInstRef cond = resolveInst(sema, condition_ref); + AirInstRef uncasted_cond = resolveInst(sema, condition_ref); + AirInstRef cond + = semaCoerce(sema, block, IP_INDEX_BOOL_TYPE, uncasted_cond); // Comptime-known condition: only analyze the taken branch // on the parent block (matches Zig sema line 18343-18353). if (cond == AIR_REF_FROM_IP(IP_INDEX_BOOL_TRUE)) { - return analyzeBodyInner( - sema, block, then_body, then_body_len); + return analyzeBodyInner(sema, block, then_body, then_body_len); } if (cond == AIR_REF_FROM_IP(IP_INDEX_BOOL_FALSE)) { - return analyzeBodyInner( - sema, block, else_body, else_body_len); + return analyzeBodyInner(sema, block, else_body, else_body_len); } // Analyze then-body in a sub-block, collecting branch hint. @@ -5405,8 +5427,6 @@ static bool analyzeBodyInner( else_block.need_debug_scope = NULL; uint8_t false_hint = analyzeBodyRuntimeBreak( sema, &else_block, else_body, else_body_len); - - // Emit AIR_INST_COND_BR. // Extra data: {then_body_len, else_body_len, branch_hints, // then_body[...], else_body[...]} uint32_t extra_start @@ -5425,12 +5445,10 @@ static bool analyzeBodyInner( branch_hints_packed |= (1u << 7); // else_cov = poi addAirExtra(sema, branch_hints_packed); - for (uint32_t ti = 0; - ti < then_block.instructions_len; ti++) { + for (uint32_t ti = 0; ti < then_block.instructions_len; ti++) { addAirExtra(sema, then_block.instructions[ti]); } - for (uint32_t ei = 0; - ei < else_block.instructions_len; ei++) { + for (uint32_t ei = 0; ei < else_block.instructions_len; ei++) { addAirExtra(sema, else_block.instructions[ei]); } @@ -5468,17 +5486,22 @@ static bool analyzeBodyInner( continue; } + if (block->is_comptime) { + // Comptime break: record result without emitting BR. + // Ported from src/Sema.zig zirBreak comptime path. + mergesAppend(&label->merges, operand, 0); + return false; + } + // Emit AIR br instruction. AirInstData br_data; memset(&br_data, 0, sizeof(br_data)); br_data.br.block_inst = label->merges.block_inst; br_data.br.operand = operand; - AirInstRef br_ref - = blockAddInst(block, AIR_INST_BR, br_data); + AirInstRef br_ref = blockAddInst(block, AIR_INST_BR, br_data); // Record merge result. - mergesAppend(&label->merges, - operand, AIR_REF_TO_INST(br_ref)); + mergesAppend(&label->merges, operand, AIR_REF_TO_INST(br_ref)); return false; // break is terminal } @@ -5488,8 +5511,7 @@ static bool analyzeBodyInner( // unreach. case ZIR_INST_PANIC: { // Resolve the string message operand. - ZirInstRef msg_ref - = sema->code.inst_datas[inst].un_node.operand; + ZirInstRef msg_ref = sema->code.inst_datas[inst].un_node.operand; AirInstRef msg = resolveInst(sema, msg_ref); (void)msg; // msg used in call args below @@ -5551,13 +5573,12 @@ static bool analyzeBodyInner( // Saves/restores the parent hint, resets hint for the body, // returns the collected hint (0=none if no @branchHint was set). static uint8_t analyzeBodyRuntimeBreak( - Sema* sema, SemaBlock* block, - const uint32_t* body, uint32_t body_len) { + Sema* sema, SemaBlock* block, const uint32_t* body, uint32_t body_len) { int8_t parent_hint = sema->branch_hint; sema->branch_hint = -1; - (void)analyzeBodyInner(sema, block, body, body_len); - uint8_t result = (sema->branch_hint >= 0) - ? (uint8_t)sema->branch_hint : 0; + bool inner_ok = analyzeBodyInner(sema, block, body, body_len); + (void)inner_ok; + uint8_t result = (sema->branch_hint >= 0) ? (uint8_t)sema->branch_hint : 0; sema->branch_hint = parent_hint; return result; } diff --git a/stage0/sema.h b/stage0/sema.h @@ -51,6 +51,7 @@ typedef struct SemaBlockInlining { InternPoolIndex func; bool is_generic_instantiation; bool has_comptime_args; + bool comptime_returned; AirInstRef comptime_result; SemaBlockMerges merges; } SemaBlockInlining; diff --git a/stage0/sema_test.zig b/stage0/sema_test.zig @@ -233,7 +233,8 @@ extern fn zig_compile_air([*:0]const u8, ?[*:0]const u8, [*]u8) ZigCompileAirRes extern fn zig_compile_air_free(*ZigCompileAirResult) void; pub fn airCompareFromSource(source: [:0]const u8, c_func_air_list: c.SemaFuncAirList) !void { - const tmp_path = "/tmp/zig0_sema_test_tmp.zig"; + var buf: [64]u8 = undefined; + const tmp_path = std.fmt.bufPrintZ(&buf, "/tmp/zig0_sema_{d}.zig", .{std.os.linux.getpid()}) catch unreachable; { const f = std.fs.cwd().createFile(tmp_path, .{}) catch return error.TmpFileCreate; defer f.close(); @@ -498,6 +499,10 @@ fn airDataRefSlots(tag_val: u8) [2]bool { => .{ true, true }, // arg: type(Ref) + zir_param_index(u32) c.AIR_INST_ARG => .{ true, false }, + // br: block_inst(u32) + operand(Ref) + c.AIR_INST_BR => .{ false, true }, + // pl_op (cond_br): operand(Ref) + payload(u32) + c.AIR_INST_COND_BR => .{ true, false }, // Default: assume no refs (compare directly). // If a tag with refs is missed, the comparison will fail // and we add it here. @@ -600,18 +605,23 @@ fn airCompareOne(name: []const u8, zig_air: *const c.Air, c_air: *const c.Air) ! const zig_word = std.mem.readInt(u32, zig_datas[s..][0..4], .little); const c_word = std.mem.readInt(u32, c_datas[s..][0..4], .little); + // Skip data comparison for dead BLOCKs (tag 51). + // Dead BLOCKs have undefined data in Zig (0xaa.. + // or stale values) vs zeroed in C. + if (tag_val == 51 and c_word == 0 and zig_word != 0) continue; + if (ref_slots[slot]) { // This slot is a Ref — canonicalize IP refs. const zig_canon = canonicalizeRef(zig_word, &zig_ref_map, &next_zig_id); const c_canon = canonicalizeRef(c_word, &c_ref_map, &next_c_id); if (zig_canon != c_canon) { - std.debug.print("'{s}': datas ref mismatch at inst[{d}] slot {d}: zig=0x{x} c=0x{x} (canon: zig={d} c={d})\n", .{ name, j, slot, zig_word, c_word, zig_canon, c_canon }); + std.debug.print("'{s}': datas ref mismatch at inst[{d}] slot {d}: zig=0x{x} c=0x{x} (canon: zig={d} c={d}) (tag={d})\n", .{ name, j, slot, zig_word, c_word, zig_canon, c_canon, tag_val }); return error.AirMismatch; } } else { // Non-ref field — compare directly. if (zig_word != c_word) { - std.debug.print("'{s}': datas mismatch at inst[{d}] slot {d}: zig=0x{x} c=0x{x}\n", .{ name, j, slot, zig_word, c_word }); + std.debug.print("'{s}': datas mismatch at inst[{d}] slot {d}: zig=0x{x} c=0x{x} (tag={d})\n", .{ name, j, slot, zig_word, c_word, tag_val }); return error.AirMismatch; } } @@ -622,6 +632,36 @@ fn airCompareOne(name: []const u8, zig_air: *const c.Air, c_air: *const c.Air) ! // Extra if (zig_air.extra_len != c_air.extra_len) { std.debug.print("'{s}': extra_len mismatch: zig={d} c={d}\n", .{ name, zig_air.extra_len, c_air.extra_len }); + // Print first divergence point + const min_len = @min(zig_air.extra_len, c_air.extra_len); + if (min_len > 0) { + const zig_e: [*]const u32 = cToOpt(u32, zig_air.extra).?; + const c_e: [*]const u32 = cToOpt(u32, c_air.extra).?; + var printed: u32 = 0; + for (0..min_len) |ei| { + if (zig_e[ei] != c_e[ei] and printed < 40) { + std.debug.print(" extra[{d}]: zig={d} c={d}\n", .{ ei, zig_e[ei], c_e[ei] }); + printed += 1; + } + } + // Also dump the raw extra arrays around the first divergence + var first_diff: usize = min_len; + for (0..min_len) |ei| { + if (zig_e[ei] != c_e[ei]) { + first_diff = ei; + break; + } + } + if (first_diff < min_len) { + const start = if (first_diff > 5) first_diff - 5 else 0; + const end = @min(first_diff + 20, min_len); + std.debug.print(" zig extra[{d}..{d}]:", .{ start, end }); + for (start..end) |ei| std.debug.print(" {d}", .{zig_e[ei]}); + std.debug.print("\n c extra[{d}..{d}]:", .{ start, end }); + for (start..end) |ei| std.debug.print(" {d}", .{c_e[ei]}); + std.debug.print("\n", .{}); + } + } return error.AirMismatch; } const extra_len = zig_air.extra_len; diff --git a/stage0/stages_test.zig b/stage0/stages_test.zig @@ -61,7 +61,11 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) // the module root, while keeping logical paths under .zig-cache/tmp/ // to avoid 'std' module conflicts with lib/std/. const this_dir = comptime std.fs.path.dirname(@src().file) orelse "."; - const symlink_path = ".zig-cache/tmp/zig0_test"; + + // Use PID-based symlink path to avoid races when all-zig0 runs + // multiple compilers in parallel. + var symlink_buf: [64:0]u8 = undefined; + const symlink_path = std.fmt.bufPrintZ(&symlink_buf, ".zig-cache/tmp/zig0_test_{d}", .{std.os.linux.getpid()}) catch unreachable; // All corpus paths start with "../"; strip to get repo-relative path. const repo_relative = comptime blk: { @@ -79,7 +83,8 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) // Compute source directory for import resolution. const repo_dir = comptime std.fs.path.dirname(repo_relative) orelse "."; - const source_dir_path: [:0]const u8 = symlink_path ++ "/" ++ repo_dir; + var source_dir_buf: [128:0]u8 = undefined; + const source_dir_path = std.fmt.bufPrintZ(&source_dir_buf, "{s}/{s}", .{ symlink_path, repo_dir }) catch unreachable; var c_ip = sc.ipInit(); defer sc.ipDeinit(&c_ip); @@ -90,9 +95,9 @@ fn stagesCheck(gpa: Allocator, comptime path: []const u8, source: [:0]const u8) var c_func_air_list = sc.semaAnalyze(&c_sema); defer sc.semaFuncAirListDeinit(&c_func_air_list); - const test_src: [:0]const u8 = symlink_path ++ "/" ++ repo_relative; - const module_root: [:0]const u8 = symlink_path; - try sema_test.airCompare(test_src.ptr, module_root.ptr, c_func_air_list); + var test_src_buf: [256:0]u8 = undefined; + const test_src = std.fmt.bufPrintZ(&test_src_buf, "{s}/{s}", .{ symlink_path, repo_relative }) catch unreachable; + try sema_test.airCompare(test_src.ptr, symlink_path.ptr, c_func_air_list); } } @@ -108,7 +113,7 @@ const corpus_files = .{ "../lib/compiler_rt/absvdi2.zig", // 311 -- needs alloc_mut, block, condbr, panic, call "../lib/compiler_rt/absvsi2.zig", // 311 "../lib/compiler_rt/absvti2.zig", // 314 - //"../lib/compiler_rt/addhf3.zig", // 319 -- needs @import, need_debug_scope/ensurePostHoc phantom blocks + "../lib/compiler_rt/addhf3.zig", // 319 //"../lib/compiler_rt/addxf3.zig", // 323 //"../lib/compiler_rt/mulhf3.zig", // 323 //"../lib/compiler_rt/mulxf3.zig", // 323