diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 92d8ae8..dd8137b 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -5385,6 +5385,16 @@ class CallGenMixin: sz_builtin = 1 elif string.strcmp(sz_cls_name, "bool") == 0: sz_builtin = 1 + # 泛型 T 解析出的裸内建名(resolve_generic_type_name 返回 + # _infer_generic_ctor_type_arg 的输出,如 T→"double"/"float"): + # 缺省会让 sz_builtin=-1 → 落到"回退 2"把 Name(T) 当值翻译 → + # NameError(VectorGenericTest Vector[double].__init__ 回归根因) + elif string.strcmp(sz_cls_name, "double") == 0: + sz_builtin = 8 + elif string.strcmp(sz_cls_name, "float") == 0: + sz_builtin = 4 + elif string.strcmp(sz_cls_name, "char") == 0: + sz_builtin = 1 if sz_builtin >= 0: # 注意: 必须用 const_int64 而非 ConstInt(...,"0") # ConstInt 的 name 参数是 IR 文本表示,ValuePrint 输出时使用 Name 而非 IntVal diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index 9269db0..0bbf8c7 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -57,6 +57,10 @@ META_KEY_LEN: t.CDefine = 128 # 泛型特化缓存容量上限(与 HandlesClassDef.SPEC_CACHE_MAX 一致) SPEC_CACHE_MAX: t.CDefine = 512 +# 泛型模板注册容量上限(GenericClassTpParams stdlib 数组槽数, +# 与 GenericClassNames 等 pool 表可容纳的模板数对齐) +GENERIC_TEMPLATE_MAX: t.CDefine = 512 + # 泛型特化递归深度上限(泛型循环套用防护) # 正常嵌套特化深度 ≤ 3(如 list[list[str]]);64 远超合理值,命中即循环 GENERIC_SPEC_MAX_DEPTH: t.CDefine = 64 @@ -113,6 +117,12 @@ class ClassGenMixin: # 的 T(...) 错报成 vectortest.vp 的 return 0)。此表用于特化期间 # 临时切换 CurrentFile 到模板文件,翻译完恢复。 GenericClassFiles: list[str] | t.CPtr + # 泛型模板类型参数名表(与 GenericClassNames 同序,stdlib 深拷贝)。 + # 模板节点 type_params 是 Phase 1 pool 分配的 list,跨 Phase 引用时内容 + # 可能失效(VectorGenericTest 特化 Vector[int].__init__ 的 T.__sizeof__() + # NameError 根因方向);注册时展平深拷贝到 stdlib,特化时从本表恢复。 + # 每槽指向一个空格分隔、NUL 结尾的 stdlib 缓冲区(如 "T" 或 "T K")。 + GenericClassTpParams: t.CSizeT | t.CPtr # --- vtable 方法收集表(原 _g_vt_full_names/_g_vt_method_names/ # _g_vt_parent_sha1s/_g_vt_method_count) --- @@ -1729,6 +1739,11 @@ class ClassGenMixin: self.GenericClassNodes = list[ast.ClassDef | t.CPtr](pool, 8) self.GenericClassSha1s = list[str](pool, 8) self.GenericClassFiles = list[str](pool, 8) + if self.GenericClassTpParams is None: + self.GenericClassTpParams = stdlib.malloc(GENERIC_TEMPLATE_MAX * 8) + if self.GenericClassTpParams is None: + return + string.memset(self.GenericClassTpParams, 0, GENERIC_TEMPLATE_MAX * 8) # 用局部变量接收 list,触发类型推断(避免字段 __len__ 失败) names_local: list[str] | t.CPtr = self.GenericClassNames cn: t.CSizeT = len(names_local) @@ -1737,11 +1752,95 @@ class ClassGenMixin: nm: str = names_local.get(i) if nm is not None and string.strcmp(nm, cd.name) == 0: return + # type_params stdlib 深拷贝:模板节点的 type_params 是 pool 分配的 + tp_names_buf: str = self._copy_type_params_stdlib(cd.type_params) self.GenericClassNames.append(cd.name) self.GenericClassNodes.append(cd) self.GenericClassSha1s.append(module_sha1) # 同时保存模板来源文件路径(错误定位用,见字段注释) self.GenericClassFiles.append(HandlesType.get_current_file()) + if tp_names_buf is not None and cn < GENERIC_TEMPLATE_MAX: + self.GenericClassTpParams[cn] = t.CSizeT(tp_names_buf) + + def _copy_type_params_stdlib(self, tp: list[str] | t.CPtr) -> str: + """将模板 type_params 深拷贝为空格分隔、NUL 结尾的 stdlib 缓冲区 + + TPC Python 参考实现:type_params = [tp.name for tp in Node.type_params] + (Python 字符串不可变天然跨阶段存活);Viper 的 type_params 是 pool + 分配 list,跨 Phase 内容可能被复用污染,必须注册时拷贝到 stdlib。 + 返回缓冲区指针;tp 为 None(非泛型)或分配失败返回 None。 + """ + if tp is None: + return None + tp_n: t.CSizeT = len(tp) + if tp_n == 0: + return None + # 计算总长:各名字长度 + (n-1) 个空格 + NUL + total: t.CSizeT = 1 + ti: t.CSizeT + for ti in range(tp_n): + tp_name: str = tp.get(ti) + if tp_name is not None: + total += string.strlen(tp_name) + 1 + buf: str = stdlib.malloc(total) + if buf is None: + return None + buf[0] = '\0' + for ti in range(tp_n): + tp_name = tp.get(ti) + if tp_name is None: + continue + if ti > 0 and buf[0] != '\0': + string.strcat(buf, " ") + string.strcat(buf, tp_name) + return buf + + def _find_generic_template_tp_params(self, pool: memhub.MemBuddy | t.CPtr, + class_name: str) -> list[str] | t.CPtr: + """查找泛型类模板的类型参数名列表,未找到返回 None + + 从注册时深拷贝的 stdlib 缓冲区重建当前 pool 的 list[str] + (特化期间 pool 有效,方法与体翻译共用)。与 GenericClassNames 同序。 + """ + if self.GenericClassNames is None or class_name is None \ + or self.GenericClassTpParams is None: + return None + names_local: list[str] | t.CPtr = self.GenericClassNames + cn: t.CSizeT = len(names_local) + i: t.CSizeT + for i in range(cn): + nm: str = names_local.get(i) + if nm is not None and string.strcmp(nm, class_name) == 0: + if i >= GENERIC_TEMPLATE_MAX: + return None + buf_addr: t.CSizeT = self.GenericClassTpParams[i] + if buf_addr == 0: + return None + buf_tp: str = (str | t.CPtr)(t.CVoid(buf_addr, t.CPtr)) + # 空格分隔 → list[str] + # 注意:list[str](pool, 8) 第二个参数是 elem_size(指针 8 字节), + # 不是容量!传 4 会导致 get(i) 偏移算错、append 相互覆盖 + # (Pair[T1,T2] 的 seg1 写 data+4 覆盖 seg0 高 4 字节 → strcmp + # 收到 0x15B78D5015B78CB0 混合垃圾崩溃,GenericTest 回归根因) + tp_list: list[str] | t.CPtr = list[str](pool, 8) + if tp_list is None: + return None + seg_start: t.CSizeT = 0 + pos: t.CSizeT = 0 + blen: t.CSizeT = string.strlen(buf_tp) + while pos <= blen: + if buf_tp[pos] == ' ' or buf_tp[pos] == '\0': + seg_len: t.CSizeT = pos - seg_start + if seg_len > 0: + seg: str = pool.alloc(seg_len + 1) + if seg is not None: + string.strncpy(seg, buf_tp + seg_start, seg_len) + seg[seg_len] = '\0' + tp_list.append(seg) + seg_start = pos + 1 + pos += 1 + return tp_list + return None def _find_generic_template(self, class_name: str) -> ast.ClassDef | t.CPtr: """查找泛型类模板,返回 ClassDef 节点或 None""" @@ -2122,7 +2221,11 @@ class ClassGenMixin: # (补译模式),否则其 MethodsEmitted==1 防重复检查会拦截补译, # 方法体永不发射 → 特化方法只有 call 无 define → 链接 undefined symbol。 pre_existing.MethodsEmitted = 1 - tp_names_pre: list[str] | t.CPtr = template_cd.type_params + # 从 stdlib 深拷贝表取 type_params(模板节点 pool list 跨 Phase 可能失效) + tp_names_pre: list[str] | t.CPtr = self._find_generic_template_tp_params( + pool, class_name) + if tp_names_pre is None: + tp_names_pre = template_cd.type_params spec_cd_pre: ast.ClassDef | t.CPtr = self._clone_classdef_for_spec( pool, template_cd, spec_name, tp_names_pre, type_args) if spec_cd_pre is not None: @@ -2195,7 +2298,11 @@ class ClassGenMixin: self._cache_spec(pool, spec_key, spec_name) # 5. 拷贝 ClassDef 节点 - tp_names: list[str] | t.CPtr = template_cd.type_params + # 从 stdlib 深拷贝表取 type_params(模板节点 pool list 跨 Phase 可能失效) + tp_names: list[str] | t.CPtr = self._find_generic_template_tp_params( + pool, class_name) + if tp_names is None: + tp_names = template_cd.type_params spec_cd: ast.ClassDef | t.CPtr = self._clone_classdef_for_spec( pool, template_cd, spec_name, tp_names, type_args) if spec_cd is None: diff --git a/App/lib/core/Gen/Generator.vp b/App/lib/core/Gen/Generator.vp index 59bdeb7..adc997f 100644 --- a/App/lib/core/Gen/Generator.vp +++ b/App/lib/core/Gen/Generator.vp @@ -61,6 +61,8 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.TempDir = None self.GenericTpNames = None self.GenericTypeArgs = None + self.GenericCtxStack = None + self.GenericCtxDepth = 0 self.CDefineNames = None self.CDefineValues = None self.CDefineDoubleFlags = None @@ -117,6 +119,7 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.GenericClassNames = None self.GenericClassNodes = None self.GenericClassSha1s = None + self.GenericClassTpParams = None self.VtFullNames = None self.VtMethodNames = None self.VtParentSha1s = None diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index a148e67..f7e138c 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -57,6 +57,12 @@ class TypeGenMixin: # --- 全局泛型特化上下文(原 _g_generic_tp_names/_g_generic_type_args) --- GenericTpNames: list[str] | t.CPtr GenericTypeArgs: list[str] | t.CPtr + # 嵌套安全栈:set_generic_context 压入外层值,clear_generic_context 弹出 + # 恢复(原单槽赋值:嵌套特化/泛型函数翻译退出时把外层上下文清成 None → + # 外层方法体后续的 T 替换丢失 → T.__sizeof__() NameError,VectorGenericTest + # 特化 Vector[int].__init__ 回归根因)。 + GenericCtxStack: t.CSizeT | t.CPtr # i64* 固定数组:每层 2 个槽(tp_names, type_args 指针) + GenericCtxDepth: int # --- 全局 CDefine 编译期常量表(原 _g_cdefine_*) --- CDefineNames: list[str] | t.CPtr @@ -92,6 +98,8 @@ class TypeGenMixin: self.TempDir = None self.GenericTpNames = None self.GenericTypeArgs = None + self.GenericCtxStack = None + self.GenericCtxDepth = 0 self.CDefineNames = None self.CDefineValues = None self.CDefineDoubleFlags = None @@ -680,14 +688,36 @@ class TypeGenMixin: def set_generic_context(self, tp_names: list[str] | t.CPtr, type_args: list[str] | t.CPtr): - """设置泛型特化上下文(进入泛型类方法体翻译时调用)""" + """设置泛型特化上下文(进入泛型类方法体翻译时调用),嵌套安全 + + 先压栈当前(外层)上下文,再设置新值。clear_generic_context 弹出恢复, + 避免嵌套特化/泛型函数翻译退出时清空外层上下文(T.__sizeof__() 回归根因)。 + """ + if self.GenericCtxStack is None: + self.GenericCtxStack = stdlib.malloc(8 * 2 * 32) + if self.GenericCtxStack is None: + self.GenericTpNames = tp_names + self.GenericTypeArgs = type_args + return + self.GenericCtxDepth = 0 + if self.GenericCtxDepth < 32: + self.GenericCtxStack[self.GenericCtxDepth * 2] = t.CSizeT(self.GenericTpNames) + self.GenericCtxStack[self.GenericCtxDepth * 2 + 1] = t.CSizeT(self.GenericTypeArgs) + self.GenericCtxDepth += 1 self.GenericTpNames = tp_names self.GenericTypeArgs = type_args def clear_generic_context(self): - """清除泛型特化上下文(离开泛型类方法体翻译时调用)""" - self.GenericTpNames = None - self.GenericTypeArgs = None + """清除泛型特化上下文(离开泛型类方法体翻译时调用),恢复外层而非置 None""" + if self.GenericCtxDepth > 0: + self.GenericCtxDepth -= 1 + self.GenericTpNames = (list[str] | t.CPtr)(t.CVoid( + self.GenericCtxStack[self.GenericCtxDepth * 2], t.CPtr)) + self.GenericTypeArgs = (list[str] | t.CPtr)(t.CVoid( + self.GenericCtxStack[self.GenericCtxDepth * 2 + 1], t.CPtr)) + else: + self.GenericTpNames = None + self.GenericTypeArgs = None def resolve_generic_type_name(self, name: str) -> str: """如果 name 是泛型类型参数,返回对应的具体类型名;否则返回 name 本身 diff --git a/App/lib/core/Handles/HandlesAssign.vp b/App/lib/core/Handles/HandlesAssign.vp index 69a1b9d..2a9ec5b 100644 --- a/App/lib/core/Handles/HandlesAssign.vp +++ b/App/lib/core/Handles/HandlesAssign.vp @@ -454,6 +454,13 @@ class AssignHandle(HandlesBase.Mixin): alloca: llvmlite.Value | t.CPtr = HandlesVar.get_or_create_sym( self.Trans.SymTab, pool, builder, nm.id, rhs_val.Ty) if alloca is not None: + # 无注解赋值:从 RHS 表达式推断类名设置变量注解 + # (联合类型 X|t.CPtr 简化为 Ptr(i8),属性访问按需过滤需要 + # AnnotClassName 才能定位结构体字段) + cls_inf: str = self._infer_rhs_annot(pool, mod, asgn.value, self.Trans, rhs_val) + if cls_inf is not None: + HandlesVar.set_var_annot_class_name( + self.Trans.SymTab, nm.id, cls_inf) # 按 alloca 类型对值进行转换(如 double → float) store_val: llvmlite.Value | t.CPtr = rhs_val if alloca.Ty is not None and alloca.Ty.Pointee is not None: @@ -480,6 +487,234 @@ class AssignHandle(HandlesBase.Mixin): return new_vars + # ============================================================ + # _infer_rhs_annot - 从 RHS 表达式推断变量注解类名 + # + # 无注解赋值(如 v = Vec2(...) / v3 = v1 + v2)时,变量 AnnotClassName + # 缺失会导致后续属性访问(.x)无法"按需过滤":联合类型 X|t.CPtr 在 LLVM + # 中简化为 Ptr(i8),不携带类名,无法定位结构体字段(字段未找到 → fast-fail)。 + # 此函数按 RHS 结构推断类名: + # Call → 类实例化(func 为已注册结构体名才采用,避免普通函数误标); + # func 为 Attribute 时是方法调用(a.cross()),obj 类名 + + # 方法存在 + rhs_val 为指针类型(联合对象)三条件判定 + # Name → 源变量的 AnnotClassName + # BinOp/UnaryOp → lhs 类名 + 该类存在对应 dunder 方法(运算符重载返回同类对象) + # Attribute → 字段注解类名(委托 Generator 单例 _resolve_attr_field_class) + # 仅当目标为已注册结构体时采用,与"按需过滤,找不到 fast-fail"原则一致。 + # ============================================================ + def _infer_rhs_annot(self, pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + node: ast.AST | t.CPtr, + trans: HT.Translator | t.CPtr, + rhs_val: llvmlite.Value | t.CPtr = None) -> str: + """从 RHS 表达式推断类名,无法推断返回 None""" + if node is None or trans is None: + return None + k: int = node.kind() + # Call: 类实例化 / 方法调用 + if k == ast.ASTKind.Call: + cl: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl is None or cl.func is None: + return None + if cl.func.kind() == ast.ASTKind.Name: + # 类实例化:func 为已注册结构体名才采用 + cand: str = ((ast.Name | t.CPtr)(cl.func)).id + if cand is None: + return None + se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(cand) + if se is not None: + return cand + return None + if cl.func.kind() == ast.ASTKind.Attribute: + # 方法调用 a.cross(b):从 vpi 返回注解缓存精确解析返回类名 + # (方法可返回非同类对象,如 Transform.world_position → 'Vec3', + # 启发式取 obj 类名会 GEP 错字段)。vpi 未命中时返回 None, + # 由后续属性访问 fast-fail 暴露 vpi 缺失根因(不静默回退)。 + ca: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if ca is None or ca.attr is None or ca.value is None: + return None + obj_cls: str = self._infer_rhs_annot(pool, mod, ca.value, trans) + if obj_cls is None: + return None + if self._cls_has_dunder(pool, mod, obj_cls, ca.attr) == 0: + return None + # 方法定义所在模块 SHA1(优先 struct entry,回退当前模块 sha1) + cls_sha1: str = HandlesStruct.get_struct_sha1(obj_cls) + if cls_sha1 is None and trans.ModuleSha1 is not None: + cls_sha1 = trans.ModuleSha1 + ret_cls: str = None + if cls_sha1 is not None: + import lib.StubGen.VpiReader as VpiReader + temp_dir: str = None + import lib.core.Gen.Generator as Generator + g2: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g2 is not None and g2.TempDir is not None: + temp_dir = g2.TempDir + if temp_dir is not None: + VpiReader._ensure_vpi_ret_annot_loaded(cls_sha1, temp_dir) + annot_ret: str = VpiReader._lookup_vpi_ret_annot( + cls_sha1, obj_cls, ca.attr) + if annot_ret is not None: + ret_cls = self._extract_annot_class_name(pool, annot_ret) + return ret_cls + return None + # Name: 源变量注解 + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm is None or nm.id is None: + return None + ve: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, nm.id) + if ve is None: + return None + return ve.AnnotClassName + # BinOp/UnaryOp: 运算符重载返回同类对象 + if k == ast.ASTKind.BinOp: + bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) + if bop is None or bop.left is None: + return None + dunder: str = self._op_to_dunder(bop.op) + if dunder is None: + return None + lhs_cls: str = self._infer_rhs_annot(pool, mod, bop.left, trans) + if lhs_cls is None or self._cls_has_dunder(pool, mod, lhs_cls, dunder) == 0: + return None + return lhs_cls + if k == ast.ASTKind.UnaryOp: + uop: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(node) + if uop is None or uop.operand is None: + return None + dunder: str = self._op_to_dunder(uop.op) + if dunder is None: + return None + opr_cls: str = self._infer_rhs_annot(pool, mod, uop.operand, trans) + if opr_cls is None or self._cls_has_dunder(pool, mod, opr_cls, dunder) == 0: + return None + return opr_cls + # Attribute: 字段注解类名(属性链递归,如 p = r.origin) + if k == ast.ASTKind.Attribute: + import lib.core.Gen.Generator as Generator + g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)( + t.CVoid(HandlesExpr._g_gen, t.CPtr)) + if g is not None: + return g._resolve_attr_field_class(pool, trans, node) + return None + return None + + # ============================================================ + # _extract_annot_class_name - 从返回注解文本提取结构体类名 + # + # vpi 返回注解文本(如 "'Vec3' | CPtr")按 '|' 分段,每段去除 + # 空格/引号后查结构体注册表:命中即类名(内置类型 str/int/ + # CFloat64T/t.CPtr 等均不注册 struct,天然过滤)。"A | B" 联合 + # 多候选时取首个命中段。 + # ============================================================ + def _extract_annot_class_name(self, pool: memhub.MemBuddy | t.CPtr, + annot: str) -> str: + """从返回注解文本提取已注册结构体类名,无命中返回 None""" + if pool is None or annot is None: + return None + alen: t.CSizeT = string.strlen(annot) + if alen == 0: + return None + seg: str = pool.alloc(256) + if seg is None: + return None + start: t.CSizeT = 0 + i: t.CSizeT + for i in range(alen): + if annot[i] == '|': + seglen: t.CSizeT = i - start + # 段 [start, i):去除首尾空格与引号后查注册表 + s: t.CSizeT = start + e: t.CSizeT = i + while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): + s += 1 + while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'): + e -= 1 + seglen = e - s + if seglen > 0 and seglen < 256: + string.strncpy(seg, annot + s, seglen) + seg[seglen] = '\0' + se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(seg) + if se is not None: + return seg + start = i + 1 + # 最后一段(无 '|' 后缀) + s: t.CSizeT = start + e: t.CSizeT = alen + while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): + s += 1 + while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'): + e -= 1 + seglen: t.CSizeT = e - s + if seglen > 0 and seglen < 256: + string.strncpy(seg, annot + s, seglen) + seg[seglen] = '\0' + se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(seg) + if se is not None: + return seg + return None + + # ============================================================ + # _op_to_dunder - 运算符 → dunder 方法名(本地副本,避免跨 Mixin 调用) + # ============================================================ + def _op_to_dunder(self, op: int) -> str: + """BinOp/UnaryOp 运算符 → dunder 方法名,无映射返回 None""" + 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 "__truediv__" + if op == ast.OpKind.FloorDiv: return "__floordiv__" + if op == ast.OpKind.Mod: return "__mod__" + if op == ast.OpKind.Pow: return "__pow__" + if op == ast.OpKind.BitAnd: return "__and__" + if op == ast.OpKind.BitOr: return "__or__" + if op == ast.OpKind.BitXor: return "__xor__" + if op == ast.OpKind.LShift: return "__lshift__" + if op == ast.OpKind.RShift: return "__rshift__" + if op == ast.OpKind.USub: return "__neg__" + if op == ast.OpKind.UAdd: return "__pos__" + if op == ast.OpKind.Invert: return "__invert__" + return None + + # ============================================================ + # _cls_has_dunder - 检查类(含继承链)是否存在 dunder 方法 + # + # 与 ExprGenMixin.try_operator_overload 的查找一致:当前模块内定义 + # 或跨模块类有 SHA1(stub 中声明)即视为存在。仅在确实存在时 + # 才采用运算符重载推断的类名,避免把原生运算误标为类类型。 + # ============================================================ + def _cls_has_dunder(self, pool: memhub.MemBuddy | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + class_name: str, dunder: str) -> int: + """类及其 MRO 祖先链中是否存在 dunder 方法,存在返回 1""" + if class_name is None or dunder is None or mod is None: + return 0 + lookup_name: str = pool.alloc(128) + if lookup_name is None: + return 0 + viperlib.snprintf(lookup_name, 128, "%s.%s", class_name, dunder) + if HandlesExprCall.find_func_in_module(mod, lookup_name) is not None: + return 1 + # 继承链祖先查找 + mro_ptr: t.CSizeT | t.CPtr = HandlesStruct.get_mro_chain(pool, class_name) + mi: int = 1 + while mro_ptr is not None and mro_ptr[mi] != 0: + anc: str = (str | t.CPtr)(t.CVoid(mro_ptr[mi], t.CPtr)) + if anc is not None: + anc_lookup: str = pool.alloc(128) + if anc_lookup is not None: + viperlib.snprintf(anc_lookup, 128, "%s.%s", anc, dunder) + if HandlesExprCall.find_func_in_module(mod, anc_lookup) is not None: + return 1 + mi += 1 + # 跨模块:类有 SHA1(stub 中声明该方法)视为存在 + if HandlesStruct.get_struct_sha1(class_name) is not None: + return 1 + return 0 + # ============================================================ # NewAssignHandle - 工厂函数:分配并初始化 AssignHandle diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index 2f68b75..09b9ebf 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -988,7 +988,15 @@ def _CommentOutDeclareInBuf(out_buf: bytes, func_sig: str) -> int: # 或完整定义,以 text 中的定义为准,将 stub 中的同名行注释掉。 # ============================================================ def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int: - """在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';'""" + """在 out_buf 中搜索全局变量定义行(以 global_name 开头),将行首改为 ';' + + 注释掉**所有**匹配行(而非仅第一个):跨模块同名裸全局(如 main.vp 与 + argparse.vp 都声明 _mbuddy)时,本地 stub external + 依赖 stub external + + 依赖 text 定义会同时存在。只注释第一个会导致依赖 text 的定义残留 → + 合成 IR 重复定义 → llc "redefinition of global '@_mbuddy'"(ArgparseTest + 回归根因)。"以 text 中定义为准"的语义:后追加者注释掉全部旧行,最终 + 只保留最后一个定义。 + """ if out_buf is None or global_name is None: return 0 name_len: t.CSizeT = string.strlen(global_name) @@ -996,14 +1004,15 @@ def _CommentOutGlobalInBuf(out_buf: bytes, global_name: str) -> int: if name_len == 0 or out_len == 0 or name_len > out_len: return 0 pos: t.CSizeT = 0 + commented: int = 0 while pos + name_len <= out_len: if string.strncmp(out_buf + pos, global_name, name_len) == 0: # 确认是行首(前一个是换行或 pos==0) if pos == 0 or out_buf[pos - 1] == '\n': out_buf[pos] = ';' - return 1 + commented = 1 pos += 1 - return 0 + return commented # ============================================================