diff --git a/lib/core/LLVMCG/MemoryOps.py b/lib/core/LLVMCG/MemoryOps.py index 7d5b685..1b609f5 100644 --- a/lib/core/LLVMCG/MemoryOps.py +++ b/lib/core/LLVMCG/MemoryOps.py @@ -198,7 +198,7 @@ class MemoryOpsMixin: @staticmethod def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool: """基本标量「无损」判定(与 TransPyV `_scalar_convert_reason` 同口径,逐条照搬): - - 整数 ↔ 整数:**所有方向都允许**(高→低是安全降级;低→高无损); + - 整数 ↔ 整数:允许 ⇔ **目标位宽 ≥ 源位宽**(低→高无损;高→低是**有损窄化**,禁); - 浮点 ↔ 浮点:**所有方向都允许**; - 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁); - 浮点 → 整数:**不允许**(有损 → 禁)。 @@ -213,9 +213,9 @@ class MemoryOpsMixin: 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 + return Dst.width >= Src.width # 浮点 ↔ 浮点:允许 if s_flt and d_flt: return True @@ -656,7 +656,20 @@ class MemoryOpsMixin: # 目标 i1:真值归一化(与 bool(x)/if 条件同一判据),不是 trunc 取最低位 if target_type.width == 1: return self._CoerceIntToI1(val, name="bool_norm_store") - return self.builder.trunc(val, target_type, name="trunc_store") + # 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"): + # 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。 + # 唯一豁免:源是**编译期常量且值落进目标位宽**(无信息损失)。 + if isinstance(val, ir.Constant) and isinstance(val.constant, int): + Lo: int = -(1 << (target_type.width - 1)) + Hi: int = (1 << target_type.width) - 1 + if Lo <= val.constant <= Hi: + return self.builder.trunc(val, target_type, name="trunc_const_store") + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"隐式窄化禁止:{val.type.width} 位整数不能隐式赋给 {target_type.width} 位类型" + f"(会丢高位);请显式转换(如 int32(x) / t.CInt32T(x))或把目标类型加宽" + f"{self._DescribeValueNode(Node)}" + ) elif val.type.width == 1: # i1 是布尔值(来自 icmp/fcmp/__contains__ 归一化),无符号语义, # 必须用 zext 而非 sext,否则 i1 的 1 会被符号扩展为 -1