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(