fix(类型转换): int→i1 目标改为真值归一化 != 0,与 bool(x)/if 条件同判据

类别:a(编译器缺陷)。TPC 侧此前对 i1 目标一律 trunc(取最低位):运行时值 2 →
最低位 0 → false,而同一值在 `if 2:` / `bool(2)`(icmp ne 0)为真 ⇒ 条件上下文与
落存储/强转两处结论相反(静默语义分裂)。

- MemoryOps._CoerceIntToI1(新增,唯一判据源):非 i1 整数 → `icmp ne x, 0`。
- MemoryOps._coerce_value:窄化且目标位宽 == 1 时走 _CoerceIntToI1。
- HandlesAnnAssign._StoreWithCoerce:同上(self.字段 / obj.字段 落存储)。
- HandlesExprCall._HandleTypeCastLlvm:显式强转目标 i1 时同上。

与 TransPyV 侧同批修复(coerce_to_type / _translate_t_type_cast)对齐。

注:TPC 的 `bool` 注解仍是 i8(t.CBool.Size=8),故 `b: bool = 2` 仍按 i8 保位存储
(int(b)=2);本次只覆盖**真 i1 目标**,双端 bool 位宽差异见
SymbolUtils.py:1218-1223 已记录的全局语义分裂。
This commit is contained in:
TermiNexus committed 2026-09-30 15:14:31 +08:00
1 parent 3efe83f1ea
commit 7ee6ebe6b0
3 files changed
+30 -2

No files matched your search

+6 -1
View File
@@ -248,7 +248,12 @@ class AnnAssignHandle(BaseHandle):
else Gen._ZeroConst(TargetType))
elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.IntType):
if TargetType.width < Value.type.width:
Value = Gen.builder.trunc(Value, TargetType, name="trunc")
# 目标 i1:真值归一化 `x != 0`(与 bool(x)/if 条件同一判据),
# 不是 trunc 取最低位(运行时值 2 会得 false,与 `if 2:` 相反)
if TargetType.width == 1:
Value = Gen._CoerceIntToI1(Value, name="bool_norm")
else:
Value = Gen.builder.trunc(Value, TargetType, name="trunc")
else:
Value = Gen.builder.zext(Value, TargetType, name="zext")
elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType):
+6 -1
View File
@@ -2276,7 +2276,12 @@ class ExprCallHandle(BaseHandle):
Gen._unsigned_results[id(result)] = True
return result
if val.type.width > target_type.width:
result = Gen.builder.trunc(val, target_type, name="trunc_cast")
# 目标 i1:真值归一化 `x != 0`(与 bool(x) 内建 / if 条件 / 落存储
# 同一判据),不是 trunc 取最低位(运行时值 2 会得 false)
if target_type.width == 1:
result = Gen._CoerceIntToI1(val, name="bool_norm_cast")
else:
result = Gen.builder.trunc(val, target_type, name="trunc_cast")
if is_dst_unsigned:
Gen._unsigned_results[id(result)] = True
return result
+18
View File
@@ -578,6 +578,21 @@ class MemoryOpsMixin:
return self._load(GVar, name=name)
return None
def _CoerceIntToI1(self, val: ir.Value, name: str = "bool_norm") -> ir.Value:
"""整数 → **i1** 目标的真值归一化(C 6.3.1.2 `_Bool` 转换语义):`x != 0` → 1/0。
判据与三处真值化上下文**同一**:`bool(x)` 内建(HandlesExprBuiltin.py:74/76)、
条件 `if/while`(HandlesIf.py:196)、`and/or` 操作数(HandlesExprOps.py:122-127)。
此前「落存储」(本文件 `_coerce_value`、HandlesAnnAssign._StoreWithCoerce)与
「显式强转」(HandlesExprCall._HandleTypeCastLlvm)对 i1 目标都走 `trunc`
(取最低位)⇒ 运行时值 2 得 0 → false,与 `if 2:` 的真值结论相反
(静默语义分裂);值 3 得 true 而同值域下 `!= 0` 亦为 true,故只在偶数非零时暴露。
非整数 / 已是 i1 原样返回。
"""
if not isinstance(val.type, ir.IntType) or val.type.width == 1:
return val
return self.builder.icmp_signed('!=', val, self._ZeroConst(val.type), name=name)
def _coerce_value(self, val: ir.Value, target_type: ir.Type, Node: Any = None) -> ir.Value | None:
if val.type == target_type:
return val
@@ -593,6 +608,9 @@ class MemoryOpsMixin:
return val
if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.IntType):
if target_type.width < val.type.width:
# 目标 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")
elif val.type.width == 1:
# i1 是布尔值(来自 icmp/fcmp/__contains__ 归一化),无符号语义,