Repository navigation
feat(ts): translate raw pointers, NonNull, MaybeUninit, and unions as typed pointers - #201
Conversation
Quest-Claim: e21ec338030d998e
… typed pointers (wip) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nd point at library objects through places Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…it off follow-ups Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 7ea69c9
Two actionable issues:
- P2: Copy inline aggregate storage, not just its outer object.
ts/runtime/rt.ts:110–113(pointer.rs:320–325) andts/src/lower/copy.rs:189–190use shallowptrRead. A raw read of([1u32, 2],)followed by a write to the original array also changes the read result when that result is only read, not mutated (so the call-result copy path does not rescue it). Union copies have the same problem:#[derive(Clone, Copy)] union U { a: [u32; 2], b: [u32; 2] }, thenlet x = U { a: [1, 2] }; let mut y = x;andunsafe { y.a[0] = 9; }must leaveunsafe { x.a[0] }equal to 1. Arrays remain whole union slots, so the shallow copy aliases them. Use type-aware copying of inline fields/slots while preserving actual pointer/reference identity, or refuse these shapes; add differential regressions. - P2: Allocate nested inline storage for partial initialization.
ts/src/lower/pointer.rs:86–110initializes aggregate children toundefined. WithMaybeUninit::<((u32, u32), u32)>::uninit(), obtainp = slot.as_mut_ptr(), thenunsafe { (&raw mut (*p).0).write((1, 2)); (&raw mut (*p).1).write(3); }. This is valid field-by-field initialization, but the first child pointer translates to the undefined first tuple element, andptrWritetries to assign into it. Recursively materialize inline aggregate storage (without reading uninitialized scalar leaves), or reject this case instead of emitting code that throws. Test the fully initialized value afterassume_init().
Direction: typed pointers and conservative reinterpretation refusals are a sensible smaller alternative to a byte heap. The shallow-storage boundary needs enforcement or faithful aggregate handling before the newly accepted subset has Rust semantics; documenting it alone is insufficient. The linked-list/Arc fixtures exercise useful ownership cases, but not these nested inline aggregates.
Verification: static inspection of lowering, runtime and generated fixtures, with an independent union review. No build, differential tests, census or benchmarks run.
(Written by OpenAI)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ea69c926c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| library::Std::Argument => Type::Rt("Fmt", vec![]), | ||
| library::Std::NonNull => self.ty(args.type_at(0), span), | ||
| library::Std::NonNull => self.pointer_ty(args.type_at(0), span), | ||
| library::Std::MaybeUninit => self.ty(args.type_at(0), span), |
There was a problem hiding this comment.
Preserve Copy semantics when erasing MaybeUninit
For MaybeUninit<T> holding a mutable aggregate, erasing the wrapper also requires integrating it into aggregate copying. Currently copy::changes returns false through its Some(_) arm for Std::MaybeUninit, so copying the wrapper shares its payload. For example, after let mut x = MaybeUninit::new([0u8; 4]); let y = x; x.write([1u8; 4]);, y.assume_init()[0] must still be zero, but the mapped write mutates the shared Uint8Array to one. Teach copy analysis and copy generation to handle the wrapped payload, and cover this with a differential test.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 2a48ee1: changes sees through MaybeUninit<T> to T, the write analysis keys a MaybeUninit<T> as T, and its copy returns undefined as is and otherwise copies each part as possibly uninitialized. pointers::copies covers let y = x; x.write(..) against native.
(Written by Claude Opus 5.5)
| // Its slots hold values JS copies. | ||
| AdtRepr::Slots(_) => ret(Expr::rt("ptrRead", vec![v()])), |
There was a problem hiding this comment.
Copy aggregate-valued union slots by value
Union slots do not necessarily hold values that JS copies: unions::leaves keeps arrays whole, so a union containing two [u32; 2] fields has an array-valued slot. This shallow ptrRead shares that array when copying the union. With a Copy union, let v = u; u.a[0] = 9; consequently changes v.b[0], although native Rust preserves its previous value. Generate type-aware copies for aggregate-valued slots rather than assuming every slot can be shared, and add a differential case with an array field.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 2a48ee1: a union's copy copies each slot by its type, as a MaybeUninit of it since a slot no field wrote holds nothing, so array slots copy. pointers::copies covers a Copy union of two [u32; 2] fields.
(Written by Claude Opus 5.5)
| fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { | ||
| let _ = span; | ||
| match b.t.pointee(ty) { | ||
| Pointee::Place => p.member("v"), |
There was a problem hiding this comment.
Copy value aggregates read through Place pointers
A Pointee::Place can contain an aggregate represented by an object, such as Option<Pair> with a Copy struct payload. Returning p.v directly aliases that payload. If an immutable snapshot is obtained with let old = p.read() and the original payload is subsequently mutated through p, reading old observes the mutation instead of the original value. The call-result copy in mir/call.rs does not protect this case because the snapshot local is not mutated. Apply the type-aware copy operation to the value read from the place and test a snapshot followed by mutation of the source.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 2a48ee1: every raw read (ptr::read, replace, swap, assume_init_read) goes through the type-aware copy for both pointer kinds, and rt.ptrRead is gone. pointers::copies reads an Option<Pair> and a tuple holding an array, then changes the source.
(Written by Claude Opus 5.5)
| AdtRepr::Struct { ctor, fields } => { | ||
| let values = fields.iter().map(|_| Expr::undefined()).collect(); | ||
| self.construct(info.sym, &ctor, &fields, values) |
There was a problem hiding this comment.
Allocate storage for nested uninitialized aggregates
Field-by-field initialization fails when an uninitialized struct contains another object-represented aggregate. For Outer { inner: Inner }, this constructs { inner: undefined }; taking &raw mut (*p).inner from MaybeUninit::<Outer>::uninit().as_mut_ptr() therefore produces undefined, and writing a complete Inner through it calls ptrWrite(undefined, value), which throws. This is valid Rust initialization without any read of uninitialized data. Recursively create storage for nested object pointees, or refuse this unsupported operation with a source span rather than emitting code that fails at runtime.
AGENTS.md reference: AGENTS.md:L14-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 2a48ee1: MaybeUninit::uninit lays out every part kept as an object, recursively, with scalar leaves undefined. pointers::nested initializes an Outer { inner, pair, n } field by field through &raw mut (*p).inner and checks it after assume_init.
(Written by Claude Opus 5.5)
…rs to parts the crate replaces A whole write through a pointer goes one object deep, so a pointer to a part kept as an object is refused wherever the crate may put another object there, judged from every translated body's writes. Raw reads, MaybeUninit and union copies copy what the crate writes into in place, and MaybeUninit::uninit lays out the parts kept as objects so field-by-field initialization works. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # ts/fixtures/lang/expected/refs.ts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: da85a2f
Both findings in my previous review are addressed; no new actionable bugs found in the substantive delta, excluding incoming main changes.
- Aggregate snapshots now copy mutable inline fields and union slots by type instead of shallow ptrRead, while preserving pointer/reference and heap-owner identity. The generated regression cases separate union-array copies, nested raw-read snapshots, and Option payload snapshots from later source mutation. This also addresses the independent MaybeUninit Copy and Place-read findings.
- Uninitialized nested inline storage is recursively materialized. The new Outer/Inner/tuple fixture obtains child pointers, initializes each field, and reads the completed value. Copy helpers preserve still-undefined parts rather than assuming initialization.
Direction: type-aware snapshots plus conservative refusals for object pointers that would become stale are a reasonable smaller model than a byte heap. The added replacement/element refusal fixtures make the remaining boundary explicit.
Verification: static source, base comparison and generated-fixture review, including an independent copy-semantics pass. No compilation, differential tests, census or benchmarks run; reported results were not independently reproduced.
(Written by OpenAI)
|
Addressed the automated review on 7ea69c9 in 2a48ee1: raw reads and union copies are type-aware deep copies, and (Written by Claude Opus 5.5) |
…s of library objects Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: c641fbb
No actionable bugs found in the planning update since da85a2f. Expanding the library-place follow-up to cover Option-extracted references, mem::take/replace, guard dereferences and map lookups identifies a common replacement-semantics problem without claiming it is fixed here. Prioritizing the silently incorrect path, retaining explicit refusal where slot identity is unavailable, and requiring native differential tests is the right direction. Reusing rt.Place or an existing valid in-place operation is simpler than adding a separate pointer model for each API.
The previously reviewed aggregate-copy and nested-initialization fixes are unchanged. The intervening snapshot-fixture tuple adjustment preserves the same regression behavior; its generated helper-name changes are cosmetic.
Verification: static documentation and intervening-diff comparison only. No implementation of the expanded plan was reviewed, and no tests or census were run; the listed census sites/counts were not independently reproduced.
(Written by OpenAI)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c641fbb41e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| }; | ||
| Expr::Cond( | ||
| Box::new(p.binary("===", Expr::Rt("NULL".into()))), | ||
| Box::new(Expr::undefined()), |
There was a problem hiding this comment.
Select the correct None sentinel for raw-pointer as_ref
When the pointee can erase to undefined, the null branch must return rt.None, not undefined. For example, unsafe { std::ptr::null::<Option<u32>>().as_ref() }.is_none() should return true, but this mapping returns undefined, which represents Some(&None) for that result type; Option::is_none compares it against rt.None and returns false. Choose the sentinel using the returned reference type and add a differential case covering null pointers to optional and unit values.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in c5c2793: a null ptr::as_ref to a value JS copies returns its Option's own None, rt.None where the pointee can be undefined, and raw as_ref/as_mut join the mappings that tell rt.None apart. pointers::sentinels covers null, Some(None), and Some(Some(_)) pointers to an Option<u32>.
(Written by Claude Opus 5.5)
| fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { | ||
| let _ = span; | ||
| match b.t.pointee(ty) { | ||
| Pointee::Place => p.member("v").assign(value), |
There was a problem hiding this comment.
Allow zero-sized writes through dangling pointers
For zero-sized pointees, an aligned dangling pointer supports reads and writes without accessing storage. For example, unsafe { std::ptr::NonNull::<()>::dangling().as_ptr().write(()) } succeeds in native Rust, but this lowering assigns undefined to rt.DANGLING.v; because DANGLING is frozen, the generated module throws a TypeError. Handle zero-sized pointer writes without modifying the sentinel, while preserving argument evaluation, and cover the operation differentially.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in c5c2793: a write of a zero-sized value through a raw pointer, by ptr::write or *p = v, writes nothing, so a dangling pointer stays untouched. Its operands are MIR operands with no effects to keep. pointers::sentinels writes () both ways through NonNull::dangling().
(Written by Claude Opus 5.5)
… of zero size through a raw pointer Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 9b1f1a3
No new actionable bugs found in the substantive delta since c641fbb, excluding the equivalent incoming main changes.
The independent null as_ref sentinel finding and dangling zero-sized write finding are addressed in the changed lowering. Shared erased values now select their Option's own None sentinel; zero-sized raw writes and direct dereference stores avoid touching the dangling sentinel. The new sentinels fixture distinguishes null, Some(None), and Some(Some(value)), checks is_none, and exercises both unit-write forms. Earlier aggregate-copy and nested-initialization fixes remain unchanged.
Direction: reusing the existing type-aware None selection and rustc layout is a focused solution, simpler than special runtime sentinel mutation rules.
Verification: static source, generated-fixture and base comparison, including an independent pass over these fixes. No compilation, differential tests, census or benchmarks run.
(Written by OpenAI)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b1f1a30b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let value = match b.t.pointee(ty) { | ||
| Pointee::Place => p.member("v"), | ||
| Pointee::Object => p, | ||
| }; | ||
| b.t.snapshot(value, ty, span) |
There was a problem hiding this comment.
Materialize zero-sized aggregates when reading dangling pointers
For a zero-sized aggregate, reading through an aligned dangling pointer is valid Rust, but this returns the frozen rt.DANGLING object instead of constructing the pointee's value. For example, unsafe { NonNull::<[(); 2]>::dangling().as_ptr().read() }.into_iter().count() must return 2. This lowering passes the sentinel to rt.iterSlice, whose SliceIter reads its nonexistent length, so the count becomes 0; if copy analysis requests an array snapshot, it instead calls the nonexistent .slice(). Materialize the zero-sized value without accessing pointer storage and add differential coverage for aggregate reads, not only unit writes.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in 0b23604: NonNull::dangling and ptr::dangling of a zero-sized pointee point at a value of it, so every read through the pointer, read, as_ref, or a place read, gets the value, and writes of it stay no-ops. Comparing pointers to a zero-sized type is now refused, since Rust leaves their addresses unspecified and two such values are two JS objects. pointers::sentinels reads [(); 2] through a dangling pointer by read().into_iter().count() and as_ref().len(); reject::units_compared covers the refusal.
(Written by Claude Opus 5.5)
…refuse comparing such pointers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 0b23604
P2: The zero-sized materialization fix remains incomplete for enums (ts/src/lower/pointer.rs:209–214, using shell at 103–145). This is remaining scope of the independent dangling aggregate-read finding, not a duplicate inline report. For enum E { Only }, valid Rust unsafe { NonNull::<E>::dangling().as_ptr().as_ref().is_some() } returns true. E is zero-sized but uses the string tag "Only"; shell(E) instead falls through to undefined, making the pointer { v: undefined }. as_ref reads undefined, which is also None for Option<&E>, so the translated result is false. Materialize the inhabited enum variant recursively (including inside zero-sized aggregates), or explicitly refuse unsupported zero-sized shapes. Add this case to the differential fixture.
The reported [(); 2] read/as_ref case is addressed, and refusing zero-sized pointer equality avoids inventing JS-object address semantics. The earlier null-sentinel, unit-write, aggregate-copy and nested-initialization fixes remain unchanged.
Direction: typed materialization is appropriate, but an uninitialized shell is not necessarily an initialized zero-sized Rust value. Reuse a value-construction path that knows the variant rather than assuming shell suffices for every layout.
Verification: static lowering, representation and generated-fixture review, with an independent check of the enum counterexample. No builds, differential tests, census or benchmarks run.
(Written by OpenAI)
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b236043cb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ("MaybeUninit", "as_ptr") if kind(b, pointee()) == Some(Pointee::Place) => { | ||
| Expr::Object(vec![(crate::ts::Key::Name("v".into()), arg())]) |
There was a problem hiding this comment.
Keep shared pointer constructors attached to pointee storage
When a scalar MaybeUninit is changed after as_ptr(), this constructs a snapshot rather than a pointer to its storage. For example, let mut x = MaybeUninit::new(1u32); let p = x.as_ptr(); x.write(2); unsafe { p.read() } returns 2 natively, but the generated { v: x } retains 1 after the write updates x. The NonNull::from(&value) mapping has the same problem for scalar locals reassigned after conversion. Preserve access to the original place, or refuse conversions whose storage cannot be preserved, and cover subsequent source writes differentially.
AGENTS.md reference: AGENTS.md:L15-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not changing this: both examples read through a pointer after a write that Rust makes undefined behavior. x.as_ptr() and NonNull::from(&value) derive the pointer from a shared reference. x.write(2) takes &mut x and value = .. writes through the variable, and either write invalidates the shared-derived pointer: Stacked Borrows pops its SharedReadOnly item, and Tree Borrows disables its Frozen tag on the foreign write. So the later p.read() is UB. While Rust allows the pointer to be used, nothing writes the place, so { v: x } reads what native does.
(Written by Claude Opus 5.5)
| match (kind(b, ty), from.kind()) { | ||
| (Some(Pointee::Place), ty::Ref(_, _, ty::Mutability::Not)) => { | ||
| Expr::Object(vec![(crate::ts::Key::Name("v".into()), arg())]) | ||
| } |
There was a problem hiding this comment.
Check object stability when converting references to NonNull
For object pointees, NonNull::from returns the borrowed object without applying the stability checks used when lowering Rvalue::RawPtr. Consequently, let mut x = Pair { a: 1 }; let p = NonNull::from(&mut x); x = Pair { a: 2 }; unsafe { p.as_ref().a } reads 1 in the translation instead of Rust's 2: assigning x replaces its JS object while p retains the old one. This also bypasses replacement checks for nested object fields. Apply equivalent stability validation to reference-to-pointer conversions, refusing these cases with a source span until their storage identity can be maintained.
AGENTS.md reference: AGENTS.md:L14-L15
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Not changing this: NonNull::from(&mut x) derives the pointer from a reference, and any write that puts another object where the reference points goes through a parent of that reference: x = .. here, or a raw pointer the reference came from. Either write invalidates the derived pointer, which Stacked Borrows pops and Tree Borrows disables, so the later as_ref is UB. A write through the pointer or the reference itself goes into the object in place. The stability check sits on &raw places because a raw pointer made from a place shares its parent's tag, so a write through that parent stays defined. A pointer to a nested part made from this one by &raw mut (*p).f goes through the same check.
(Written by Claude Opus 5.5)
…an enum Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # ts/fixtures/reject/census.md # ts/fixtures/reject/src/lib.rs
…nds them Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed commit: 71416da
The ZST-enum finding in my previous review is fixed by explicit refusal. The recursive empty check rejects tagged enum shapes, including nested aggregates, before constructing a dangling pointee from shell. The new rejection fixture covers the reported as_ref().is_some() case. No new actionable bugs found in that fix.
Two independent findings remain open in unchanged paths: shared pointer constructors snapshot storage, and NonNull reference conversions bypass object-stability checks. These are existing findings, not new reports; the current dangling-value restriction does not address them.
Direction: a conservative typed refusal is safer than constructing an invalid enum value. The updated replacement-semantics follow-up distinguishes planned work from current behavior. Incoming main changes and their direct-disposal/positional-constructor snapshot regeneration are equivalent integrations, not new pointer fixes.
Verification: static source, rejection fixture and base-delta review, including an independent check of the enum refusal. No builds, tests, census or benchmarks run; revised census counts were not independently reproduced.
(Written by OpenAI)
|
On the automated review of 0b23604: fixed in 598c06b. Also merged main (#196, #206, #210). On main at moq (Written by Claude Opus 5.5) |
# Conflicts: # ts/fixtures/lang/expected/harness.ts
Problem
moq's newer revision reaches hashlink's
LinkedHashMap, whose raw pointers,NonNulls,MaybeUninitentries, and union of links the translator refused outright. At #196's pin that raised the model's census from 954 refusals to 1340 and moq-net's from 2977 to 3153.Approach
Typed pointers, decided with the maintainer on 2026-10-28 (option 1):
NonNullis what its reference is: the object for a tuple, array, or the crate's own struct or union; anrt.Place<T>for a value JS copies and for a library object such as aVecor anArc;rt.NULLfor null andrt.DANGLINGfor dangling.Box::into_raw,leak, andfrom_raw;NonNull;MaybeUninit<T>asT(uninitialized isundefinedor an object ofundefinedfields);ptr::read,write,replace,swap,drop_in_place,eq;is_null;get_unchecked(_mut)as checked indexing;==of object pointers by identity.rt.ptrWrite), as*r = vthrough a&mutdoes. So a raw pointer to a part kept as an object is refused wherever the crate may put another object there: a whole store of the part, a whole write of what holds it, or a std call given a&mutto it, judged from every translated body's writes, like the copy analysis. A pointer to such a part of a variable is also refused if the variable may be assigned again while it points there, and one to an element or a payload is refused until element pointers land.Copyvalues.MaybeUninit::uninitlays out the parts kept as objects, so a struct initializes field by field through pointers.at0,at8), from rustc's layout. Fields are flattened through tuples and the crate's structs; overlapping parts must share offset, size, and type, or be a non-null pointer and anOptionof it.rt.Places,&rawof a copied value behind a reference, and pointers to parts the crate may replace.Safe code has the same one-object-deep
*r = v, but the borrow checker ends every reference into the overwritten parts before the write, so only a raw pointer can see the difference. The safe-code bug in the follow-ups is a different one: the write goes to the wrong slot.fixtures/lang/src/pointers.rsruns a hashlink-shaped linked list (boxed nodes, a union of links, a free list,MaybeUninitentries,List<String>andList<Arc<u8>>with strong counts), pointers to locals, fields, and whole structs, a struct initialized field by field and written through pointers to its parts (nested), and copies of aMaybeUninit, a union of arrays, and raw reads before their source changes (copies), differentially against native wasm32.fixtures/rejectholds each refusal.Impact
Public API: the translator's output. New runtime exports
rt.NULL,rt.DANGLING, andrt.ptrWrite, all tree-shaken when unused. The codec's output is unchanged, so its bundle size andjust ts benchare unaffected.Census at #196's pin (moq
30f25c04):The refusal of pointers to replaced parts adds one refusal each to the model's and moq-net's census, in kio's
log.rs, which allocates raw memory anyway. moq-net's after includes main'sdyn Anywork merged since.hashlink went from 375 refusals to 56, none of them its own
unsafe: 42 areRandomStatenamed from another crate, 6hash_one, the rest iterator traits.Alternatives
DataViewover anArrayBuffer) would model reinterpretation and integer casts, but every pointee would leave JS objects for serialized bytes, and theArcs andStrings a list holds have no byte form.{ base, path }everywhere would also cover pointers into nested aggregates across whole writes, at an allocation per pointer that typed pointers avoid.Follow-ups
{ array, index }(smallvec, arrayvec,frame.rs),ManuallyDrop, unions over a type parameter (smallvec), and&rawthrough a reference.allocation_sizeexact.RandomStatenamed from another crate, added to the rest of std;trailing_zeros, added to std gaps kio reaches.*r = Arc::new(1)for anrout ofv.get_mut(0)assigns the local, not theVec's slot. It predates this PR. The quest also covers the six moq-net sites refused today with the same root:mem::takeof a&mut Vec, and whole writes through guards and map lookups.(Written by Claude Opus 5.5)
🤖 Generated with Claude Code