Repository navigation
feat(ffi): export generic parameters through blanket impls, and leave out traits C cannot implement - #670
Merged
Conversation
Quest-Claim: 119062c50befb018
…oice bounds, and leave out traits C cannot implement Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… for the call Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rameter names Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efusals/generics # Conflicts: # ffi/README.md # ffi/fixtures/reject/expected.txt # ffi/src/collect.rs
`impl IntoIterator<Item = &'a X>` takes `&'a [X]` rather than a slice whose borrow is erased, so the borrows analysis of the instantiated signature decides whether C's slice lives long enough, as it does for any `&'a` parameter. The hand-written `lent` guard goes, along with the cases it missed: a lifetime that a later-chosen handle parameter pins to `'static`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the slice borrowed for the items' lifetime, `first_of`'s returned borrow is copied out before the call ends, so it exports soundly: move it to the api fixture and test it in every language. Regenerate the reject fixture's expected refusals after merging main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A parameter kept for a foreign implementation stayed a parameter in every bound, so `unmet` skipped `B: From<U>` and kept a choice of `B` whose glue does not compile. A bound that names only kept parameters, other than one of them as its own subject, which what the call passes meets, now holds only as the item's own environment proves it for every type passed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kixelated
marked this pull request as ready for review
November 10, 2026 15:22
|
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 45 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. |
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 quest/m1/ffi-export/refusals/generics.md and rewrites it to what remains.
Problem
The census at moq
ffcb7cd9refused 51 of moq's own items for a generic parameter, type argument, trait object, supertrait, associated type, unmet bound, callback or closure signature, or blanket impl. moq-ffi'ssession.rsandserver.rswrapClient::connectandServer::acceptby hand becauseBoxablerefused them.Approach
impl Boxableis, exports at each type implementing what the impl requires (Server::accept<transport::memory::Session>, and moq-tokio's webSessionuntil narrowing drops it).instantiatechooses each type parameter at each choice of the earlier ones, sotrack<C, R>(.., config: impl Into<Option<R>>)takesOption<R>for eachR, rather thanStringchosen once withRunbound, which failed as an unmet bound (6 items).impl io::Readtakes&[u8](Catalog::from_reader, 3 items);impl IntoIterator<Item = &X>takes&[X].BytesandDurationname instances (health::Limits<Duration>).Box<dyn Source>does, is refused with a note naming why (16 of moq's items; 101 traits in the census, mostly rustls's and moq-tokio's). The api fixture'sSeed,Kept, andPayloadexcludes go.Census of moq-ffi at the pin: these constructs refuse 25 items of moq's own, down from 58, and 13 of the 25 are moq-tokio items outside its session side, which narrowing drops;
Client::connectandServer::accept_requestnow pass their generic parameter but stay refused for a future borrowed from a function that is notasync, which poll.md owns.build_prefixexports once moq stops re-exportingbyteswhole, which makesBytesa handle. The rest, futures and kept closures as parameters, is the rewritten quest, waiting on how they cross from C.Choices made without asking:
transport::poll::Session,Container,IntoBytes, and the like.&[u8]is taken only forio::Read, not as a general fallback, which picked&[u8]for anyT: Copyand brokePair::beside.ffi/moq/ffi.tomlkeeps its excludes: deleting it is the epic's work, and narrowing edits it in parallel.Impact
rust2-ffi: a trait C cannot implement no longer fails the export; the census gains a section listing them and its Traits row no longer counts them refused.impl io::Readandimpl IntoIterator<Item = &X>parameters, instances atBytesandDuration, and parameters bounded by an earlier one.just ffi moqpasses unchanged.Alternatives
Follow-ups
free) and an owned closure in the exporter.Client::connectandServer::accept_requestnow reach its "borrowing future" construct. Recommend: fold into poll.md when it measures again, no new quest.Decisions
'ain theimpl IntoIterator<Item = &'a X>candidate, as&'a [X]instead of an erased borrow, so the existingborrowsanalysis of the instantiated signature decides, and delete the hand-writtenlentguard (rejected: (b) drop the rule and leavebuild_prefixto the rewritten quest, (c) keep adding guards). Landed (a): the candidate takes the item's own region, whichinstantiatealready keeps as it is, so no region plumbing was needed. A'staticitem, a handle's&mut Text<'a>, a'b: 'abound, and a handle chosen for an earlierT: Into<Text<'a>>all make'a'static, whichborrowsrefuses as'static borrowat its span, as for any&'aparameter. This closes the review finding atffi/src/collect.rs:2331. Reject fixtures:summed_foreverandretext_last(rounds 1-2), and newoutlastandretext_into(round 3).first_of, which returnsOption<&'a String>borrowed from the items, now exports, and soundly: the borrow is copied out before C's slice ends, as any borrowed return of aClonetype is. It moved from the reject fixture to the api fixture, tested in every language.unmetskip a bound naming it, such asB: From<U>, keeping a choice whose glue does not compile; the per-choice loop this PR rewrites carried that skip over. Fixed here: a bound naming only kept parameters, other than one of them as its own subject (met by what the call passes), must hold in the item's own environment. The api fixture'stallyexports only atLogCount, and fails to compile atPointwith the change reverted; the reject fixture'stalliedrefuses as an unmet bound.Checks
just cipasses at3c278e88.just ffi moqfails onmaintoo: moq-dev/rust2#668 refuses moq'shang::moq_netandtokio::moq_netwhole-crate re-exports, which moq-dev/rust2#676 excludes. With docs(quest): narrow the moq pin quest to what moq and the maintainer owe #676's two excludes applied locally,just ffi moqpasses at3c278e88.Models
Size: L
Implementer: Claude Code, Claude Opus 5.5, high effort
Implementer: Claude Code, claude-opus-5-5, high effort (iterate, L)
(Written by Claude Opus 5.5 with high)
🤖 Generated with Claude Code
Review
Rewritten by
quest reviewafter each round.01a1c557-5ae9-7921-ae6c-8fdfd59f4c86, round 2The fix validates dependent bounds before retaining callback specializations, with regression fixtures covering valid exports and source-span refusals; no remaining issues were found by inspection.ffi/src/collect.rs:2226:Validate dependent bounds before keeping a callback parameter;ffi/src/collect.rs:2313 now checks dependent bounds involving kept parameters, filtering invalid choices, while ffi/fixtures/api/src/lib.rs:1493 and ffi/fixtures/reject/expected.txt cover successful specialization and refusal.