Appearance
Phase 7 P7.2 计划:Runtime 合同扩展与 Rust 互操作矩阵
创建时间:2026-03-29 前置计划:Phase 7 近期优先级计划(2026-03-28)(Tasks 1-5 均已落地)
这份文档是 P7.1 完成后的下一步可执行切片计划,不重复已经落地的内容。
当前已落地基线(P7.1 完成后的事实面)
P7.1 五个任务均已完成并进入回归锁定状态:
- projected task-handle consume/write-reinit(tuple/struct-field/fixed-array literal index 三条路径)已在 typeck/borrowck/codegen/driver/CLI 各层锁定
- 非
Task[...]元素的 dynamic array assignment 已在 driver 内部单测与 CLI 黑盒层锁定 Task[...]动态数组索引写入已有显式 fail contract(driver 单测build_file_surfaces_dynamic_task_array_index_assignment_diagnostic_once+ CLI 黑盒 fixture)for awaitfixed-array iterable 已在 library-mode async body(staticlib+ 最小 asyncdylib)打通- 最小 async
dylib子集已开放(带同步extern "c"导出面的 library-style async body) - aggregate-carried task-handle payload(tuple/fixed-array/struct/nested fixed-shape)已全面覆盖
- zero-sized async parameter/result 已有端到端回归锁定
- chained-await(
await outer() -> Task[T],再await next)已打通
下一步切片计划
Task 1:runtime hook ABI 合同细化(P7.2 核心)
优先级:高
背景: 当前 hook ABI(qlrt_async_frame_alloc、qlrt_async_task_create、qlrt_executor_spawn、qlrt_task_await、qlrt_task_result_release、qlrt_async_iter_next)已冻结第一版 LLVM-facing contract string(ccc + opaque ptr),但真实内存布局、task result 的具体传递协议和 frame 生命周期管理尚未冻结。
目标(保守):
- 不引入多态/泛型 hook 变体,不要在这一刀做 result 序列化协议
- 只把当前隐含的内部假设("opaque ptr 指向可直接 load 的 payload")补成显式文档和回归测试
- 在
ql-runtime中把 frame alloc/release、task create/destroy、result payload 的生命周期边界写成注释规约,作为后续 safe runtime 实现的参考基线
关键文件:
crates/ql-runtime/src/lib.rs:补充 hook 合同文档注释,明确 caller 与 callee 的生命周期约定crates/ql-runtime/tests/executor.rs:补充 inline executor 对 hook 合同的单测断言crates/ql-codegen-llvm/src/lib.rs:审查当前 await lowering 中qlrt_task_await的 load 假设,确保与 runtime 文档规约对齐
完成标准:
ql-runtime里每一条 hook symbol 都有明确的 caller/callee 约定注释ql-runtime单测至少覆盖"create → await → result-load → release"这条最小生命周期- backend 里的 load 位置与注释规约对齐,不再靠默契维持正确性
已完成(2026-03-29):ql-runtime/src/lib.rs 补充了完整的 hook 生命周期规约注释(enum-level overview + 每条 variant 的 caller/callee 约定,含 TaskAwait 的"backend load assumption");ql-runtime/tests/executor.rs 新增三项生命周期单测(hook_lifecycle_create_await_result_load_release_abi_contract、hook_lifecycle_full_llvm_declaration_sequence_is_stable、async_iter_next_abi_contract_is_stable);ql-codegen-llvm/src/lib.rs 的 await lowering load 位置补充了引用 RuntimeHook::TaskAwait 合同的 INVARIANT 注释。14 项 ql-runtime 单测全部通过,100 项 ql-codegen-llvm 单测全部通过。
Task 2:Rust interop 工作流矩阵扩展(P7.3)
优先级:中
背景:examples/ffi-rust 与对应 CLI FFI 集成测试已建立基线,但当前只覆盖了最简单的单向 export 场景。真实项目更常见的是:Qlang staticlib 同时有多个导出函数、导入函数(回调),以及 Qlang 内部用到复合类型返回。
目标(保守):
- 不新增 ABI surface,不尝试跨 crate 的 Qlang 模块系统
- 只把
examples/ffi-rust扩展成"更接近真实双向互操作"的示例 - 补充对应的 CLI 集成测试回归
关键文件:
examples/ffi-rust/:扩展示例,增加至少一个双向互操作场景(Qlang 导出 + 导入 C/Rust callback)crates/ql-cli/tests/ffi.rs:对应回归
具体扩展方向(选择最小一个):
- 选项 A:Qlang staticlib 导出两个函数,Rust host 按序调用并验证返回值
- 选项 B:Qlang staticlib 同时导出函数并从 Rust host 通过
extern "c"接收 callback,验证双向调用 - 选项 B 更能体现当前 FFI 设计的真实可用性,建议优先
完成标准:
examples/ffi-rust扩展后仍可cargo run --quiet成功- CLI 集成测试中的 committed example 回归继续通过
- 双向互操作路径(export + import callback)有专项 CLI 集成测试锁定
已完成(2026-03-29):examples/ffi-rust/ql/callback_add.ql 新增第二个 import (q_host_multiply) 和第二个 export (q_scale);examples/ffi-rust/host/src/main.rs 提供两个 Rust 回调并调用两个 Qlang 导出,验证两条双向路径均返回 42;crates/ql-cli/tests/ffi.rs 的 ffi_rust_example_cargo_host_runs 已扩展为同时断言 q_scale(6, 7) = 42。LLVM IR 确认两条 FFI 路径均正确编译,CLI 集成测试通过。
Task 3:for await 扩展评估(P7.4 前序)
优先级:低(评估性)
背景: 当前 for await 只支持 fixed array iterable(library-mode async body)。从 IR 结构看,固定长度的 slice/span view、或者通过 hook 驱动的 async iterator 协议,是下一个自然扩展点。但这两条路都有非平凡的 ABI 设计需求。
目标(本轮仅评估,不实现):
- 分析"从 fixed array 扩到动态长度 array slice"需要哪些额外 ABI 约定和 MIR 结构变化
- 分析"通用 async iterator 协议(基于
qlrt_async_iter_nexthook)"的最小合同需要什么 - 产出一份简短的评估笔记,作为后续实现的前置设计参考
产出形式: 在本文档末尾的"延后评估区"补充评估结论(不新建文件),并在 development-plan.md 中更新 P7.4 的推进条件。
已完成(2026-03-30):本文档“延后评估区”已补充 fixed array / Slice[T] / dynamic array / 通用 async iterator 的对比矩阵,结论明确为:当前不冻结 qlrt_async_iter_next 协议、不开放 dynamic-array for await;若后续扩面,优先考虑不新增 runtime hook 的 Slice[T] / span-like fixed-shape view 设计。
Task 4:async build surface 推进条件评估(P7.4)
优先级:低(评估性)
背景: 当前 P7.4 列了四个评估方向,但没有给出明确的推进条件。在 P7.2/P7.3 稳定之前这是正确的,但现在可以把这些条件写得更具体,以便下一轮开发者判断是否到了可以推进的时机。
目标: 为以下四个方向各写一条可观测的推进条件(即"满足什么就可以开始"):
- 放宽更多
await/spawnpayload 路径 - 扩大
for awaititerable surface - 扩大 async
dylib或开放 async program build - 开放更广义的 async callable / effect surface
产出形式: 更新 development-plan.md 中 P7.4 部分的措辞,将"评估是否..."替换为带明确前提条件的推进门槛。
已完成(2026-03-29):docs/roadmap/development-plan.md 已将四个 P7.4 方向改写为明确的推进门槛,并补充当前已落地的首个 program-build 子集:BuildEmit::Executable 下的 async fn main 现在可以通过 driver/runtime hook gate,并由 backend host @main wrapper 驱动 task_create -> executor_spawn -> task_await -> result_load -> task_result_release -> trunc/ret 的最小生命周期。对应代码已在 ql-codegen-llvm、ql-driver、CLI codegen 黑盒 fixture 三层锁定;当时 llvm-ir / object emit 仍只保留 capability fail contract,不额外暴露 program-entry hook 诊断,更广义 program bootstrap 仍保守关闭。
后续更新(2026-04-01):当前这条最小 async program surface 已进一步扩到 BuildEmit::LlvmIr 与 BuildEmit::Object。ql-driver 现会让 llvm-ir / object 复用与 BuildEmit::Executable 相同的 async capability gate 与 async fn main entry-lifecycle hook augmentation,因此 async fn main 及其当前已支持的 await / spawn / fixed-array for await 路径现在都可直接产出 LLVM IR 或对象文件,并已在 driver 单测与 CLI codegen 黑盒快照层锁定。
后续更新(2026-04-01):当前这条 BuildEmit::Executable async 子集已从“host entry 生命周期可出 IR”进一步推进到“受控样例可直接链接并运行”。ql-codegen-llvm 在 program mode 下现会为 body-bearing async fn 额外合成 __async_entry result thunk,并在模块内定义最小 runtime hook 集合(qlrt_async_frame_alloc、qlrt_async_task_create、qlrt_executor_spawn、qlrt_task_await、qlrt_task_result_release,以及按需生成的 qlrt_async_iter_next stub),因此当前支持范围内的 async executable 不再依赖外部 runtime 库即可闭环。这个更新只覆盖受支持的 async fn main / fixed-array for await / loadable payload 子集,不代表更广义的 program bootstrap 或最终 runtime ABI 已冻结。
执行顺序建议
Task 1(runtime hook ABI 细化)
↓
Task 2(Rust interop 双向示例)
↓
Task 3 + Task 4(评估性,可并行于 Task 2 之后)Task 1 应最先执行,原因:它把当前 backend 里最多"默契维持"的假设补成文档和测试,减少后续切片在这个层面踩坑的风险。Task 2 不依赖 Task 1 的实现,但建议顺序执行而不是并行,以保持上下文焦点。
刻意推后的事项
下列内容在本计划窗口内不执行:
- cancellation / polling / drop 语义:需要单独的语义设计,不应夹在 runtime 合同细化里
- generic async ABI / layout substitution:blast radius 高,需要独立 RFC
- 更广义的 async executable / program bootstrap:当前仅完成
BuildEmit::LlvmIr/BuildEmit::Object/BuildEmit::Executable下async fn main的最小程序入口生命周期;更复杂的 bootstrap / 更广 program surface 仍需独立计划 Task[...]动态数组索引赋值:fail contract 已锁定,开放时机需评估 place-sensitive lifecycle 的更广义设计- cross-file LSP / workspace index:属于 Phase 8,不属于 Phase 7 收口
与现有计划的关系
- 本文档是 2026-03-28 近期优先级计划 的续集,在其 Task 5 完成后接手
- 本文档的推进进展应同步更新到
docs/roadmap/development-plan.md和docs/roadmap/phase-progress.md - 每完成一个 Task,在本文档对应任务下加一行"已完成"注记,不要修改原始任务描述
延后评估区
Task 3:for await iterable surface 扩展评估结论(2026-03-30)
当前 fixed-array for await lowering 不是通用 iterator 协议的薄封装,而是直接建立在 concrete fixed-array layout 之上:ql-codegen-llvm 里的 SupportedForLoopLowering 只记录 array_len,render_for_await_setup_block 会拿到 iterable place pointer 后,直接对具体 [N x T] LLVM type 做 getelementptr ... i64 0, i64 {index} 并 load 元素。这说明现有实现复用的是 fixed-shape aggregate lowering,而不是可直接替换成 runtime-driven next() 协议的抽象层。
| 路线 | 需要新增或冻结的事实面 | 当前判断 |
|---|---|---|
| fixed array(已完成) | 无新增 ABI;继续复用现有 concrete [T; N] layout、index-slot 与 direct load lowering | 继续作为当前唯一 shipped for await iterable surface |
Slice[T] / span-like fixed-shape view | 需要先冻结源码层表示与 LLVM lowering(最小也要有 ptr + len 或等价 fixed-shape view),并让 backend 改成基于 base-ptr + len 的 compiler-driven index/load | 这是 blast radius 最小的下一候选,但仍应作为单独设计切片,不在本轮直接实现 |
| dynamic array | 需要动态数组元素布局、长度/容量/所有权/drop 边界先稳定;还会与当前“非 Task[...] dynamic array assignment 已开放、Task[...] dynamic array assignment 仍关闭”的现状发生耦合 | 当前不建议推进 |
基于 qlrt_async_iter_next 的通用 async iterator | 需要冻结 iterator -> next item ptr/null 之外的 item ownership/release protocol,且当前 runtime 明确写明该 hook 仍是 capability/ABI placeholder | 当前不建议推进,继续保留预留态 |
补充判断:
ql-runtime目前只承诺qlrt_async_iter_next(iterator: ptr) -> ptr,其中null表示结束,非空返回值的释放协议仍是 TBD;这还不足以作为通用 iterator runtime ABI 冻结。- 因此本轮不应把
qlrt_async_iter_next从“占位 hook”误升级成“已承诺的通用 async iterator 协议”。 - 如果后续确实要扩大
for awaititerable surface,优先级应是单独设计Slice[T]/ span-like fixed-shape view,并满足三个条件:- lowering 继续由 compiler 侧 index/load 驱动,而不是把逐项取值转交给 runtime hook;
- 不新增 runtime hook,也不要求冻结新的 item release protocol;
ql-driver仍可沿用现有async-iterationcapability gate,不把 placeholder hook 写成更强承诺。
当前结论:
- P7.4 当前窗口内不冻结通用
qlrt_async_iter_next协议。 - P7.4 当前窗口内不开放 dynamic-array
for await。 - Task 4 现阶段完成的是 docs-first 评估,不是实现承诺;当前最适合继续推进的实现主线仍是 Task 3 的 payload / lowering 子集收口。