fix(gen): while 1 无限循环无 break 判定 merge 不可达

HandlesWhile 翻译 while 后无条件置 _ctrl_merge_reachable=1,导致末句为
`while 1:`(条件常量真且循环体仅 return 无 break)的函数在收尾检查被
误判"流程掉出末尾未显式 return"(vrandom.gammavariate TypeError)。
新增 _IsConstTrue/_StmtHasBreak(跳过嵌套循环子树):常量真且无本层
break 时 merge 结构性不可达 → 置 0,函数收尾对末句 While 生成 unreachable。
This commit is contained in:
TermiNexus committed 2026-08-19 20:35:22 +08:00
1 parent b2e69c2f75
commit f5b3e46543
1 file changed
+67
+67
View File
@@ -143,6 +143,13 @@ class WhileHandle(HandlesBase.Mixin):
llvmlite.position_at_end(builder, end_bb)
# 追加90:while 的 cond_br 恒引用 end_bb → merge 恒可达(需正常收尾)
trans._ctrl_merge_reachable = 1
# 无限循环(条件常量真 1/True)且循环体无本层 break → 运行时无任何边
# 到达 end 块(merge 结构性不可达)→ 置 0,函数收尾对末句 While 生成
# unreachable(追加90:无 br 到达 merge 时才安全;否则 llc O2 把
# br-to-unreachable 优化为 fall-through → 双分支执行崩溃)。
# 修复 vrandom.gammavariate 的 while 1 全 return 被误判"流程掉出末尾"。
if _IsConstTrue(while_node.test) != 0 and _StmtHasBreak(body) == 0:
trans._ctrl_merge_reachable = 0
return 0
@@ -157,3 +164,63 @@ def NewWhileHandle(pool: memhub.MemBuddy | t.CPtr,
if g is not None:
return g.NewWhileHandle(pool, trans)
return None
# ============================================================
# 无限循环判定辅助(merge 可达性)
#
# _IsConstTrue: while 条件为常量真(CONST_INT 非 0 / CONST_BOOL 真)
# _NodeHasBreak / _StmtHasBreak: 扫描本层 break(跳过嵌套 While/For
# 子树——嵌套循环的 break 归内层,不影响外层 merge 可达性)
# ============================================================
def _IsConstTrue(test: ast.AST | t.CPtr) -> int:
"""while 条件为常量真(1 / True)返回 1"""
if test is None:
return 0
if test.kind() == ast.ASTKind.Constant:
cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(test)
if cn.const_kind == ast.CONST_INT and cn.int_val != 0:
return 1
if cn.const_kind == ast.CONST_BOOL and cn.int_val != 0:
return 1
return 0
def _NodeHasBreak(node: ast.AST | t.CPtr) -> int:
"""递归扫描节点子树(跳过嵌套 While/For 子树),含 Break 返回 1"""
if node is None:
return 0
ch: list[ast.AST | t.CPtr] | t.CPtr = node.children
if ch is not None:
n: t.CSizeT = len(ch)
for bi in range(n):
c: ast.AST | t.CPtr = ch.get(bi)
if c is None:
continue
k: int = c.kind()
if k == ast.ASTKind.Break:
return 1
if k == ast.ASTKind.While or k == ast.ASTKind.For:
continue
if _NodeHasBreak(c) != 0:
return 1
return 0
def _StmtHasBreak(body: list[ast.AST | t.CPtr] | t.CPtr) -> int:
"""语句列表是否含本层 break(嵌套循环的 break 归内层)"""
if body is None:
return 0
n: t.CSizeT = len(body)
for bi in range(n):
s: ast.AST | t.CPtr = body.get(bi)
if s is None:
continue
k: int = s.kind()
if k == ast.ASTKind.Break:
return 1
if k == ast.ASTKind.While or k == ast.ASTKind.For:
continue
if _NodeHasBreak(s) != 0:
return 1
return 0