test(count): lock the null-container sentinel path after the #585 normalization#616
Conversation
|
The remaining count() parity gap mentioned in the description (non-null scalars in a mixed cell silently counting as 0, where PHP raises TypeError with the scalar type in the message) is now tracked as #617 — the machinery from this PR makes it a small follow-up, but it changes a test-locked behavior so it's kept separate. |
|
CI caught a real interaction here: extending the TypeError to plain null receivers breaks the deliberate BUG-0 CLI convention that off-web superglobals count as 0 ( |
|
Correction pushed ( |
…tudio#585 normalization count() on a Mixed receiver carrying the null-container sentinel used to dereference it and segfault (issue illegalstudio#602). The normalization added for issue illegalstudio#585 turns that sentinel into a real null before count() sees it, so the crash is gone on main without a dedicated guard. These tests lock the resulting behavior: the sentinel path no longer crashes, reports the missed read and counts as 0, and stays heap-clean; a real null cell counts the same way; a populated merge and an empty container are unaffected. One test records that no TypeError is raised where PHP raises one, so the divergence tracked in issue illegalstudio#617 cannot be changed silently. Claude-Session: https://claude.ai/code/session_01124pGFSEbzYQcGbNykWN7V
ac606f3 to
6d88381
Compare
|
This PR no longer needs to change any runtime code — your #585 normalization already fixed #602. I've rewritten it as tests only and retitled it accordingly. Rebasing it onto today's So I checked whether the original crash still exists. On $rows = [[1, 2]];
$r = $argc == 1 ? $rows[5] : ["a", "b"];
echo count($r), "\n";Normalization turns the sentinel into a real null before What remains is the PHP-parity gap: The six tests lock what Verified on |
Summary
count()on a boxedmixedreceiver whose payload is the null-container sentinel segfaulted(exit 139) instead of raising PHP's
TypeError. A missed array read forwarded through aternary merge (
$argc == 1 ? $rows[5] : ["a", "b"]) is boxed as an indexed-arraymixedcell whose payload pointer is the in-band
NULL_SENTINEL;__rt_mixed_countdereferencedthat payload past a plain-zero guard.
Repro
Run with no arguments (missed-read arm):
Warning: Undefined array key 5, then SIGSEGV (exit 139).Fatal error: Uncaught TypeError: count(): Argument #1 ($value) must be of type Countable|array, null given(exit 1) — matching PHP's diagnosticin elephc's uncaught-exception format, and catchable as a
TypeError.Root cause
The
count()lowering for a boxedMixedreceiver delegated to__rt_mixed_count, whosetag-4/5 payload read (
ldr x0, [x9]/mov rax, [r10]) was guarded only against a zeropointer. The non-zero in-band
NULL_SENTINELslipped through and was dereferenced — the samehole family as #526/#533 (read-side hardening), #585/PR #591 (get/set helpers), and
#592/PR #600 (append path). The concrete-array
count()arm already raised this exactTypeErrorviaemit_type_error; theMixedarm lacked the equivalent guard.Fix
__rt_mixed_countnow recognizes every null form of the receiver — a null pointer, thein-band sentinel as the cell or as a tag-4/5 payload (checked before the dereference, per
fix(codegen): guard container consumers against the null-container sentinel (chained-read miss segfault) #533), and a boxed null cell (tag 8) — and returns
NULL_SENTINELas a "receiver was anull container" signal. Other non-countable tags still return
0.count()call site turns that signal into PHP'sTypeErrorvia the existingemit_type_error, matching the concrete-array path. A plain0result (an empty container)passes through unchanged, so the sentinel signal never collides with a legitimate count.
Real null (
count(json_decode("null")), tag 8) previously returned0silently; it nowraises the identical
TypeError, so the sentinel and real null behave the same way and bothmatch PHP. Non-null non-countable scalars (e.g.
count(json_decode("42"))) still return0,unchanged (tracked separately as a quiet PHP-parity divergence).
Tests
New regression file
tests/codegen/regressions/mixed_count_sentinel.rs:count()prints the PHPTypeErrorfatal and exits with failure;TypeError;mixedcell still counts as0;Focused slices green:
mixed_count_sentinel(6),count(67),mixed(286),sentinel(43),
miss(119, including the #526 array-miss controls), sentinels lib unit tests (5).Fixes #602
https://claude.ai/code/session_01HRvbcjPHa3kAojdCjNZL5L