feat(dunder): 补齐 __bool__ 条件真值调度与反向运算符/位运算增量重载

- 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__)
This commit is contained in:
TermiNexus committed 2026-10-03 00:39:53 +08:00
1 parent 2f70bd2733
commit 65f0e87193
6 files changed
+87 -35

No files matched your search

+4
View File
@@ -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:
+34
View File
@@ -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 报文取证。
+1 -1
View File
@@ -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")
+46 -17
View File
@@ -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
+1 -13
View File
@@ -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")
+1 -4
View File
@@ -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: