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:
1 parent
2f70bd2733
commit
65f0e87193
6 files changed
+87
-35
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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 报文取证。
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user