feat(types): 禁止隐式窄化(整数 高→低)——_coerce_value 与 pointee 判据同步

与 TransPyV 同口径:_coerce_value 的 int→int 窄化分支改为 fail-fast(隐式窄化禁止),仅豁免 ① 目标 i1 真值归一化 ② 编译期常量且值落进目标位宽;_ScalarConvertLossless 的 int↔int 改为 Dst.width>=Src.width。
This commit is contained in:
TermiNexus committed 2026-10-10 21:57:25 +08:00
1 parent 4f9c234ea8
commit 3b16334f5d
1 file changed
+17 -4
+17 -4
View File
@@ -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