Skip to content

fix(compiler): char_from_int(0) NUL string (B1) + top-level float-const binop (B2)#12

Merged
zemo-g merged 1 commit into
masterfrom
fix/compiler-b1b2
Jun 15, 2026
Merged

fix(compiler): char_from_int(0) NUL string (B1) + top-level float-const binop (B2)#12
zemo-g merged 1 commit into
masterfrom
fix/compiler-b1b2

Conversation

@zemo-g

@zemo-g zemo-g commented Jun 15, 2026

Copy link
Copy Markdown
Owner

Two independent codegen fixes, gated on the full self-compile fixed point.

  • B1 char_from_int(0) returned "" (the 1-byte string went through _rail_wrap_str, which re-derives length via _strlen and truncates at the NUL). Now builds a correct 1-byte tagged string [tag=9, len=1, byte, NUL] directly. Scope: a single correct NUL byte; embedded NULs through cat/join are still out of scope. t171 locks it.
  • B2 is_float's V-node branch checked only __float_NAME, never __float_ret_NAME (the A-node branch already did), so a top-level float constant in a binary op was misclassified → segfault/garbage. Added the __float_ret_ check. Also repairs the same-rooted tuple-destructure-float bug. t172 locks it.

Gate: ./rail_native test177/177 (was 175; t171+t172 added), ./rail_native self ×2 → byte-identical fixed point. Both fixes are fixed-point-safe by construction (compile.rail has no top-level float consts and never calls char_from_int, so its own self-compiled asm is unchanged).

Public builds.json badge regen is a separate follow-up.

🤖 Generated with Claude Code

…st binop (B2)

Two independent codegen fixes, co-landed under one self-compile bootstrap; both
fixed-point-safe by construction (compile.rail has no top-level float consts and
never calls char_from_int, so its own self-compiled asm is unchanged).

B1 (char_from_int NUL): the emit branch (~compile.rail:1156) built 1-byte strings
via _rail_wrap_str, which re-derives length with _strlen and truncates at an
embedded NUL, so char_from_int(0) returned an empty string. Now builds a correct
1-byte tagged string [tag=9, len=1, byte, NUL] directly. SCOPE: a correct single
1-byte NUL string only; embedded NULs still do not survive cat/join concat (that
needs full length-tracked strings, a much larger change). t171 locks it.

B2 (top-level float const in binary op): is_float's V-node branch (~:978) checked
only __float_NAME, never __float_ret_NAME (the A-node branch already did), so a
top-level float constant used in a binary op was misclassified and segfaulted /
returned garbage. Added the __float_ret_ check. Also repairs the same-rooted
tuple-destructure-float bug. t172 locks it. CAVEAT (unreachable in suite/stdlib/
armsim/compile.rail, noted for the future): a local 'let X = INT_EXPR' shadowing a
top-level float-returning fn X would now misclassify as float (env shadowing only
re-adds __int_/__float_, never removes the stale __float_ret_X).

GATE: ./rail_native test -> 177/177 (was 175; t171+t172 added, count bumped),
./rail_native self twice -> byte-identical fixed point. Lands on master via
fast-forward (authorized). The public builds.json badge regen is a separate
follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@zemo-g zemo-g merged commit 7b2747e into master Jun 15, 2026
3 checks passed
@zemo-g zemo-g deleted the fix/compiler-b1b2 branch June 15, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant