feat(gen): 协程体里的 new heap slife fail-fast(释放点无法确定)

AsyncGenMixin 的状态机会把协程 body 拆成多个 state 块并多次转译,编译器无法确定 slife 的释放点;此前是静默漏释放。新增锚点 trans._coro_body_func(Translator 字段 + init 置 None),只在 body 翻译期间由 translate_async_function 设置并保存/恢复旧值;_translate_new_expr 以**函数对象同一性**(_coro_body_func == _cur_func)判定并 fatal_error(LifetimeError)。嵌套普通函数会换掉 _cur_func 故不误报;new stack 与 hlife 不受影响。
This commit is contained in:
TermiNexus committed 2026-10-08 20:42:09 +08:00
1 parent 2178970ff7
commit 70390858d8
3 files changed
+20

No files matched your search

+6
View File
@@ -883,7 +883,13 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
trans._cur_builder = builder
seg_bb: mlirlite.BasicBlock* = mlirlite.BasicBlock*(state_bbs[0])
mlirlite.position_at_end(builder, seg_bb)
# M3 硬约束锚点:登记"正在翻译其 body 的协程函数",CallGenCtor._translate_new_expr 据此对
# 协程体内的 `new heap slife` fail-fast(协程体被状态机拆分,释放点无法确定)。
# 保存/恢复旧值:协程里再定义协程时不会把外层的标记冲掉。
old_coro_body: mlirlite.Function* = trans._coro_body_func
trans._coro_body_func = func
self._handle_segment(trans, builder, pool, mod, fd.children)
trans._coro_body_func = old_coro_body
# body fell through -> implicit return (ret=0 + done=1 + br exit)
if mlirlite.builder_cur_block_is_terminated(builder) == 0:
zret: mlirlite.Value* = mlirlite.const_int32(pool, 0)
+9
View File
@@ -333,6 +333,15 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if pool is None or builder is None or new_node is None or trans is None: return None
src: int = new_node.Source
life: int = new_node.Life
# M3 硬约束:async 函数体被状态机拆成多个 state 块,释放点无法确定 →
# `new heap slife`(编译器独占释放的那种)在协程体里 fail-fast,绝不让"登记了却永不释放"
# 静默通过。判定用**函数对象同一性**(trans._coro_body_func,见 AsyncGenMixin 的登记点):
# 协程体内嵌套的普通函数会换掉 _cur_func,所以不会误报。
# `new stack`(帧内 alloca,无需释放)与 `hlife`(自行管理)不受影响。
if trans._coro_body_func is not None and trans._cur_func is not None:
if uint64(trans._coro_body_func) == uint64(trans._cur_func) and self._new_is_owned_heap(new_node) != 0:
self.Gen.TypeGenErr.fatal_error(new_node, "`new heap slife ...` cannot be used inside an async function body: the coroutine body is split into state blocks, so the compiler cannot place the release point; use `new heap hlife ...` (and manage it yourself), or move the `slife` allocation into a regular function", "LifetimeError")
return None
# 缺省规则(§3.2 / D3):来源缺省 = heap;生命期缺省由来源决定
# stack → slife(帧生命期;故 `new stack T(...)` 不写 slife 也等效)
# heap / memgr → hlife(自由生命期)
+5
View File
@@ -76,6 +76,10 @@ novirtual class Translator:
_coro_frame: mlirlite.Value*
_coro_next_state: int
_coro_nseg: int
# M3 硬约束锚点:正在翻译其 body 的协程函数(= create_/resume_ 那个合成函数)。
# 只有 body 翻译期间非 None,且与 _cur_func 同一对象才算"在协程体里"——嵌套的普通函数
# 会换掉 _cur_func,故不会误报。用于禁止协程体内的 `new heap slife`(释放点无法确定)。
_coro_body_func: mlirlite.Function*
# M2: async parameter table (char* array, 8 bytes/item) + pre-scan counts
_coro_params: str
_coro_param_count: int
@@ -283,6 +287,7 @@ novirtual class Translator:
self._coro_frame = None
self._coro_next_state = 1
self._coro_nseg = 0
self._coro_body_func = None
self._coro_params = None
self._coro_param_count = 0
self._coro_hoist_count = 0