From 49ff1b995f21a12b381634c69c84cb999b657ac2 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 28 Sep 2026 16:48:52 +0800 Subject: [PATCH] =?UTF-8?q?fix(cast):=20=E6=98=BE=E5=BC=8F=E6=8C=87?= =?UTF-8?q?=E9=92=88=20cast=20=E4=B8=80=E5=BE=8B=E6=94=BE=E8=A1=8C?= =?UTF-8?q?=EF=BC=88=E6=8C=89=20bitcast=20=E9=87=8D=E8=A7=A3=E9=87=8A?= =?UTF-8?q?=EF=BC=89+=20MemoryOps=20=E6=94=B9=E7=94=A8=20vast=20=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E5=99=A8=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ① 新增 _IsExplicitPtrCastNode:值节点是写明的指针转换调用(t.CPtr(x)/CPtr(x)/c.CPtr(x)/rawptr[T](x)/t.CPtr[T](x)/(T*)x/T*(x))时,_ClassifyPtrImplicitConvert 直接放行(用户规则「显式转换都可以」),调用方按既有 bitcast 重解释;非 cast 调用(MemBuddy(x)/list[X](x)/foo(x))不受影响。 根因:includes/_list.vp:60 的 elem_ptr: T | t.CPtr = t.CPtr(地址) 在 list[X*] 特化下 T|t.CPtr = X**(T 本身是指针),而 t.CPtr(...) 的值是 i8* ⇒ 被判「层数不同」,与报错自己给的「如需转换请显式 cast(如 t.CPtr(...))」建议自相矛盾(ExprGenFstr.vp 编译失败的 2 条 elem_ptr 报错)。 ② MemoryOps.py 的 import ast 是标准库 —— 但 TPC 全部源码由 vast 解析,节点类为 lib.vast.Nodes.*,与标准库 ast 互不兼容 ⇒ 该文件所有节点类型判据对 Viper 源码静默失效(_value_node_of 子表达式下沉、_CtorCallStructType 构造物化豁免、以及新增的 cast 判据都不生效)。改为 import vast as ast,一并修复。 --- lib/core/Handles/HandlesAnnAssign.py | 2 +- lib/core/Handles/HandlesAssign.py | 2 +- lib/core/LLVMCG/MemoryOps.py | 51 ++++++++++++++++++++++++++-- 3 files changed, 50 insertions(+), 5 deletions(-) diff --git a/lib/core/Handles/HandlesAnnAssign.py b/lib/core/Handles/HandlesAnnAssign.py index 8cdd5c1..aaa40c6 100644 --- a/lib/core/Handles/HandlesAnnAssign.py +++ b/lib/core/Handles/HandlesAnnAssign.py @@ -917,7 +917,7 @@ class AnnAssignHandle(BaseHandle): elif isinstance(InitValue.type, ir.PointerType) and isinstance(VarType, ir.PointerType): Allowed: bool Reason: str - Allowed, Reason = Gen._ClassifyPtrImplicitConvert(InitValue.type, VarType) + Allowed, Reason = Gen._ClassifyPtrImplicitConvert(InitValue.type, VarType, Node.value) if not Allowed: from lib.core.LLVMCG.FuncGen import FailFastError raise FailFastError( diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index 9d40413..f29f163 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -1568,7 +1568,7 @@ class AssignHandle(BaseHandle): # 被指类型无损时放行,非基本/有损/层数不同 fail-fast。 Allowed: bool Reason: str - Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType) + Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType, Node) if not Allowed: from lib.core.LLVMCG.FuncGen import FailFastError raise FailFastError( diff --git a/lib/core/LLVMCG/MemoryOps.py b/lib/core/LLVMCG/MemoryOps.py index 42b54a2..9f7b3fe 100644 --- a/lib/core/LLVMCG/MemoryOps.py +++ b/lib/core/LLVMCG/MemoryOps.py @@ -1,6 +1,11 @@ from __future__ import annotations from typing import Any -import ast +# 必须用 TPC 自己的解析器模块(vast),**不是**标准库 ast:TPC 全部源码 +# (.vp 与 .py)都由 vast 解析,产出的是 lib.vast.Nodes.* 节点,与标准库 +# ast 的节点类互不兼容(isinstance 恒 False)。此前误用标准库 ast,使本文件 +# 全部节点类型判据对 Viper 源码静默失效(_value_node_of 的子表达式下沉、 +# _CtorCallStructType 的构造物化豁免、指针 cast 判据都不生效)。 +import vast as ast import llvmlite.ir as ir @@ -265,7 +270,40 @@ class MemoryOpsMixin: return True return False - def _ClassifyPtrImplicitConvert(self, Src: Any, Dst: Any) -> tuple[bool, str]: + def _IsExplicitPtrCastNode(self, Node: Any) -> bool: + """值节点是否为「显式指针 cast」调用(用户规则:显式转换一律放行)。 + + 形式(TPC 解析产物的共同点 = ast.Call 且 func 是**指针类型表达式**): + - `t.CPtr(x)` / 别名 `CPtr(x)` / `c.CPtr(x)` + - `rawptr[T](x)` / `t.CPtr[T](x)` / 裸名 `CPtr[T](x)` + - `(T*)x` / `(T*)(x)` / `T*(x)`(含 `**` 两层;ExprParser 把三种写法统一 + 产成 Call(func = `T | t.CPtr`)) + - 裸 `rawptr(x)`(≡ t.CPtr) + 非 cast 调用(构造函数 / 普通函数,如 `MemBuddy(x)`、`list[X](...)`)不命中 + ⇒ 仍按隐式转换规则判定。只判 func 节点形状,不做类型解析(不与 Gen 耦合)。 + + 必要性:`includes/_list.vp:60` 的 `elem_ptr: T | t.CPtr = t.CPtr(地址)` 在 + `list[X*]` 特化下 T | t.CPtr = `X**`(T 本身是指针),而 `t.CPtr(...)` 的值是 + `i8*`——层数不同被判"隐式转换已禁止",与报错自己给的"如需转换请显式 cast + (如 t.CPtr(...))"建议自相矛盾。显式 cast 一律放行后按 bitcast 重解释。 + """ + if not isinstance(Node, ast.Call): + return False + Func: Any = Node.func + from lib.core.SymbolUtils import (IsCPtrNode, IsPtrSubscriptNode, + AnnotationContainsName) + if IsCPtrNode(Func) or IsPtrSubscriptNode(Func): + return True + if isinstance(Func, ast.Attribute) and Func.attr == 'CPtr': + return True + if isinstance(Func, ast.Name) and Func.id in ('CPtr', 'rawptr'): + return True + if isinstance(Func, ast.BinOp) and isinstance(Func.op, ast.BitOr): + return AnnotationContainsName(Func, 'CPtr') + return False + + def _ClassifyPtrImplicitConvert(self, Src: Any, Dst: Any, + Node: Any = None) -> tuple[bool, str]: """隐式转换判据:返回 (Allowed, Reason)。 规则(用户决定,与 TransPyV 同口径): @@ -280,8 +318,15 @@ class MemoryOpsMixin: **唯一例外**是「构造物化」——该豁免需 AST 层判据,故在 coerce 调用点 由 _CtorCallStructType 单独放行(本判据只给出「禁」的结论)。 ④ 完全相同类型不算转换 ⇒ 放行。 + ⑤ **显式 cast 一律放行**(用户规则「显式转换都可以」):值侧节点本身是写明的 + 指针转换调用(t.CPtr(x) / rawptr[T](x) / (T*)x ...,见 + _IsExplicitPtrCastNode)⇒ 目标类型即程序员指定的转换目标,调用方按 + bitcast 重解释,不做层数/被指类型判定。 Reason 供报文说明「哪一级不允许 + 原因(非基本类型/有损/层数不同)」。 """ + # ⑤ 显式 cast:先于一切判据放行(Node 缺省 None ⇒ 不适用,等价既有行为) + if self._IsExplicitPtrCastNode(Node): + return (True, '显式 cast(按 bitcast 重解释)') if Src == Dst: return (True, '同类型(不构成转换)') s_ptr: bool = isinstance(Src, ir.PointerType) @@ -507,7 +552,7 @@ class MemoryOpsMixin: # 被指类型无损时放行,非基本类型/有损/层数不同一律 fail-fast。 Allowed: bool Reason: str - Allowed, Reason = self._ClassifyPtrImplicitConvert(val.type, target_type) + Allowed, Reason = self._ClassifyPtrImplicitConvert(val.type, target_type, Node) if not Allowed: from lib.core.LLVMCG.FuncGen import FailFastError raise FailFastError(