fix(handles): 对象化前的最后稳定版本

- 修复 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 精确宽度浮点映射
This commit is contained in:
TermiNexus committed 2026-08-09 22:19:21 +08:00
1 parent da23160ca6
commit f63dcd7fed
6 files changed
+225 -23

No files matched your search

+35 -9
View File
@@ -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. 收集当前类的虚方法(跳过已从父类继承的)
+8
View File
@@ -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 的类型不匹配)
+69 -14
View File
@@ -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() 调用
#
+87
View File
@@ -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)
# ============================================================
# 翻译二元运算(自动类型提升)
# ============================================================
+15
View File
@@ -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)
+11
View File
@@ -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))