feat(ffi-linkage): fn-path accepts postfix extern/export + lib/name fields (Phase 1.0a)
parseFnDecl now calls parseOptionalExternExport() after the callconv
slot and stores the modifier on FnDecl.extern_export. For 'extern' the
body is ';' (an empty-block placeholder — the modifier carries the
linkage, no *_expr node, per the naming constraint). Both fn-decl
lookahead predicates (isFunctionDef, hasFnBodyAfterArrow) now treat
kw_extern/kw_export as fn-body markers beside kw_callconv, so
'(...) -> R extern;' is recognized as a fn def rather than a fn-type
const.
Per user feedback, decision 4 ("library separate") is REVISED: extern
carries an optional LIB + "csym" axis mirroring '#foreign LIB "csym"',
so it is a true #foreign superset (Gate A->B requirement — the Part B
migration of 466 #foreign uses across 6 libs must preserve each
symbol's library). Added FnDecl.extern_lib/extern_name and
VarDecl.extern_lib (beside is_extern/extern_name).
All unconsumed by lowering: extern parses, but a fn still errors at
sema (body produces no value). Suite green (443 unit / 633 corpus).
lock commit.
This commit is contained in:
@@ -10,14 +10,18 @@ superset of `#foreign`, and Part A isn't "done" until Part B reaches the invaria
|
||||
|
||||
**Decided syntax**
|
||||
```sx
|
||||
name :: (sig) -> Ret [callconv(.x)] [extern | export] [;|{…}]; // functions
|
||||
Name :: #objc_class("X") [extern | export] { … }; // aggregates (mirrors `struct #compiler`)
|
||||
g : Type extern ["csym"]; // extern global
|
||||
name :: (sig) -> Ret [callconv(.x)] [extern | export] [LIB] ["csym"] [;|{…}]; // functions
|
||||
Name :: #objc_class("X") [extern | export] { … }; // aggregates (mirrors `struct #compiler`)
|
||||
g : Type extern [LIB] ["csym"]; // extern global
|
||||
```
|
||||
- `extern` = import (no body, external linkage, C ABI, no sx ctx) — `#foreign`'s role.
|
||||
- `export` = define **and** expose (body + external linkage + C ABI + no ctx) — **new**.
|
||||
- `extern`/`export` imply `callconv(.c)`; write `callconv` only to override.
|
||||
- Library stays a separate axis (`#library`/build flags), not folded into `extern`.
|
||||
- Optional `LIB` (a `#library` alias) + `"csym"` rename mirror `#foreign LIB "csym"`,
|
||||
so `extern` is a true `#foreign` **superset** (Gate A→B): carried on
|
||||
`extern_lib`/`extern_name`. The `#library` declaration + build-flag linking
|
||||
mechanism stays a separate axis — `extern` *references* a lib, it doesn't fold
|
||||
in `#library` itself. (Revises the original "library fully separate" decision 4.)
|
||||
|
||||
> **END-STATE INVARIANT (hard requirement).** After this stream, `foreign` appears
|
||||
> **nowhere** in the live tree — not the `#foreign` surface, and **not** internal
|
||||
@@ -29,8 +33,9 @@ g : Type extern ["csym"]; // extern g
|
||||
`extern`-named representations only — do **not** reuse or extend
|
||||
`ForeignExpr`/`foreign_expr`/`VarDecl.is_foreign`. Carry extern/export on a new
|
||||
`FnDecl.extern_export` modifier with a `;`/`{…}` body (so there is **no** `*_expr`
|
||||
node for it); add `VarDecl.is_extern`/`extern_name`. The IR is already extern-named
|
||||
(`Function.is_extern`, `Builder.declareExtern`).
|
||||
node for it) + `FnDecl.extern_lib`/`extern_name`; add `VarDecl.is_extern`/
|
||||
`extern_lib`/`extern_name`. The IR is already extern-named (`Function.is_extern`,
|
||||
`Builder.declareExtern`).
|
||||
|
||||
**Key finding (scopes Part A):** the IR + LLVM emit **already support everything** —
|
||||
`Function.linkage` (`.external/.internal/.private`), `is_extern`, `call_conv`, and a
|
||||
@@ -147,7 +152,11 @@ per-file/subsystem commits — not one sweep.
|
||||
## Open decisions
|
||||
*Part A (ratified — recommendations stand):* 1. bare keywords (not `#extern`).
|
||||
2. aggregate position postfix (`#objc_class(…) extern`, like `struct #compiler`).
|
||||
3. `extern ⇒ callconv(.c)`. 4. library separate.
|
||||
3. `extern ⇒ callconv(.c)`. 4. **REVISED** (user, 2026-06-14): `extern` carries an
|
||||
optional `LIB`+`"csym"` axis (`extern_lib`/`extern_name`), mirroring `#foreign LIB
|
||||
"csym"`, so it's a true `#foreign` superset (Gate A→B). The `#library` declaration +
|
||||
build-flag linking mechanism stays separate — `extern` references a lib, doesn't
|
||||
fold in `#library`. (Was: "library fully separate / not on `extern`".)
|
||||
*Part B (confirm before Phase 9):* 5. runtime-class rename target — **`Runtime*Class*`**
|
||||
(recommended; it's the object-model axis, not linkage) vs `Extern*Class*`.
|
||||
6. historical carve-out — keep `issues/*.md` (+ design-doc prose) as provenance,
|
||||
|
||||
Reference in New Issue
Block a user