Repository navigation
feat(ts): option fields of a generic type at a unit instance - #612
Conversation
Quest-Claim: 3b0b5822d2d16cc5
A generic type's Option<T> and Poll<T> of a type parameter are rs.OptionOf<T> and rs.PollOf<T>, conditional types that resolve at each instance to that instance's own Option or Poll, so Idle<()> and oneshot::Inner<()> translate, as do trait objects whose Option<Self::Item> holds an Item that can be undefined. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t-option-fields # Conflicts: # ts/src/lower/mod.rs
…onOf Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An instance with an open parameter, such as a field's ByteAnd<u8, U>, still decides its other fields' None, so skip only the fields over that parameter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars.
|
kixelated
left a comment
There was a problem hiding this comment.
Automated review by review (OpenAI)
Reviewed head 1a1c60cbaa177cb18295a7db059dd87936e5e6b2 against base 777c50c1028039f8f3bab7aa6737fa4ca347edfe.
No actionable findings in the inspected changes. The conditional OptionOf/PollOf types agree with the existing sentinel selection for unit, optional, pending, and ordinary payloads. Constructor casts preserve the instance argument instead of inferring it from the sentinel. The field-level checks retain rejection of nested sentinel collisions and the MaybeUninit cases where TypeScript's type and Rust's erasure disagree, including a concrete field under a partially open instance. The trait-object compatibility check applies the same distinction.
Direction: the conditional-type approach is reasonable and avoids tracking all reached instances; no changes requested. The added shapes and trait-object fixtures exercise the newly accepted representations, and reject fixtures cover the incompatible representations.
Verification: GitHub-only static inspection of lowering/runtime changes, surrounding erasure and trait-object logic, fixture sources and generated examples. No local commands, TypeScript checks, differential tests, or census run performed. The exact-head PR workflow returned by GitHub is Queue, skipped; this is not a passing-build claim.
|
quest land watch: the merge queue removed this pull request at 1a1c60c because it conflicts with The land fixer works on it in the background, and the watcher re-enqueues it at the back of the queue once it is fixed or judged flaky, or hands it back here with what it needs. (written by quest land watch) |

Finishes quest/m2/ts-model/unit-option-fields.md: a generic type's
Option<T>orPoll<T>field now translates at an instance whoseTis alreadyundefinedor pending in JS, such as moq-net'sIdle<()>(util/idle.rs'sparked) and tokio'soneshot::Inner<()>(UnsafeCell<Option<T>>).Changes
Option<T>whoseSomeis a bare type parameter is typedrs.OptionOf<T>, and aPoll<T>rs.PollOf<T>. These are new runtime conditional types:T | (undefined extends T ? None : undefined)andT | (typeof Pending extends T ? OuterPending : typeof Pending). TypeScript resolves each instance to the type that instance's ownOptionorPollalready has, soIdle<undefined>.parkedisundefined | rs.NoneandIdle<number>.parkedstaysnumber | undefined. Monomorphized bodies already pickrs.Noneorrs.OuterPendingby the instance's type, so reads need no cast.field_value, through a newdecided_fieldsmap). Without the cast, TypeScript infers the class parameter from theNoneorPendingargument, for exampleParked<symbol | u8 | undefined>.generic_erasurenow refuses only what this can't represent:Tand Rust's erasure disagree, e.g.MaybeUninit<u32>: its TS type isnumber, but itsOption'sNoneisrs.None;Maybe<Option<()>>'sOption<Option<()>>;undefinedor pending at the instance (the old message);Option<MaybeUninit<T>>field, typedMaybeUninit<T> | rs.None, at a byteT, whoseMaybeUninit<u8>is a number, so itsOption'sNonewould beundefined. Before this PR nothing refused it.ByteAnd<u8, U>, is still checked on its fields that don't depend onU.Option<Self::Item>is nowrs.OptionOf<Item>, andfitsindynamic.rsaccepts what that resolves to.dyn Source<Item = Option<u8>>,dyn Source<Item = ()>, and a generic trait'sOption<T>atT = ()orOption<u8>translate. They were refused before (reject/objects.rs'snested_item,received,passed).Tests
lang/shapesgets a newgenerics::unitscase: aParked<T>withOption<T>,Poll<T>, andUnsafeCell<Option<T>>fields, at(),Option<u8>,Poll<u8>, andu8. It parks, readies, sends, takes, and compares, checked differentially against native Rust.lang/dynsgets a newdyns::optionscase: objects returningOption<Option<u8>>andOption<()>through an associated type and a generic trait's parameter.reject: the cases that now translate are removed. Added:values::generic_uninit(MaybeUninit<u32>),values::generic_nested(Option<Option<()>>through a field),values::generic_uninit_byteandvalues::generic_uninit_byte_open(Option<MaybeUninit<T>>atu8, directly and through a generic field with an open parameter), andobjects::uninit_item(the trait object counterpart).would be rs.Noneorwould be rs.OuterPendingrefusals are left in the model (66 refusals now) or in moq-net as a whole (385). No newOption-related refusal replaced them.just cipasses locally.Choices made without asking
Option<T>field's declaration now readsrs.OptionOf<T>instead ofT | undefined, even where no instance needsrs.None; it still resolves toT | undefinedat those instances.Option<Box<T>>andOption<&T>also translate, because their TS type isT. Forms whose TS type isn'tT, such asrs.Ptr<T>, keep the old refusal.reject/futures.rs'sgeneric_pending. It now translates andgenerics::unitscovers it. I found noPollcase the new check would refuse, so it has no replacement.Public API
The translator's output: generic types'
Option<T>/Poll<T>fields and trait-object method types are nowrs.OptionOf<T>/rs.PollOf<T>, the same as before at every instance that translated before. Constructions of such types cast those fields' values. The runtime adds theOptionOfandPollOftypes.Follow-ups
None.
Caused by
No single PR: the
Option<MaybeUninit<T>>gap atu8is as old as the generic-field erasure check.Models
Size: S
Implementer: Claude Code, claude-opus-5-5, high effort
Review
mechanical fix by quest land watch:
merged main at 91a905c94899792e4edade5f89dafa84e16c5034 with the quest merge driver(head f952a04)Rewritten by
quest reviewafter each round.01a1b39a-f5da-7812-81eb-10d920c23e97, round 1No actionable defects found in the specified diff; runtime and cached fixture TypeScript checks passed, but differential execution was blocked by the read-only environment’s inability to acquire the required test gate.