fix(handles): 补指针操作符目标写入与实参解引用,消除静默丢弃

- HandlesAssign: 新增 ast.UnaryOp 赋值目标分支(*p = v 复用 _StoreWithCoerce);Tuple/List 目标合并;删除 Tuple 分支里未绑定的死代码(UnboundLocalError)

- HandlesAugAssign: 新增 ast.UnaryOp 目标分支实现真 *p += v,其余形态 fail-fast

- HandlesExpr: 新增 ast.Starred 分支,值位置 *p 归一为 UnaryOp(Deref)(修 t.CPtr(*((uint64*)(blk))) 恒为 0)
This commit is contained in:
TermiNexus committed 2026-09-27 00:19:44 +08:00
1 parent 58050a1ab2
commit 3b5d85ce9d
3 files changed
+108 -4

No files matched your search

+41 -3
View File
@@ -492,7 +492,42 @@ class AssignHandle(BaseHandle):
self._RaiseUnresolvedExpr("下标写入", Node.value, Gen)
return
self._HandleSubscriptStoreLlvm(Target, Value)
elif isinstance(Target, ast.Tuple):
elif isinstance(Target, ast.UnaryOp):
# 解引用写入 `*p = v`(与读侧同一节点形态)。
# 解析链:语句级 Assign 走 vast_c.c ParseSimpleStmt 的
# ParseExprList + SetStoreContext;`*p` 由 ParseFactor 产出
# UnaryOp(op=Deref)(与 `*p` 读侧完全同形),而 SetStoreContext 只改
# Attribute/Subscript/Name/Tuple/List/Starred 的 ctx,**不改 UnaryOp**
# ⇒ 赋值目标就是 UnaryOp(Deref)。
# 此前本分派(Name/Attribute/Subscript/Tuple)无 UnaryOp 分支且无兜底
# ⇒ 该语句被整条静默丢弃(既不落盘也不报错):memhub 的块头
# `(order<<1)|1`、空闲链 next、canary 三处写入全不发射 ⇒ 块头恒为 0
# (free 被拒)、free_lists 链读到未初始化 0xBE、池单调漏满 ⇒ 1 GiB OOM。
# `&x`/`-x`/`~x` 作目标不是合法左值,故除 Deref 外一律 fail-fast。
if not isinstance(Target.op, ast.Deref):
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Target.op)
raise Exception(
f"赋值目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
f"{Gen._get_node_info()}"
)
Ptr: ir.Value | None = self.HandleExprLlvm(Target.operand)
if Ptr is None:
self._RaiseUnresolvedExpr("解引用写入", Target.operand, Gen)
return
if not isinstance(Ptr.type, ir.PointerType):
raise Exception(
f"解引用赋值 '*' 的操作数必须是指针类型(类型推断退化,"
f"实际 {Ptr.type}){Gen._get_node_info()}"
)
DerefValue: ir.Value | None = self.HandleExprLlvm(Node.value)
if DerefValue is None:
self._RaiseUnresolvedExpr("解引用写入", Node.value, Gen)
return
self._StoreWithCoerce(DerefValue, Ptr)
elif isinstance(Target, (ast.Tuple, ast.List)):
# ast.List 与 ast.Tuple 字段表逐字相同(Nodes.py:119-120 均为
# ('elts', 'ctx')),故 `[a, b] = f()` 与 `a, b = f()` 共用本分支。
# 此前只认 Tuple ⇒ List 目标落入无分支区被静默丢弃(不落盘、不报错)。
if isinstance(Node.value, ast.Call):
FuncName: str | None = None
ModulePath: str | None = None
@@ -586,8 +621,11 @@ class AssignHandle(BaseHandle):
if isinstance(ret_type, ir.LiteralStructType):
CReturnTypes = [None] * len(ret_type.elements)
if CReturnTypes and len(Target.elts) == len(CReturnTypes):
if func is None:
func = Gen.functions.get(FuncName)
# 注:此处原有两行 `if func is None: func = ...`,但 func 只在上面
# 「not CReturnTypes」分支里绑定;当 CReturnTypes 来自返回注解
# (`-> tuple[T1, T2]`)时该分支未执行 ⇒ UnboundLocalError 崩溃。
# 且紧接着的 `func = Gen.functions.get(FuncName)` 本就覆盖它,
# 故直接删除(`a, b = f()` 因此从未真正跑通)。
CallArgs: list[ir.Value | None] = []
for arg in Node.value.args:
ArgVal: ir.Value | None = self.HandleExprLlvm(arg)
+55 -1
View File
@@ -239,4 +239,58 @@ class AugAssignHandle(BaseHandle):
if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType):
if NewVal.type.width > ElemPtr.type.pointee.width:
NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result")
Gen._store(NewVal, ElemPtr)
Gen._store(NewVal, ElemPtr)
elif isinstance(Node.target, ast.UnaryOp):
# 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref)
# (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。
# 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
if not isinstance(Node.target.op, ast.Deref):
BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)"
f"{Gen._get_node_info()}"
)
DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand)
if DerefPtr is None:
raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}")
if not isinstance(DerefPtr.type, ir.PointerType):
raise Exception(
f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})"
f"{Gen._get_node_info()}"
)
DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op)
DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val")
if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType):
if DerefOld.type.width < Value.type.width:
DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref")
elif DerefOld.type.width > Value.type.width:
Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref")
elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType):
if DerefOp in ('+', '-'):
if Value.type.width < 64:
Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref")
OffVal: ir.Value
if DerefOp == '-':
OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref")
else:
OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref")
Gen._store(OffVal, DerefPtr)
return
DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True)
if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType):
if DerefNew.type.width > DerefPtr.type.pointee.width:
DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result")
Gen._store(DerefNew, DerefPtr)
else:
# fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 TransPyV
# translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、
# 变量静默不变"。
TargetOp: str | None = None
if isinstance(Node.target, ast.UnaryOp):
TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op)
raise Exception(
f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用"
f"(当前 {type(Node.target).__name__}"
+ (f" op='{TargetOp}'" if TargetOp else "")
+ f"){Gen._get_node_info()}"
)
+12
View File
@@ -76,6 +76,18 @@ class ExprHandle(BaseHandle):
return self.Trans.ExprOpsHandle._HandleBoolOpLlvm(Node)
elif isinstance(Node, ast.UnaryOp):
return self.Trans.ExprOpsHandle._HandleUnaryOpLlvm(Node)
elif isinstance(Node, ast.Starred):
# `*p` 出现在值位置(典型:调用实参 `f(*p)`):C/Python 解析器都按
# Python `*args` 语义归类为 Starred(Load)(vast_c.c ParseCallTail、
# ExprParser.py:543-548)。Viper 未实现解包 ⇒ 与 `*p` 读侧对齐,一律
# 归一为 UnaryOp(Deref) 解引用读取(与 TransPyV ExprGenMixin 的同类归一
# 逐字同口径)。此前无本分支 ⇒ 落到函数末尾兜底 `return i32 0`,
# 把 `f(*p)` 静默变成 `f(0)`。
StarredInner: ast.expr | None = Node.value
if StarredInner is None:
raise Exception(f"值位置的单目 '*' 缺少操作数{Gen._get_node_info()}")
return self.Trans.ExprOpsHandle._HandleUnaryOpLlvm(
ast.UnaryOp(op=ast.Deref(), operand=StarredInner))
elif isinstance(Node, ast.Call):
return self.Trans.ExprCallHandle._HandleCallLlvm(Node)
elif isinstance(Node, ast.Await):