fix(gen/for): 负数字面量 stop 的 range 循环退回有符号谓词,修复 _find_provider 死循环

stop 边界为负数字面量(如 range(top - 1, -1, -1))时,3.6 的边界推导把无符号 start(UINT64 字段)判为无符号循环,负步长分支随即由 sgt 覆盖为 ugt;而比较在 stop 类型(i32)上进行,-1 在无符号域是 UINT32_MAX,条件恒假 => 循环体成为死代码。_withcontent._find_provider 因此恒返回 NULL,with 上下文的 __mbuddy__ 注入永不生效(FakeDuckTest Test 9 FAIL,随后 _str.upper 解引用 NULL vtable 触发 ASan READ)

新增 3.7: stop 为负数字面量时撤销无符号谓词(判定与既有 step_neg_const 同口径),退回 slt/sgt;正步长非负 stop 的既有 ult 优化与 range(stop) 单参形态不受影响
This commit is contained in:
TermiNexus committed 2026-09-15 22:45:28 +08:00
1 parent 296f6a739e
commit de86a1ed70
1 file changed
+30
+30
View File
@@ -1300,6 +1300,36 @@ class StmtGenCifMixin:
loop_unsigned = 1
break
# 3.7 stop 边界为负数字面量(-1 / -N)→ 撤回无符号谓词,必须用有符号比较。
# 倒序循环条件为 i > stop;stop = -1 在无符号域里是 UINT_MAX,任何 i
# 都 ugt 不过它 → 条件恒假 → 循环体整段变死代码。TPC 的 for 条件只按
# 循环变量注解定符号性(HandlesFor.py L218 `Gen._is_var_unsigned`),
# 未声明循环变量的 range(top - 1, -1, -1) 走有符号 sgt 可正常执行;
# 边界推导(上文 3.6)把「start 边界无符号」也算成无符号,覆盖了负步长的
# SGT(即 step_neg_const 处注释里「修复 _find_provider 的
# range(top-1,-1,-1) 循环体不执行」失效)→ _find_provider 恒返回 NULL
# → with 上下文的 __mbuddy__ 注入不上(FakeDuckTest Test 9 FAIL +
# 后续 _str.upper 解引用 NULL 崩溃)。负数字面量语义上属有符号域,
# 此处无条件退回有符号谓词(slt/sgt),与 3.6 的无符号优化不冲突。
stop_neg_const: int = 0
if arg_count >= 2:
stop_arg_node: ast.AST | t.CPtr = args.get(1)
if stop_arg_node is not None:
stn_k: int = stop_arg_node.kind()
if stn_k == ast.ASTKind.UnaryOp:
stop_uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(stop_arg_node)
if stop_uop.op == ast.OpKind.USub and stop_uop.operand is not None:
if stop_uop.operand.kind() == ast.ASTKind.Constant:
stop_uc: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(stop_uop.operand)
if stop_uc.int_val > 0:
stop_neg_const = 1
elif stn_k == ast.ASTKind.Constant:
stop_c: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(stop_arg_node)
if stop_c.int_val < 0:
stop_neg_const = 1
if stop_neg_const != 0:
loop_unsigned = 0
# 4. 创建/查找循环变量 alloca
# 必须用 lookup_current(仅当前作用域):闭包内 lookup_var 会跨函数
# 命中父函数同名变量(如父函数自己的循环计数器 i)→ 循环计数写到