feat(dunder): 补齐 dunder 协议(__bool__/__call__/__str__/__abs__/__del__/**/增量/反向)

- __bool__:ExprGenOps.coerce_cond_to_bool 统一 if/while/and/or/not/三元/协程条件的真值归一,优先调度 __bool__

- __call__:obj(args) 分发到 obj.__call__(CallGenMixin 裸名分支)

- __str__:str()/print/f-string 调度(CallGenDispatch/CallGenLeafKit/ExprGenFstr)

- __abs__:abs(obj) 调度(CallGenMixin)

- __del__/__delete__:del obj → __delete__;del obj.attr 无 property deleter 时回退对象 __del__

- ** / __pow__:新增 llvm.pow 内联(整数按常量指数回写),_binop_to_dunder 补 Pow

- 增量赋值:__iX__ 优先、缺失回退 __X__(新增 _aug_op_to_dunder)

- 反向运算符:新增 _rbinop_to_dunder,lhs 无 dunder 时调度 rhs.__rX__
This commit is contained in:
TermiNexus committed 2026-10-03 00:40:00 +08:00
1 parent a3bb62f2c3
commit 4eec3f0908
9 files changed
+375 -110

No files matched your search

+71 -1
View File
@@ -1269,6 +1269,11 @@ novirtual class AssignGenMixin:
trans: HT.Translator* = None,
lhs_node: ast.AST* = None) -> llvmlite.Value*:
"""应用增强赋值的二元运算(浮点 + 指针算术 + 整数运算)"""
# `**=` 幂运算:基础类型 → llvm.pow(与 BinOp 共用 ExprGenOps.emit_pow)
if op == ast.OpKind.Pow:
if trans is None:
return None
return self.Gen.ExprGenOps.emit_pow(pool, builder, trans.Module, lhs, rhs, node, trans)
lhs_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(rhs.Ty)
lhs_fbits: int = self.Gen.ExprGenBase.get_llvm_float_bits(lhs.Ty)
@@ -1650,8 +1655,50 @@ novirtual class AssignGenMixin:
# fail-fast: RHS 翻译失败
self.Gen.TypeGenErr.fatal_error(target, "rhs 翻译失败", "TypeError")
# 3b. 运算符重载:对象变量 += → __iadd__;该类无 __iadd__ 时回退 __add__
# (对齐 TPC HandlesAugAssign._AUGOP_MAP/_AUGOP_FALLBACK)。
# 仅局部变量(与 TPC 同范围:全局/nonlocal 不调度增量 dunder)。
if scope_type == 0 and target_alloca is not None:
aug_dup: str = self._aug_op_to_dunder(aug.op)
if aug_dup is not None:
ovl_self_aug: llvmlite.Value* = target_alloca
# 指针存储槽(Ptr(Ptr(Struct))):load 出对象指针作为 self
if target_alloca.Ty is not None and target_alloca.Ty.Pointee != 0 \
and self.Gen.ExprGenBase.is_ptr_type(target_alloca.Ty.Pointee) != 0:
load_self_aug: llvmlite.Value* = llvmlite.build_load(builder, target_alloca.Ty.Pointee, target_alloca)
if load_self_aug is not None:
ovl_self_aug = load_self_aug
# 仅当目标是结构体(类实例)时才尝试增量 dunder,避免无谓的实参池分配
if self.Gen.StructGen.get_struct_type_from_value(ovl_self_aug) is not None:
aug_extra: sizet* = pool.alloc(8)
if aug_extra is not None:
aug_extra[0] = sizet(rhs_val)
aug_ovl: llvmlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, ovl_self_aug, aug_dup, trans, aug_extra, 1)
if aug_ovl is None:
aug_fb: str = self._op_to_dunder(aug.op)
if aug_fb is not None:
aug_ovl = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, ovl_self_aug, aug_fb, trans, aug_extra, 1)
if aug_ovl is not None:
aug_res: llvmlite.Value* = aug_ovl
if target_ty is not None and aug_res.Ty is not None \
and self.Gen.ExprGenBase._same_agg_ty(pool, aug_res.Ty, target_ty) == 0:
# 目标为结构体值、返回为指针 → load 出结构体值(按值存储语义)
if self.Gen.ExprGenBase.is_ptr_type(target_ty) == 0 \
and self.Gen.ExprGenBase.is_ptr_type(aug_res.Ty) != 0 \
and aug_res.Ty.Pointee != 0:
aug_res = llvmlite.build_load(builder, aug_res.Ty.Pointee, aug_res)
else:
coerced_aug: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, aug_res, target_ty, node, trans)
if coerced_aug is not None:
aug_res = coerced_aug
if aug_res is None or target_ty is None \
or self.Gen.ExprGenBase._same_agg_ty(pool, aug_res.Ty, target_ty) == 0:
# fail-fast:重载返回值与目标变量类型不匹配,禁止静默写坏内存
self.Gen.TypeGenErr.fatal_error(node, "增量赋值运算符重载返回值类型与目标变量不匹配", "TypeError")
llvmlite.build_store(builder, aug_res, target_alloca, vol_aa)
return 0
# 4. 类型对齐 + 应用二元运算
# 注意: AugAssign 不走运算符重载(语义上需要 __iadd__ 而非 __add__)
result: llvmlite.Value* = self._apply_aug_op(pool, builder, aug.op, cur_val, rhs_val, node, trans, target)
if result is None:
# fail-fast: 二元运算失败
@@ -3558,6 +3605,29 @@ novirtual class AssignGenMixin:
if op == ast.OpKind.Invert: return "__invert__"
return None
# ============================================================
# _aug_op_to_dunder - 增量运算符 → 增量 dunder 方法名(__iadd__ 等)
#
# 无对应增量 dunder 时返回 None;调用方回退普通 dunder(_op_to_dunder,
# 即 __iadd__ 缺失时用 __add__),与 TPC HandlesAugAssign._AUGOP_MAP /
# _AUGOP_FALLBACK 同口径。
# ============================================================
def _aug_op_to_dunder(self, op: int) -> str:
"""AugAssign 运算符 → 增量 dunder 方法名,无映射返回 None"""
if op == ast.OpKind.Add: return "__iadd__"
if op == ast.OpKind.Sub: return "__isub__"
if op == ast.OpKind.Mult: return "__imul__"
if op == ast.OpKind.Div: return "__itruediv__"
if op == ast.OpKind.FloorDiv: return "__ifloordiv__"
if op == ast.OpKind.Mod: return "__imod__"
if op == ast.OpKind.Pow: return "__ipow__"
if op == ast.OpKind.BitAnd: return "__iand__"
if op == ast.OpKind.BitOr: return "__ior__"
if op == ast.OpKind.BitXor: return "__ixor__"
if op == ast.OpKind.LShift: return "__ilshift__"
if op == ast.OpKind.RShift: return "__irshift__"
return None
# ============================================================
# _cls_has_dunder - 检查类(含继承链)是否存在 dunder 方法
#
+7 -18
View File
@@ -1235,7 +1235,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
cond: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ifn.test, None, 0, trans)
if cond is None:
return
cond1: llvmlite.Value* = self._normalize_cond(builder, pool, cond)
cond1: llvmlite.Value* = self._normalize_cond(builder, pool, mod, cond, trans)
func: llvmlite.Function* = trans._cur_func
then_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, "coro.then")
merge_bb: rawptr[llvmlite.BasicBlock] = llvmlite.create_block(pool, func, "coro.end")
@@ -1296,7 +1296,7 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
cond: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, whn.test, None, 0, trans)
if cond is None:
cond = llvmlite.const_int32(pool, 0)
cond1: llvmlite.Value* = self._normalize_cond(builder, pool, cond)
cond1: llvmlite.Value* = self._normalize_cond(builder, pool, mod, cond, trans)
llvmlite.build_cond_br(builder, cond1, body_bb, after_bb)
# 循环体
llvmlite.position_at_end(builder, body_bb)
@@ -1610,22 +1610,11 @@ novirtual class AsyncGenMixin(CallGenBase.CallGenBase):
# ============================================================
def _normalize_cond(self, builder: llvmlite.IRBuilder*,
pool: memhub.MemBuddy*,
cond: llvmlite.Value*) -> llvmlite.Value*:
if cond is None:
return None
ty: llvmlite.LLVMType* = cond.Ty
is_i1: int = 0
if ty is not None:
match ty:
case LLVMType.Int():
if ty.Bits == 1:
is_i1 = 1
case _:
pass
if is_i1 != 0:
return cond
zero: llvmlite.Value* = llvmlite.ConstZero(pool, ty)
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond, zero)
mod: llvmlite.LLVMModule*,
cond: llvmlite.Value*,
trans: HT.Translator*) -> llvmlite.Value*:
"""条件归一到 i1(协程 if/while 用):委托 coerce_cond_to_bool,优先调度 __bool__"""
return self.Gen.ExprGenOps.coerce_cond_to_bool(pool, builder, mod, cond, trans)
# ============================================================
# 参数名读取(_coro_params 表)
+5
View File
@@ -242,6 +242,11 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
# str(x) / bytes(x) → i8* (bitcast)
cast_arg: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans)
if cast_arg is not None:
# 对象定义了 __str__ → 调度 __str__(对齐 TPC HandlesExprBuiltin)
if string.strcmp(cast_fn_name, "str") == 0:
str_ov: llvmlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, cast_arg, "__str__", trans)
if str_ov is not None:
return str_ov
i8_ptr_ty_builtin: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int8(pool))
return self.Gen.CallGenCtor._translate_t_type_cast(pool, builder, cast_arg, i8_ptr_ty_builtin)
return None
+5
View File
@@ -916,6 +916,11 @@ novirtual class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin):
self.Gen.TypeGenErr.fatal_error(arg, "print 参数翻译失败(表达式返回 None)", "TypeError")
return
# 对象定义了 __str__ → 用其返回的字符串(i8*)按 %s 打印(对齐 TPC)
str_ov_pr: llvmlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, arg_val, "__str__", trans)
if str_ov_pr is not None:
arg_val = str_ov_pr
# 数组名(如 print(l),l: t.CArray[char, N]):
# translate_value 会 load 出数组值([N x T]),print 无法格式化;
# 按数组退化语义取数组 alloca 地址——字符数组 → %s(打印字符串内容),
+33
View File
@@ -744,6 +744,20 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
is_seg2_mod = 1
if is_seg2_cls != 0 or is_seg2_mod == 0:
found_func = None
# ---- abs(obj):对象定义了 __abs__ 时优先调度(对齐 TPC _HandleAbsLlvm)----
# 仅当实参是类实例(注解类名可解析)且该类(含 MRO 祖先)定义了 __abs__ 时
# 命中;否则保持既有内建/整数/浮点 abs 路径不变。
if found_func is None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name \
and string.strcmp(func_name, "abs") == 0 and can == 1:
abs_arg: ast.AST* = cargs.get(0)
if abs_arg is not None:
abs_cls: str = self.Gen.CallGenProp._resolve_obj_class_name(pool, trans, abs_arg)
if abs_cls is not None:
abs_sc: str = self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, abs_cls, "__abs__", trans)
if abs_sc is not None:
abs_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, abs_arg, None, 0, trans)
if abs_val is not None:
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, abs_sc, "__abs__", abs_val, None, 0, trans)
# C 库函数裸名调用支持:若模块中找不到且是已知 C 库函数,自动创建 declare
# 对应 TPC 的 _C_LIB_FUNCS 机制,支持 memset/memmove/memcmp/strcmp 等裸名调用
if found_func is None and cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
@@ -935,6 +949,25 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase):
else:
fp_alloca = self.Gen.VarGen.lookup_var(trans.SymTab, func_name)
if fp_alloca is not None:
# ---- 可调用对象 __call__:变量是类实例且类内定义了 __call__ ----
# obj(args) → obj.__call__(args)。仅当变量非闭包、非函数指针且其值
# 指向结构体、该类(含 MRO 祖先)定义了 __call__ 时命中;其余情况
# (get_struct_type_from_value 返回 None)保持既有闭包/函数指针路径。
if trans is not None \
and self.Gen.CallGenLeafKit.is_closure_name(func_name) == 0 \
and self.Gen.CallGenLeafKit._is_func_ptr_alloca(fp_alloca) == 0:
c_obj: llvmlite.Value* = self.Gen.ExprGenBase._deref_if_ptr_ptr(builder, fp_alloca)
if c_obj is None:
c_obj = fp_alloca
c_struct: llvmlite.LLVMType* = None
if c_obj is not None:
c_struct = self.Gen.StructGen.get_struct_type_from_value(c_obj)
if c_struct is not None:
c_cls: str = self.Gen.StructGen.get_class_name_by_type(pool, c_struct)
if c_cls is not None:
c_call_cls: str = self.Gen.ExprGenOps._resolve_dunder_by_class(pool, mod, c_cls, "__call__", trans)
if c_call_cls is not None:
return self.Gen.CallGenCall._translate_method_call(pool, builder, mod, c_call_cls, "__call__", c_obj, cl, trans)
# 闭包变量(嵌套函数提升,已注册到闭包名集合):提取 fn_ptr/env_ptr 间接调用
if self.Gen.CallGenLeafKit.is_closure_name(func_name) != 0:
return self.Gen.CallGenLeafKit.translate_closure_call(pool, builder, mod, fp_alloca,
+10
View File
@@ -71,6 +71,11 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
# fail-fast:f-string 格式化值翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
continue
# 对象定义了 __str__ 且未指定格式 → 用其返回的字符串(对齐 TPC)
if fv.format_spec is None:
str_ov_fs: llvmlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
if str_ov_fs is not None:
val = str_ov_fs
# 确定 C 格式说明符(format_spec 优先,否则按值类型推断)
c_fmt: str = None
if fv.format_spec is not None:
@@ -119,6 +124,11 @@ novirtual class ExprGenFstr(ExprGenBase.ExprGenBase):
# fail-fast:裸 FormattedValue 翻译失败必须终止编译,绝不在
self.Gen.TypeGenErr.fatal_error(fv.value, "f-string 格式化值翻译失败(表达式返回 None)", "TypeError")
else:
# 对象定义了 __str__ 且未指定格式 → 用其返回的字符串(对齐 TPC)
if fv.format_spec is None:
str_ov_sf: llvmlite.Value* = self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, val, "__str__", trans)
if str_ov_sf is not None:
val = str_ov_sf
c_fmt: str = None
if fv.format_spec is not None:
c_fmt = self._fstr_parse_format_spec(pool, fv.format_spec, val, fv.value)
+223 -65
View File
@@ -42,18 +42,8 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
# 1. 求值条件
cond_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, ie.test, None, 0, trans)
if cond_val is None: return None
# 转换为 i1
cond_bits: int = self.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value* = cond_val
else:
if self.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较
null_cond: llvmlite.Value* = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
# 转换为 i1(优先调度 __bool__)
cond_i1: llvmlite.Value* = self.coerce_cond_to_bool(pool, builder, mod, cond_val, trans)
# 2. 创建三个基本块
cnt: int = trans._label_counter
@@ -797,25 +787,12 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
# +x = x
return operand
elif uo.op == ast.OpKind.Not:
# not x = (x == 0/null),返回 i1
if self.is_ptr_type(operand.Ty) != 0:
# 指针类型:与 null 比较
null_op: llvmlite.Value* = llvmlite.ConstNull(pool, operand.Ty, "null")
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, null_op)
# 结构体值类型:永远为真(非空),not 返回 false
operand_is_struct_not: int = 0
match operand.Ty:
case llvmlite.LLVMType.Struct(_, _, _):
operand_is_struct_not = 1
case _:
operand_is_struct_not = 0
if operand_is_struct_not != 0:
return llvmlite.const_int(pool, 1, 0)
operand_bits_not: int = self.get_llvm_type_bits(operand.Ty)
zero = llvmlite.const_int32(pool, 0)
if operand_bits_not == 64:
zero = llvmlite.const_int64(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, operand, zero)
# not x = (bool(x) == 0),返回 i1;优先调度 __bool__
not_cond: llvmlite.Value* = self.coerce_cond_to_bool(pool, builder, mod, operand, trans)
if not_cond is None:
return None
true_val: llvmlite.Value* = llvmlite.const_int(pool, 1, 1)
return llvmlite.build_icmp(builder, llvmlite.ICMP_EQ, not_cond, true_val)
elif uo.op == ast.OpKind.Invert:
# ~x = x ^ -1
operand_bits_inv: int = self.get_llvm_type_bits(operand.Ty)
@@ -1007,17 +984,8 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if val is None:
return None
# 转为 i1(已经是 i1 的直接用,否则与 0/null 比较)
val_bits: int = self.get_llvm_type_bits(val.Ty)
val_i1: llvmlite.Value* = val
if val_bits != 1:
if self.is_ptr_type(val.Ty) != 0:
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
null_val: llvmlite.Value* = llvmlite.ConstNull(pool, val.Ty, "null")
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
else:
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
val_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
# 转为 i1(已是 i1 直接使用;否则走 coerce_cond_to_bool:优先 __bool__)
val_i1: llvmlite.Value* = self.coerce_cond_to_bool(pool, builder, mod, val, trans)
# 创建 next BB(求值下一个值)
cnt = builder.Counter
@@ -1049,16 +1017,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
last_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, last_node, funcs_ptr, func_count, trans)
if last_val is None:
return None
last_bits: int = self.get_llvm_type_bits(last_val.Ty)
last_i1: llvmlite.Value* = last_val
if last_bits != 1:
if self.is_ptr_type(last_val.Ty) != 0:
# 指针类型:与 null 比较(避免 i8* 与 i32 类型不匹配)
null_val2: llvmlite.Value* = llvmlite.ConstNull(pool, last_val.Ty, "null")
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, null_val2)
else:
zero = llvmlite.const_int32(pool, 0)
last_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, last_val, zero)
last_i1: llvmlite.Value* = self.coerce_cond_to_bool(pool, builder, mod, last_val, trans)
llvmlite.build_br(builder, merge_bb)
@@ -1075,6 +1034,81 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
i1_ty: llvmlite.LLVMType* = llvmlite.Int1(pool)
return llvmlite.build_phi(builder, i1_ty, phi_head, phi_count)
# ============================================================
# emit_pow - `**` 幂运算(基础类型)
#
# 对齐 TPC ExprGen.emit_binary_op 的 Pow 分支:
# 浮点(任一操作数浮点)→ llvm.pow(结果浮点,目标位宽取大者);
# 整数 → 提升到 double 计算 llvm.pow;若指数为非负整数常量,则
# 结果 fptosi 回写为左操作数类型(Python `int ** int = int`)。
# 自定义类型的 `**` 由重载路径(__pow__)先行处理,不进入此处。
# ============================================================
def emit_pow(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
lhs: llvmlite.Value*,
rhs: llvmlite.Value*,
node: ast.AST* = None,
trans: HT.Translator* = None) -> llvmlite.Value*:
"""翻译 `**` 幂运算(基础类型走 llvm.pow)"""
if lhs is None or rhs is None:
return None
lhs_fbits: int = self.get_llvm_float_bits(lhs.Ty)
rhs_fbits: int = self.get_llvm_float_bits(rhs.Ty)
f64_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
# 浮点路径:任一操作数为浮点 → 结果浮点
if lhs_fbits != 0 or rhs_fbits != 0:
target_f: llvmlite.LLVMType* = f64_ty
if lhs_fbits == 32 and rhs_fbits == 32:
target_f = llvmlite.Float(pool)
lf: llvmlite.Value* = self.coerce_to_type(builder, lhs, target_f, node, trans)
rf: llvmlite.Value* = self.coerce_to_type(builder, rhs, target_f, node, trans)
return self._call_llvm_pow(pool, builder, mod, lf, rf)
# 整数路径:提升到 double 计算
li: llvmlite.Value* = self.coerce_to_type(builder, lhs, f64_ty, node, trans)
ri: llvmlite.Value* = self.coerce_to_type(builder, rhs, f64_ty, node, trans)
res: llvmlite.Value* = self._call_llvm_pow(pool, builder, mod, li, ri)
if res is None:
return None
# 指数为非负整数常量 → fptosi 回写为左操作数类型(Python int ** int = int)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
if rhs_bits != 0 and rhs.IsConst == 1 and rhs.IntVal >= 0:
return self.coerce_to_type(builder, res, lhs.Ty, node, trans)
return res
def _call_llvm_pow(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
lf: llvmlite.Value*,
rf: llvmlite.Value*) -> llvmlite.Value*:
"""调用 llvm.pow.f64 内联函数;f32 结果再截断回 float"""
if lf is None or rf is None:
return None
f64_ty: llvmlite.LLVMType* = llvmlite.Double(pool)
pow_fn: llvmlite.Function* = self.Gen.CallGenBase.find_func_in_module(mod, "llvm.pow.f64")
if pow_fn is None:
pow_fn = llvmlite.create_declare(pool, mod, "llvm.pow.f64", f64_ty)
if pow_fn is not None:
llvmlite.add_param(pool, pow_fn, f64_ty, "x")
llvmlite.add_param(pool, pow_fn, f64_ty, "y")
lf_d: llvmlite.Value* = lf
if self.get_llvm_float_bits(lf.Ty) != 64:
lf_d = llvmlite.build_fpext(builder, lf, f64_ty)
rf_d: llvmlite.Value* = rf
if self.get_llvm_float_bits(rf.Ty) != 64:
rf_d = llvmlite.build_fpext(builder, rf, f64_ty)
if lf_d is None or rf_d is None:
return None
lf_d.Next = rf_d
rf_d.Next = None
res: llvmlite.Value* = llvmlite.build_call(builder, "llvm.pow.f64", lf_d, 2, f64_ty, 0)
if res is None:
return None
# f32 目标:llvm.pow 恒为 f64,结果截断回 float
if self.get_llvm_float_bits(lf.Ty) == 32 and self.get_llvm_float_bits(rf.Ty) == 32:
res = llvmlite.build_fptrunc(builder, res, llvmlite.Float(pool))
return res
# ============================================================
# BinOp 运算符 → dunder 方法名
# ============================================================
def _binop_to_dunder(self, op: int) -> str:
@@ -1087,6 +1121,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if op == ast.OpKind.Mult: return "__mul__"
if op == ast.OpKind.Div: return "__truediv__"
if op == ast.OpKind.Mod: return "__mod__"
if op == ast.OpKind.Pow: return "__pow__"
if op == ast.OpKind.BitAnd: return "__and__"
if op == ast.OpKind.BitOr: return "__or__"
if op == ast.OpKind.BitXor: return "__xor__"
@@ -1095,6 +1130,27 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if op == ast.OpKind.FloorDiv: return "__floordiv__"
return None
# ============================================================
# BinOp 运算符 → 反向 dunder 方法名(__radd__ 等)
#
# 左操作数无对应普通 dunder 时,调度右操作数的反向 dunder:
# a + b → a.__add__(b) 缺失时 b.__radd__(a)
# ============================================================
def _rbinop_to_dunder(self, op: int) -> str:
"""将 BinOp 运算符映射到反向 dunder 方法名,无映射时返回 None"""
if op == ast.OpKind.Add: return "__radd__"
if op == ast.OpKind.Sub: return "__rsub__"
if op == ast.OpKind.Mult: return "__rmul__"
if op == ast.OpKind.Div: return "__rtruediv__"
if op == ast.OpKind.FloorDiv: return "__rfloordiv__"
if op == ast.OpKind.Mod: return "__rmod__"
if op == ast.OpKind.Pow: return "__rpow__"
if op == ast.OpKind.BitAnd: return "__rand__"
if op == ast.OpKind.BitOr: return "__ror__"
if op == ast.OpKind.BitXor: return "__rxor__"
if op == ast.OpKind.LShift: return "__rlshift__"
if op == ast.OpKind.RShift: return "__rrshift__"
return None
# ============================================================
# Compare 运算符 → dunder 方法名
# ============================================================
def _cmpop_to_dunder(self, op: int) -> str:
@@ -1236,15 +1292,21 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
dunder: str = self._unaryop_to_dunder(op)
if dunder is None:
return None
return self._try_named_dunder(pool, builder, mod, operand, dunder, trans)
# 检查 operand 是否为结构体指针
struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(operand)
if struct_ty is None:
return None
def _resolve_dunder_by_class(self, pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
class_name: str,
dunder: str,
trans: HT.Translator*,
struct_ty: llvmlite.LLVMType* = None) -> str:
"""在 class_name(含 MRO 祖先)中解析 dunder 方法的定义类名。
# 获取类名
class_name: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
if class_name is None:
返回「实际定义该方法的类名」(search_class);该类(含祖先)未定义则返回
None。可选 struct_ty:提供时从其类型 entry 直接取 SHA1(跨模块类更可靠)。
跨模块类以依赖模块 vpi stub 为准,避免发出幽灵 call(链接 undefined)。
"""
if class_name is None or dunder is None:
return None
# 在继承链中查找 dunder 方法
@@ -1274,9 +1336,10 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
# 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入)
cls_sha1: str = None
uop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
if uop_entry is not None:
cls_sha1 = uop_entry.ModuleSha1
if struct_ty is not None:
uop_entry: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_by_type(struct_ty)
if uop_entry is not None:
cls_sha1 = uop_entry.ModuleSha1
if cls_sha1 is None:
cls_sha1 = self.Gen.StructGen.get_struct_sha1(search_class)
if cls_sha1 is None:
@@ -1288,7 +1351,7 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
cls_sha1 = self.Gen.StructGen.get_struct_sha1(anc_p)
mj += 1
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算
# 既无函数定义也无 SHA1 → 该类没有此 dunder 方法
if found_func is None and cls_sha1 is None:
return None
@@ -1297,9 +1360,86 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if found_func is None and trans is not None and trans.ModuleSha1 is not None:
if cls_sha1 is not None and string.strcmp(cls_sha1, trans.ModuleSha1) == 0:
return None
# 跨模块类:stub 可能未注入,方法存在性以依赖模块 vpi stub 为准。
# vpi 未命中 → 回退(不发幽灵 call,避免链接 undefined)
if cls_sha1 is not None:
vpi_dup: llvmlite.LLVMType* = self.Gen.CallGenBase._lookup_method_ret_ty_via_vpi(pool, cls_sha1, search_class, dunder)
if vpi_dup is None:
return None
return search_class
# 一元方法无额外参数(extra_args 为空)
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, search_class, dunder, operand, None, 0, trans)
def _try_named_dunder(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
operand: llvmlite.Value*,
dunder: str,
trans: HT.Translator*,
extra_args: sizet* = None,
extra_count: int = 0) -> llvmlite.Value*:
"""按名字在 operand 的类(含 MRO 祖先)中查找并调用 dunder 方法。
一元运算符(__neg__/__pos__/__invert__)、条件真值 __bool__ 与可调用
对象 __call__ 共用此查找/发射逻辑。extra_args/extra_count:附加实参
(__call__ 传入调用实参;一元/__bool__ 为空)。找不到方法返回 None。
"""
if operand is None or dunder is None:
return None
# 检查 operand 是否为结构体指针
struct_ty: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(operand)
if struct_ty is None:
return None
# 获取类名
class_name: str = self.Gen.StructGen.get_class_name_by_type(pool, struct_ty)
if class_name is None:
return None
search_class: str = self._resolve_dunder_by_class(pool, mod, class_name, dunder, trans, struct_ty)
if search_class is None:
return None
# 调用方法:operand.__dunder__(extra_args…)
return self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, search_class, dunder, operand, extra_args, extra_count, trans)
# ============================================================
# coerce_cond_to_bool - 条件真值归一到 i1(唯一实现)
#
# if / while / and / or / 三元条件的条件转换全部汇聚于此:
# 1. 已是 i1 → 原样返回;
# 2. 类实例定义了 __bool__ → 调用之,结果 != 0;
# 3. 指针 → != null;
# 4. 其他 → != 0。
# 语义唯一来源,避免各处各自演化出「struct 恒真」这类不一致判据。
# ============================================================
def coerce_cond_to_bool(self, pool: memhub.MemBuddy*,
builder: llvmlite.IRBuilder*,
mod: llvmlite.LLVMModule*,
val: llvmlite.Value*,
trans: HT.Translator*) -> llvmlite.Value*:
"""把条件值归一到 i1,优先调度 __bool__(类实例),否则与 0/null 比较"""
if val is None:
return None
bits: int = self.get_llvm_type_bits(val.Ty)
if bits == 1:
return val
# 类实例:优先调用 __bool__(指针指向结构体且类内定义了该方法)
bres: llvmlite.Value* = self._try_named_dunder(pool, builder, mod, val, "__bool__", trans)
if bres is not None:
b_bits: int = self.get_llvm_type_bits(bres.Ty)
if b_bits == 1:
return bres
if self.is_ptr_type(bres.Ty) != 0:
null_b: llvmlite.Value* = llvmlite.ConstNull(pool, bres.Ty, "null")
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, bres, null_b)
zero_b: llvmlite.Value* = llvmlite.const_int32(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, bres, zero_b)
if self.is_ptr_type(val.Ty) != 0:
# 指针类型:与 null 比较(不能用整数 0,否则 llc 报类型不匹配)
null_val: llvmlite.Value* = llvmlite.ConstNull(pool, val.Ty, "null")
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, null_val)
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, val, zero)
# ============================================================
# fail_if_double_ptr_binop - 双指针算术 fail-fast(**唯一实现**)
#
@@ -1432,6 +1572,24 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase):
if ovl_result2 is not None:
return ovl_result2
# === 运算符重载路径 3: 反向运算符 —— lhs 无对应 dunder 时调度 rhs.__rX__(lhs) ===
# (Python 语义:a + b → 先 a.__add__(b),缺失再 b.__radd__(a))
# 先判 rhs 是否为结构体(非结构体直接跳过,避免无谓的实参池分配)
r_dunder: str = self._rbinop_to_dunder(op)
if r_dunder is not None:
if self.Gen.StructGen.get_struct_type_from_value(rhs) is not None:
rev_extra: sizet* = pool.alloc(8)
if rev_extra is not None:
rev_extra[0] = sizet(lhs)
r_ovl: llvmlite.Value* = self._try_named_dunder(pool, builder, mod, rhs, r_dunder, trans, rev_extra, 1)
if r_ovl is not None:
return r_ovl
# `**` 幂运算:基础类型 → llvm.pow(整数结果按指数常量回写)。
# 必须在浮点/整数分流之前处理,且已在重载路径之后(自定义类型走 __pow__)。
if op == ast.OpKind.Pow:
return self.emit_pow(pool, builder, mod, lhs, rhs, node, trans)
# 获取操作数类型信息
lhs_bits: int = self.get_llvm_type_bits(lhs.Ty)
rhs_bits: int = self.get_llvm_type_bits(rhs.Ty)
+4 -23
View File
@@ -745,21 +745,11 @@ novirtual class StmtGenCifMixin:
cond_val: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, trans.Module, if_node.test,
trans._funcs, trans._func_count, trans)
# 2. 转换为 i1 条件
# Compare/Not 表达式已返回 i1,直接使用;指针类型与 null 比较;其他类型与 0 比较
# 2. 转换为 i1 条件(优先调度 __bool__;Compare/Not 已返回 i1)
if cond_val is None:
# fail-fast: if 条件表达式翻译失败
self.Gen.TypeGenErr.fatal_error(if_node.test, "if 条件表达式翻译失败", "TypeError")
cond_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value* = cond_val
elif self.Gen.ExprGenBase.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较(不能用整数 0,否则 llc 报 "integer constant must have integer type")
null_cond: llvmlite.Value* = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
cond_i1: llvmlite.Value* = self.Gen.ExprGenOps.coerce_cond_to_bool(pool, builder, trans.Module, cond_val, trans)
# 3. 创建基本块(使用 trans._label_counter 生成唯一标签名,不与 SSA 名共享)
cnt: int = trans._label_counter
@@ -961,17 +951,8 @@ novirtual class StmtGenCifMixin:
# fail-fast: while 条件表达式翻译失败
self.Gen.TypeGenErr.fatal_error(while_node.test, "while 条件表达式翻译失败", "TypeError")
# Compare/Not 表达式已返回 i1,直接使用;指针类型与 null 比较;其他类型与 0 比较
cond_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(cond_val.Ty)
if cond_bits == 1:
cond_i1: llvmlite.Value* = cond_val
elif self.Gen.ExprGenBase.is_ptr_type(cond_val.Ty) != 0:
# 指针类型:与 null 比较(不能用整数 0,否则 llc 报 "integer constant must have integer type")
null_cond: llvmlite.Value* = llvmlite.ConstNull(pool, cond_val.Ty, "null")
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, null_cond)
else:
zero: llvmlite.Value* = llvmlite.const_int32(pool, 0)
cond_i1 = llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cond_val, zero)
# 转换为 i1(优先调度 __bool__;Compare/Not 已返回 i1)
cond_i1: llvmlite.Value* = self.Gen.ExprGenOps.coerce_cond_to_bool(pool, builder, trans.Module, cond_val, trans)
llvmlite.build_cond_br(builder, cond_i1, body_bb, else_bb_while if else_bb_while is not None else end_bb)
# 4. body 块: 翻译循环体,跳回 cond
+17 -3
View File
@@ -124,11 +124,25 @@ novirtual class StmtGenMixin(StmtGenDeclMixin.StmtGenDeclMixin):
tgt: ast.AST* = dnode.targets.get(ti)
if tgt is None:
continue
# del obj.attr → property deleter
if tgt.kind() == ast.ASTKind.Attribute:
# del obj → __delete__(对齐 TPC HandlesDelete.py:Name 目标调用 __delete__,
# 类未定义 __delete__ 时静默无操作)
if tgt.kind() == ast.ASTKind.Name:
del_obj: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, tgt, None, 0, trans)
if del_obj is not None:
del_st_nm: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type_from_value(del_obj)
if del_st_nm is not None:
del_cls_nm: str = self.Gen.StructGen.get_class_name_by_type(pool, del_st_nm)
if del_cls_nm is not None:
self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, del_obj, "__delete__", trans)
# del obj.attr → property deleter;无 deleter 时回退对象 __del__(对齐 TPC)
elif tgt.kind() == ast.ASTKind.Attribute:
del_at: ast.Attribute* = ast.Attribute*(tgt)
if del_at is not None:
self.Gen.CallGenProp._try_property_deleter(pool, builder, mod, del_at, trans)
del_prop: int = self.Gen.CallGenProp._try_property_deleter(pool, builder, mod, del_at, trans)
if del_prop == 0:
del_av: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, del_at, None, 0, trans)
if del_av is not None:
self.Gen.ExprGenOps._try_named_dunder(pool, builder, mod, del_av, "__del__", trans)
# del obj[key] → __delitem__(obj, key)(对齐 TPC HandlesDelete.py)。
# 不能静默忽略 Subscript 目标——否则 del 不生效(len 不变)。
elif tgt.kind() == ast.ASTKind.Subscript: