Repository navigation
feat(ts)!: parts of arrays written where they are, without arrayView - #662
Conversation
Quest-Claim: 4035ae0cf9b09116
A `&mut [T; N]` to part of an array is read and written where its slice holds it: `mem::replace` and `mem::swap` by `rs.sliceReplaceAll`, `ptr::replace` and `ptr::swap` by `rs.sliceCopy`, `Cell::from_mut` and `UnsafeCell::from_mut` by a cell of the slice whose pointer is the slice, `ptr::as_mut` and `NonNull::as_mut` as the slice itself, and `Box::from_raw` as the array a box owns all of. The runtime's `arrayView` and `rs.sliceView` go. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…oxes taken back A reference from a raw pointer or a cell to an array a `MaybeUninit` holds is the array, as a reborrow makes it, with a mutable one through a raw pointer refused; an array of arrays' copy copies objects a write lands in only where one does; and `Box::from_raw` takes back arrays `as_chunks` split from all of a box's array. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… arrays A shared reference through a raw pointer to part of an array a `MaybeUninit` holds is a copy, so comparing pointers to such arrays is refused; and `Box::from_raw` of arrays of arrays stays refused, as arrays `as_chunks` split from one flat array have none of their own to box. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A shared reference or as_ref through a raw pointer to an array a MaybeUninit or ManuallyDrop holds, of other than bytes, was a copy where the pointer points into part of an array, so assume_init_ref compared unequal where Rust's is equal. It is now refused, as the mutable one is, and pointer comparisons of such arrays are no longer refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Design change: per the maintainer's decision on 2026-11-07 at the round-3 checkpoint, a reborrow through a raw pointer of an array a
The next review starts fresh with (Written by Claude Opus 5.5 with high effort) |
A box of a zero-sized array owns no allocation, so Rust takes any pointer to one back as a box; it no longer aborts where the pointer covers part of an array. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pointer to an array of no bytes may claim more elements than it points at, so the box is a new array of its length, as a dangling pointer's is, and refused where its elements are more than structs and tuples. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 33 minutes. View limit details
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
No actionable findings from this static review.
Reviewed the in-place slice replacement/copy paths, ArrayCell and cellArray pointer preservation, nested-array copy behavior, raw-pointer and cell reference lowering, wrapped-array refusals, zero-sized array reboxing, and RoomView changes against the surrounding implementation and Rust/generated TypeScript fixtures. The direction is consistent: references keep the original slice, whole writes land there, and unsupported non-byte wrapped-array borrows are rejected with a span rather than silently copied.
Validation limits: GitHub-only source review. I did not run builds, tests, the differential suite, or quest commands, and have not independently verified the reported CI results. This is a COMMENT review, not approval to merge.
(Written by review (OpenAI))

Finishes quest/m3/ts-slice-object.md and deletes it.
Decisions
✅ Decided with the maintainer on 2026-11-07 at the round-3 design checkpoint: refuse, with a span, a shared reference or
as_refthrough a raw pointer to an array aMaybeUninitorManuallyDropholds (other than bytes), since the pointer may point into part of a bigger array, where the reference was a copy. That closes theassume_init_ref()route, whereptr::eqreturned false and Rust returns true. It applies m3's unsafe policy: a silent mismatch is refused rather than recorded.&*pof the same shape already shared that copy on main (rvalue.rs), so it is refused too, for consistency.slices::array_sourcescase 3's reborrow of aMaybeUninit<[[u8; 2]; 4]>through a raw pointer, from moq-dev/rust2#608, is dropped: it was the only use of the reborrow in the suite. Its borrowedCowmatch stays.WeakMap(pointer metadata, which the policy refuses); dropping the PR.Problem
The runtime's
arrayViewwas aProxyJS array over part of another, whichrs.sliceViewmade wherever a&mut [T; N]to part of an array was taken whole as a JS array: bymem::replaceandmem::swap,ptr::replaceandptr::swap,Cell::from_mutandUnsafeCell::from_mut, std mappings given one to write through, and a raw pointer'sas_mutorBox::from_raw. Every pointer helper (arrayAsPtr,elemOf,sliceSame) also had to see through it.Approach
Each construct now reads and writes the slice where it is:
mem::replace/mem::swapof a*rcallrs.sliceReplaceAll(s, items, depth), andptr::replace/ptr::swapcopy out byrs.sliceCopy(s, depth). Both copy elements a write lands in todepth, asobjectCopydoes.Cell::from_mutandUnsafeCell::from_mutof a&mut [T; N]makers.cellOfSlice(s), anArrayCell: a read gives a copy and a write lands in the slice. A cell's pointer (as_ptr,UnsafeCell::get,get_mut,as_ref_unchecked,as_mut_unchecked) is nowrs.cellArray(cell, from): the slice the cell was made of, or else the slice of all of the array it holds.rs.cellOfArraySlicemakes the same kind of cell.ptr::as_ref/as_mutandNonNull::as_ref/as_mutof a pointer to an array return the pointer's slice as the reference, with nolend(mapping-land arrays) in between. For an array aMaybeUninitorManuallyDropholds, whose reference is the array, they follow the reborrow of*p: a byte view, and else refused.Box::from_rawof a pointer to an array of elements callsrs.boxArrayFromRaw. It gives the array when the pointer covers all of it, asBox::into_rawmade it, and otherwise aborts the way std'sunsafe precondition(s) violatedchecks do. A box of an array of no bytes owns no allocation, so any pointer makes one: a new array of its length, as a dangling pointer's is.arrayView,rs.sliceView, and theViewclass are gone.RoomView, theProxyover aVec's items and spare capacity, keeps its own traps without thestart/partthat only views of views needed.Differential tests against native Rust:
slices::array_parts(replace and swap of numbers and strings;Cell/UnsafeCellof numbers, strings, and arrays of arrays through each read, write, and pointer; aCell<[[T; N]; M]>pointer read afterset; a pinned box's allocation kept acrossCell::replace; std'sOption/Vecholding a&mut [T; N]) andpointers::array_parts(as_mut,NonNull::as_mut,ptr::replace,ptr::swap, andBox::from_rawof arrays, including arrays of no bytes reboxed shorter, longer, and from part of aVec,as_mutof arrays of arrays, and wrapped arrays lent byas_refof bytes and by a cell'sget_mutandas_mut_unchecked).Fixed on the way, both reproduced against native first:
Cell::from_mutof an array of arrays read a copy, so a write through itsas_ptrlanded in the copy. The newslicescase gave 61 differences from native with main's pointer.Cell<[[T; N]; M]>was written whole by replacing its array, so a pointer taken earlier byas_ptrread the old one. Its writes now land in place, as other arrays' do.mem::replace,Cell::replace,ptr::replace) copied every object element byobjectCopy, which gave a pinned box a new allocation. It now copies only where a write lands in the object, in an array of onesliceInorsliceOfPlacemade, assliceCopyandobjectCopyalready decide. Without that, the pinned-box case gives 32 differences from native.ptr::as_refof a pointer to an array aMaybeUninitholds gaveundefinedwhere itsOptionneedsrs.None, whichtscrejected.UnsafeCell::from_mutof an array of arrays, andas_mut/NonNull::as_mutof a raw pointer to an array of arrays.Caused by
rs.cellOfArraySliceread a copy, whichCell::as_ptrthen pointed atCellof arrays of arrays written whole, leaving an earlieras_ptron the old arrayImpact
sliceViewand the internalarrayVieware removed. Translated code no longer calls them.sliceCopy,sliceReplaceAll,boxArrayFromRaw,cellOfSlice,cellArray.ArraySlicecopies (arraySliceCopy,arraySliceReplace,arraySliceBy,cellOfArraySlice) apply anobjectCopyelement copy only in arrays of onesliceInorsliceOfPlacemade.Cell<[T; N]>andCell<[[T; N]; M]>pointers read throughrs.cellArray, andptr::as_mutof an array pointer is the slice itself.codec,model, andcensustranslations, passes.&mut [T; N]of non-bytes to write through as an array (only bytes have a JS view).slice::from_mut/array::from_mutof aMaybeUninit<[T; N]>that is part of an array (the raw-pointer route to it is refused earlier already).MaybeUninitorManuallyDropholds, other than bytes:as_ref,as_mut,NonNull::as_ref/as_mut, and&*p(shared, which main translated as a copy where the pointer points into part of an array) and&mut *p(already refused). See Decisions.Box::from_rawof an array of arrays stays refused, now saying why: arraysas_chunkssplit from one flat array have no array of their own to box.Box::from_rawof an array of no bytes whose elements are more than structs and tuples (such as an enum of one variant), which the translator can't make anew, asptr::danglingof one is refused already.Choices made without asking
givepath for a&mut [T; N]of non-bytes is refused rather than given a copy written back. No std call in any fixture or in moq-net reaches it, and a copy is only exact when the call keeps no borrow, whicharray_slice::givealready has to refuse for slices of arrays.ArrayCell). That replacedcellOfArraySlice's accessor place and fixed its pointer.Box::from_rawof part of an array aborts as UB, per the runtime-throws decision that UB guards stay as precondition aborts.Alternatives
sliceViewas a translator-only marker thatsliceFromcancels, and refuse any that survives. Rejected: every survivor had an exact form once the pointer mappings return the slice directly.as_ptrwould land in the mirror with nothing to flush them.Follow-ups
TryFrom<&mut [T]> for &mut [T; N](<&mut [T; N]>::try_from(&mut s[..2])),Cell::as_array_of_cells, andconvert::identityof a&mut [T; N]are unsupported calls today; the tests usefirst_chunk_mutinstead.try_intoof a slice into an array reference is common in parsers.[u32; N]anywhere in theslicesfixture made itsrest @ ..patterns fail with "a reference through which a raw pointer may be made into[u32; 4]in[u32; 5]". So the raw-pointer tests live inpointers. It is the raw-pointer model working as designed (Unsafe code the translator can model owns it).Validation
quest gate nix develop --command just ci: passed at 3b9fefb.Proxyfrom the paths it touches.Models
Size: M
Implementer: Claude Code, claude-opus-5-5, high effort
🤖 Generated with Claude Code
(Written by Claude Opus 5.5 with high effort)
Review
Written by hand:
quest reviewprintedrecord skipped, as this session reviewed from a local branch namediterate/ts-slice-object(the PR's branch is checked out in another worktree).01a1b8a4-efc7-7fb0-86a2-4f882e71fca4, round 3 (new session after the design change)The length regression is fixed, runtime checks and 24 targeted differential inputs pass, and no remaining defects were found; full suites were blocked by the read-only gate.ts/runtime/rs.ts:923:[P2] Handle zero-sized arrays before enforcing whole-storage reboxing(aBox<[()]>reboxed as*mut [(); 2]aborted).ts/src/lower/pointer.rs:852:[P2] Preserve the requested length when reboxing zero-sized arrays(reboxed as*mut [(); 5], it had three elements).