Repository navigation
Conversation
Quest-Claim: dda497bfe3dc255e
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…acity pointer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…through realloc Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Design change: the maintainer picked a runtime (Written by Claude Opus 5.5 with high effort) |
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.

Finishes quest/m3/ts-unsafe/vec-from-raw-parts.md.
Problem
rust2-ts refused
Vec::from_raw_partsas an unsupported call. So aVeccould not adopt memory thatallocgave for aLayout::array::<T>(n). Typed allocation is a JS array ofTs since the epic's 2026-11-01 decision, and anrs.Vecholds std's capacity since moq-dev/rust2#555. smallvec'sDropandinto_vechand their spilled heap back this way.Approach
Vec::<T>::from_raw_parts(p, length, capacity)lowers tors.vecFromRawParts(p, length, capacity, size).ppoints at the start of a JS array: memory fromalloc(anrs.Block's array), or aVec's own items given up byManuallyDropormem::forget. The newrs.Vecholds that same array withcap = capacity. Everything pastlengthmoves into the array'srs.Room(moq-dev/rust2#623), so aset_lenback over slots written before the adoption reads them, as std's does. A room the array already had, from an earlierVec, carries over to the new one. A slotallocnever wrote stays unwritten.relength, shared withvecSetLen.Vec<u8>lowers tors.byteVecFromRawParts, which takes the sameUint8Arrayas its storage. Byte allocation fromallocstays refused, as the epic decided, so this covers a byteVec's own buffer.Vecwith the given capacity (Vec::from_raw_parts(NonNull::dangling().as_ptr(), 0, 0)).Box::into_raw's, gives aVecof an array of that one value. A runtimeWeakMap(HOMES, throughhomeOf) records the storage each such place was given, and the place is redefined to read and write that storage's slot 0. A write through either side shows through the other, and adopting the same value again reuses the storage, so a pointer from the firstVecsees writes through the second. Any later route that turns a lone value into array storage can reusehomeOf; onlyfrom_raw_partspays for it now. Bytes fromallocthat no cast typed (anrs.Block) give a byteVecnew storage of the capacity, since a write to them is refused.reallocof the memory under aVecnow carries over what theVec's room holds past its length (rs/alloc.ts). Before, it copied only the items array, so it lost what a pointer orset_lenhad put past the length.ub_checksmessage. That panic can't unwind, so it aborts, located atfrom_raw_parts_in's check (newvec_from_raw_partssite incaller.rs).rust2-ts:error (tests/pointers.ts).as_mut_ptr) and zero-sized elements.adoptmodule in thelang/heapfixture:adopt::allocated:u32s,Arc<u8>s (strong counts show each drop and leak), andOption<(u8, u16)>s. They are adopted fromallocwithLayout::arrayor an equalfrom_size_alignlayout, grown byreallocor not, with slots written past the length. EachVecthen pushes past the capacity, takes in the written slots byset_len, truncates, or shrinks.adopt::readopted:Vec<u32>,Vec<Arc<u8>>, andVec<u8>round-tripped throughManuallyDrop, with room written through the pointer. Also: an emptyVec's spare-capacity pointer; aVecgrown byreallocand adopted again;Vec<u32>andVec<u8>of aBox's value, written through both theVecand the pointer; the sameBoxadopted twice, foru32andu8, written through the secondVec, the firstVec's pointer, and theBox's pointer;Vec<u8>of bytes fromalloc; and the dangling emptyVec.panicsfixture's new arm 74 covers the abort.Impact
Vec::from_raw_partstranslates where it was refused.vecFromRawPartsandbyteVecFromRawParts.reallockeeps aVec's room.Vec::from_raw_partsis verbatim in constructs. The model census at the moq pin (ffcb7cd) lists noVec::from_raw_partsrefusal (50 refusals in all). Before this PR, smallvec'sDropfor its two instances (Weak<Waker>,NonNull<Segment>) reached it, per the quest.Choices made without asking
Vec's own buffer is adopted too, not only memory fromalloc. It is the same runtime path: the pointer isas_mut_ptr'srs.AtIndexinto the items, whose room the newVecinherits. It covers the commonManuallyDropround trip, and leaving it out would be a refusal with no simpler result.capacity * size_of::<T>()to equal the allocation's size, and breaking that is UB, so the runtime takes the capacity it is given. Only the byte path checks it, because aByteVec's capacity is its storage's length.allocalready refuses them, and the JS value of a zero-sized type is not alwaysundefined.rs.Blockstays registered. A later cast of theVec's pointer back to bytes fordeallocfinds it either way. Unregistering would change nothing a defined program sees.Decisions
Vec::from_raw_partsadopts (maintainer, 2026-11-07, at the round-3 design checkpoint): ✅ a runtimeWeakMapfrom the lone value's place to the array storage it was given, so adopting it again reuses that storage, built as a general mechanism any later route turning a lone value into array storage can reuse, and paid for only byfrom_raw_parts(rejected: make everyBox::into_rawpointer an array of one from the start; dropBoxadoption and throw; merge as is).Alternatives
rs.Vec: simpler, but a pointer taken before the adoption (p.add(i)) would no longer reach theVec's items or its room.Follow-ups
String::from_raw_parts(andString::from_utf8_uncheckedof an adoptedVec<u8>). Drop: no consumer at the moq pin, and byte allocation fromallocstays refused.Caused by
No single PR:
Vec::from_raw_partswas never supported.Validation
On head 3342dd820 (origin/main fd62edf merged in):
just ci origin/main(innix develop): lint and quest check pass, and so do the 8 unit tests, all 89 fixture tests (lang::dynstoo, since moq-dev/rust2#633 landed), and the 10 runtime tests.Boxcase against the round-3 runtime (noWeakMap): 164 differences in 841lang/heapcases; with it, 0.Earlier, on head b11502c:
just ci origin/main(innix develop): lint and quest check pass, and so do the 8 unit tests and 88 of 89 fixture tests. The one failure islang::dyns(ids.tsTypeId hashes), which depends on the checkout's path. Per the coordinator's known-issues list it reproduces on bareorigin/main(I did not rerun it there), and moq-dev/rust2#633 is fixing it.just test --test runtime, which cargo skipped after that failure, passes on its own (10 tests).lang/heapcase against the native build: 15,086 cases, 0 differences. Each runtime fix was checked by removing it in the copy under test, with the new cases in place:relength's room write: 149 differences in 1,586 cases;realloc's room copy: 313 differences in 1,586 cases;rehomeor lone-place adoption: 164 differences in 841 cases.panicsfixture at 1,500 inputs per case: 0 differences. With the length check moved past the capacity, it shows 14 differences.ffcb7cd: 50 refusals, none of themVec::from_raw_parts.Models
Size: S
Implementer: Claude Code, claude-opus-5-5, high effort
(Written by Claude Opus 5.5 with high effort)
🤖 Generated with Claude Code
Review
Rewritten by
quest reviewafter each round.01a1b61d-72dd-78b1-a322-97d31ffe645c, round 3The fix preserves aliases during the first adoption, but repeated adoption still splits one Rust allocation into separate storage.ts/runtime/rs.ts:2651:Reuse the boxed pointer's storage when adopting it againts/runtime/rs.ts:2651:Preserve the original pointer's storage when adopting a boxed value;The rehome calls fix the initial write-through scenario, but ts/runtime/rs.ts:2651 still copies storage on repeated adoption, leaving pointers from the first vector disconnected.