Skip to content

docs: write down the FFI ownership contract - #36

Merged
evolvedlight merged 1 commit into
evolvedlight:developfrom
msallin:docs/ffi-ownership-contract
Sep 6, 2026
Merged

docs: write down the FFI ownership contract#36
evolvedlight merged 1 commit into
evolvedlight:developfrom
msallin:docs/ffi-ownership-contract

Conversation

@msallin

@msallin msallin commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Closes the part of #29 that is still open.

The allocation fix and the missing null check both landed in #33: vec_to_raw now goes through into_boxed_slice, so the deallocation layout matches by construction, and compile_internal rejects a null compiler. What was left is the third item: the ownership contract was nowhere in the source, and free_compile_result was a safe extern "C" fn even though calling it twice frees the same allocations twice.

What this adds

A module-level doc stating the rules a caller has to rely on across calls, and a # Safety section plus a description on each exported function and each #[repr(C)] type. The four guarantees the issue asked to have written down are now on compile and CompileResult: the buffers are independent allocations owned by the result, they do not borrow from the compiler, they stay valid until free_compile_result whatever else happens in between, and that call must happen exactly once.

create_compiler, free_compiler, set_sys_inputs, compile and free_compile_result are now unsafe extern "C" fn. reset_world takes no pointers and stays safe.

Two things worth knowing

This is slightly wider than the issue, which named only free_compile_result. Every one of those five dereferences a caller-supplied raw pointer, so marking one and not the others would have been arbitrary. Happy to narrow it to free_compile_result alone if you would rather keep the diff minimal.

Bindings.g.cs is regenerated and included. csbindgen lifts Rust doc comments into C# XML summaries, so the contract now shows up in IntelliSense on the managed side, which is where it actually gets relied on. No signature changed: unsafe is a Rust-side marker and does not touch the ABI, so every DllImport is byte-for-byte what it was.

Testing

cargo test green, and the managed suite green on linux-x64 against a libtypst_core.so built from this branch. No behaviour change, so the value here is entirely in what a future refactor will be told before it breaks something.

Every exported function takes or returns raw pointers, so the ownership
rules are the contract between the crate and its caller, but none of them
were stated anywhere in the source. The managed side depends on four of
them in particular: the buffers in a CompileResult are independent
allocations owned by the result, they do not borrow from the compiler that
produced them, they stay valid until free_compile_result whatever else
happens in between, and that call has to happen exactly once. Those hold
today; nothing said so, and nothing would have caught a refactor that
changed them.

A module-level doc states the rules that span calls, and each exported
function and repr(C) type carries a description and, where it dereferences
caller-supplied pointers, a Safety section.

create_compiler, free_compiler, set_sys_inputs, compile and
free_compile_result become unsafe extern "C" fn. All five dereference raw
pointers they are handed, and free_compile_result frees the same
allocations twice if it is called twice. reset_world takes no pointers and
stays safe.

csbindgen lifts the doc comments into the generated bindings, so the
contract also reaches the managed side. No signature changes: unsafe is a
Rust-side marker and does not touch the ABI.
@msallin
msallin force-pushed the docs/ffi-ownership-contract branch from 498dfa4 to e34d597 Compare August 31, 2026 20:24
@evolvedlight
evolvedlight merged commit f9332a4 into evolvedlight:develop Sep 6, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants