Skip to content

feat(ts): translate Box::new_uninit, new_zeroed, write, and assume_init for one value - #639

Merged
kixelated merged 3 commits into
mainfrom
quest/m3/ts-unsafe/box-new-uninit
Nov 7, 2026
Merged

kixelated merged 3 commits into
mainfrom
quest/m3/ts-unsafe/box-new-uninit

Conversation

@kixelated

@kixelated kixelated commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

Completes quest/m3/ts-unsafe/box-new-uninit.md.

Summary

rust2-ts now translates Box::new_uninit and Box::new_zeroed for one value, written through MaybeUninit::write, its pointer, Box::write, or a raw pointer from Box::into_raw, and taken by assume_init.

  • Box::new_uninit holds what MaybeUninit::uninit makes.
  • Box::write writes into the box's own object or array, as MaybeUninit::write does, so a pointer taken before the write still points at what the box holds. The pointer analyses (derives, stores) treat it as they treat MaybeUninit::write.
  • Box::assume_init passes the value through. It used to be mapped for boxed slices only, and now covers one value too.
  • Box::new_zeroed builds the value of zero bytes part by part. Arrays, tuples, and the crate's structs are built field by field. Wrapping, Saturating, Reverse, and AssertUnwindSafe, which JS keeps as the value they wrap, are zeroed through to that value. Cell/UnsafeCell, RefCell/RwLock, and Mutex wrap their zeroed value, unborrowed and unlocked. An enum becomes the variant its zero tag or niche picks, with that variant's fields zeroed in turn. Any other part that rustc's ValidityRequirement::Zero check allows is lowered as a constant of zero bytes. A part that zero bytes aren't a value of, such as a reference or a String, stays unwritten (undefined, as MaybeUninit::uninit leaves it) until a write fills it.
  • MIR constant lowering reads a pattern type (ty::Pat) as its base type and builds a Duration from its two fields. Both are needed to zero std types like NonZero's inner pattern type and Duration.
  • The README's MaybeUninit<T> row documents both constructors.

The new lang/manual cases uninit::boxed_one and uninit::boxed_zeroed cover:

  • writes through each route
  • a pointer taken before Box::write, compared to the box afterwards, for a struct and an array
  • a box that is never written, or written but never taken by assume_init, which drops nothing
  • generic callers
  • zeroed scalars, pointers, char, f32
  • enums with a zero tag or a zero niche
  • Cell, AtomicU32, Wrapping, Duration, RefCell, Mutex
  • a Wrapping<(u32, &u8)> and an UnsafeCell<(u32, &u8)> with only the reference written
  • an enum whose zero variant holds a reference
  • arrays and tuples written in part
  • a crate struct with a drop-counting field

Both cases match native Rust.

Choices made without asking

  • Box::write is mapped too. The quest asked to check whether write was already covered. It wasn't, and it's the natural way to fill a Box<MaybeUninit<T>>, so it's included.
  • Parts that zero bytes aren't valid for stay unwritten instead of being refused. In Rust, reading such a part before writing it is UB, and writing first is sound. So Box::<(&u8, u32)>::new_zeroed() translates, and only an actual read of the unwritten part differs.
  • Duration and pattern types in constant lowering. These were added to the general MIR constant path, not special-cased in the zero builder, so any other constant of these types benefits too.

Impact

Public API: the translator's output. Code using these four Box functions, which was refused before, now translates. Other output only gains Duration and pattern-type constants. The runtime is unchanged.

Validation

  • UPDATE_EXPECT=1 just ts test --test fixtures lang::manual: passes against native Rust. The snapshot diff adds only the zeroed Cell, AtomicU32, Wrapping, and Duration cases, plus renumbered temporaries.
  • quest check --fix: ok.
  • just ci on a21768f: everything passes except lang::dyns and lang::panics. Their diffs touch only TypeId hash strings, and bare origin/main (efa6300cc) fails the same way in this checkout. moq-dev/rust2#633, which fixes those snapshots, is still open.

Follow-ups

Each is recommended to plan:

  • MaybeUninit::zeroed, mem::zeroed, and Box::new_zeroed_slice, reusing the zero-value builder (Fx::zeroed) added here.
  • Rc::new_uninit/Arc::new_uninit and their new_zeroed.
  • A raw pointer to a nested array element, &raw mut (*p)[1][0]. The fixture writes its zeroed grid through a slice instead.

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 review after each round.

  • Reviewer: Codex, gpt-6.1-sol, medium effort
  • Session: 01a1b61f-018c-7ae0-b705-eeecb4ea0dcd, round 2
  • Head: a21768f
  • Base: origin/main at efa6300
  • Score: 10/10, Both reported defects are fixed and covered by differential fixtures, and inspection found no additional concrete defects; tests were not rerun in the read-only environment.
  • Findings: none
  • The last round's findings, at e0c5410:
    • Fixed: medium, ts/src/lower/library.rs:2477: Preserve the destination allocation in Box::write; ts/src/lower/pointer.rs now writes object and array values into the original allocation and returns it, with retained-pointer comparisons for Pair and [u16; 3] in uninit::boxed_one.
    • Fixed: medium, ts/src/lower/mir/place.rs:1628: Preserve zeroed fields inside partially invalid standard wrappers; ts/src/lower/mir/place.rs now recursively zeroes supported wrappers and cells, with partial-reference initialization cases for Wrapping and UnsafeCell in uninit::boxed_zeroed.

@pullfrog

pullfrog Bot commented Nov 7, 2026

Copy link
Copy Markdown

Pullfrog  | Review this ➔

…it for one value

`Box::new_uninit` holds what `MaybeUninit::uninit` makes, and `write` and
`assume_init` pass the value through. `Box::new_zeroed` builds the value of
zero bytes part by part: arrays, tuples, and the crate's structs field by
field, an enum as the variant its zero tag or niche picks, and any other part
rustc says zero bytes are valid for as that constant; a part they aren't
valid for, such as a reference, stays unwritten for a write to fill.

MIR constant lowering now reads a pattern type as its base and builds a
`Duration` from its fields, which a zeroed `Duration` needs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title A Box of one uninitialized value feat(ts): translate Box::new_uninit, new_zeroed, write, and assume_init for one value Nov 7, 2026
Box::write now writes into the box's own object or array, as
MaybeUninit::write does, so a pointer taken before it still points at
what the box holds. Box::new_zeroed recurses through Wrapping,
Saturating, Reverse, and AssertUnwindSafe, which JS keeps as the value
they wrap, and through Cell, UnsafeCell, RefCell, RwLock, and Mutex, so
a part zero bytes are a value of stays zero beside one they are not.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated marked this pull request as ready for review November 7, 2026 13:20
@coderabbitai

coderabbitai Bot commented Nov 7, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You'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 14 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 60dd0d37-c845-436c-8979-067d3dc7e11b
Commits

Reviewing files that changed from the base of the PR and between efa6300 and a21768f.

Files selected for processing: 18

  • quest/m3/ts-unsafe/README.md
  • quest/m3/ts-unsafe/box-new-uninit.md
  • ts/README.md
  • ts/fixtures/lang/manual/expected/core/option.ts
  • ts/fixtures/lang/manual/expected/lib.ts
  • ts/fixtures/lang/manual/expected/uninit.Held.ts
  • ts/fixtures/lang/manual/expected/uninit.Pair.ts
  • ts/fixtures/lang/manual/expected/uninit.Tag.ts
  • ts/fixtures/lang/manual/expected/uninit.Zeroed.ts
  • ts/fixtures/lang/manual/expected/uninit.ts
  • ts/fixtures/lang/manual/src/lib.rs
  • ts/fixtures/lang/manual/src/uninit.rs
  • ts/src/lower/library.rs
  • ts/src/lower/mir/call.rs
  • ts/src/lower/mir/place.rs
  • ts/src/lower/mir/rvalue.rs
  • ts/src/lower/pointer.rs
  • ts/src/lower/time.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

Share

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review by review (OpenAI)

Reviewed commit a21768f against base efa6300.

No actionable findings identified in this static review. Extending the existing MaybeUninit representation and constant lowering is a consistent direction. I checked Box::write's in-place object/array write and pointer-analysis integration, recursive zero construction for aggregates and supported wrappers, validity-gated constant lowering, enum selection, pattern types and Duration, and the added initialization/drop/retained-pointer fixtures. The two defects described as fixed in the PR are addressed in the current code.

Verification limits: GitHub source/diff inspection only. I did not run the translator, native differential fixtures, or just ci, and have not independently reproduced the reported passing cases or checkout-dependent TypeId snapshot failures. This is a COMMENT, not an approval.

(Written by review (OpenAI))

@kixelated
kixelated added this pull request to the merge queue Nov 7, 2026
Merged via the queue into main with commit 84f07c8 Nov 7, 2026
10 checks passed
@kixelated
kixelated deleted the quest/m3/ts-unsafe/box-new-uninit branch November 7, 2026 14:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant