Repository navigation
Reference ffi
Mirrored from docs/reference/ffi.md in the repo — that is the canonical source; edits to this wiki page are overwritten when the wiki is re-synced.
Thesis context: The FFI tests that the arena model coexists with C's heap without escape hatches that would weaken it. Foreign strings are copied into the caller's arena at the call site; opaque handles (
ptr) are shuttled but never dereferenced. The boundary is narrow on purpose — anything richer would either break value semantics or depend on an unstable C ABI.
extern fn declares a C function Tycho can call. It's bodyless, bound to a direct C symbol,
and can optionally name the library to link.
extern fn getpid() -> int # libc
extern "m" fn cos(x: float) -> float # links -lm
extern fn sx_col_text(stmt: ptr, i: int) -> string # C string in, Tycho string out
extern fn crc32(data: bytes, len: u32) -> u32 # sized ints: real uint32_t at the C ABI
Here's what can cross:
-
Scalars —
int,char,float,bool. -
Sized integers — the first-class
u32,u64,f32cross as themselves (anexternparameter or return of one of those types takes/produces a value of that exact type). The narrower spellingsu8 u16 i8 i16 i32 i64are valid only in anexternsignature and areintto Tycho — you pass and receive ordinaryintvalues, but the emitted C prototype uses the real fixed-width type so a call matches e.g.int16_t f(uint8_t)at the ABI. On the way in theintnarrows to the C width; on the way back the C result widens toint, sign-extended fori8/i16/i32/i64and zero-extended foru8/u16(sou8(-1)reads back as255,i8(200)as-56). -
string— passed as a Cchar*; a C-returned string is copied into the caller's arena at the call site, so Tycho never holds a pointer into C-owned memory. A nullable C return is declared-> Option(string). -
bytes— a binary buffer (interior NULs intact), crossing as a(pointer, length)pair. -
[int]and[float]— a scalar array crosses as a(const T*, long)pair (likebytes); an array of any other element type does not. -
ptr— an opaque foreign handle, avoid*Tycho never dereferences. Thenullliteral andis_null(p)work on it. -
typed
handles —handle Name: free: c_fndeclares avoid*whose C destructor runs automatically at scope exit (RAII), so a foreign resource won't leak or get used after close. -
inoutscalar out-parameters (a numeric scalar orptr) — cross too; astring,bytes, handle, or compositeinoutout-parameter is rejected (no trivial out-param ABI).
Maps, structs, and non-scalar arrays are rejected at the boundary — fail closed. (The
scalar arrays [int]/[float] do cross, above.)
The "race-free by construction" guarantee is over Tycho values and does not cross the FFI.
A C function touching process-global or static state is invisible to the compiler, so you'll
need to isolate such state per thread or serialize the calls. See Concurrency.
Ergonomics live on the tychoc command line: --link (a raw linker flag), --pkg
(pkg-config), and --shim (compile a companion .c alongside). For a complete worked binding,
in-memory SQLite is at examples/sqlite/, and the full rules are in
the FFI design note.
The reader docs here are mirrored from /docs in the repo — the canonical source. · tycho-lang.com · MIT licensed
- Tutorial
- The Tycho thesis: value semantics makes implicit arenas work
- Architecture & project status
- From
mallocto implicit arenas
- Tycho language reference
- Basics
- Types
- Functions and closures
- Structs and tuples
- Enums, options, and
match - Arrays and slices
- Subscripts (user-defined projections)
- Maps
- Generics
- Concurrency
- FFI (calling C)
- Packages
- Builtins
- Memory model in the self-hosted compiler
- Tycho aggregates: arrays & structs under value semantics
- Maps with arbitrary value types
m[k]as a place: in-place map-value mutation- Generics (monomorphized)
- Concurrency in Tycho
- FFI — calling C from Tycho
- Packages & modules
- corelib — Tycho's standard library
- Performance: the self-hosted transpiler vs the C one
- Debugging generated C