fix(gen): 泛型 type_params 生命周期与无注解赋值 annot 推断

- ClassGenMixin: list[str](pool, 8) elem_size 修正(原 4 致 Pair[T1,T2]
  重叠写),新增 GenericClassTpParams stdlib 深拷贝表,特化统一取深拷贝
- Generator: 初始化 GenericClassTpParams 防 0xBEBEBEBEBEBEBEBE
- TypeGenMixin: 泛型上下文栈式管理,支持嵌套特化
- CallGenMixin: sz_builtin 补 double/float/char,修复 T.__sizeof__ NameError
- StubMerger: _CommentOutGlobalInBuf 循环注释同名全局,修复 @_mbuddy 重复定义
- HandlesAssign: 无注解赋值按 RHS 推断 AnnotClassName(类实例化/源变量/
  运算符重载/字段注解/方法返回注解 vpi),修复联合类型 Ptr(i8) 属性访问失败
This commit is contained in:
TermiNexus committed 2026-08-13 17:31:54 +08:00
1 parent 19f48eea02
commit a0edc3d961
6 files changed
+403 -9

No files matched your search

+10
View File
@@ -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
+109 -2
View File
@@ -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:
+3
View File
@@ -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
+34 -4
View File
@@ -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 本身
+235
View File
@@ -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
+12 -3
View File
@@ -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
# ============================================================