fix(类型转换): int→i1 目标改为真值归一化 != 0,不再 trunc 取最低位

类别:a(编译器缺陷)。`b: bool = f()`(f 运行时返回偶数非零,如 2)此前经
coerce_to_type 的窄化分支走 trunc ⇒ 最低位 0 ⇒ false,而同一值在 `if 2:` /
`bool(2)`(icmp ne 0)为真 —— 两处判据相反(静默语义分裂)。

- ExprGenBase.coerce_to_type:整数窄化分支前先判目标位宽 == 1 ⇒ `icmp ne x, 0`
  (C 6.3.1.2 的 _Bool 转换语义)。归一化不丢位,故对 i1 目标不再适用下方
  「窄化截断溢出」常量诊断(先返回)——`b: bool = 2` 由编译期 OverflowError
  变为正确的 true。
- CallGenCtor._translate_t_type_cast:显式 `t.CBool(x)` 与隐式落存储同一判据。

判据与 bool(x)(CallGenDispatch.vp:255-281)、条件归一化(AsyncGenMixin.vp:1611
_normalize_cond)、and/or 操作数三处统一。App/ 与 includes/ 全量检索未见
`: bool = …(& | >> | // | |)` 位提取写法,既有代码无依赖 trunc 取最低位的点。

生效需重新自举 TransPyV.exe(并清缓存)。
This commit is contained in:
TermiNexus committed 2026-09-30 15:14:32 +08:00
1 parent ef4421db20
commit 2a798dd643
2 files changed
+18

No files matched your search

+7
View File
@@ -686,6 +686,13 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
return llvmlite.build_zext(builder, val, target_ty)
return llvmlite.build_sext(builder, val, target_ty)
else:
# 目标 i1(bool / t.CBool):显式强转 `t.CBool(x)` 与隐式落存储
# **同一判据** —— 真值归一化 `x != 0 → 1/0`(coerce_to_type 的同名
# 分支、CallGenDispatch 的 bool(x)、条件归一化三处一致),不是
# trunc(取最低位,会让 x=2 得 false 而 `if 2:` 为真)。
if target_bits == 1:
zero_tc: llvmlite.Value* = llvmlite.const_int(pool, val_bits, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero_tc)
return llvmlite.build_trunc(builder, val, target_ty)
# 整数 → 浮点: sitofp
+11
View File
@@ -865,6 +865,17 @@ novirtual class ExprGenBase:
if val_bits != 0 and target_bits != 0:
if val_bits == target_bits:
return val
# 目标 i1(bool / t.CBool):**真值归一化** `x != 0 → 1/0`(C 6.3.1.2 的
# _Bool 转换语义),不是 trunc(取最低位)。判据与 `bool(x)`
# (CallGenDispatch.vp:255-281)、条件归一化(AsyncGenMixin.vp:1611
# _normalize_cond)、and/or 操作数三处**同一**:同一个运行时值 2 在
# `if 2:` 里为真,赋给 bool 变量/字段/形参必须同样为真。此处此前走 trunc
# ⇒ 值 2 取最低位得 0 → false,与条件上下文结论相反(静默语义分裂);
# 值 3 得 true 而同值域下 `!= 0` 亦为 true,故只在偶数非零时暴露。
# 归一化不丢位 ⇒ 下方「窄化截断溢出」常量诊断对 i1 目标不适用(先返回)。
if target_bits == 1:
zero_i1: llvmlite.Value* = llvmlite.const_int(builder.Pool, val_bits, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero_i1)
if val_bits < target_bits:
# 统一 zext/sext(问题2修正):i1/i8 默认 zext;AST 节点解析为
# 无符号类型(t.CUIntXXT 强转/无符号类型名)走 zext;否则 sext。