feat(coro): M5——协程内 try/except 捕获(raise 与 await 传播异常)

- _HandleCoroTry:except_bb/merge_bb 控制流,单 except 捕获所有异常
- 协程内 raise 与 await 子协程异常传播:处于 try 栈内时改跳 except 处理器,
  栈外维持原终止语义(done=1 + br exit)
- except X as e:异常码绑定到 e(帧槽),捕获后清帧 exc
- 预扫描/_CollectFrameVars 递归 Try(handler.name 进帧槽,跨挂起存活)
- CoroTest 验证:try 捕获 await 传播异常 → coro result = 42, exc = 0
This commit is contained in:
TermiNexus committed 2026-08-21 14:28:52 +08:00
1 parent 411e0bb774
commit 69447025a7
2 files changed
+104 -18

No files matched your search

+87 -6
View File
@@ -96,6 +96,7 @@ class AsyncFunctionHandle(BaseHandle):
'for_counter': getattr(Gen, '_coro_for_counter', None),
'wake_ptr': getattr(Gen, '_coro_wake_ptr', None),
'exc_ptr': getattr(Gen, '_coro_exc_ptr', None),
'try_stack': getattr(Gen, '_coro_try_stack', None),
}
try:
self.Trans.VarScopes.append({})
@@ -139,6 +140,7 @@ class AsyncFunctionHandle(BaseHandle):
Gen._coro_for_counter = saved_coro_state['for_counter']
Gen._coro_wake_ptr = saved_coro_state['wake_ptr']
Gen._coro_exc_ptr = saved_coro_state['exc_ptr']
Gen._coro_try_stack = saved_coro_state['try_stack']
# ------------------------------------------------------------------
# 帧结构体与局部变量收集
@@ -195,6 +197,20 @@ class AsyncFunctionHandle(BaseHandle):
if stmt.orelse:
_walk(stmt.orelse)
continue
elif isinstance(stmt, ast.Try):
# except X as e:e 进帧槽(异常码)
for _h in stmt.handlers:
if _h.name and _h.name not in seen:
seen.add(_h.name)
vars_.append((_h.name, ir.IntType(32)))
_walk(stmt.body)
for _h in stmt.handlers:
_walk(_h.body)
if stmt.orelse:
_walk(stmt.orelse)
if stmt.finalbody:
_walk(stmt.finalbody)
continue
else:
continue
if target not in seen:
@@ -271,6 +287,15 @@ class AsyncFunctionHandle(BaseHandle):
if stmt.orelse:
_walk(stmt.orelse)
continue
if isinstance(stmt, ast.Try):
_walk(stmt.body)
for _h in stmt.handlers:
_walk(_h.body)
if stmt.orelse:
_walk(stmt.orelse)
if stmt.finalbody:
_walk(stmt.finalbody)
continue
if isinstance(stmt, ast.For):
if not any(isinstance(n, ast.Await) for n in ast.walk(stmt)):
continue # 无 await:委托 ForHandler,不需额外槽
@@ -449,6 +474,9 @@ class AsyncFunctionHandle(BaseHandle):
if isinstance(stmt, ast.While):
self._HandleCoroWhile(stmt, Gen)
continue
if isinstance(stmt, ast.Try):
self._HandleCoroTry(stmt, Gen)
continue
if isinstance(stmt, ast.For):
self._HandleCoroFor(stmt, Gen)
continue
@@ -678,6 +706,10 @@ class AsyncFunctionHandle(BaseHandle):
exc_ptr: ir.Value | None = getattr(Gen, '_coro_exc_ptr', None)
if exc_ptr is not None:
Gen._store(exc_code, exc_ptr)
coro_try_stack: list[ir.Block] | None = getattr(Gen, '_coro_try_stack', None)
if coro_try_stack:
Gen.builder.branch(coro_try_stack[-1])
return
done_ptr_r: ir.Value | None = getattr(Gen, '_coro_done_ptr', None)
if done_ptr_r is not None:
Gen._store(ir.Constant(ir.IntType(32), 1), done_ptr_r)
@@ -783,12 +815,16 @@ class AsyncFunctionHandle(BaseHandle):
parent_exc_ptr: ir.Value | None = getattr(Gen, '_coro_exc_ptr', None)
if parent_exc_ptr is not None:
Gen._store(child_exc, parent_exc_ptr)
parent_done_ptr: ir.Value | None = getattr(Gen, '_coro_done_ptr', None)
if parent_done_ptr is not None:
Gen._store(ir.Constant(ir.IntType(32), 1), parent_done_ptr)
exit_bb_ref: ir.Block | None = getattr(Gen, '_coro_exit_bb', None)
if exit_bb_ref is not None:
Gen.builder.branch(exit_bb_ref)
coro_try: list[ir.Block] | None = getattr(Gen, '_coro_try_stack', None)
if coro_try:
Gen.builder.branch(coro_try[-1])
else:
parent_done_ptr: ir.Value | None = getattr(Gen, '_coro_done_ptr', None)
if parent_done_ptr is not None:
Gen._store(ir.Constant(ir.IntType(32), 1), parent_done_ptr)
exit_bb_ref: ir.Block | None = getattr(Gen, '_coro_exit_bb', None)
if exit_bb_ref is not None:
Gen.builder.branch(exit_bb_ref)
# 正常路径:存父帧局部变量槽 + 挂起
Gen.builder.position_at_start(normal_bb)
slot: ir.Value | None = getattr(Gen, '_coro_await_slot_ptr', None)
@@ -918,6 +954,51 @@ class AsyncFunctionHandle(BaseHandle):
else:
Gen.builder.position_at_end(AfterBB)
def _HandleCoroTry(self, Node: ast.Try, Gen: LlvmCodeGenerator) -> None:
"""协程内 try/except:捕获 raise 与 await 传播的异常(M5:单 except、捕获所有)。"""
if not Node.handlers:
raise SyntaxError(
f"[coro-M5] 协程内 try 需要 except 子句 (line {getattr(Node, 'lineno', 0)})"
)
if len(Node.handlers) > 1:
raise SyntaxError(
f"[coro-M5] 协程内仅支持单个 except(多 except 留待扩展)"
f"(line {getattr(Node, 'lineno', 0)})"
)
if Node.finalbody:
raise SyntaxError(
f"[coro-M5] 协程内暂不支持 try/finally (line {getattr(Node, 'lineno', 0)})"
)
except_bb: ir.Block = Gen.func.append_basic_block('coro_try_except')
merge_bb: ir.Block = Gen.func.append_basic_block('coro_try_merge')
if not getattr(Gen, '_coro_try_stack', None):
Gen._coro_try_stack = []
Gen._coro_try_stack.append(except_bb)
# try body
self._HandleCoroSegment(Node.body, Gen)
if Gen.builder is not None and not Gen.builder.block.is_terminated:
Gen.builder.branch(merge_bb)
Gen._coro_try_stack.pop()
# except handler:异常码已在帧 exc 槽,绑定到 handler.name 后清异常
Gen.builder.position_at_start(except_bb)
handler: Any = Node.handlers[0]
exc_ptr: ir.Value | None = getattr(Gen, '_coro_exc_ptr', None)
if handler.name:
e_slot: ir.Value | None = Gen.variables.get(handler.name)
if e_slot is not None and exc_ptr is not None:
exc_val: ir.Value = Gen._load(exc_ptr, name='coro_exc_val')
Gen._store(exc_val, e_slot)
Gen._record_var_signedness(handler.name, False)
if exc_ptr is not None:
Gen._store(ir.Constant(ir.IntType(32), 0), exc_ptr)
self._HandleCoroSegment(handler.body, Gen)
if Gen.builder is not None and not Gen.builder.block.is_terminated:
Gen.builder.branch(merge_bb)
if not merge_bb.is_terminated:
Gen.builder.position_at_start(merge_bb)
else:
Gen.builder.position_at_end(merge_bb)
def _HandleCoroFor(self, Node: ast.For, Gen: LlvmCodeGenerator) -> None:
if any(isinstance(n, ast.Await) for n in ast.walk(Node)):
if not self._IsRangeFor(Node):