Repository navigation
feat(ffi): export u128, const generics, a () parameter, and an empty enum - #675
Merged
Merged
Conversation
Quest-Claim: 369b964a0792f4f7
… enum as an object Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts: # ffi/README.md # ffi/fixtures/reject/expected.txt # ffi/fixtures/reject/src/lib.rs # quest/m1/ffi-export/refusals/small-shapes.md
…eger Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…census Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efusals/small-shapes # Conflicts: # ffi/README.md
A const parameter of a generic type takes its default, as a type parameter does, and a function's takes each value an instance it names has there. The IR carries a const argument as Type::Const; a C name appends it, as api_drain_4. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efusals/small-shapes # Conflicts: # ffi/fixtures/reject/expected.txt # ffi/src/glue.rs # quest/m1/ffi-export/refusals/README.md
…shapes to its array Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ead_frames A future's Done closure lowers its result when C polls, after the async block has dropped what the result borrows; the block now clones a borrowed result first. The api fixture's async fill<const N> covers it, as group::Consumer::read_frames needs. ffi.toml drops write_frames and read_frames, which export, and lists Buffer::drain and flac::Config::md5, which the census refuses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erve Swift's runtime names A function's parameter choice checked every generic type its signature names, one naming a later parameter too, which no instance matches, so two const parameters refused the function. Each type is now checked once its parameters are chosen; the api fixture's capacities<N, M> covers it. Swift reserves the runtime's U128, SocketAddr, and Comparable, and a test keeps every runtime declaration reserved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
marked this pull request as ready for review
November 10, 2026 22:07
|
Warning Review limit reachedYou'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 6 minutes. View limit details
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. |
…efusals/small-shapes # Conflicts: # ffi/README.md # ffi/moq/ffi.toml # ffi/src/glue.rs # ffi/tests/api.c # quest/m1/ffi-export/refusals/README.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A synchronous Context's task records its place among Rust's parameters, which a () before it shifts from its C index. The glue now maps each C parameter to its Rust place before matching. The api fixture's Channel::listen takes a () before its context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 11, 2026
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.

Works on quest/m1/ffi-export/refusals/small-shapes.md.
Summary
()parameter of a function is passed by nothing in C; the glue passes()to Rust. Clears the fourmux::container::Producer<C>::setinstances at the moq pin (Rdefaults to()). A callback trait's()parameter stays refused, since the glue's trait implementation names every parameter.transport::poll::Ungrouped's "enum without variants".u128exports by rule:<prefix>_u128in C, a struct of itsloandhi64-bit halves, and above it Pythonint, Kotlinjava.math.BigInteger, DartBigInt, Go*big.Int, Swift aU128struct ofhighandlow(withComparable,Hashable, and its decimaldescription) until its deployment target allowsUInt128, and C++ the C struct asu128. A value passed in out of range raises the language's error: PythonOverflowError, KotlinIllegalArgumentException, DartArgumentError, and a Go panic. ClearsTimestamp::as_scale,as_millis,as_micros, andas_nanos.Type::Const(Literal)in the IR, beside type arguments. A generic type's const parameter infers its default, as a type parameter does, soframe::Buffer<const N: usize = 8>exports asBuffer, and any other value a signature names exports too (Batch<2>isapi_Batch_2). A function's const parameter takes each value an instance of a generic type it passes the parameter to has, checked against each generic type the signature names once that type's parameters are all chosen, sogroup::Producer::write_framesandgroup::Consumer::read_framesexport atN = 8, and a C name appends the value (api_drain_4,api_drain_2). The glue names a const argument in its turbofish. A const argument that is not aboolor an integer below 128 bits is refused, and so is a function's const parameter that nothing names.()parameter and a poll's context are both omitted from what C passes; the glue places each by its position among Rust's parameters, so a()before aContextstill lends the context where Rust takes it.read_frames's&'a mut [Frame]borrows its&mut Bufferargument, failed to compile: the glue lowered the result in the closure C polls, after the async block had dropped what it borrows. The block now clones a borrowed result first, as the synchronous path already does when it lowers.reach(start, (), end),scaled(v: u128, by: u64) -> Option<u128>,Batch<const N: usize = 4>withpair() -> Batch<2>,drain<const N>(&mut Batch<N>),capacities<const N, const M>(&Batch<N>, &Batch<M>), and the asyncfill<const N>(&mut Batch<N>, v) -> &mut [u32]in the api fixture, called from every language against the Rust transcript; out-of-range checks in Python, Kotlin, and Dart; Swift's decimaldescription;enum Void {}in the api fixture's census. The reject fixture refuses acharconst argument (Fixed<'a'>) and a const parameter nothing names (probe<const N>).U128,SocketAddr, andComparable, so an export that takes one is refused at its source rather than generating Swift that fails to compile, and a unit test keeps every type the runtime declares, and every protocol one conforms to, reserved.ffi/moq/ffi.tomldropswrite_framesandread_frames, which export, and listsframe::Buffer::drain(an arrayvec iterator, newly reached now thatBufferexports) andfield@mux::codec::flac::Config::md5(an array, reached since #678), as #693 also spells it.Census at the moq pin after this: no moq-own item refused for unit, an empty enum,
u128, or a const generic. Two arrays are left,tokio::tls::parse_fingerprint's[u8; 32]andflac::Config::md5's[u8; 16]: the quest expected narrowing the stand-in to drop the arrays, and it did not. The quest is rewritten to them, with the mapping left for the maintainer.Decisions
Settled by the maintainer on 2026-11-09:
u128: ✅ map it by rule as<prefix>_u128, twouint64_thalves in C, with each language's exact big integer above it (rejected: leave out). Implemented here across all seven generators.i128stays refused: its sign is a conversion of its own in each language and no consumer uses it, so it is not the same rule at no extra cost.Send(clock::Entered,clock::Rng,catalog::Guard): owned by not-send's thread check, decided 2026-11-09; dropped from this PR.group::Producer::write_frames,group::Consumer::read_framesoverframe::Buffer<const N = 8>): ✅ do it in this PR once generics, moq-dev/rust2#670, lands, since both edit instance inference (rejected: a separate quest). feat(ffi): export generic parameters through blanket impls, and leave out traits C cannot implement #670 has merged; implemented here.Unattended picks, made for the maintainer on 2026-11-10 while they were away:
mainput #679's poll contexts beside this PR's omitted()parameters, and a synchronousContextwas matched by its place among Rust's parameters against the compacted C index. ✅ (a) keep both omitted kinds at their Rust place and have the glue map each C parameter to it before matching (rejected: (b) keeping()inparamsas aType::Uniteach of the seven generators skips; (c) refusing a()beside a context). Fixed in 99ab758; the api fixture'sChannel::listen(&self, _: (), cx: &Context)covers it in every language.ffi/moq/moq.cpp's C++ failure after feat(ffi)!: C++ checks ownership at compile time under Clang #694 belongs to #693, which keepsjust ffi moqgreen; it is left out of this PR.Choices made without asking:
u128rather thanunsigned __int128, which MSVC lacks.write_frames_8), so a later instance never renames it.boolor an integer below 128 bits, the valuesLiteralalready holds; acharone is refused, as no consumer has one.[T; N]maps is a design decision the quest now asks the maintainer, recommending bytes.Public API
C ABI: a function with a
()parameter now exports, without that argument. Au128parameter or return now exports as<prefix>_u128, and the runtime claims the C name<prefix>_u128. A const-generic type exports at its inferred instances, named by any const argument past its default (<prefix>_Batch_2), and a const-generic function at each inferred value, its C name ending with the value (<prefix>_drain_4). moq's C API gainsmoq_frame_Bufferandmoq_group_Producer_write_frames_8andmoq_group_Consumer_read_frames_8. No item that exported before changes.Follow-ups
[u8; N].frame::Buffer::drainreturns animpl ExactSizeIterator + '_over an arrayvecDrain, excluded inffi/moq/ffi.tomlhere; recommend planning it with the other borrowed iterators.Models
Size: M
Implementer: Claude Code, claude-opus-5-5, medium effort (iterate, M)
Implementer: Claude Code, claude-opus-5-5, medium effort (iterate, M)
Checks
just cipassed locally on 99ab758, and the full ffi suite (every language's api fixture and the reject fixture).just ffi moqfails onmainin two places this PR does not touch:varint::decode_quic's&mutparameterr, which moq-dev/rust2#693 fixes.ffi/moq/moq.cpp:326, whosecatalog->mutatelambda takesmoq::hang_Catalog_Extra &where #694's header now passesmoq::borrow<moq::hang_Catalog_Extra>.ffi/moq/moq.cppis unchanged frommainhere; fix: the nightly passes: NaN results as one NaN, ART's lent bytes in their own frame, Kotlin's reader apart from params, and a failure issue #693 owns the fix.With #693's
ffi/src/kotlin.rschange applied locally, the moq export built and its C, Kotlin, Python, Swift, Go, and Dart programs passed.Review
Rewritten by
quest reviewafter each round.01a1c778-5888-70c0-8479-9648eee01db3, round 4The Context index mismatch is fixed, and code inspection found no new issues in the delta; builds and tests were not run as instructed.ffi/src/collect.rs:2307:Adjust synchronous Context indices for omitted unit parameters;glue.rs now matches task.at against each parameter's original Rust position after skipping omitted arguments, and the Channel::listen fixture covers a unit argument preceding a synchronous Context.