Skip to content

feat(ts): translate Rc, Weak, and Pin trait objects, and Rc and Arc from a box of one - #450

Merged
moq-bot[bot] merged 13 commits into
mainfrom
ts-dyn-rc
Nov 2, 2026
Merged

moq-bot[bot] merged 13 commits into
mainfrom
ts-dyn-rc

Conversation

@kixelated

@kixelated kixelated commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A trait object of the crate's own trait, dyn Any, or dyn Fn translated behind &, &mut, Box, and Arc, but std coerces to one behind more pointers, and these were refused: Pin<Box<dyn Trait>> and other pins, a Weak<T> unsized to Weak<dyn Trait>, Rc::from and Arc::from of a Box<dyn Trait>, and Rc<dyn Any>'s downcast. The trait objects quest's goal names these pointers. moq builds none of them; this is std coverage the quest calls for.

Approach

  • A Pin is the pointer it holds, so an unsizing of a Pin<P> unsizes P.
  • A Weak<T> unsized to a Weak<dyn Trait> is rt.weakDyn of it: the same count, plus a view that makes the rt.ArcDyn as it upgrades. It cannot make the object as it unsizes, since Rc::new_cyclic's Weak unsizes before its value is there. Weak::clone keeps the view. An upcast is the same Weak.
  • Arc::from and Rc::from of a Box<T>, a trait object's included, are an rt.Arc of what the box is, which drops it with the last clone. A boxed byte array, held as its own Uint8Array, stays refused.
  • Rc::downcast maps as Arc::downcast does. Of an Arc made from a box, rt.arcDowncast gives an rt.ArcDyn of the value that shares the box's count.

The reject fixture's weak_object case now translates, so it is gone. Rc<RefCell<dyn Trait>>, a struct whose last field unsizes, stays refused and is a new quest.

Tests: dyns::pointers in the lang fixture's differential test against native wasm32 covers:

  • Rc<dyn Named> and an upcast to Rc<dyn Shape>, with counts.
  • A Weak unsized from an Rc<Square> and from an Arc<Noisy>: upgrades before and after the value drops, ptr_eq against a downgrade of the trait object, weak_count, and a clone.
  • A Weak<dyn Shape> made inside Rc::new_cyclic and upgraded after.
  • Pin<Box<dyn Shape>>, which drops what it holds at the end of its scope, and Pin<&mut dyn Shape>.
  • Rc::from and Arc::from of boxes of the crate's shapes, a number, and an enum, with the drop order of clones.
  • Rc::downcast of an Rc<dyn Any>, including one made from a Box<dyn Any>, which shares its count until the clone drops.
  • Arc::from of a Box<dyn Any + Send + Sync> and its downcast.
  • Rc<dyn Fn>, and Arc::from of a Box<dyn Fn>.

With rt.weakDyn making the object as it unsizes instead of as it upgrades, the differential test fails (214 differences in 709,542 cases).

Census at the moq pin, before → after: unchanged (959 / 227 / 139 for moq-net / model / kio), since moq builds none.

mul_add and #400

Earlier, this PR's new case shifted one random input onto floats::mul_adds, where wasm32's fma misrounds a near tie (rust-lang/compiler-builtins#1353). #400 has since landed with the allowance for it, and this branch has merged it.

Impact

  • The translator's output: these pointers translate instead of failing. rt.Weak gains an optional view. New runtime export rt.weakDyn. rt.arcDowncast takes a third argument, the function giving what the object holds.
  • No breaking change for a consumer: rt is the translation's own runtime.

Alternatives

  • Make a Weak's object as it unsizes, as an Arc's is: rejected. It fails for a Weak unsized inside Rc::new_cyclic, and the differential test catches it.
  • Rc::from(Box<dyn Trait>) as an rt.ArcDyn over a new rt.Arc: needs the value's own type, which a box of a trait object no longer names. An rt.Arc of the object works for every call and drop.

Follow-ups

  • New quest quest/m3/ts-dyn-tail.md: a trait object in a struct's last field, such as Rc<RefCell<dyn Trait>>.
  • Arc<dyn Iterator>, dyn FusedIterator, and &mut dyn Debug, the quest's remaining items, wait on #437 landing.

Review

mechanical fix by codex / Codex: Merged base and regenerated the harness snapshot, preserving both sides; passed UPDATE_EXPECT=1 cargo test --locked --test fixtures lang -- --exact and cargo test --locked in ts/, plus tsc -p ts/runtime. (head f0f151f)

mechanical fix by codex / Codex: Merged base and combined both README changes without changing behavior; passed RUST2_TS_TOOLCHAIN=/nix/store/i222nr66l2r3xfahzfpp1m6mawvhxyl2-rust-default-1.101.0-nightly-2026-10-15 just ts test; same toolchain with just ts cargo fmt --all --check; tsc -p ts/runtime; git diff --cached --check. (head bc13b8c)

mechanical fix by codex / Codex: Merged base and regenerated reject snapshots and lang harness names; with the installed pinned nightly, just ts cargo build --locked and just ts test -- --skip stable_names passed; stable_names passed in just ts test; tsc -p ts/runtime passed. (head 44ef507)

Codex gpt-6.1-sol at medium effort, via quest review, round 2 on head 246f079dea901fb0d193f8852963eced2a07af69: no findings. Round 3, on head 2f7006d73e910ca33dbf67d4432a03b6a55e18dd after merging origin/main (#439, #400, #443, #447, #418) and refreshing snapshots: no findings.

Round 1 (head 412a002d) findings:

  • Two downcasts of an Rc<dyn Any> made from a box gave distinct views, so Rc::ptr_eq was false: fixed. rt.arcDowncast keeps one view per allocation, and arcRoot unwraps every ArcDyn. dyns::pointers covers it.
  • Weak<dyn Trait> to Weak<dyn Any> skipped object_of's check: fixed. It is refused with a span, pinned by the reject fixture's weak_any_upcast.
  • Two findings on sorting, refuted: they are in #439's code, which landed on main during the round, outside this branch's diff.

🤖 Generated with Claude Code

kixelated and others added 7 commits November 2, 2026 10:08
…rom a box of one

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and refuse a Weak's upcast to dyn Any

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#400 covers the same bug (rust-lang/compiler-builtins#1353) with quest/m3/ts-fma-allowance.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

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: 5d8060df-d77b-4968-b33d-34d33c1c6b45
  • 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 and others added 2 commits November 2, 2026 12:51
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated
kixelated enabled auto-merge (squash) November 2, 2026 21:13
@moq-bot
moq-bot Bot disabled auto-merge November 2, 2026 21:32
@moq-bot
moq-bot Bot enabled auto-merge (squash) November 2, 2026 21:32
@moq-bot

moq-bot Bot commented Nov 2, 2026

Copy link
Copy Markdown

codex / Codex land fixer note:

Merged base into this branch and regenerated the two conflicting rejection snapshots from the combined sources. Also refreshed generated local names in the language harness snapshot. All 29 TypeScript driver, fixture, and runtime tests passed across the validation runs, including stable_names and the language differential test. The package build and runtime TypeScript type-check passed. No behavior changes were made by the fix.

(written by codex / Codex)

@moq-bot
moq-bot Bot disabled auto-merge November 2, 2026 21:44
@moq-bot
moq-bot Bot enabled auto-merge (squash) November 2, 2026 21:44
@moq-bot
moq-bot Bot disabled auto-merge November 2, 2026 22:02
@moq-bot
moq-bot Bot enabled auto-merge (squash) November 2, 2026 22:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant