sema: port compareIntsOnlyPossibleResult, fix int coercion dedup, bump 75→80

Port compareIntsOnlyPossibleResult from upstream Sema.zig (line 32511):
when a comparison has one comptime operand, intern the other type's
min/max bounds as side effects. This matches the Zig compiler's
IP entry sequence for comparison operations.

Fix semaCoerceIntRef: switch from ipForceIntern to ipIntern for
integer type coercion. The cross-shard dedup issue is now handled
by the skip_dedup mechanism, so ipForceIntern is no longer needed.
Using ipIntern allows proper deduplication of identical coerced
values (e.g., two comparisons with `> 0` share the same int_u32(0)).

Tests 75-79 now pass (var_bitcast_and_if, shr_exact, nested_if,
conditional_bitwise_and, conditional_sub).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-03 07:33:39 +00:00
parent 072929759e
commit e6cda567d0
2 changed files with 66 additions and 8 deletions

View File

@@ -3,7 +3,7 @@
/// `num_passing` controls how many files are tested and pre-generated.
/// Both build.zig and stages_test.zig import this file.
/// To enable more tests: just increment `num_passing`.
pub const num_passing: usize = 75;
pub const num_passing: usize = 80;
pub const files = [_][]const u8{
"stage0/sema_tests/empty.zig",

View File

@@ -532,12 +532,10 @@ static AirInstRef semaCoerceIntRef(
return air_ref; // already the right type
// Create a new IP entry with the target type but same value.
// Use ipForceIntern to skip deduplication: the Zig compiler's
// sharded IP creates separate entries in different shards (e.g.
// CallingConvention enum values in the memoized shard vs function
// body values in the function shard). The C sema's single IP
// would incorrectly deduplicate, returning an index from the
// memoized state instead of creating a new function-local entry.
// Use ipIntern for deduplication: entries with the same type and
// value are shared. Cross-shard separation is handled by the
// skip_dedup mechanism (preamble entries are skipped, main analysis
// entries dedup normally).
InternPoolKey new_key;
memset(&new_key, 0, sizeof(new_key));
new_key.tag = IP_KEY_INT;
@@ -545,7 +543,7 @@ static AirInstRef semaCoerceIntRef(
new_key.data.int_val.value_lo = key.data.int_val.value_lo;
new_key.data.int_val.value_hi = key.data.int_val.value_hi;
new_key.data.int_val.is_negative = key.data.int_val.is_negative;
uint32_t new_ip_idx = ipForceIntern(sema->ip, new_key);
uint32_t new_ip_idx = ipIntern(sema->ip, new_key);
return AIR_REF_FROM_IP(new_ip_idx);
}
@@ -1324,6 +1322,66 @@ static AirInstRef zirArithmetic(
}
emit_runtime:;
// Ported from Sema.zig cmpNumeric → compareIntsOnlyPossibleResult
// (line 32299-32325): when one operand is comptime-known and the
// other is a runtime integer, intern the type's min/max bounds.
// These entries are side effects of the bounds check; even if the
// comparison can't be folded, the boundary values must be interned
// to match the Zig compiler's IP entry sequence.
if (air_tag >= AIR_INST_CMP_LT && air_tag <= AIR_INST_CMP_GT) {
TypeIndex lhs_ty = semaTypeOf(sema, lhs);
TypeIndex rhs_ty = semaTypeOf(sema, rhs);
bool lhs_is_ct = (lhs_ty == IP_INDEX_COMPTIME_INT_TYPE);
bool rhs_is_ct = (rhs_ty == IP_INDEX_COMPTIME_INT_TYPE);
if ((lhs_is_ct != rhs_is_ct) && !lhs_is_ct
&& sema->ip->items[lhs_ty].tag == IP_KEY_INT_TYPE) {
// RHS is comptime, LHS is runtime int → intern LHS
// type bounds.
uint16_t bits = sema->ip->items[lhs_ty].data.int_type.bits;
bool is_signed = sema->ip->items[lhs_ty].data.int_type.signedness;
uint64_t min_val = 0;
uint64_t max_val
= (bits >= 64) ? UINT64_MAX : ((1ULL << bits) - 1);
if (is_signed) {
min_val = (bits >= 64) ? (uint64_t)INT64_MIN
: ~((1ULL << (bits - 1)) - 1);
max_val = (bits >= 64) ? (uint64_t)INT64_MAX
: ((1ULL << (bits - 1)) - 1);
}
InternPoolKey mk;
memset(&mk, 0, sizeof(mk));
mk.tag = IP_KEY_INT;
mk.data.int_val.ty = lhs_ty;
mk.data.int_val.value_lo = min_val;
(void)ipIntern(sema->ip, mk);
mk.data.int_val.value_lo = max_val;
(void)ipIntern(sema->ip, mk);
} else if ((lhs_is_ct != rhs_is_ct) && !rhs_is_ct
&& sema->ip->items[rhs_ty].tag == IP_KEY_INT_TYPE) {
// LHS is comptime, RHS is runtime int → intern RHS
// type bounds.
uint16_t bits = sema->ip->items[rhs_ty].data.int_type.bits;
bool is_signed = sema->ip->items[rhs_ty].data.int_type.signedness;
uint64_t min_val = 0;
uint64_t max_val
= (bits >= 64) ? UINT64_MAX : ((1ULL << bits) - 1);
if (is_signed) {
min_val = (bits >= 64) ? (uint64_t)INT64_MIN
: ~((1ULL << (bits - 1)) - 1);
max_val = (bits >= 64) ? (uint64_t)INT64_MAX
: ((1ULL << (bits - 1)) - 1);
}
InternPoolKey mk;
memset(&mk, 0, sizeof(mk));
mk.tag = IP_KEY_INT;
mk.data.int_val.ty = rhs_ty;
mk.data.int_val.value_lo = min_val;
(void)ipIntern(sema->ip, mk);
mk.data.int_val.value_lo = max_val;
(void)ipIntern(sema->ip, mk);
}
}
TypeIndex peer_ty = semaResolvePeerTypes(sema, lhs, rhs);
lhs = semaCoerce(sema, block, peer_ty, lhs);
rhs = semaCoerce(sema, block, peer_ty, rhs);