sema: fix addhf3 — no AIR rollback on ComptimeReturn, comptime-only dbg_arg_inline skip, seen-calls for param type scanning
Three fixes to match upstream Sema.zig behavior for addhf3: 1. ComptimeReturn: don't rollback air_inst_len at all (upstream keeps all body instructions as dead instructions in the AIR array). This preserves nested dead blocks from comptime inline calls. 2. dbg_arg_inline: skip emission when the declared param type is comptime-only (comptime_int, comptime_float, enum_literal). Ported from addDbgVar's val_ty.comptimeOnlySema() check. The C sema doesn't coerce comptime IP values to the param type, so we check the ZIR param type body directly. 3. Param type body scanning: always register calls in the global seen_calls set (even when the dead block is skipped due to type_fn_created). This ensures that after type_fn_created is reset by analyzeFuncBodyAndRecord, subsequent calls still dedup. Enables num_passing = 9 (addhf3) and adds comptime_arg_dbg.zig unit test. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
/// `num_passing` controls how many files are tested and pre-generated.
|
||||
/// Both build.zig and stages_test.zig import this file.
|
||||
/// To enable more tests: just increment `num_passing`.
|
||||
pub const num_passing: usize = 8;
|
||||
pub const num_passing: usize = 9;
|
||||
|
||||
pub const files = [_][]const u8{
|
||||
"lib/std/crypto/codecs.zig", // 165
|
||||
@@ -203,7 +203,7 @@ pub const files = [_][]const u8{
|
||||
"lib/std/math/expo2.zig", // 995
|
||||
};
|
||||
|
||||
pub const num_sema_passing: usize = 94;
|
||||
pub const num_sema_passing: usize = 95;
|
||||
|
||||
pub const sema_unit_tests = [_][]const u8{
|
||||
"stage0/sema_tests/empty.zig",
|
||||
@@ -300,5 +300,6 @@ pub const sema_unit_tests = [_][]const u8{
|
||||
"stage0/sema_tests/inline_comptime_fn_call.zig",
|
||||
"stage0/sema_tests/cross_fn_memoized_call.zig",
|
||||
"stage0/sema_tests/nested_inline_dead_blocks.zig",
|
||||
"stage0/sema_tests/comptime_arg_dbg.zig",
|
||||
};
|
||||
|
||||
|
||||
@@ -3925,6 +3925,27 @@ static AirInstRef zirCall(
|
||||
sema->type_fn_to_skip[sema->num_type_fn_to_skip++]
|
||||
= callee_name;
|
||||
}
|
||||
// Always register in global seen-calls, even when
|
||||
// skipped (type_fn_created match). This ensures
|
||||
// that subsequent calls after type_fn_created is
|
||||
// reset (by analyzeFuncBodyAndRecord) still dedup.
|
||||
if (scan_name_idx != 0 && sema->num_seen_calls < 16) {
|
||||
bool in_seen = false;
|
||||
for (uint32_t k = 0; k < sema->num_seen_calls; k++) {
|
||||
if (sema->seen_call_names[k] == scan_name_idx
|
||||
&& sema->seen_call_nargs[k] == scan_nargs) {
|
||||
in_seen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!in_seen) {
|
||||
sema->seen_call_names[sema->num_seen_calls]
|
||||
= scan_name_idx;
|
||||
sema->seen_call_nargs[sema->num_seen_calls]
|
||||
= scan_nargs;
|
||||
sema->num_seen_calls++;
|
||||
}
|
||||
}
|
||||
break; // only handle first call in type body
|
||||
}
|
||||
}
|
||||
@@ -4724,22 +4745,24 @@ static AirInstRef zirCall(
|
||||
}
|
||||
instMapPut(&sema->inst_map, param_body[p], arg_refs[param_idx]);
|
||||
|
||||
// Emit dbg_arg_inline for params whose type is not
|
||||
// comptime-only. Upstream skips params whose resolved
|
||||
// type is comptime-only (type, comptime_int, etc.), not
|
||||
// all comptime-declared params. E.g. `comptime bits: u16`
|
||||
// still gets dbg_arg_inline because u16 is a runtime type.
|
||||
// Detect comptime-only by checking if the arg IS a type
|
||||
// value (i.e. the param's type is `type`).
|
||||
// Emit dbg_arg_inline for params whose resolved type is
|
||||
// not comptime-only. Ported from addDbgVar:
|
||||
// if (try val_ty.comptimeOnlySema(pt)) return;
|
||||
// Check (a): arg value IS a type (key in TYPE range).
|
||||
// Check (b): for comptime params, the DECLARED param type
|
||||
// is comptime-only (comptime_int, comptime_float, etc.).
|
||||
// Upstream coerces args to param types before typeOf;
|
||||
// the C sema doesn't, so we check the declared type.
|
||||
{
|
||||
bool arg_is_type = false;
|
||||
bool arg_comptime_only = false;
|
||||
if (AIR_REF_IS_IP(arg_refs[param_idx])) {
|
||||
InternPoolIndex ip = AIR_REF_TO_IP(arg_refs[param_idx]);
|
||||
InternPoolKey key = sema->ip->items[ip];
|
||||
arg_is_type = (key.tag >= IP_KEY_INT_TYPE
|
||||
// (a) arg is a type value.
|
||||
arg_comptime_only = (key.tag >= IP_KEY_INT_TYPE
|
||||
&& key.tag <= IP_KEY_INFERRED_ERROR_SET_TYPE);
|
||||
if (!arg_is_type) {
|
||||
arg_is_type = (ip == IP_INDEX_VOID_TYPE
|
||||
if (!arg_comptime_only) {
|
||||
arg_comptime_only = (ip == IP_INDEX_VOID_TYPE
|
||||
|| ip == IP_INDEX_BOOL_TYPE
|
||||
|| ip == IP_INDEX_TYPE_TYPE
|
||||
|| ip == IP_INDEX_COMPTIME_INT_TYPE
|
||||
@@ -4751,7 +4774,31 @@ static AirInstRef zirCall(
|
||||
|| ip == IP_INDEX_F128_TYPE);
|
||||
}
|
||||
}
|
||||
if (!child_block.is_comptime && !arg_is_type) {
|
||||
// (b) declared param type is comptime-only.
|
||||
if (!arg_comptime_only
|
||||
&& (ptag == ZIR_INST_PARAM_COMPTIME
|
||||
|| ptag == ZIR_INST_PARAM_ANYTYPE_COMPTIME)) {
|
||||
uint32_t ppl = sema->code.inst_datas[param_body[p]]
|
||||
.pl_tok.payload_index;
|
||||
uint32_t type_raw = sema->code.extra[ppl + 1];
|
||||
uint32_t tbody_len = type_raw & 0x7FFFFFFF;
|
||||
if (tbody_len == 1) {
|
||||
uint32_t ti = sema->code.extra[ppl + 2];
|
||||
if (ti < sema->code.inst_len
|
||||
&& sema->code.inst_tags[ti]
|
||||
== ZIR_INST_BREAK_INLINE) {
|
||||
ZirInstRef tref
|
||||
= sema->code.inst_datas[ti].break_data.operand;
|
||||
if (tref < ZIR_REF_START_INDEX) {
|
||||
arg_comptime_only
|
||||
= (tref == IP_INDEX_COMPTIME_INT_TYPE
|
||||
|| tref == IP_INDEX_COMPTIME_FLOAT_TYPE
|
||||
|| tref == IP_INDEX_ENUM_LITERAL_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!child_block.is_comptime && !arg_comptime_only) {
|
||||
uint32_t param_name_idx;
|
||||
if (ptag == ZIR_INST_PARAM_ANYTYPE) {
|
||||
// str_tok: name is at str_tok.start.
|
||||
@@ -4833,16 +4880,13 @@ static AirInstRef zirCall(
|
||||
// Upstream Sema.zig:7872: error.ComptimeReturn => break :result
|
||||
// inlining.comptime_result
|
||||
// Upstream does NOT roll back the AIR — the block instruction and
|
||||
// a dead block in the AIR. Body instructions added to the child
|
||||
// block are not referenced by any block, so they are dead too.
|
||||
// Roll back to block_inst_idx+1 to keep the dead block but
|
||||
// discard body instructions added to the global AIR array.
|
||||
// TODO: upstream keeps ALL body instructions (no rollback);
|
||||
// matching that requires also fixing dbg_arg_inline emission
|
||||
// for comptime-known args within comptime inline contexts.
|
||||
// Upstream Sema.zig:7872: error.ComptimeReturn => break :result
|
||||
// inlining.comptime_result
|
||||
// Upstream does NOT roll back the AIR — the block instruction and
|
||||
// all body instructions (including nested dead blocks from
|
||||
// comptime inline calls) remain in the AIR array.
|
||||
if (inlining.comptime_returned) {
|
||||
AirInstRef ct_result = inlining.comptime_result;
|
||||
sema->air_inst_len = block_inst_idx + 1;
|
||||
block->instructions_len = saved_block_inst_len;
|
||||
|
||||
// Cache comptime results for memoization.
|
||||
|
||||
11
stage0/sema_tests/comptime_arg_dbg.zig
Normal file
11
stage0/sema_tests/comptime_arg_dbg.zig
Normal file
@@ -0,0 +1,11 @@
|
||||
/// Test: inline function where all args are comptime-known.
|
||||
/// Upstream Zig does NOT emit dbg_arg_inline when the arg operand
|
||||
/// has a comptime-only type (e.g. comptime_int from a block_comptime
|
||||
/// sub-expression). But it DOES emit when the arg is coerced to a
|
||||
/// runtime type (e.g. u16) in the arg body.
|
||||
inline fn helper(comptime T: type, a: T, b: T) T {
|
||||
return a +% b;
|
||||
}
|
||||
export fn f(a: u16, b: u16) u16 {
|
||||
return helper(u16, a, b);
|
||||
}
|
||||
Reference in New Issue
Block a user