From e6cda567d009f8cdccd1b0b1611231483d91acda Mon Sep 17 00:00:00 2001 From: Motiejus Date: Tue, 3 Mar 2026 07:33:39 +0000 Subject: [PATCH] =?UTF-8?q?sema:=20port=20compareIntsOnlyPossibleResult,?= =?UTF-8?q?=20fix=20int=20coercion=20dedup,=20bump=2075=E2=86=9280?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- stage0/corpus.zig | 2 +- stage0/sema.c | 72 ++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/stage0/corpus.zig b/stage0/corpus.zig index 43f76d18ab..9082af643a 100644 --- a/stage0/corpus.zig +++ b/stage0/corpus.zig @@ -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", diff --git a/stage0/sema.c b/stage0/sema.c index e393616de6..6ead655c2e 100644 --- a/stage0/sema.c +++ b/stage0/sema.c @@ -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);