You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
compiler: lower String ==/!= to a byte comparison (string-wall slice 9) (#587)
## Summary
String equality was polymorphic at the type level (`'a -> 'a -> Bool`)
but the wasm backend lowered it unconditionally to `I32Eq`, comparing
the two `[len][utf8]` **pointers** rather than their bytes. Two
equal-valued strings at distinct heap addresses — e.g. a built string vs
a literal — compared **unequal**. This is the root cause of the
"integer-gate everything" pattern seen across the idaptik `.res →
.affine` migration (every `cmd == "scan"` had to be re-decomposed to
integer codes).
This is **string-wall slice 9**, the equality counterpart to slice 8b
(String `++`).
## Approach (mirrors the slice-8b `++` machinery exactly)
- **typecheck** records String-typed `==`/`!=` nodes by physical
identity during `synth` (`string_eq_sites`) — they land in the
polymorphic-equality `else` branch, not the `comparison` branch
- `elaborate_string_concat` rewrites them to a new **`ExprStringEq`**
AST node (one walk now drives both concat and eq; the existing single
wasm-path call site is unchanged)
- **codegen** lowers `ExprStringEq` to a length-prefixed byte
comparison: length guard, then a byte loop; `!=` negates via `I32Eqz`.
The loop runs **only** after the length check passes, so it never reads
past either string (no out-of-bounds linear-memory trap)
- type-blind passes get matching arms (`interp`/`opt`/`effect_sites`)
`Int`/`Bool` `==` is untouched — only String operands are recorded, so
the change is inert for every existing program.
## Verification
- value comparison: `"sc" ++ "an" == "scan"` is now `true` (was `false`
under pointer compare)
- edge cases: empty strings, length-mismatch (guard branch),
same-length-differing-byte (loop body), `!=` negation
- **no regression** across 38,975 int-`==` parity cases (DeviceType 39,
VmState 38577, VmInstruction 281, NetworkZones 64, Detection 14)
- new e2e fixture (`test/e2e/fixtures/string_eq.affine`) + test
(`test_e2e.ml`, slice-9), mirroring the slice-8b concat test
## Not in scope
String **relational** ops (`<`/`>`/`<=`/`>=`, #458) still lower to
pointer compares — they typecheck in the `comparison` branch but the
byte-lexicographic lowering is a separate follow-up (the byte loop here
generalizes to a `{-1,0,1}` compare). The var-to-var gap that the full
"type channel" was needed to close for `++` is already closed here for
`==`/`!=`, since the recording is type-directed from the start.
https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WoKhFQePiRsAj7aqnxbG8s)_
Co-authored-by: Claude <noreply@anthropic.com>
0 commit comments