3. FFI and native interop
Start from the native declaration, not from a guessed Flecha type. Record target ABI, calling convention, exact integer widths, pointer constness, structure layout, string representation, lifetime, thread rules, and error reporting. Reject a platform at build time when those facts do not match.
Keep extern declarations private to one interop module. That module validates
safe inputs, performs the smallest raw conversion, calls native code, copies or
adopts results according to the documented owner, and converts failure to a
Flecha error. Callers should not need unsafe merely because the implementation
uses FFI.
Embedded native sources are part of the package and build contract. Pin their compiler flags, qualify each target, and test nulls, empty buffers, maximum lengths, callbacks, repeated calls, and cleanup after native failure.
Tested example
Embed one C translation unit, declare its exact scalar ABI, and return to safe Flecha immediately.
File: curriculum/main-evidence/single/foreign_add.fl
foreign c { #include <stdint.h>
int32_t native_add(int32_t left, int32_t right) { return left + right; }}
extern i32 native_add(i32 left, i32 right);
i32 main() { stdoutln(native_add(20, 22)); return 0;}The block above is the complete file executed by the commands below.
The external declaration matches one scalar C function and returns immediately to typed Flecha. This example does not cover structures, callbacks, ownership transfer, or target-specific ABI differences.
Commands
flecha check curriculum/main-evidence/single/foreign_add.flflecha build curriculum/main-evidence/single/foreign_add.fl -o build/foreign_addbuild/foreign_addExpected output
42exit status: 0Related reference: /docs/reference/extern/.