From f63dcd7fedd05a5e9d4967b7825a1adc01546962 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sun, 9 Aug 2026 22:19:21 +0800 Subject: [PATCH] =?UTF-8?q?fix(handles):=20=E5=AF=B9=E8=B1=A1=E5=8C=96?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E6=9C=80=E5=90=8E=E7=A8=B3=E5=AE=9A=E7=89=88?= =?UTF-8?q?=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 t.CVoid(x, CPtr) 裸名 CPtr 识别失败导致方法体 ret null(scale/move/__new__/take_damage 链式调用对 NULL 解引用崩溃) - c.Addr(self) 返回对象指针(load 一层 Ptr(Struct)), 与 c.Addr(self.field) 语义一致 - return 语句指针→指针元素类型不匹配(i8* → 类指针)自动 bitcast, 避免 llc 类型不匹配 - 多层继承 vtable 槽沿父类链用 find_func_global 定位实际定义类(修复 ElectricCar.is_running=null) - 新增一元运算符重载(__neg__/__pos__/__invert__) - 新增表达式接收者方法调用 (expr).method(args) - map_t_type 补 CFloat8/16/32/64/128T 精确宽度浮点映射 --- App/lib/core/Handles/HandlesClassDef.vp | 44 ++++++++++--- App/lib/core/Handles/HandlesExpr.vp | 8 +++ App/lib/core/Handles/HandlesExprCall.vp | 83 +++++++++++++++++++---- App/lib/core/Handles/HandlesExprOps.vp | 87 +++++++++++++++++++++++++ App/lib/core/Handles/HandlesReturn.vp | 15 +++++ App/lib/core/Handles/HandlesType.vp | 11 ++++ 6 files changed, 225 insertions(+), 23 deletions(-) diff --git a/App/lib/core/Handles/HandlesClassDef.vp b/App/lib/core/Handles/HandlesClassDef.vp index f925092..d178b36 100644 --- a/App/lib/core/Handles/HandlesClassDef.vp +++ b/App/lib/core/Handles/HandlesClassDef.vp @@ -3035,6 +3035,8 @@ def _collect_vtable_methods(trans: HT.Translator | t.CPtr, pool: memhub.MemBuddy | t.CPtr = trans.Pool mod: llvmlite.LLVMModule | t.CPtr = trans.Module + # 懒导入(find_func_global 探测方法实际定义类用) + import lib.core.Handles.HandlesExprCall as HandlesExprCall children: list[ast.AST | t.CPtr] | t.CPtr = cd.children if children is None: @@ -3107,13 +3109,33 @@ def _collect_vtable_methods(trans: HT.Translator | t.CPtr, break # 构造 full_name # 子类覆盖且未标记 NoVTable → 用子类实现 - # 子类未覆盖 或 子类覆盖但标记 NoVTable → 用父类实现(保持 vtable 槽位) + # 子类未覆盖 或 子类覆盖但标记 NoVTable → 用实际定义类实现(保持 vtable 槽位) + # + # 多层继承修复: 直接父类可能只是继承而非定义该方法 + # (如 ElectricCar 继承 Car 的 is_running 实际定义在 Vehicle), + # 沿父类链向上用 find_func_global("类名.方法名") 探测实际定义类, + # 避免生成 "Car.is_running" 这类找不到函数的 full_name → vtable 槽变 null。 + def_class: str = class_name + if child_overrides == 1 and child_nvt == 0: + def_class = class_name + else: + def_class = parent_name + probe_class: str = parent_name + while probe_class is not None: + probe_buf: t.CChar | t.CPtr = pool.alloc(128) + if probe_buf is not None: + viperlib.snprintf(probe_buf, 128, "%s.%s", probe_class, pmethod_name) + if HandlesExprCall.find_func_global(probe_buf) is not None: + def_class = probe_class + break + probe_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( + probe_class) + if probe_entry is None or probe_entry.ParentName is None: + break + probe_class = probe_entry.ParentName pfname_buf: t.CChar | t.CPtr = pool.alloc(128) if pfname_buf is not None: - if child_overrides == 1 and child_nvt == 0: - viperlib.snprintf(pfname_buf, 128, "%s.%s", class_name, pmethod_name) - else: - viperlib.snprintf(pfname_buf, 128, "%s.%s", parent_name, pmethod_name) + viperlib.snprintf(pfname_buf, 128, "%s.%s", def_class, pmethod_name) full_names_buf[method_count] = t.CSizeT(pfname_buf) # 复制方法名(短名) pmname_len: t.CSizeT = string.strlen(pmethod_name) @@ -3121,14 +3143,18 @@ def _collect_vtable_methods(trans: HT.Translator | t.CPtr, if pmname_buf is not None: string.strcpy(pmname_buf, pmethod_name) method_names_buf[method_count] = t.CSizeT(pmname_buf) - # 记录 mangling 用的 SHA1(继承未覆盖的方法用父模块 SHA1) + # 记录 mangling 用的 SHA1(继承未覆盖的方法用实际定义类的模块 SHA1) if child_overrides == 1 and child_nvt == 0: # 子类覆盖: 用当前模块 SHA1(保持 0) method_parent_sha1s[method_count] = 0 else: - # 继承未覆盖: 用父类模块 SHA1 - if vt_parent_entry is not None and vt_parent_entry.ModuleSha1 is not None: - method_parent_sha1s[method_count] = t.CSizeT(vt_parent_entry.ModuleSha1) + # 继承未覆盖: 用实际定义类的模块 SHA1(跨模块继承时 mangling 正确) + def_entry2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct( + def_class) + if def_entry2 is not None and def_entry2.ModuleSha1 is not None: + method_parent_sha1s[method_count] = t.CSizeT(def_entry2.ModuleSha1) + else: + method_parent_sha1s[method_count] = 0 method_count += 1 # 2. 收集当前类的虚方法(跳过已从父类继承的) diff --git a/App/lib/core/Handles/HandlesExpr.vp b/App/lib/core/Handles/HandlesExpr.vp index 7bad67b..ad5f884 100644 --- a/App/lib/core/Handles/HandlesExpr.vp +++ b/App/lib/core/Handles/HandlesExpr.vp @@ -1366,6 +1366,14 @@ def translate_unaryop(builder: llvmlite.IRBuilder | t.CPtr, if operand is None: return None + # === 一元运算符重载: -obj/+obj/~obj 调用 __neg__/__pos__/__invert__ === + # 仅 USub/UAdd/Invert 有重载语义(not 无 dunder 方法) + if uo.op == ast.OpKind.USub or uo.op == ast.OpKind.UAdd or uo.op == ast.OpKind.Invert: + ovl_u: llvmlite.Value | t.CPtr = HandlesExprOps.try_unary_operator_overload( + pool, builder, mod, operand, uo.op, trans) + if ovl_u is not None: + return ovl_u + if uo.op == ast.OpKind.USub: # -x # 浮点类型用 fneg(直接生成 fneg 指令,避免 0.0 - x 的类型不匹配) diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index f6b937e..f4e0e9c 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -881,6 +881,25 @@ def translate_c_addr(pool: memhub.MemBuddy | t.CPtr, nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arg0) if nm.id is None: return None + if trans is not None: + # self/cls 对象引用:c.Addr(self) 返回对象指针本身(Ptr(Struct)), + # 而非指针变量的地址(Ptr(Ptr(Struct)))。方法与 Attribute 分支 + # (c.Addr(self.field) 先 load self)语义一致,供 + # return t.CVoid(c.Addr(self), CPtr) 返回 this 使用。 + if string.strcmp(nm.id, "self") == 0 or string.strcmp(nm.id, "cls") == 0: + self_entry: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, nm.id) + if self_entry is not None and self_entry.Alloca is not None and self_entry.Alloca.Ty is not None: + match self_entry.Alloca.Ty: + case llvmlite.LLVMType.Ptr(inner1): + match inner1: + case llvmlite.LLVMType.Ptr(inner2): + obj_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, inner2) + return llvmlite.build_load(builder, obj_ty, self_entry.Alloca) + case _: + pass + case _: + pass return HandlesVar.lookup_var(trans.SymTab, nm.id) # Attribute 形式: c.Addr(self.field) → 取字段地址(GEP 到字段,不 load) @@ -4620,20 +4639,30 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, # 注意: void* 在 LLVM 中非法,CVoid* 应表示为 i8* if can == 2: tc_arg2_node: ast.AST | t.CPtr = cargs.get(1) - if tc_arg2_node is not None and tc_arg2_node.kind() == ast.ASTKind.Attribute: - tc_arg2_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(tc_arg2_node) - if tc_arg2_at.attr is not None and string.strcmp(tc_arg2_at.attr, "CPtr") == 0: - if tc_arg2_at.value is not None and tc_arg2_at.value.kind() == ast.ASTKind.Name: - tc_arg2_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tc_arg2_at.value) - if tc_arg2_nm.id is not None and string.strcmp(tc_arg2_nm.id, "t") == 0: - # CVoid* → i8*(LLVM 中 void* 表示为 i8*) - match target_ty: - case llvmlite.LLVMType.Void(): - target_ty = llvmlite.Int8(pool) - case _: - pass - target_ty = llvmlite.Ptr(pool, target_ty) - tc_effective_can = 1 + tc_arg2_is_cptr: int = 0 + if tc_arg2_node is not None: + # 形式 1: t.CPtr(Attribute 形式) + if tc_arg2_node.kind() == ast.ASTKind.Attribute: + tc_arg2_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(tc_arg2_node) + if tc_arg2_at.attr is not None and string.strcmp(tc_arg2_at.attr, "CPtr") == 0: + if tc_arg2_at.value is not None and tc_arg2_at.value.kind() == ast.ASTKind.Name: + tc_arg2_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tc_arg2_at.value) + if tc_arg2_nm.id is not None and string.strcmp(tc_arg2_nm.id, "t") == 0: + tc_arg2_is_cptr = 1 + # 形式 2: 裸名 CPtr(from t import CPtr 后直接使用,如 t.CVoid(x, CPtr)) + elif tc_arg2_node.kind() == ast.ASTKind.Name: + tc_arg2_bn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tc_arg2_node) + if tc_arg2_bn.id is not None and string.strcmp(tc_arg2_bn.id, "CPtr") == 0: + tc_arg2_is_cptr = 1 + if tc_arg2_is_cptr != 0: + # CVoid* → i8*(LLVM 中 void* 表示为 i8*) + match target_ty: + case llvmlite.LLVMType.Void(): + target_ty = llvmlite.Int8(pool) + case _: + pass + target_ty = llvmlite.Ptr(pool, target_ty) + tc_effective_can = 1 if tc_effective_can == 1: tc_arg_node: ast.AST | t.CPtr = cargs.get(0) if tc_arg_node is not None: @@ -4997,6 +5026,32 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, _ensure_method_declare(pool, mod, mangled3, ret_ty3, head3, total3) return llvmlite.build_call(builder, mangled3, head3, total3, ret_ty3, 0) + # ============================================================ + # 检测表达式接收者方法调用: (expr).method(args) + # + # 当 method_at.value 是 BinOp/Subscript/Call 等非 Name/Attribute 表达式时 + # (如 (a + b).dot(c)、(v1 + v2) * 3.0),先 translate_value 求值接收者, + # 再从值的 LLVM 类型(Ptr(Struct))解析类名,走方法调用路径。 + # ============================================================ + if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + expr_method_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if expr_method_at is not None and expr_method_at.value is not None and trans is not None: + evk: int = expr_method_at.value.kind() + if evk != ast.ASTKind.Name and evk != ast.ASTKind.Attribute: + # 翻译接收者表达式(求值结果应为 Ptr(Struct)) + expr_obj: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, expr_method_at.value, funcs_ptr, func_count, trans) + if expr_obj is not None and expr_obj.Ty is not None: + expr_cls_nm: str = None + match expr_obj.Ty: + case llvmlite.LLVMType.Ptr(expr_struct_ty): + expr_cls_nm = HandlesStruct.get_class_name_by_type( + pool, expr_struct_ty) + if expr_cls_nm is not None: + return _translate_method_call( + pool, builder, mod, expr_cls_nm, func_name, + expr_obj, cl, trans) + # ============================================================ # 检测 self.field.method() 调用 # diff --git a/App/lib/core/Handles/HandlesExprOps.vp b/App/lib/core/Handles/HandlesExprOps.vp index d186445..31e14d8 100644 --- a/App/lib/core/Handles/HandlesExprOps.vp +++ b/App/lib/core/Handles/HandlesExprOps.vp @@ -49,6 +49,14 @@ def _cmpop_to_dunder(op: int) -> str: if op == ast.OpKind.Ge: return "__ge__" return None +# UnaryOp 运算符 → dunder 方法名 +def _unaryop_to_dunder(op: int) -> str: + """将 UnaryOp 运算符映射到 dunder 方法名,无映射时返回 None""" + if op == ast.OpKind.USub: return "__neg__" + if op == ast.OpKind.UAdd: return "__pos__" + if op == ast.OpKind.Invert: return "__invert__" + return None + # ============================================================ # try_operator_overload - 尝试运算符重载 @@ -156,6 +164,85 @@ def try_operator_overload(pool: memhub.MemBuddy | t.CPtr, lhs, extra_args, 1, trans) +# ============================================================ +# try_unary_operator_overload - 尝试一元运算符重载 +# +# 检查 operand 是否为结构体指针,并在该类(含继承链)中查找对应的 +# dunder 方法(__neg__/__pos__/__invert__)。找到则生成方法调用 +# operand.__dunder__(),返回调用结果;未找到返回 None,调用方回退到原生运算。 +# ============================================================ +def try_unary_operator_overload(pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + operand: llvmlite.Value | t.CPtr, + op: int, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """尝试一元运算符重载:operand 是结构体指针时调用 dunder 方法,否则返回 None""" + if operand is None: + return None + + # 延迟导入避免循环依赖 + import lib.core.Handles.HandlesStruct as HandlesStruct + import lib.core.Handles.HandlesExprCall as HandlesExprCall + + dunder: str = _unaryop_to_dunder(op) + if dunder is None: + return None + + # 检查 operand 是否为结构体指针 + struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type_from_value(operand) + if struct_ty is None: + return None + + # 获取类名 + class_name: str = HandlesStruct.get_class_name_by_type(pool, struct_ty) + if class_name is None: + return None + + # 在继承链中查找 dunder 方法 + lookup_name: t.CChar | t.CPtr = pool.alloc(128) + if lookup_name is None: + return None + viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder) + found_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, lookup_name) + + search_class: str = class_name + if found_func is None: + cur_parent: str = HandlesStruct.get_parent_name(class_name) + while cur_parent is not None: + parent_lookup: t.CChar | t.CPtr = pool.alloc(128) + if parent_lookup is not None: + viperlib.snprintf(parent_lookup, 128, "%s.%s", cur_parent, dunder) + parent_func: llvmlite.Function | t.CPtr = HandlesExprCall.find_func_in_module(mod, parent_lookup) + if parent_func is not None: + found_func = parent_func + search_class = cur_parent + break + cur_parent = HandlesStruct.get_parent_name(cur_parent) + + # 方法不在当前模块中时,检查类(含父类)是否有 SHA1(stub 可能未注入) + cls_sha1: str = None + uop_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty) + if uop_entry is not None: + cls_sha1 = uop_entry.ModuleSha1 + if cls_sha1 is None: + cls_sha1 = HandlesStruct.get_struct_sha1(search_class) + if cls_sha1 is None: + cur_p: str = HandlesStruct.get_parent_name(search_class) + while cur_p is not None and cls_sha1 is None: + cls_sha1 = HandlesStruct.get_struct_sha1(cur_p) + cur_p = HandlesStruct.get_parent_name(cur_p) + + # 既无函数定义也无 SHA1 → 该类没有此 dunder 方法,返回 None 回退原生运算 + if found_func is None and cls_sha1 is None: + return None + + # 一元方法无额外参数(extra_args 为空) + return HandlesExprCall._call_method_on_ptr( + pool, builder, mod, search_class, dunder, + operand, None, 0, trans) + + # ============================================================ # 翻译二元运算(自动类型提升) # ============================================================ diff --git a/App/lib/core/Handles/HandlesReturn.vp b/App/lib/core/Handles/HandlesReturn.vp index ce08fbc..0e56e82 100644 --- a/App/lib/core/Handles/HandlesReturn.vp +++ b/App/lib/core/Handles/HandlesReturn.vp @@ -114,6 +114,21 @@ class ReturnHandle(HandlesBase.Mixin): # ptr 类型不一致性:val 是整数但 ret 是 ptr(或反之),必须转换 if val_is_ptr != ret_is_ptr: val = HandlesExpr.coerce_to_type(builder, val, ret_ty_rt, node) + elif val_is_ptr != 0 and ret_is_ptr != 0: + # 指针 → 指针且元素类型不同(如 t.CVoid(x, CPtr) 得到 i8*, + # 函数返回类型是类指针 Circle* 时)需 bitcast 回, + # 否则 llc 报 "value doesn't match function result type"。 + val_pointee: llvmlite.LLVMType | t.CPtr = None + ret_pointee: llvmlite.LLVMType | t.CPtr = None + match val.Ty: + case llvmlite.LLVMType.Ptr(v_inner): + val_pointee = v_inner + match ret_ty_rt: + case llvmlite.LLVMType.Ptr(r_inner): + ret_pointee = r_inner + if val_pointee is not None and ret_pointee is not None: + if t.CUInt64T(val_pointee) != t.CUInt64T(ret_pointee): + val = HandlesExpr.coerce_to_type(builder, val, ret_ty_rt, node) elif val_bits != 0 and ret_bits != 0 and val_bits != ret_bits: if val_bits == 1 and val_bits < ret_bits: val = llvmlite.build_zext(builder, val, ret_ty_rt) diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index b0b64cf..fca4db4 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -1138,6 +1138,17 @@ def map_t_type(pool: memhub.MemBuddy | t.CPtr, return llvmlite.Float(pool) elif string.strcmp(type_name, "CDouble") == 0: return llvmlite.Double(pool) + # 精确宽度浮点类型(t.py 的 float_map: 8/16→half, 32→float, 64→double, 128→fp128) + elif string.strcmp(type_name, "CFloat8T") == 0: + return llvmlite.Half(pool) + elif string.strcmp(type_name, "CFloat16T") == 0: + return llvmlite.Half(pool) + elif string.strcmp(type_name, "CFloat32T") == 0: + return llvmlite.Float(pool) + elif string.strcmp(type_name, "CFloat64T") == 0: + return llvmlite.Double(pool) + elif string.strcmp(type_name, "CFloat128T") == 0: + return llvmlite.FP128(pool) # 指针/void: t.CPtr = i8*, t.CChar | t.CPtr = i8* = str = bytes elif string.strcmp(type_name, "CPtr") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool))