Skip to content

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 await fixed-array iterable 已在 library-mode async body(staticlib + 最小 async dylib)打通
  • 最小 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_allocqlrt_async_task_createqlrt_executor_spawnqlrt_task_awaitqlrt_task_result_releaseqlrt_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_contracthook_lifecycle_full_llvm_declaration_sequence_is_stableasync_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.rsffi_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_next hook)"的最小合同需要什么
  • 产出一份简短的评估笔记,作为后续实现的前置设计参考

产出形式: 在本文档末尾的"延后评估区"补充评估结论(不新建文件),并在 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 稳定之前这是正确的,但现在可以把这些条件写得更具体,以便下一轮开发者判断是否到了可以推进的时机。

目标: 为以下四个方向各写一条可观测的推进条件(即"满足什么就可以开始"):

  1. 放宽更多 await / spawn payload 路径
  2. 扩大 for await iterable surface
  3. 扩大 async dylib 或开放 async program build
  4. 开放更广义的 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-llvmql-driver、CLI codegen 黑盒 fixture 三层锁定;当时 llvm-ir / object emit 仍只保留 capability fail contract,不额外暴露 program-entry hook 诊断,更广义 program bootstrap 仍保守关闭。

后续更新(2026-04-01):当前这条最小 async program surface 已进一步扩到 BuildEmit::LlvmIrBuildEmit::Objectql-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_allocqlrt_async_task_createqlrt_executor_spawnqlrt_task_awaitqlrt_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::Executableasync 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.mddocs/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_lenrender_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 await iterable surface,优先级应是单独设计 Slice[T] / span-like fixed-shape view,并满足三个条件:
    • lowering 继续由 compiler 侧 index/load 驱动,而不是把逐项取值转交给 runtime hook;
    • 不新增 runtime hook,也不要求冻结新的 item release protocol;
    • ql-driver 仍可沿用现有 async-iteration capability gate,不把 placeholder hook 写成更强承诺。

当前结论:

  • P7.4 当前窗口内不冻结通用 qlrt_async_iter_next 协议。
  • P7.4 当前窗口内不开放 dynamic-array for await
  • Task 4 现阶段完成的是 docs-first 评估,不是实现承诺;当前最适合继续推进的实现主线仍是 Task 3 的 payload / lowering 子集收口。

Qlang research repository