From 97c0a1c6fd1be1f1aaca2ad4ee4e71d73a256f3c Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 13 Oct 2026 16:07:43 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen):=20=E5=8F=96=E6=B6=88=E3=80=8C?= =?UTF-8?q?=E5=AE=BD=E6=95=B4=E5=9E=8B=20->=20bool(i1)=E3=80=8D=E7=9A=84?= =?UTF-8?q?=E9=9A=90=E5=BC=8F=E7=9C=9F=E5=80=BC=E5=BD=92=E4=B8=80=E5=8C=96?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E4=B8=BA=20fail-fast=EF=BC=88=E4=BB=85?= =?UTF-8?q?=E4=BF=9D=E7=95=99=E7=BC=96=E8=AF=91=E6=9C=9F=E5=B8=B8=E9=87=8F?= =?UTF-8?q?=E8=B1=81=E5=85=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/core/Gen/Call/CallGenCtor.vp | 6 ++++-- App/lib/core/Gen/Expr/ExprGenBase.vp | 21 +++++++++++++-------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/App/lib/core/Gen/Call/CallGenCtor.vp b/App/lib/core/Gen/Call/CallGenCtor.vp index 69099d0..173b412 100644 --- a/App/lib/core/Gen/Call/CallGenCtor.vp +++ b/App/lib/core/Gen/Call/CallGenCtor.vp @@ -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) diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index 930635c..438e8fe 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -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.