fix(gen): 取消「宽整型 -> bool(i1)」的隐式真值归一化,改为 fail-fast(仅保留编译期常量豁免)

This commit is contained in:
TermiNexus committed 2026-10-13 16:07:43 +08:00
1 parent d7c5af9b80
commit 97c0a1c6fd
2 files changed
+17 -10

No files matched your search

+4 -2
View File
@@ -784,8 +784,10 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
if self.Gen.ExprGenBase._zext_or_sext_val(pool, val, src_node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
return mlirlite.build_sext(builder, val, target_ty)
else:
# Target i1 (bool / t.CBool): both explicit casts and implicit stores normalise
# truth as `x != 0 -> 1/0` (not trunc, which would make x=2 false while `if 2:` is true).
# Target i1 (bool / t.CBool) via an EXPLICIT cast `t.CBool(x)` / `(bool)(x)`:
# normalise truth as `x != 0 -> 1/0` (not trunc, which would make x=2 false while
# `if 2:` is true). Implicit wide-int -> bool stores no longer normalise here; they
# fail fast in coerce_to_type (rule 2026-10-13), so only explicit casts reach this.
if target_bits == 1:
zero_tc: mlirlite.Value* = mlirlite.const_int(pool, val_bits, 0)
return mlirlite.build_icmp(builder, mlirlite.ICMP_NE, val, zero_tc)
+13 -8
View File
@@ -598,20 +598,16 @@ novirtual class ExprGenBase:
return val
if val_bits != 0 and target_bits != 0:
if val_bits == target_bits: return val
# Target i1 (bool / t.CBool): truth normalization `x != 0 -> 1/0`
# (C 6.3.1.2), not trunc; keeps parity with `bool(x)` and condition paths.
if target_bits == 1:
zero_i1: mlirlite.Value* = mlirlite.const_int(builder.Pool, val_bits, 0)
return mlirlite.build_icmp(builder, mlirlite.ICMP_NE, val, zero_i1)
if val_bits < target_bits:
# Unified zext/sext: i1/i8 default zext, unsigned AST nodes zext,
# otherwise sext. trans is required so unsigned annotations resolve.
if self._zext_or_sext_val(builder.Pool, val, node, trans) != 0: return mlirlite.build_zext(builder, val, target_ty)
return mlirlite.build_sext(builder, val, target_ty)
# 隐式窄化**禁止**(规则 2026-10-10 收紧,取代"整数↔整数 所有方向都允许"):
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。仅两类豁免:
# ① 目标 i1:真值归一化(x != 0),语义明确,不是位截断(已在上方返回);
# ② 源是**编译期常量且值能落进目标位宽**(如字面量 5 → i32),无信息损失。
# 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。豁免只剩一类:
# 源是**编译期常量且值能落进目标位宽**(如字面量 5 → i32、True/False → i1),无信息损失。
# (注意 `True`/`False` 常量按 i32 1/0 物化,见 ExprGenConst.translate_constant,
# 正是靠此豁免才能赋给 i1 目标。)
if val.IsConst == 1:
iv: int64 = val.IntVal
lo_c: int64 = -(int64(1) << (target_bits - 1))
@@ -619,6 +615,15 @@ novirtual class ExprGenBase:
if iv < lo_c or iv > hi_c:
self.Gen.TypeGenErr.fatal_error(node, f"narrowing truncation overflow: constant {iv} is outside the {target_bits}-bit representable range [{lo_c}, {hi_c}]; use an explicit cast or widen the target type", "OverflowError")
return mlirlite.build_trunc(builder, val, target_ty)
# 规则 2026-10-13:目标 i1(bool / t.CBool)**不再**对宽整型做隐式真值归一化
# (旧的 `x != 0 -> 1/0`)。真值归一化会掩盖真实的位宽不匹配(如 `b: bool = some_i32`),
# 属静默转换,必须 fail-fast;需要显式 `bool(x)` / `t.CBool(x)`。
# 合法写法不受影响:比较(!=/==/…)、`and` / `or`、`not` 本身即产出 i1
# (translate_compare / translate_boolop / translate_unaryop),i1 源走上面的
# `val_bits == target_bits` 直接放行;True/False 常量走上面的常量豁免。
if target_bits == 1:
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to bool (i1) (it would be silently reduced to its truthiness); use an explicit cast (e.g. bool(x) / t.CBool(x))", "CastError")
return val
self.Gen.TypeGenErr.fatal_error(node, f"implicit narrowing is forbidden: a {val_bits}-bit value cannot be implicitly assigned to a {target_bits}-bit type (the high bits would be lost); use an explicit cast (e.g. int32(x) / t.CInt32T(x)) or widen the target type", "CastError")
return val
# Float conversion.