fix(gen): 装饰器标记支持 + len 分发 + 跨模块调用修复
- _is_enum_class/_is_union_class/_is_renum_class 支持 @t.CEnum/@t.CUnion/ @t.REnum 装饰器形式(此前只认继承基类,装饰器形式 Color 未注册枚举, Color.GREEN 落到跨模块 CDefine FATAL) - forward_declare_functions/translate_function_def 支持 @t.CExport/@t.CExtern/ @t.State 函数存储装饰器(对齐 TPC _extract_decorator_storage_flags); 用 HandlesClassDef._has_decorator 委托(Mixin @t.NoVTable 静态绑定, self. 跨类调生成不存在的 FuncGenMixin._has_decorator → 链接 undefined) - len() 内建对结构体对象分发 __len__(此前恒 strlen 读对象首字段垃圾长度) - 静态调用检测 elif:module.ClassName.static_method 形态;零参 @staticmethod 去掉 head is None 守卫;_translate_property_call 按形参类型 coerce 实参 - translate_compare 指针判空(ptr != 0/== 0)跳过 __eq__/__ne__ 重载走 icmp null;同模块类无此 dunder 干净返回 None 防幽灵 call;/ → __truediv__
This commit is contained in:
1 parent
48d9fbab4c
commit
83d46dd646
4 files changed
+176
-18
No files matched your search
@@ -3206,6 +3206,34 @@ class CallGenMixin:
|
||||
else:
|
||||
call_ret_ty = self._fail_method_ret_ty(method_name)
|
||||
|
||||
# 参数类型对齐:按目标函数声明的参数类型转换实参(与 _translate_method_call
|
||||
# 一致)。例如 from_long(10) 的 i32 字面量 → i64 形参(v: t.CLong),
|
||||
# 不转换则 call(i32) 与 declare(i64) 不匹配,llc 报参数类型错误。
|
||||
# 目标函数优先用已找到的 found_func,否则按 mangled 调用名查模块 declare。
|
||||
tgt_func_pc: llvmlite.Function | t.CPtr = found_func
|
||||
if tgt_func_pc is None:
|
||||
tgt_func_pc = self.find_func_in_module(mod, call_name)
|
||||
if tgt_func_pc is not None:
|
||||
prev_pc: llvmlite.Value | t.CPtr = None
|
||||
cur_arg_pc: llvmlite.Value | t.CPtr = call_head
|
||||
cur_param_pc: llvmlite.Param | t.CPtr = llvmlite.function_get_param_head(tgt_func_pc)
|
||||
while cur_arg_pc is not None and cur_param_pc is not None:
|
||||
param_ty_pc: llvmlite.LLVMType | t.CPtr = llvmlite.param_get_ty(cur_param_pc)
|
||||
if param_ty_pc is not None and cur_arg_pc.Ty is not None:
|
||||
coerced_pc: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type(
|
||||
builder, cur_arg_pc, param_ty_pc)
|
||||
if coerced_pc is not None and coerced_pc != cur_arg_pc:
|
||||
# 保持链表完整性:新 Value 继承原节点的 Next,前驱指向新节点
|
||||
llvmlite.value_set_next(coerced_pc, cur_arg_pc.Next)
|
||||
if prev_pc is None:
|
||||
call_head = coerced_pc
|
||||
else:
|
||||
llvmlite.value_set_next(prev_pc, coerced_pc)
|
||||
cur_arg_pc = coerced_pc
|
||||
prev_pc = cur_arg_pc
|
||||
cur_arg_pc = cur_arg_pc.Next
|
||||
cur_param_pc = llvmlite.param_get_next(cur_param_pc)
|
||||
|
||||
# 确保 declare 存在,然后调用
|
||||
self._ensure_method_declare(pool, mod, call_name, call_ret_ty, call_head, call_count)
|
||||
return llvmlite.build_call(builder, call_name, call_head, call_count, call_ret_ty, 0)
|
||||
@@ -3413,9 +3441,11 @@ class CallGenMixin:
|
||||
tail = arg_val
|
||||
total_count += 1
|
||||
|
||||
if head is None:
|
||||
return None
|
||||
|
||||
# 零参 @staticmethod(如 PyListObj.new()):head 保持 None 是合法的
|
||||
# 无参调用,照常走 _translate_property_call(SHA1 前缀名 + 精确返回类型)。
|
||||
# 若此处 return None,调用方回退通用路径把方法名当裸函数解析,生成
|
||||
# call i32 @new() → 链接 undefined reference(CPythonTest 根因)。
|
||||
# @classmethod 恒有 cls_ptr(head 非 None),不受影响。
|
||||
return self._translate_property_call(pool, builder, mod, class_name,
|
||||
method_name, head, total_count, trans)
|
||||
|
||||
@@ -5137,7 +5167,7 @@ class CallGenMixin:
|
||||
if count_ptr is not None:
|
||||
return llvmlite.build_load(builder, i64_ty_len, count_ptr)
|
||||
|
||||
# 检测 len() 内置函数调用 — 转换为 strlen 调用
|
||||
# 检测 len() 内置函数调用 — 结构体对象走 __len__ 方法,字符串走 strlen
|
||||
# len(s) 对 str 类型调用 strlen,返回 i64
|
||||
if cl.func is not None and cl.func.kind() == ast.ASTKind.Name:
|
||||
if func_name is not None and string.strcmp(func_name, "len") == 0:
|
||||
@@ -5147,6 +5177,42 @@ class CallGenMixin:
|
||||
len_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||
builder, pool, mod, len_arg_node, None, 0, trans)
|
||||
if len_val is not None:
|
||||
# len(对象):结构体指针且类定义了 __len__ → 调用
|
||||
# Class.__len__(obj)(返回类型取方法定义,如 i64)。
|
||||
# 此前恒走 strlen:对结构体对象生成 strlen(struct*)
|
||||
# → 把对象首字段当字符串读 → 垃圾长度(CPythonTest
|
||||
# PyUnicodeObj len('Hello') / PyListObj len == 3 根因)。
|
||||
len_struct_ty: llvmlite.LLVMType | t.CPtr = \
|
||||
HandlesStruct.get_struct_type_from_value(len_val)
|
||||
if len_struct_ty is not None:
|
||||
len_cls_nm: str = HandlesStruct.get_class_name_by_type(
|
||||
pool, len_struct_ty)
|
||||
if len_cls_nm is not None:
|
||||
len_lookup: t.CChar | t.CPtr = pool.alloc(128)
|
||||
if len_lookup is not None:
|
||||
viperlib.snprintf(len_lookup, 128, "%s.__len__", len_cls_nm)
|
||||
len_fn2: llvmlite.Function | t.CPtr = self.find_func_in_module(
|
||||
mod, len_lookup)
|
||||
if len_fn2 is not None:
|
||||
len_ret_ty2: llvmlite.LLVMType | t.CPtr = \
|
||||
llvmlite.function_get_ret_ty(len_fn2)
|
||||
len_fn_name2: t.CChar | t.CPtr = \
|
||||
llvmlite.function_get_name(len_fn2)
|
||||
if len_ret_ty2 is not None and len_fn_name2 is not None:
|
||||
# self 参数类型对齐(按方法首个形参 bitcast)
|
||||
len_self_arg2: llvmlite.Value | t.CPtr = len_val
|
||||
len_first_param2: llvmlite.Param | t.CPtr = \
|
||||
llvmlite.function_get_param_head(len_fn2)
|
||||
if len_first_param2 is not None:
|
||||
len_self_ty2: llvmlite.LLVMType | t.CPtr = \
|
||||
llvmlite.param_get_ty(len_first_param2)
|
||||
if len_self_ty2 is not None and len_val.Ty is not None:
|
||||
len_self_arg2 = llvmlite.build_bitcast(
|
||||
builder, len_val, len_self_ty2)
|
||||
len_self_arg2.Next = None
|
||||
return llvmlite.build_call(
|
||||
builder, len_fn_name2, len_self_arg2,
|
||||
1, len_ret_ty2, 0)
|
||||
# 确保 strlen 在模块中有声明
|
||||
strlen_fn: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, "strlen")
|
||||
if strlen_fn is None:
|
||||
@@ -5194,6 +5260,22 @@ class CallGenMixin:
|
||||
pool, builder, mod, st_nm.id, func_name, cl, trans)
|
||||
if st_res is not None:
|
||||
return st_res
|
||||
elif st_at.value.kind() == ast.ASTKind.Attribute:
|
||||
# 模块限定静态调用: module.ClassName.static_method(args)
|
||||
# cl.func = Attribute(value=Attribute(value=Name(module),
|
||||
# attr=ClassName), attr=method_name)
|
||||
# 此前未检测 → 走通用路径把 method_name 当裸函数解析,
|
||||
# 生成 call i32 @from_long(无类名/SHA1 前缀)→ 链接
|
||||
# undefined reference(CPythonTest PyLongObj.from_long 根因)
|
||||
st_at2: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(st_at.value)
|
||||
if st_at2 is not None and st_at2.attr is not None and st_at2.value is not None:
|
||||
if st_at2.value.kind() == ast.ASTKind.Name:
|
||||
# ClassName 是已注册结构体类名时尝试静态/类方法
|
||||
if HandlesStruct.get_struct_type(st_at2.attr) is not None:
|
||||
st_res2: llvmlite.Value | t.CPtr = self._translate_static_class_call(
|
||||
pool, builder, mod, st_at2.attr, func_name, cl, trans)
|
||||
if st_res2 is not None:
|
||||
return st_res2
|
||||
|
||||
# 检测方法调用: obj.method(args)
|
||||
# 当 cl.func 是 Attribute 且 obj 是已注册的结构体变量时,翻译为方法调用
|
||||
|
||||
@@ -130,8 +130,14 @@ class ClassGenMixin:
|
||||
return 0
|
||||
|
||||
def _is_enum_class(self, cd: ast.ClassDef | t.CPtr) -> int:
|
||||
"""检查 ClassDef 是否继承自 t.CEnum,返回 1=是 / 0=否"""
|
||||
if cd is None or cd.bases is None:
|
||||
"""检查 ClassDef 是否为 CEnum:继承自 t.CEnum 或 @t.CEnum 装饰器形式"""
|
||||
if cd is None:
|
||||
return 0
|
||||
# 装饰器形式: @t.CEnum class Color:(无基类,等价 class Color(t.CEnum):)
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CEnum") == 1:
|
||||
return 1
|
||||
if cd.bases is None:
|
||||
return 0
|
||||
bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases
|
||||
bn: t.CSizeT = bases.__len__()
|
||||
@@ -165,8 +171,14 @@ class ClassGenMixin:
|
||||
return 0
|
||||
|
||||
def _is_union_class(self, cd: ast.ClassDef | t.CPtr) -> int:
|
||||
"""检查 ClassDef 是否继承自 t.CUnion,返回 1=是 / 0=否"""
|
||||
if cd is None or cd.bases is None:
|
||||
"""检查 ClassDef 是否为 CUnion:继承自 t.CUnion 或 @t.CUnion 装饰器形式"""
|
||||
if cd is None:
|
||||
return 0
|
||||
# 装饰器形式: @t.CUnion class Data:
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "CUnion") == 1:
|
||||
return 1
|
||||
if cd.bases is None:
|
||||
return 0
|
||||
bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases
|
||||
bn: t.CSizeT = bases.__len__()
|
||||
@@ -200,8 +212,14 @@ class ClassGenMixin:
|
||||
return 0
|
||||
|
||||
def _is_renum_class(self, cd: ast.ClassDef | t.CPtr) -> int:
|
||||
"""检查 ClassDef 是否继承自 t.REnum,返回 1=是 / 0=否"""
|
||||
if cd is None or cd.bases is None:
|
||||
"""检查 ClassDef 是否为 REnum:继承自 t.REnum 或 @t.REnum 装饰器形式"""
|
||||
if cd is None:
|
||||
return 0
|
||||
# 装饰器形式: @t.REnum class X:
|
||||
if cd.decorator_list is not None:
|
||||
if self._has_decorator(cd.decorator_list, "REnum") == 1:
|
||||
return 1
|
||||
if cd.bases is None:
|
||||
return 0
|
||||
bases: list[ast.AST | t.CPtr] | t.CPtr = cd.bases
|
||||
bn: t.CSizeT = bases.__len__()
|
||||
|
||||
@@ -1200,6 +1200,19 @@ class ExprGenMixin:
|
||||
if lhs_cn is not None and lhs_cn.const_kind == ast.CONST_NONE:
|
||||
cmp_with_none = 1
|
||||
|
||||
# === 指针判空模式: ptr != 0 / ptr == 0 ===
|
||||
# 跳过运算符重载走身份比较(icmp null)。跨模块类(如 cpython.PyObject)
|
||||
# 未定义 __eq__/__ne__ 时,try_operator_overload 无法判定方法不存在
|
||||
# (类在依赖模块,cls_sha1 != trans.ModuleSha1),会误发幽灵
|
||||
# `call void @...__ne__` → 链接 undefined reference(CPythonTest main.vp
|
||||
# `py_num != 0` 根因)。C 语义:指针与字面量 0 比较 = 判空。
|
||||
cmp_zero: int = 0
|
||||
if op == ast.OpKind.Eq or op == ast.OpKind.Ne:
|
||||
if rhs_node is not None and self._is_zero_int_literal(rhs_node) != 0:
|
||||
cmp_zero = 1
|
||||
if cmp_zero == 0 and lhs_node is not None and self._is_zero_int_literal(lhs_node) != 0:
|
||||
cmp_zero = 1
|
||||
|
||||
# === 比较运算符重载路径 1: lhs 是 Name 且对应结构体变量 ===
|
||||
# 对于值类型变量(如 cnt: Counter),用 alloca 指针尝试重载
|
||||
# None 比较跳过重载(走身份比较)
|
||||
@@ -1209,10 +1222,11 @@ class ExprGenMixin:
|
||||
lhs_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(
|
||||
trans.SymTab, nm.id)
|
||||
if lhs_alloca is not None:
|
||||
ovl_result: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||||
pool, builder, mod, lhs_alloca, rhs, op, trans, 1)
|
||||
if ovl_result is not None:
|
||||
return ovl_result
|
||||
if cmp_zero == 0 or self.get_ptr_level(lhs_alloca.Ty) == 0:
|
||||
ovl_result: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||||
pool, builder, mod, lhs_alloca, rhs, op, trans, 1)
|
||||
if ovl_result is not None:
|
||||
return ovl_result
|
||||
|
||||
# 正常翻译 lhs
|
||||
lhs: llvmlite.Value | t.CPtr = self.translate_value(
|
||||
@@ -1222,8 +1236,8 @@ class ExprGenMixin:
|
||||
|
||||
# === 比较运算符重载路径 2: lhs 是 Ptr(Struct) ===
|
||||
# Is/IsNot 不在 _cmpop_to_dunder 映射中,会自动返回 None 回退原生比较
|
||||
# None 比较跳过重载(走身份比较)
|
||||
if cmp_with_none == 0:
|
||||
# None 比较跳过重载(走身份比较);指针判空(cmp_zero + 指针)同样跳过
|
||||
if cmp_with_none == 0 and (cmp_zero == 0 or self.get_ptr_level(lhs.Ty) == 0):
|
||||
ovl_result2: llvmlite.Value | t.CPtr = self.try_operator_overload(
|
||||
pool, builder, mod, lhs, rhs, op, trans, 1)
|
||||
if ovl_result2 is not None:
|
||||
@@ -4013,11 +4027,14 @@ class ExprGenMixin:
|
||||
# BinOp 运算符 → dunder 方法名
|
||||
# ============================================================
|
||||
def _binop_to_dunder(self, op: int) -> str:
|
||||
"""将 BinOp 运算符映射到 dunder 方法名,无映射时返回 None"""
|
||||
"""将 BinOp 运算符映射到 dunder 方法名,无映射时返回 None
|
||||
|
||||
Python 3 语义:/ 映射 __truediv__(Python 2 的 __div__ 已废弃)。
|
||||
"""
|
||||
if op == ast.OpKind.Add: return "__add__"
|
||||
if op == ast.OpKind.Sub: return "__sub__"
|
||||
if op == ast.OpKind.Mult: return "__mul__"
|
||||
if op == ast.OpKind.Div: return "__div__"
|
||||
if op == ast.OpKind.Div: return "__truediv__"
|
||||
if op == ast.OpKind.Mod: return "__mod__"
|
||||
if op == ast.OpKind.BitAnd: return "__and__"
|
||||
if op == ast.OpKind.BitOr: return "__or__"
|
||||
@@ -4138,6 +4155,14 @@ class ExprGenMixin:
|
||||
if found_func is None and cls_sha1 is None:
|
||||
return None
|
||||
|
||||
# 方法在"定义它的模块"中确实不存在(同模块类但模块内无此 dunder):
|
||||
# 返回 None 回退原生运算,绝不发出幽灵 call——否则 IR 残留
|
||||
# `call void @...__div__`(副作用)且调用方回退原生 sdiv 双重发射,
|
||||
# llc 报 invalid operand type for instruction(CPythonTest PyFloatObj 根因)
|
||||
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
|
||||
|
||||
# 构建 extra_args 数组(仅含 rhs 一个参数)
|
||||
extra_args: t.CSizeT | t.CPtr = pool.alloc(8)
|
||||
if extra_args is None:
|
||||
@@ -4229,6 +4254,12 @@ class ExprGenMixin:
|
||||
if found_func is None and cls_sha1 is None:
|
||||
return None
|
||||
|
||||
# 同模块类但模块内无此 dunder → 返回 None 回退原生运算,不发幽灵 call
|
||||
# (与 try_operator_overload 一致,防 IR 残留 call void @...__neg__)
|
||||
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
|
||||
|
||||
# 一元方法无额外参数(extra_args 为空)
|
||||
return HandlesExprCall._call_method_on_ptr(
|
||||
pool, builder, mod, search_class, dunder,
|
||||
|
||||
@@ -16,6 +16,7 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal
|
||||
import lib.core.Handles.HandlesType as HandlesType
|
||||
import lib.core.Handles.HandlesBody as HandlesBody
|
||||
import lib.core.Handles.HandlesImports as HandlesImports
|
||||
import lib.core.Handles.HandlesClassDef as HandlesClassDef
|
||||
|
||||
|
||||
# ============================================================
|
||||
@@ -362,6 +363,19 @@ class FuncGenMixin:
|
||||
has_extern = HandlesType.has_decorator_marker(fd.returns, "CExtern")
|
||||
has_state = HandlesType.has_decorator_marker(fd.returns, "State")
|
||||
has_export = HandlesType.has_decorator_marker(fd.returns, "CExport")
|
||||
# 装饰器形式存储类标记(@t.CExport/@t.CExtern/@t.State/@t.CStatic)
|
||||
# 对齐 TPC HandlesFunctions._extract_decorator_storage_flags;
|
||||
# CExport/CExtern/State 影响函数名(导出裸名),CStatic 仅标记。
|
||||
# 注意: 用 HandlesClassDef._has_decorator 委托而非 self._has_decorator
|
||||
# (Mixin 是 @t.NoVTable 静态绑定,跨 Mixin 方法经 MRO 调用会生成
|
||||
# 不存在的 FuncGenMixin._has_decorator → 链接 undefined symbol)
|
||||
if fd.decorator_list is not None:
|
||||
if has_extern == 0 and HandlesClassDef._has_decorator(fd.decorator_list, "CExtern") == 1:
|
||||
has_extern = 1
|
||||
if has_state == 0 and HandlesClassDef._has_decorator(fd.decorator_list, "State") == 1:
|
||||
has_state = 1
|
||||
if has_export == 0 and HandlesClassDef._has_decorator(fd.decorator_list, "CExport") == 1:
|
||||
has_export = 1
|
||||
is_extern_decl: int = 0
|
||||
if has_extern != 0 or has_state != 0:
|
||||
is_extern_decl = 1
|
||||
@@ -509,6 +523,19 @@ class FuncGenMixin:
|
||||
# t.CExtern/t.State 忽略 body 体,仅声明(无论 body 是否为 pass)
|
||||
if has_extern != 0 or has_state != 0:
|
||||
is_extern_decl = 1
|
||||
# 装饰器形式存储类标记(@t.CExport/@t.CExtern/@t.State/@t.CStatic),
|
||||
# 对齐 TPC HandlesFunctions._extract_decorator_storage_flags(与
|
||||
# forward_declare_functions 一致,保证 declare 与 define 同名同语义)。
|
||||
# 用 HandlesClassDef._has_decorator 委托(Mixin 静态绑定不能 self. 跨类调)
|
||||
if fd.decorator_list is not None:
|
||||
if has_extern == 0 and HandlesClassDef._has_decorator(fd.decorator_list, "CExtern") == 1:
|
||||
has_extern = 1
|
||||
if has_state == 0 and HandlesClassDef._has_decorator(fd.decorator_list, "State") == 1:
|
||||
has_state = 1
|
||||
if has_export == 0 and HandlesClassDef._has_decorator(fd.decorator_list, "CExport") == 1:
|
||||
has_export = 1
|
||||
if (has_extern != 0 or has_state != 0) and is_extern_decl == 0:
|
||||
is_extern_decl = 1
|
||||
|
||||
# SHA1 命名空间:t.CExtern/t.State/t.CExport 不加前缀,直接用裸名
|
||||
# (裸名映射到 C 标准库符号,带 sha1 前缀会导致 undefined reference)
|
||||
|
||||
Reference in new issue
Block a user