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:
1 parent
da23160ca6
commit
f63dcd7fed
6 files changed
+225
-23
No files matched your search
@@ -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. 收集当前类的虚方法(跳过已从父类继承的)
|
||||
|
||||
@@ -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 的类型不匹配)
|
||||
|
||||
@@ -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() 调用
|
||||
#
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
# ============================================================
|
||||
# 翻译二元运算(自动类型提升)
|
||||
# ============================================================
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in new issue
Block a user