From 4eec3f09089523ebf72709ee7c4485166f3c3270 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 3 Oct 2026 00:40:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(dunder):=20=E8=A1=A5=E9=BD=90=20dunder=20?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=EF=BC=88=5F=5Fbool=5F=5F/=5F=5Fcall=5F=5F/?= =?UTF-8?q?=5F=5Fstr=5F=5F/=5F=5Fabs=5F=5F/=5F=5Fdel=5F=5F/**/=E5=A2=9E?= =?UTF-8?q?=E9=87=8F/=E5=8F=8D=E5=90=91=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - __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__ --- App/lib/core/Gen/AssignGenMixin.vp | 72 +++++- App/lib/core/Gen/AsyncGenMixin.vp | 25 +- App/lib/core/Gen/Call/CallGenDispatch.vp | 5 + App/lib/core/Gen/Call/CallGenLeafKit.vp | 5 + App/lib/core/Gen/Call/CallGenMixin.vp | 33 +++ App/lib/core/Gen/Expr/ExprGenFstr.vp | 10 + App/lib/core/Gen/Expr/ExprGenOps.vp | 288 ++++++++++++++++++----- App/lib/core/Gen/Stmt/StmtGenCifMixin.vp | 27 +-- App/lib/core/Gen/Stmt/StmtGenMixin.vp | 20 +- 9 files changed, 375 insertions(+), 110 deletions(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index e207f10..948c2c5 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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 方法 # diff --git a/App/lib/core/Gen/AsyncGenMixin.vp b/App/lib/core/Gen/AsyncGenMixin.vp index 45b595d..82346be 100644 --- a/App/lib/core/Gen/AsyncGenMixin.vp +++ b/App/lib/core/Gen/AsyncGenMixin.vp @@ -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 表) diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index 1d42548..510e8de 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -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 diff --git a/App/lib/core/Gen/Call/CallGenLeafKit.vp b/App/lib/core/Gen/Call/CallGenLeafKit.vp index 9079ca5..4e8a340 100644 --- a/App/lib/core/Gen/Call/CallGenLeafKit.vp +++ b/App/lib/core/Gen/Call/CallGenLeafKit.vp @@ -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(打印字符串内容), diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 372144a..4fce92c 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -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, diff --git a/App/lib/core/Gen/Expr/ExprGenFstr.vp b/App/lib/core/Gen/Expr/ExprGenFstr.vp index 97f2e46..94e9340 100644 --- a/App/lib/core/Gen/Expr/ExprGenFstr.vp +++ b/App/lib/core/Gen/Expr/ExprGenFstr.vp @@ -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) diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index cba15c4..767d25f 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -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) diff --git a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp index ddbb44c..ec6ff63 100644 --- a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp @@ -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 diff --git a/App/lib/core/Gen/Stmt/StmtGenMixin.vp b/App/lib/core/Gen/Stmt/StmtGenMixin.vp index d605515..a006c61 100644 --- a/App/lib/core/Gen/Stmt/StmtGenMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenMixin.vp @@ -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: