fix(array): ask the typed-array question before clean_arr_ptr rejects it - #8090
Merged
proggeramlug merged 3 commits intoAug 14, 2026
Merged
Conversation
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.
Summary
On a typed array,
fill,reverseandcopyWithinsilently do nothing. No error, no diagnostic — the array is simply unchanged.sort,withandtoReversedare broken by the same cause and are not fixed here (see the end).Root cause: the typed-array question was asked too late
Each mutator already had a typed-array delegation from #3148. All of them sat below the shared receiver funnel, which had already rejected the receiver.
array/header.rs,clean_arr_ptr:Since the 2026-07-09 audit,
typed_array_allocallocates every typed array througharena_alloc_gc_old(..., GC_TYPE_TYPED_ARRAY)— a real trackedGcHeader. Typed arrays used to be header-less side-table allocations, which the funnel'sis_registered_buffer || lookup_typed_array_kindarm let through.So
clean_arr_ptr_mutreturns null for a typed array, every mutator returns at itsarr.is_null()early-out, and the#3148delegation below it is unreachable. The pointer was never mis-boxed and the registry was never stale —lookup_typed_array_kindanswers correctly, it just never got asked.clean_arr_ptr_still_rejects_a_typed_array_receiverpins both halves of that (registered = yes, cleaned = null) so the diagnosis is asserted rather than inferred.The fix
One new funnel,
typed_array_receiverinarray/header.rs, consulted before the clean byjs_array_fill,js_array_fill_range,js_array_reverseandjs_array_copy_within.clean_arr_ptr's #7574 rejection is deliberately untouched. It is correct — aTypedArrayHeader's per-kind storage really is not boxed-f64 slots. The bug was the ordering, not the routing.Why the runtime and not codegen
Codegen could decline
is_array_exprfor a statically-typed typed array, but that strands the ~40 other helpers inarray/implementing the same #3148 contract (sort, index get/set, forEach/map/filter/join…), which are the only path those receivers have.It also would not fix the
anyreceiver at all. HIR'scopyWithinfold declines only for a known typed array, so an unknown-typed receiver lowers straight tojs_array_copy_within— the same helper. One ordering fix covers both; the codegen route needs two.js_array_copy_withinalso gained the Buffer/Uint8Arrayarm, delegating tobuffer_dispatch's byte-granularitycopyWithin. That shape arrives through the same fold and was the onlyUint8Arrayfailure.Test plan
330 assertions:
fill/reverse/copyWithin×Uint8Array/Uint16Array/Int32Array/Float64Array/plainArray× three receiver typings × function and module scope, plus 0/1/2/3-argfill, odd-lengthreverse, and negative and out-of-rangecopyWithin. Byte-compared against node 26.5.1.Uint16ArrayInt32ArrayFloat64ArrayUint8ArrayArrayBoth arms use the same compiler against different runtimes — the "before" arm links a snapshot of the pre-fix
libperry_{runtime,stdlib}.aviaPERRY_RUNTIME_DIR, per CLAUDE.md's warning thatcargo build -p perry-runtimealone does not emit those archives and leaves an A/B comparing a stale one.Verified by sabotage. Making
typed_array_receiverreturnNone— the pre-fix state — turns 5 of 8 tests red, including the element-width test (left: [0.0, 0.0], right: [-2147483648.0, -2147483648.0]). The 3 that stay green are precisely the ones that should: theclean_arr_ptrprecondition and the plain-Arraycontrols.RUST_TEST_THREADS=1 cargo test --release -p perry-runtime→ 2338 passed, 0 failed. (That flag is required; the runtime's tests share process-global side tables.)Why the element-shape matrix did not catch this, and why it should not be extended to
element_shape_matrix_tests.rsnamescopyWithin, but every fixture isshaped(4)— a plainArrayHeaderbuilt byjs_array_push_f64— and the property it asserts is element-shape proof revocation, which a typed array can never carry. It is a correct test of a different invariant.The real gap was that nothing drove an
Array.prototypeFFI entry point with a typed-array receiver.array/typed_array_receiver_tests.rsis now that test.Related issue
Refs #2879.
Deliberately not fixed here
Same root cause, still broken, each needing its own oracle matrix (comparator forms, and new-array returns rather than in-place mutation): the post-clean delegations at
array/sort.rs:563/:659andarray/immutable.rs:37/:61/:106/:246. Measured on a statically-typedInt32Array:sort()leaves3,1,4,2unsorted, andwith(1,42)andtoReversed()both return an empty array. Named in the changelog fragment so they are not lost.Pre-existing red, not from this PR:
crates/perry-codegen/tests/loop_safepoint_purity.rsfails 2 of 8, reproduced identically withperry-runtimeandperry-hirchecked out at the base commit;check_file_size.shis red ontimer.rsat 2010 lines; the addr-class ratchet reports a stale baseline forheader.rsanddyn_index.rs, which I checked with the script's ownscan_text— base and current both have 3 handle-floor sites, so that staleness predates this change and the audit exits 0.No version bump, no
CLAUDE.mdedit, noCHANGELOG.mdedit, per the template.Summary by CodeRabbit
Bug Fixes
fill, rangedfill,reverse, andcopyWithinoperations that previously performed no action.Tests
BufferandUint8Arrayscenarios.