Skip to content

feat(ffi): a contract symbol each header and library check - #146

Merged
kixelated merged 5 commits into
mainfrom
quest/m1/ffi-export/contract
Oct 28, 2026
Merged

kixelated merged 5 commits into
mainfrom
quest/m1/ffi-export/contract

Conversation

@kixelated

@kixelated kixelated commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

A header and a library from different exports can be compiled and linked together, and nothing notices until a call misbehaves. The UniFFI fork guards this with a namespaced contract version and per-method checksums; the exporter had nothing.

Approach

  • c::header hashes everything the header declares (types, functions, and their docs) with rustc's platform-independent StableHasher, already linked through rustc_data_structures, so no new dependency.
  • The header defines <PREFIX>_CONTRACT and declares uint64_t <prefix>_contract(void) right after extern "C" {; the glue exports that function returning the same value.
  • contract and CONTRACT are claimed as runtime names, so an exported item with that C name is a clash error like buf_free.
  • tests/api.c and moq/moq.c check the contract before any other call.
  • The exporter epic now requires every generator to check the contract at load and fail loud on a mismatch.

Removes quest/m1/ffi-export/contract.md and its link.

Impact

  • New exported C symbols in every export: <prefix>_contract() and #define <PREFIX>_CONTRACT. Breaking only for an item that already took either name, such as a crate root pub fn contract, which now clashes and needs a rename; see ffi/MIGRATE.md.

Choices made without asking

  • The hash covers the docs. The docs carry ABI meaning the declarations don't, such as "Moves the value out of self", so a doc-only change also changes the contract. That's stricter than strictly needed, and harmless since header and library ship together.
  • A function plus a define, not a symbol with the hash in its name. A hash-named symbol would only fail at link time if the header forced a reference, and that needs non-portable tricks. A function is something every dynamic loader (ctypes, JNA/Panama, Swift) can call.
  • The glue and runtime are left out of the hash. If the library changes behind an identical header, the ABI is still compatible, the same as a patched shared library.

Alternatives

  • Per-function checksums, as UniFFI has: more symbols for no extra safety, since one hash of the whole header catches any change.

Checks

  • just check: pass
  • just ffi test: pass (14 tests)
  • just ffi moq: pass
  • just ts test, just driver test, just uniffi test: not run, since this doesn't touch them

Follow-ups

None.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 2 commits October 27, 2026 19:36
Each export's header defines <PREFIX>_CONTRACT, a stable hash of what it
declares, and the library exports <prefix>_contract() returning it, so a
binding refuses a header and a library from different builds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title A contract symbol feat(ffi): a contract symbol each header and library check Oct 28, 2026
@kixelated
kixelated marked this pull request as ready for review October 28, 2026 02:57
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-28T08:19:26.499814Z 37e1b51 New commits
🔒 Security Review ✅ Completed 2026-10-28T08:20:16.889355Z 37e1b51 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c29a110e4a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ffi/src/abi.rs Resolved

@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: c29a110

No additional actionable implementation bugs found. The new reserved names in ffi/src/abi.rs:390-395 do break existing exports; the missing migration entry is already covered by the existing finding, rather than duplicated here.

Direction: one header hash, computed once and passed directly to glue generation, is a simple fit for whole-header compatibility; per-method checksums would add unnecessary machinery. Including docs deliberately makes compatibility stricter.

Verification: static review of header generation, glue wiring, name claims and the two C checks only. I did not run builds/tests or verify mismatch rejection with a deliberately mismatched header/library pair.

(Written by OpenAI)

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

@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: 677a932

The previously referenced migration finding is fixed: ffi/MIGRATE.md:3-7 documents both reserved names and the rename migration. This is the only change since c29a110; the base is unchanged.

No new actionable bugs found. The focused documentation fix preserves the simple whole-header contract design without adding compatibility machinery. Verification was static comparison and document inspection only; no builds or tests run.

(Written by OpenAI)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merge summary:

  • Merged main twice. The first time, the quest driver resolved the quest/m1/ffi-export/README.md link list on its own. The second time, I hand-resolved an add/add conflict in ffi/MIGRATE.md by keeping both ## Unreleased entries (feat(ffi)!: name a field apart from the method of its name with field@ #145's field@ entry first, then the contract's reserved names).
  • Codex reviewed head 37e1b51 and found nothing in its code or security reviews. Its one earlier finding (the MIGRATE entry) was fixed in 677a932 and replied to.
  • CI passed on 37e1b51. Locally, just check, just ffi test (14 tests), and just ffi moq passed. quest check --base origin/main passed.
  • No open decisions. Squash-merging as authorized by the maintainer.

(Written by Claude Opus 5.5)

@kixelated
kixelated merged commit f985630 into main Oct 28, 2026
1 check passed
@kixelated
kixelated deleted the quest/m1/ffi-export/contract branch October 28, 2026 09:03
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