feat(arrays): array_slice() on an indexed string array - #1035
Open
Guikingone wants to merge 2 commits into
Open
Guikingone wants to merge 2 commits into
Guikingone wants to merge 2 commits into
Conversation
|
Guikingone
force-pushed
the
fix/675-array-slice-string-slots
branch
2 times, most recently
from
September 18, 2026 13:09
8fe4e15 to
2978d4f
Compare
Part of #675, the `array_slice` member. print_r(array_slice(["a", "b", "c"], 1, 2)); unsupported EIR backend feature: array_slice indexed-array element PHP type Str An indexed `array<string>` stores 16-byte `{pointer, length}` slots, and both existing helpers copy 8 bytes per element -- so a string receiver was refused at compile time rather than miscompiled. `__rt_array_slice_str` copies the pair. The issue names `__rt_array_splice_str` as the model, and this follows it in both respects. The destination is allocated with 16-byte slots, and the copy DUPLICATES through `__rt_array_push_str` (which persists via `__rt_str_persist`) rather than aliasing: a string array owns its bytes exclusively, `array_slice()` leaves its argument untouched, so both arrays end up owning independent copies and freeing either one is safe. The window arithmetic is not re-derived. It goes through the shared `emit_slice_bounds` prologue every other `array_slice` variant uses, so negative offsets, negative lengths, clamping and an omitted `$length` behave identically by construction. The loop index is SPILLED rather than parked in a register: `__rt_array_push_str` persists the payload and may grow the destination, so it is a full call and every caller-saved register is fair game across it. Eighteen rows byte-identical to the host PHP 8.5.10 -- the whole offset/length matrix, plus the four that are really about ownership: writing into the result leaves the source intact, a slice outlives the local it came from, a slice of a slice is independent again, and 100/200/300-byte strings force real heap buffers. 200 slices in a loop report `allocs=1400 frees=1400` under `--gc-stats`: no leak, no double free. That is asserted as a test, because getting the duplication wrong in either direction is silent at the value level. Suites: `codegen::arrays` 568, `codegen::strings` 352, `codegen::runtime_gc` 298, `error_tests` 1534, `--bin elephc` 1915 -- clean. `array_chunk`, `array_pad`, `array_reverse`, `array_merge`, `array_unique`, `array_diff` and `shuffle` still report their own variant of the message. The first four are the same copy shape as this one; `array_unique`/`array_diff` need string COMPARISON over 16-byte slots and `shuffle` needs in-place 16-byte swaps, which are different pieces of work. Landing them one at a time keeps each helper's ownership contract reviewable on its own. Claude-Session: https://claude.ai/code/session_01KSAAWPyNBq6dP2b5puN3wr
Review follow-up: three repository requirements the first commit did not meet.
`examples/arrays/main.php` already demonstrated `array_splice()` on a string
array, next to a comment about elements "wider than a scalar slot". The
`array_slice()` counterpart now sits beside it, and shows the property that
separates a correct 16-byte copy from a plausible-looking one -- writing into the
result leaves the source alone:
Sliced names: GRACE, Linus (source still Ada, Grace, Linus, Barbara)
The whole example is byte-identical to the host PHP 8.5.10, not just the new row.
`docs/internals/the-runtime.md` gains the `__rt_array_slice_str` row beside
`__rt_array_splice_str`, stating the slot width, that the window arithmetic is
the shared `slice_bounds` prologue, and why the copy DUPLICATES rather than
aliases.
`test_array_slice_str_is_emitted_for_every_supported_target` emits the runtime
for all five targets -- `macos-aarch64`, `ios-arm64`, `ios-sim-arm64`,
`linux-aarch64`, `linux-x86_64` -- and asserts the helper is present, asks
`__rt_array_new` for 16-byte slots, and copies through `__rt_array_push_str`.
The slot width is asserted rather than assumed because it is the one number that
makes this helper different from its 8-byte siblings: get it wrong and the copy
still runs, reading half of each pair. The executable shards cover three of those
targets; the two iOS ones are covered only by emission tests like this, which is
what the policy asks for.
Claude-Session: https://claude.ai/code/session_01KSAAWPyNBq6dP2b5puN3wr
Guikingone
force-pushed
the
fix/675-array-slice-string-slots
branch
from
September 19, 2026 07:39
2978d4f to
8ef5a2c
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #675 — the
array_slicemember. The issue's headline repro:An indexed
array<string>stores 16-byte{pointer, length}slots, and both existing helpers copy 8 bytes per element — so a string receiver was refused at compile time rather than miscompiled.__rt_array_slice_strcopies the pair.Following
array_splice_strThe issue names
__rt_array_splice_stras the model, and this follows it in both respects.The destination is allocated with 16-byte slots, and the copy duplicates through
__rt_array_push_str(which persists via__rt_str_persist) rather than aliasing. A string array owns its bytes exclusively andarray_slice()leaves its argument untouched, so both arrays end up owning independent copies and freeing either one is safe.The window arithmetic is not re-derived: it goes through the shared
emit_slice_boundsprologue every otherarray_slicevariant uses, so negative offsets, negative lengths, clamping and an omitted$lengthbehave identically by construction rather than by coincidence.One detail worth flagging for review: the loop index is spilled rather than parked in a register.
__rt_array_push_strpersists the payload and may grow the destination, so it is a full call and every caller-saved register is fair game across it.Measured
Eighteen rows byte-identical to the host PHP 8.5.10 — the whole offset/length matrix (
mid,from,all,neg-off,neg-off-len,neg-len,neg-both,zero-len,past-end,too-long,too-far-back,empty-src,null-len), plus the four that are really about ownership:200 slices in a loop report
allocs=1400 frees=1400under--gc-stats— no leak, no double free. That is asserted as its own test, because getting the duplication wrong in either direction is silent at the value level: the strings still print.Verified against the defect by putting the layout gate back: the fixture then fails with the exact message above.
Suites:
codegen::arrays568,codegen::strings352,codegen::runtime_gc298,error_tests1534,--bin elephc1915 — clean.Still refused, deliberately not in this PR
array_chunk,array_pad,array_reverse,array_merge,array_unique,array_diffandshufflestill report their own variant of the message, so #675 stays open.The first four are the same copy shape as this one and should follow quickly.
array_uniqueandarray_diffneed string comparison over 16-byte slots, andshuffleneeds in-place 16-byte swaps — different pieces of work with their own ownership questions. Landing them one at a time keeps each helper's contract reviewable on its own rather than burying eight assembly emitters in one diff.🤖 Generated with Claude Code
https://claude.ai/code/session_01KSAAWPyNBq6dP2b5puN3wr