diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index a3e0653..a3cb447 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -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 是已注册的结构体变量时,翻译为方法调用 diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index b4903ce..fdfb20f 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -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__() diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index ccd9227..423131a 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -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, diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index 2afb08f..306f8bc 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -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)