diff --git a/lib/core/LLVMCG/MemoryOps.py b/lib/core/LLVMCG/MemoryOps.py index 14ea9e4..920a4a0 100644 --- a/lib/core/LLVMCG/MemoryOps.py +++ b/lib/core/LLVMCG/MemoryOps.py @@ -1,5 +1,6 @@ from __future__ import annotations from typing import Any +import ast import llvmlite.ir as ir @@ -191,10 +192,11 @@ class MemoryOpsMixin: @staticmethod def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool: - """既有整数/浮点规则下的「无损」判定:同族且目标位宽 >= 源位宽。 - - 整数→整数 / 浮点→浮点:按位宽(宽化无损、同宽无损、窄化有损); - 整数↔浮点:跨类别,一律视为非无损(有损)。 + """基本标量「无损」判定(与 TransPyV `_scalar_convert_reason` 同口径,逐条照搬): + - 整数 ↔ 整数:**所有方向都允许**(高→低是安全降级;低→高无损); + - 浮点 ↔ 浮点:**所有方向都允许**; + - 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁); + - 浮点 → 整数:**不允许**(有损 → 禁)。 """ def _FloatBits(x: Any) -> int: if isinstance(x, ir.DoubleType): @@ -202,12 +204,22 @@ class MemoryOpsMixin: if isinstance(x, ir.FloatType): return 32 return 0 - if isinstance(Src, ir.IntType) and isinstance(Dst, ir.IntType): - return Dst.width >= Src.width - sb: int = _FloatBits(Src) - db: int = _FloatBits(Dst) - if sb and db: - return db >= sb + s_int: bool = isinstance(Src, ir.IntType) + d_int: bool = isinstance(Dst, ir.IntType) + s_flt: int = _FloatBits(Src) + d_flt: int = _FloatBits(Dst) + # 整数 ↔ 整数:高→低安全降级、低→高无损,均允许 + if s_int and d_int: + return True + # 浮点 ↔ 浮点:允许 + if s_flt and d_flt: + return True + # 整数 → 浮点:目标浮点位宽 ≥ 源整数位宽才无损 + if s_int and d_flt: + return d_flt >= Src.width + # 浮点 → 整数:有损,禁止 + if s_flt and d_int: + return False return False @staticmethod @@ -218,7 +230,9 @@ class MemoryOpsMixin: ① 通用指针(t.CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。 ② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型 (结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止。 - ③ 非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止(层数不同)。 + ③ 非基本类型 T 与 T* 之间的隐式转换(Foo ↔ Foo*)一律禁止(层数不同); + **唯一例外**是「构造物化」——该豁免需 AST 层判据,故在 coerce 调用点 + 由 _CtorCallStructType 单独放行(本判据只给出「禁」的结论)。 ④ 完全相同类型不算转换 ⇒ 放行。 Reason 供报文说明「哪一级不允许 + 原因(非基本类型/有损/层数不同)」。 """ @@ -249,6 +263,58 @@ class MemoryOpsMixin: return (False, '指针与标量(须显式 cast)') return (False, '非基本类型(结构体/类/联合)') + @staticmethod + def _ValueNodeOf(Node: Any = None) -> Any: + """把「外层语句节点」下沉到其「值子表达式节点」(与 TransPyV _value_node_of 同口径)。 + + Assign/AnnAssign/AugAssign/Expr/Return 取 .value;非语句节点原样返回。 + 规则③的「构造物化」豁免须看到**真正被转换的那个右值表达式**(如 `p: Point = Point()` + 里的 `Point()`),而 coerce 入口常收到外层语句节点,故统一在此下沉。 + """ + if Node is None: + return None + if isinstance(Node, ast.Assign): + return Node.value + if isinstance(Node, ast.AnnAssign): + return Node.value + if isinstance(Node, ast.AugAssign): + return Node.value + if isinstance(Node, ast.Expr): + return Node.value + if isinstance(Node, ast.Return): + return Node.value + return Node + + def _CtorCallStructType(self, Node: Any = None) -> Any: + """AST 层判定:Node(或其值子节点)是否为「类/构造调用」右值;是则返回其构造的 + 聚合类型(IdentifiedStruct/LiteralStruct),否则返回 None。 + + 规则③(非基本类型值 ↔ 指针隐式转换一律禁止)的**唯一例外**是「构造物化」: + 右值来自该类型的构造调用时放行(构造语义,非类型转换)。判据复用本仓既有的 + 「Call 的被调方是类/构造」口径(见 HandlesExprCall._HandleCallLlvm 的分派): + - callee 裸名/属性名命中已注册类(`Gen.structs`)→ 构造; + - 泛型类模板特化(Box(42))需类型实参推断,静态取不到类型 → 保守返回 None。 + """ + N: Any = MemoryOpsMixin._ValueNodeOf(Node) + if not isinstance(N, ast.Call): + return None + Func: Any = getattr(N, 'func', None) + ClassName: str | None = None + if isinstance(Func, ast.Name): + ClassName = Func.id + elif isinstance(Func, ast.Attribute): + ClassName = Func.attr + if not ClassName: + return None + ST: Any = getattr(self, 'structs', {}).get(ClassName) + if ST is None: + return None + if isinstance(ST, ir.PointerType): + ST = ST.pointee + if not isinstance(ST, (ir.IdentifiedStructType, ir.LiteralStructType)): + return None + return ST + def _alloca(self, typ: ir.Type, name: str = '', align: int | None = None, size: ir.Value | None = None) -> ir.AllocaInstr: if align is None: align = self._get_align(typ) @@ -434,16 +500,29 @@ class MemoryOpsMixin: # 指针→非指针值的隐式 load(结构体/数组按值回读)属类型信息缺失, # 须显式解引用(c.Deref(p) / *p)或显式强转。基本标量目标已在上方 # 「指针隐式转整数」分支 fail-fast,此处只兜非基本类型。 - Allowed3: bool - Reason3: str - Allowed3, Reason3 = self._ClassifyPtrImplicitConvert(val.type, target_type) - if not Allowed3: - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转换已禁止({Reason3}):不能把指针类型 {val.type} 隐式转为 {target_type};" - f"如需转换请显式 cast(解引用 c.Deref(p) / *p,或显式强转)" - f"{self._DescribeValueNode(Node)}" - ) + # **唯一例外=「构造物化」**:右值来自该类型的构造调用时放行,如 + # `p: Point = Point()`(参照 GCC 构造语义:这是构造,不是类型转换)。 + CtorST: Any = self._CtorCallStructType(Node) + CtorExempt: bool = False + if CtorST is not None: + if CtorST == target_type: + CtorExempt = True + elif (isinstance(CtorST, ir.IdentifiedStructType) + and isinstance(target_type, ir.IdentifiedStructType) + and CtorST.name == target_type.name): + CtorExempt = True + if not CtorExempt: + Allowed3: bool + Reason3: str + Allowed3, Reason3 = self._ClassifyPtrImplicitConvert(val.type, target_type) + if not Allowed3: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"非基本类型的值/指针隐式转换已禁止(构造物化除外,{Reason3}):" + f"不能把指针类型 {val.type} 隐式转为值类型 {target_type};" + f"如需解引用请显式 c.Deref(p) / *p,或显式强转" + f"{self._DescribeValueNode(Node)}" + ) if val.type.pointee == target_type: return self.builder.load(val, name="Load_struct_for_store") if isinstance(val.type.pointee, ir.IdentifiedStructType) and isinstance(target_type, ir.IdentifiedStructType):