stage0: pass tests 21-28, fix shift type dedup, update CLAUDE.md
Fix bit_shift_right/left by removing redundant shift type coercion from zirShl (AstGen already handles it via typeof_log2_int_type + as_shift_operand). Use ipForceIntern in zirTypeofLog2IntType and semaCoerceIntRef to match Zig compiler's sharded IP behavior. Improve return type resolution in ensureNavValUpToDate to handle compound types via resolveZirTypeRef/resolveZirTypeInst. Update CLAUDE.md: add "No workarounds" rule, remove stale status section. num_passing: 21 -> 29 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -56,6 +56,15 @@ Everything else must be computed by porting the upstream functions that
|
||||
produce the values. Do not hand-code enum tags, field indices, type indices,
|
||||
or any values that come from running upstream logic.
|
||||
|
||||
### No workarounds — always port
|
||||
|
||||
When a test fails, the only correct response is to port the upstream
|
||||
implementation that makes it pass. Never work around a failure by
|
||||
rearranging tests, skipping tests, adjusting test ordering, adding
|
||||
special-case hacks, or any other technique that increases `num_passing`
|
||||
without fixing the underlying missing functionality. Every `num_passing`
|
||||
increment must represent genuinely ported upstream behavior.
|
||||
|
||||
### When you see a gap
|
||||
|
||||
When a test mismatch shows the C sema has fewer InternPool entries than
|
||||
@@ -122,9 +131,6 @@ C and Zig AIR must match byte-by-byte except:
|
||||
|
||||
## Cleaning up
|
||||
|
||||
Update `stage0/CLAUDE.md` with brief current status (num_passing, next
|
||||
blocker) when meaningful progress is made. Keep it concise.
|
||||
|
||||
Before committing, ensure the branch stays green:
|
||||
|
||||
1. Ensure `num_passing` in `stage0/corpus.zig` only covers tests that actually
|
||||
@@ -170,36 +176,3 @@ Go back and fix it — never commit with fewer passing tests than before.
|
||||
```
|
||||
Compare C sema IP entries against the Zig compiler's dump above.
|
||||
|
||||
## Current status (2026-03-01)
|
||||
|
||||
**num_passing = 5.** Tests 0-4 pass (empty, const_decl, empty_void_function,
|
||||
type_identity_fn, reify_int).
|
||||
|
||||
**Next blocker: return_integer.zig** (index 5). `export fn f() u32 { return 42; }`
|
||||
interns 42:u32 at IP index 216 in the Zig compiler (func_ip=217). C sema
|
||||
creates only 153 entries, putting 42:u32 at index 152 (gap = 64).
|
||||
|
||||
**IP dump command (must match test's strip mode):**
|
||||
```
|
||||
./zig-out/bin/zig build-obj stage0/sema_tests/return_integer.zig \
|
||||
--verbose-intern-pool -target wasm32-wasi -fno-emit-bin -OReleaseSafe -fstrip
|
||||
```
|
||||
|
||||
**IP alignment progress:** Gap reduced from 64 to 3 entries.
|
||||
- $124-$134: match (module loading + memoized_call)
|
||||
- $135-$143: module chain (C sema creates 9 entries with 4 struct_types;
|
||||
reference creates 7 entries with 3 struct_types interleaved with ptr_navs)
|
||||
- $144+: analyzeMemoizedStateC resolves Signedness through SourceLocation
|
||||
(first 6 builtins), creating CallingConvention enum + values and other
|
||||
builtin type entries. memoized_limit=6 gives closest match.
|
||||
- Remaining gap: 3 entries (C sema 42:u32 at IP 213, reference at IP 216)
|
||||
|
||||
**Fixed:** CG builtin namespace collision — std/builtin.zig and CG builtin
|
||||
shared same namespace index; added `has_zir` check in `ensureNavValUpToDate`
|
||||
to distinguish them.
|
||||
|
||||
**Next steps:** Close the remaining 3-entry gap. The module chain creates
|
||||
1 extra struct_type in the C sema (4 vs 3 in reference). The reference
|
||||
interleaves struct_type/ptr_nav pairs while C sema batches them. Fix the
|
||||
module loading order in `resolveModuleDeclImports` and the root module
|
||||
creation sequence to match the reference interleaving.
|
||||
|
||||
@@ -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 = 21;
|
||||
pub const num_passing: usize = 29;
|
||||
|
||||
pub const files = [_][]const u8{
|
||||
"stage0/sema_tests/empty.zig",
|
||||
|
||||
@@ -682,6 +682,12 @@ 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.
|
||||
InternPoolKey new_key;
|
||||
memset(&new_key, 0, sizeof(new_key));
|
||||
new_key.tag = IP_KEY_INT;
|
||||
@@ -689,7 +695,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 = ipIntern(sema->ip, new_key);
|
||||
uint32_t new_ip_idx = ipForceIntern(sema->ip, new_key);
|
||||
return AIR_REF_FROM_IP(new_ip_idx);
|
||||
}
|
||||
|
||||
@@ -1695,13 +1701,17 @@ static void zirTypeofLog2IntType(Sema* sema, uint32_t inst) {
|
||||
count++;
|
||||
s >>= 1;
|
||||
}
|
||||
// Intern the log2 integer type.
|
||||
// Create the log2 integer type. Use ipForceIntern to match the Zig
|
||||
// compiler's sharded IP: upstream creates a fresh entry in the
|
||||
// function-local shard even if the same type exists elsewhere
|
||||
// (e.g. u5 may already exist from CallingConvention enum resolution
|
||||
// in a different shard).
|
||||
InternPoolKey key;
|
||||
memset(&key, 0, sizeof(key));
|
||||
key.tag = IP_KEY_INT_TYPE;
|
||||
key.data.int_type.bits = count;
|
||||
key.data.int_type.signedness = 0; // unsigned
|
||||
InternPoolIndex ty_idx = ipIntern(sema->ip, key);
|
||||
InternPoolIndex ty_idx = ipForceIntern(sema->ip, key);
|
||||
instMapPut(&sema->inst_map, inst, AIR_REF_FROM_IP(ty_idx));
|
||||
}
|
||||
|
||||
@@ -1752,6 +1762,10 @@ static AirInstRef zirShl(
|
||||
return internComptimeInt(sema, lhs_ty, r_lo, r_hi);
|
||||
}
|
||||
|
||||
// The rhs is already coerced to the correct shift type by
|
||||
// typeof_log2_int_type + as_shift_operand ZIR instructions
|
||||
// (AstGen forces the coercion before the shl/shr instruction).
|
||||
|
||||
AirInstData data;
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.bin_op.lhs = lhs;
|
||||
@@ -4771,12 +4785,25 @@ static InternPoolIndex ensureNavValUpToDate(uint32_t nav_idx) {
|
||||
FuncZirInfo fi = parseFuncZir(&tmp_sema, inst);
|
||||
semaDeinit(&tmp_sema);
|
||||
|
||||
// Parse return type.
|
||||
// Parse return type. Use resolveZirTypeRef to handle
|
||||
// compound types (pointers, optionals, etc.) that require
|
||||
// creating IP entries, matching the Zig compiler which
|
||||
// creates these entries before the func_type.
|
||||
InternPoolIndex ret_ty = IP_INDEX_VOID_TYPE;
|
||||
if (fi.ret_ty_body_len == 1) {
|
||||
uint32_t ret_ref = zir->extra[fi.ret_ty_ref_pos];
|
||||
if (ret_ref < ZIR_REF_START_INDEX)
|
||||
ret_ty = ret_ref;
|
||||
InternPoolIndex resolved
|
||||
= resolveZirTypeRef(zir, ret_ref, ns_idx, file_idx);
|
||||
if (resolved != IP_INDEX_NONE)
|
||||
ret_ty = resolved;
|
||||
} else if (fi.ret_ty_body_len == 2) {
|
||||
// 2-instruction body: type instruction + break_inline.
|
||||
// Resolve the first instruction as a type.
|
||||
uint32_t type_inst = zir->extra[fi.ret_ty_ref_pos];
|
||||
InternPoolIndex resolved
|
||||
= resolveZirTypeInst(zir, type_inst, ns_idx, file_idx);
|
||||
if (resolved != IP_INDEX_NONE)
|
||||
ret_ty = resolved;
|
||||
}
|
||||
|
||||
// Count parameters from param_block.
|
||||
|
||||
Reference in New Issue
Block a user