From 65f0e87193243b6f8ec34b45ece00c460314dcf2 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 3 Oct 2026 00:39:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(dunder):=20=E8=A1=A5=E9=BD=90=20=5F=5Fbool?= =?UTF-8?q?=5F=5F=20=E6=9D=A1=E4=BB=B6=E7=9C=9F=E5=80=BC=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E4=B8=8E=E5=8F=8D=E5=90=91=E8=BF=90=E7=AE=97=E7=AC=A6/?= =?UTF-8?q?=E4=BD=8D=E8=BF=90=E7=AE=97=E5=A2=9E=E9=87=8F=E9=87=8D=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - BaseHandle 新增 _CoerceCondToBool:if/while/and/or/not/三元条件统一归一到 i1,类实例定义了 __bool__ 时优先调度(此前仅 if 支持) - HandlesExprOps:新增反向运算符重载(__radd__/__rsub__/... 共 12 个),左操作数无普通 dunder 时调度右操作数 __rX__ - HandlesAugAssign:_AUGOP_MAP/_AUGOP_FALLBACK 补齐位运算与移位增量(__iand__/__ior__/__ixor__/__ilshift__/__irshift__) --- lib/core/Handles/HandlesAugAssign.py | 4 ++ lib/core/Handles/HandlesBase.py | 34 +++++++++++++++ lib/core/Handles/HandlesExprLambda.py | 2 +- lib/core/Handles/HandlesExprOps.py | 63 +++++++++++++++++++-------- lib/core/Handles/HandlesIf.py | 14 +----- lib/core/Handles/HandlesWhile.py | 5 +-- 6 files changed, 87 insertions(+), 35 deletions(-) diff --git a/lib/core/Handles/HandlesAugAssign.py b/lib/core/Handles/HandlesAugAssign.py index a061ce1..bae4e6f 100644 --- a/lib/core/Handles/HandlesAugAssign.py +++ b/lib/core/Handles/HandlesAugAssign.py @@ -13,10 +13,14 @@ class AugAssignHandle(BaseHandle): _AUGOP_MAP: dict[type, str] = { ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__', ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__', + ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__', + ast.LShift: '__ilshift__', ast.RShift: '__irshift__', } _AUGOP_FALLBACK: dict[type, str] = { ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__', ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__', + ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__', + ast.LShift: '__lshift__', ast.RShift: '__rshift__', } def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type: diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index c177a58..46f8ba7 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -530,6 +530,40 @@ class BaseHandle: ConstWidthHint: int | None = None) -> _ir.Value | None: return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint) + def _CoerceCondToBool(self, Gen: "LlvmCodeGenerator", Node: Any, Cond: Any) -> Any: + """把条件表达式归一到 i1(条件真值)。 + + 与 TPV 对齐的调度顺序: + 1. 已是 i1 → 原样返回; + 2. 条件是对象(类实例)且类内定义了 `__bool__` → 调用 `__bool__`,结果 `!= 0`; + 3. 浮点 → `!= 0.0`; + 4. 指针 → `!= null`; + 5. 整数 → `!= 0`。 + + 语义唯一来源:`if` / `while` / `and` / `or` / 三元条件全部汇聚于此, + 避免各处各自演化出「struct 恒真」这类不一致判据。 + """ + if isinstance(Cond.type, _ir.IntType) and Cond.type.width == 1: + return Cond + try: + ClassName: str | None = self.Trans.ExprUtils._get_var_class(Node, Gen) + except Exception: + ClassName = None + if ClassName and Gen._has_function(f'{ClassName}.__bool__'): + Res: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, '__bool__', Cond, Gen) + if Res is not None: + if isinstance(Res.type, _ir.IntType): + if Res.type.width == 1: + return Res + return Gen.builder.icmp_signed('!=', Res, Gen._ZeroConst(Res.type), name="bool_result") + if isinstance(Res.type, (_ir.FloatType, _ir.DoubleType)): + return Gen.builder.fcmp_ordered('!=', Res, _ir.Constant(Res.type, 0.0), name="bool_result") + if isinstance(Cond.type, (_ir.FloatType, _ir.DoubleType)): + return Gen.builder.fcmp_ordered('!=', Cond, _ir.Constant(Cond.type, 0.0), name="cond_bool") + if isinstance(Cond.type, _ir.PointerType): + return Gen.builder.icmp_signed('!=', Cond, _ir.Constant(Cond.type, None), name="cond_bool") + return Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="cond_bool") + @staticmethod def _ExprShape(Node: ast.AST, Depth: int = 0) -> str: """表达式节点的**形态摘要**(类名 + 关键子节点),供 fail-fast 报文取证。 diff --git a/lib/core/Handles/HandlesExprLambda.py b/lib/core/Handles/HandlesExprLambda.py index cb1e883..d997469 100644 --- a/lib/core/Handles/HandlesExprLambda.py +++ b/lib/core/Handles/HandlesExprLambda.py @@ -137,7 +137,7 @@ class ExprLambdaHandle(BaseHandle): if not TestVal: return None if not isinstance(TestVal.type, ir.IntType) or TestVal.type.width != 1: - TestVal = Gen.builder.icmp_signed('!=', TestVal, Gen._ZeroConst(TestVal.type), name="ifexp_cond") + TestVal = self._CoerceCondToBool(Gen, Node.test, TestVal) ThenBB: ir.Block = Gen.func.append_basic_block(name="ifexp.then") ElseBB: ir.Block = Gen.func.append_basic_block(name="ifexp.else") MergeBB: ir.Block = Gen.func.append_basic_block(name="ifexp.end") diff --git a/lib/core/Handles/HandlesExprOps.py b/lib/core/Handles/HandlesExprOps.py index a46eeee..f06e36a 100644 --- a/lib/core/Handles/HandlesExprOps.py +++ b/lib/core/Handles/HandlesExprOps.py @@ -29,6 +29,15 @@ class ExprOpsHandle(BaseHandle): '&': ['__and__'], '|': ['__or__'], '^': ['__xor__'], '<<': ['__lshift__'], '>>': ['__rshift__'], } + # 反向运算符:左操作数无对应 dunder 时,调度右操作数的 __rX__ + # (Python 语义:a + b → 先 a.__add__(b),缺失再 b.__radd__(a)) + REVERSE_OVERLOAD_MAP: dict[str, list[str]] = { + '+': ['__radd__'], '-': ['__rsub__'], '*': ['__rmul__'], + '/': ['__rtruediv__'], '//': ['__rfloordiv__'], '%': ['__rmod__'], + '**': ['__rpow__'], + '&': ['__rand__'], '|': ['__ror__'], '^': ['__rxor__'], + '<<': ['__rlshift__'], '>>': ['__rrshift__'], + } if Op in OP_OVERLOAD_MAP: LeftClassName: str | None = None if isinstance(LeftVal.type, ir.PointerType): @@ -55,6 +64,32 @@ class ExprOpsHandle(BaseHandle): result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal) if result is not None: return result + # 反向运算符重载:左操作数无对应 dunder 时调度右操作数的 __rX__(左值) + if Op in REVERSE_OVERLOAD_MAP: + RightClassName: str | None = None + if isinstance(RightVal.type, ir.PointerType): + pointee_r: ir.Type = RightVal.type.pointee + if isinstance(pointee_r, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee_r == ST: + RightClassName = CN + break + elif isinstance(pointee_r, ir.IntType) and pointee_r.width == 8: + RightClassName = self.Trans.ExprUtils._get_var_class(Node.right, Gen) + if RightClassName and RightClassName in Gen.structs: + TargetStructPtrR: ir.PointerType = ir.PointerType(Gen.structs[RightClassName]) + RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}") + if RightClassName is None and isinstance(Node.right, ast.Name): + RightClassName = Gen.var_struct_class.get(Node.right.id) + if RightClassName and RightClassName in Gen.structs: + if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8: + TargetStructPtrR = ir.PointerType(Gen.structs[RightClassName]) + RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}") + if RightClassName: + for op_name_r in REVERSE_OVERLOAD_MAP[Op]: + result_r: Any = self.Trans.ExprUtils._try_operator_overLoad(RightClassName, op_name_r, RightVal, Gen, LeftVal) + if result_r is not None: + return result_r if Op in ('+', 'Add') and isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: if isinstance(Node.left, ast.Constant) and isinstance(Node.left.value, str) and len(Node.left.value) == 1: @@ -119,12 +154,10 @@ class ExprOpsHandle(BaseHandle): def _HandleBoolOpLlvm(self, Node: ast.BoolOp) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen - def _to_bool(val: Any, name: str) -> Any: - if isinstance(val.type, (ir.FloatType, ir.DoubleType)): - return Gen.builder.fcmp_ordered('!=', val, ir.Constant(val.type, 0.0), name=name) - if not isinstance(val.type, ir.IntType) or val.type.width != 1: - return Gen.builder.icmp_signed('!=', val, Gen._ZeroConst(val.type), name=name) - return val + def _to_bool(val: Any, node: ast.expr) -> Any: + if isinstance(val.type, ir.IntType) and val.type.width == 1: + return val + return self._CoerceCondToBool(Gen, node, val) values: list[ast.expr] = Node.values if len(values) < 2: @@ -142,7 +175,7 @@ class ExprOpsHandle(BaseHandle): Gen.builder.position_at_end(saved_block) return None if not isinstance(val.type, ir.IntType) or val.type.width != 1: - val = _to_bool(val, f"andcond{idx}") + val = _to_bool(val, values[idx]) cur_bb: ir.Block = Gen.builder.block next_bb: ir.Block = Gen.func.append_basic_block(name=f"and.rhs{idx}") Gen.builder.cbranch(val, next_bb, end_bb) @@ -152,7 +185,7 @@ class ExprOpsHandle(BaseHandle): if not last_val: last_val = ir.Constant(ir.IntType(1), 0) if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1: - last_val = _to_bool(last_val, "andcond_last") + last_val = _to_bool(last_val, values[-1]) last_bb: ir.Block = Gen.builder.block Gen.builder.branch(end_bb) Gen.builder.position_at_start(end_bb) @@ -174,7 +207,7 @@ class ExprOpsHandle(BaseHandle): Gen.builder.position_at_end(saved_block) return None if not isinstance(val.type, ir.IntType) or val.type.width != 1: - val = _to_bool(val, f"orcond{idx}") + val = _to_bool(val, values[idx]) cur_bb = Gen.builder.block next_bb = Gen.func.append_basic_block(name=f"or.rhs{idx}") Gen.builder.cbranch(val, end_bb, next_bb) @@ -184,7 +217,7 @@ class ExprOpsHandle(BaseHandle): if not last_val: last_val = ir.Constant(ir.IntType(1), 1) if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1: - last_val = _to_bool(last_val, "orcond_last") + last_val = _to_bool(last_val, values[-1]) last_bb = Gen.builder.block Gen.builder.branch(end_bb) Gen.builder.position_at_start(end_bb) @@ -234,13 +267,9 @@ class ExprOpsHandle(BaseHandle): if result is not None: return result if OpSymbol == 'not': - if isinstance(OperandVal.type, ir.IntType) and OperandVal.type.width == 1: - return Gen.builder.not_(OperandVal, name="not_result") - if isinstance(OperandVal.type, ir.PointerType): - null_val: ir.Constant = ir.Constant(OperandVal.type, None) - return Gen.builder.icmp_signed('==', OperandVal, null_val, name="not_result") - zero: ir.Constant = ir.Constant(OperandVal.type, 0) - return Gen.builder.icmp_signed('==', OperandVal, zero, name="not_result") + # not x = 逻辑取反(优先调度 __bool__,与 TPV 对齐) + bool_val: Any = self._CoerceCondToBool(Gen, Node.operand, OperandVal) + return Gen.builder.not_(bool_val, name="not_result") elif OpSymbol == '~': if isinstance(OperandVal.type, ir.IntType): mask: int = (1 << OperandVal.type.width) - 1 diff --git a/lib/core/Handles/HandlesIf.py b/lib/core/Handles/HandlesIf.py index 42d4a3c..0158434 100644 --- a/lib/core/Handles/HandlesIf.py +++ b/lib/core/Handles/HandlesIf.py @@ -181,19 +181,7 @@ class IfHandle(BaseHandle): self.Trans.BodyHandler.HandleBodyLlvm(Node.orelse) return if not isinstance(Cond.type, ir.IntType) or Cond.type.width != 1: - ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.test, Gen) - if ClassName and Gen._has_function(f'{ClassName}.__bool__'): - obj_val: ir.Value = Cond - if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: - obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - Cond = Gen.builder.call(Gen._get_function(f'{ClassName}.__bool__'), [obj_val], name=f"call_{ClassName}.__bool__") - if isinstance(Cond.type, ir.IntType) and Cond.type.width != 1: - Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="bool_result") - else: - if isinstance(Cond.type, (ir.FloatType, ir.DoubleType)): - Cond = Gen.builder.fcmp_ordered('!=', Cond, ir.Constant(Cond.type, 0.0), name="ifcond") - else: - Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="ifcond") + Cond = self._CoerceCondToBool(Gen, Node.test, Cond) ThenBB: ir.Block = Gen.func.append_basic_block(name="then") ElseBB: ir.Block | None = Gen.func.append_basic_block(name="else") if Node.orelse else None MergeBB: ir.Block = Gen.func.append_basic_block(name="endif") diff --git a/lib/core/Handles/HandlesWhile.py b/lib/core/Handles/HandlesWhile.py index 4c00686..2c2d551 100644 --- a/lib/core/Handles/HandlesWhile.py +++ b/lib/core/Handles/HandlesWhile.py @@ -30,10 +30,7 @@ class WhileHandle(BaseHandle): Cond: ir.Value | None = self.HandleExprLlvm(Node.test) if Cond: if not isinstance(Cond.type, ir.IntType) or Cond.type.width != 1: - if isinstance(Cond.type, (ir.FloatType, ir.DoubleType)): - Cond = Gen.builder.fcmp_ordered('!=', Cond, ir.Constant(Cond.type, 0.0), name="whilecond") - else: - Cond = Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="whilecond") + Cond = self._CoerceCondToBool(Gen, Node.test, Cond) if not Gen.builder.block.is_terminated: Gen.builder.cbranch(Cond, BodyBB, ElseBB if ElseBB else AfterBB) else: