From d5d05ec11eb1691e76d7709665c6d1c4a734b334 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 28 Oct 2026 23:15:07 +0800 Subject: [PATCH] fix(codegen): widen from the value expression, not the enclosing statement should_zext_widen decides the widening direction from the narrow side's AST node, but the return and assignment coercions passed the statement node. A statement has no type of its own, so the literal rule and the annotated variable rule were skipped and the value fell through to sext: `def f() -> uint64: return 0xDEADBEEF` produced 0xFFFFFFFFDEADBEEF and the caller compared against 0xDEADBEEF and failed. Unwrap Return/Assign/AnnAssign/AugAssign/Expr to their value expression before the direction rules run, so a non-negative literal wider than INT32_MAX zero-extends (0xDEADBEEF -> 0xDEADBEEF) and a negative one keeps its sign (-1 -> 0xFFFFFFFFFFFFFFFF). --- App/lib/core/Gen/Expr/ExprGenOps.vp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index b6a506d..6316b15 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -144,6 +144,11 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): 6. 其余 → sext。 """ if narrow_val is None or narrow_val.Ty is None: return 0 + # Some call sites hand over the enclosing statement (return / assignment) instead of the + # value expression. The statement carries no type of its own, so rules 2/3/5 would miss a + # literal or an annotated variable and fall through to sext (`return 0xDEADBEEF` from a + # uint64 function became 0xFFFFFFFFDEADBEEF). Unwrap to the value expression first. + narrow_node = self._widen_value_node(narrow_node) narrow_bits: int = self.get_llvm_type_bits(narrow_val.Ty) if narrow_bits == 1: return 1 # 2. 整数字面量:以字面量数值为准(`0xFFFFFFFF` 是 4294967295 > INT32_MAX,必须 zext) @@ -170,6 +175,18 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): if narrow_node is not None: if self._node_type_unsigned(pool, narrow_node, trans) != 0: return 1 return 0 + # _widen_value_node - 取语句节点的值表达式(Return/Assign/AnnAssign/AugAssign/Expr), + # 其他节点原样返回。加宽方向的判定必须落在**有类型的值节点**上。 + def _widen_value_node(self, node: ast.AST*) -> ast.AST*: + """Value expression of a statement node (Return/Assign/AnnAssign/AugAssign/Expr).""" + if node is None: return node + nk_wv: int = node.kind() + if nk_wv == ast.ASTKind.Return: return (ast.Return*(node)).value + if nk_wv == ast.ASTKind.Assign: return (ast.Assign*(node)).value + if nk_wv == ast.ASTKind.AnnAssign: return (ast.AnnAssign*(node)).value + if nk_wv == ast.ASTKind.AugAssign: return (ast.AugAssign*(node)).value + if nk_wv == ast.ASTKind.Expr: return (ast.Expr*(node)).value + return node # _cdefine_int_of - 取节点引用的 DefineT 常量值。支持两种引用形态: # · 裸名 `N` # · 模块属性 `mod.N`(跨模块经 .vpi 注册;常量名就是 at.attr)