Appearance
Phase 5 extern "c" Definition Export Foundation
背景
P4 已经把 extern "c" declaration direct-call 打通到了真实后端,但这还只解决了“Qlang 调 C”。
Phase 5 的第一刀更应该先解决另一半:
- Qlang 能否生成一个带稳定 C 符号的本地函数
- 这个函数能否沿着现有
ql build --emit staticlib路径进入真实产物
如果继续把这层留空,后面的 tests/ffi、头文件生成、Rust/C shim 互操作都会被迫绕过真实编译管线。
目标
- 支持顶层
extern "c"函数定义保留 body - 支持它们经过 HIR / resolve / typeck / MIR / codegen / driver 的真实流水线
- 让 LLVM IR 使用稳定 C 符号名,而不是内部 mangled name
明确不做
- 不做 header generation
- 不做 ABI 布局检查
- 不做 dynamic library
- 不做 per-symbol visibility/linkage policy
- 不做完整
tests/ffiC 侧集成 harness - 不把 program-mode
main改造成 exported C ABI entrypoint
设计决策
1. 语法层
顶层 extern "abi" fn ... 过去只允许 declaration。
这一刀改为:
- extern block 内的函数仍然只允许 declaration
- 顶层
extern "abi" fn ...改为允许 optional body
这样不会破坏 extern block 的“只承载导入声明”边界,但能给顶层 exported definition 留出语法空间。
2. 语义层
带 body 的 extern "c" 顶层函数继续复用普通函数路径:
- HIR 仍然是
ItemKind::Function - resolve / typeck / MIR 不需要引入新的特殊 item kind
也就是说,这刀的核心不是新增一套“FFI function pipeline”,而是允许 ABI 信息穿过现有函数 pipeline。
3. 后端层
当前后端规则改为:
- declaration +
extern "c"->declare @symbol - definition +
extern "c"->define @symbol - definition + non-
cABI -> 结构化 unsupported diagnostic
顶层 extern "c" 定义使用稳定 C 符号名:
extern "c" pub fn q_add(...) { ... }->define ... @q_add(...)
默认 Qlang 函数仍保留内部 mangled name。
4. 入口边界
program-mode 用户入口 main 仍然要求默认 Qlang ABI。
原因很简单:
- 当前 native build pipeline 还会生成宿主
@mainwrapper - 如果用户态
main同时要求extern "c",符号层会和宿主入口冲突
所以现阶段的规则是:
- exported C ABI entrypoint 用独立 helper
- 默认
main继续作为 Qlang 用户入口
测试面
这一刀至少要锁住四层回归:
- parser:顶层
extern "c"definition 可解析 - formatter:该语法可稳定 round-trip
- codegen:显式
extern "c"definition 使用稳定符号名 - driver / CLI:
staticlib与黑盒 LLVM IR snapshot 均覆盖这条路径
后续顺序
这刀完成后,P5 更合理的后续顺序是:
- 建立
tests/ffi的真实 C 侧集成 harness - 增加头文件生成和最小
ql ffi输出面 - 补 ABI / layout diagnostics
- 再讨论 dynamic library 与更完整 runtime glue