diff --git a/readme.md b/readme.md index 6771dde..4220f6e 100644 --- a/readme.md +++ b/readme.md @@ -409,6 +409,18 @@ printf :: (fmt: [:0]u8, args: ..Any) -> i32 #foreign libc; write_fd :: (fd: i32, buf: [*]u8, count: u64) -> i64 #foreign libc "write"; ``` +`extern` / `export` are the keyword surface for C linkage. `extern` is the modern +spelling of `#foreign` (import); `export` is its dual — define a function in sx and +expose it under the C ABI so C can call back in. Both imply `callconv(.c)` and take +the same optional `[LIB] ["csym"]` rename tail; they also apply to data globals and +to Obj-C / JNI runtime-class aggregates (postfix after the `#objc_class(…)` directive). +```sx +abs :: (x: i32) -> i32 extern; // import (== `#foreign`) +sx_square :: (x: i32) -> i32 export { x * x } // define + expose to C +__stdinp : *void extern; // extern data global +NSObject :: #objc_class("NSObject") extern { alloc :: () -> *NSObject; } // reference a runtime class +``` + Direct C header import: ```sx #import c { diff --git a/specs.md b/specs.md index 4b1d5f1..359b8a1 100644 --- a/specs.md +++ b/specs.md @@ -1209,6 +1209,40 @@ write_fd :: (fd: i32, buf: [*]u8, count: u64) -> i64 #foreign libc "write"; - `#foreign lib_ref` declares a function as external C. The library reference is optional: when present it is passed to the linker (`-lname` on Unix); when omitted (`name :: (…) -> T #foreign;`), the symbol must resolve at link time from a framework or an already-linked / auto-detected library. - `#foreign lib_ref "c_symbol"` renames the binding: the sx function name differs from the C symbol. This avoids name collisions (e.g. POSIX `write` vs an sx builtin). +#### `extern` / `export` linkage keywords + +`extern` and `export` are the keyword surface for C linkage. `extern` is the +modern spelling of `#foreign` (import a symbol defined elsewhere); `export` is +its dual — **define** a symbol in sx and expose it under the C ABI so C (or asm, +or another language) can call it. Both imply `callconv(.c)`, carry external +linkage, and suppress the implicit sx context parameter. They are postfix +modifiers, written where `callconv` would go. + +```sx +// Functions — `extern` imports, `export` defines + exposes +abs :: (x: i32) -> i32 extern; // import (== `#foreign`) +write_fd :: (fd: i32, buf: [*]u8, n: u64) -> i64 extern libc "write"; // [LIB] ["csym"] +sx_square :: (x: i32) -> i32 export { x * x } // define; C can call `sx_square` +triple_c :: (x: i32) -> i32 export "triple_c" { x * 3 } // export under a C name + +// Data globals — `extern` imports an external global +__stdinp : *void extern; // (== ` : #foreign;`) + +// Aggregates (Obj-C / JNI runtime classes) — postfix after the directive +NSObject :: #objc_class("NSObject") extern { alloc :: () -> *NSObject; } // reference +SxFoo :: #objc_class("SxFoo") export { counter: i32; bump :: (self: *Self) { … } } // define +``` + +- `extern` takes the same optional `[LIB] ["csym"]` tail as `#foreign` + (`extern libc "write"`): a `#library` alias reference then a C symbol rename. + The `#library` declaration + build-flag linking mechanism is a separate axis — + `extern` *references* a library, it does not declare one. +- `export "csym"` renames the exported symbol the same way (the C-visible name + differs from the sx name). +- On an aggregate, the prefix `#foreign` modifier and a postfix `extern`/`export` + keyword are the same axis and cannot be combined: `#objc_class("X") extern` is + exactly `#foreign #objc_class("X")`; writing both is a compile error. + ### C Interop Type Mapping | C type | sx type | Notes |