fix(TPC): 跨模块多继承布局统一 - 字段整体前置/方法表合并/父类防重复

1. 定义模块多继承字段展平 prepend(反序) 改 append(正序),与 DeclarationGenerator stub 一致,修复 ASan 定位的 Generator 字段运行时错位(0xbe)。2. 跨模块加载 class_members 已含子类自身字段,父类字段必须整体前置(prepend),否则 Value.Next 命中 FloatVal(double) 导致 store 类型错乱。3. 跨模块补充与定义模块一致的方法合并(父类槽位前置、子类覆盖放父类位置),修复 vtable 槽错位(MemBuddy.alloc→_fl_find_and_remove)。4. 父类前置按字段名过滤已存在字段,防 _EmitExternalClassDeclLlvm 多加载路径重复调用导致 class_members 重复(BasicBlock:[Next,Next,...])。
This commit is contained in:
TermiNexus committed 2026-08-10 18:08:58 +08:00
1 parent bb0f7049ce
commit 2129430446
2 files changed
+84 -17

No files matched your search

+16 -8
View File
@@ -613,26 +613,34 @@ class ClassHandle(BaseHandle):
Gen.class_parents[ClassName] = ParentClasses
if ParentClasses:
# ============================================================
# 多继承字段展平:遍历所有父类(声明顺序),字段名去重(先声明父类优先),
# 多继承字段展平:遍历所有父类(声明顺序),字段名**不去重**,
# __vtable__ 跳过(vtable 指针由 _generate_structs 按 class_vtable 统一添加)。
# 布局: [父类1字段..., 父类2字段..., 子类字段...]
# 布局: [父类1字段..., 父类2字段..., 子类字段...](MRO 正序,与
# DeclarationGenerator stub / TPV 参考实现一致)。
#
# 关键修复(2026-08-10):原实现用 `inherited + class_members`(prepend)
# 产出 MRO 反序布局,且按字段名去重(如多个 Mixin 各自声明的 TempDir
# 被剔除)——导致定义模块 struct 与跨模块 stub 布局不一致,所有多继承
# 类字段运行时错位(ASan 下读到 0xbe 填充崩溃)。Mixin 方法体以自身
# 局部索引独立编译,调用侧 this_adjust + 块内索引依赖"展平块与 Mixin
# 自身逐字段一致",因此跨父类不能去重。菱形继承(A→D, B→D)会让 D
# 字段重复——当前项目 Mixin 为兄弟无共享祖先;若未来出现真菱形需改为
# "按继承源去重"而非"按字段名去重"。
# ============================================================
for ParentClass in ParentClasses:
if ParentClass not in class_graph and ParentClass not in Gen.class_members:
continue
parent_members: list = list(Gen.class_members.get(ParentClass, []))
parent_defaults: dict = dict(Gen.class_member_defaults.get(ParentClass, {}))
existing_names: set = {m[0] for m in Gen.class_members[ClassName]}
inherited: list[tuple] = []
for pm_name, pm_type in parent_members:
if pm_name == '__vtable__':
continue
if pm_name not in existing_names:
inherited.append((pm_name, pm_type))
if pm_name in parent_defaults:
Gen.class_member_defaults[ClassName][pm_name] = parent_defaults[pm_name]
inherited.append((pm_name, pm_type))
if pm_name in parent_defaults:
Gen.class_member_defaults[ClassName][pm_name] = parent_defaults[pm_name]
if inherited:
Gen.class_members[ClassName] = inherited + Gen.class_members[ClassName]
Gen.class_members[ClassName] = Gen.class_members[ClassName] + inherited
# ============================================================
# 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先),
# vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致),
+68 -9
View File
@@ -1852,6 +1852,7 @@ class ImportHandle(BaseHandle):
# 避免 struct body 从不完整 class_members 生成导致 GEP 越界。
# 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。
_any_parent_loaded: bool = False
_all_inherited: list[tuple] = []
for _ParentClass in _ParentClasses:
if not _ParentClass:
continue
@@ -1882,18 +1883,30 @@ class ImportHandle(BaseHandle):
if _ParentClass in Gen.class_members:
_parent_members: list = list(Gen.class_members[_ParentClass])
_parent_defaults: dict = dict(Gen.class_member_defaults.get(_ParentClass, {}))
_existing_names: set = {m[0] for m in Gen.class_members[ClassName]}
_inherited: list[tuple] = []
# 跨父类不去重(与 HandlesClassDef 展平、DeclarationGenerator stub 一致):
# 多继承展平必须保留每个父类全部字段(含重名如多个 Mixin 的 TempDir),
# 否则 Mixin 方法体 this_adjust + 块内局部索引寻址与全量布局错位。
for _pm_name, _pm_type in _parent_members:
if _pm_name == '__vtable__':
continue
if _pm_name not in _existing_names:
_inherited.append((_pm_name, _pm_type))
if _pm_name in _parent_defaults:
Gen.class_member_defaults[ClassName][_pm_name] = _parent_defaults[_pm_name]
if _inherited:
Gen.class_members[ClassName] = _inherited + Gen.class_members[ClassName]
_any_parent_loaded = True
_all_inherited.append((_pm_name, _pm_type))
if _pm_name in _parent_defaults:
Gen.class_member_defaults[ClassName][_pm_name] = _parent_defaults[_pm_name]
if _all_inherited:
# 防重复注册(2026-08-10):_EmitExternalClassDeclLlvm 可能被多个模块
# 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm
# / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次
# 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...])
# → 与 struct elements 数量不匹配 → 属性偏移错乱。按字段名过滤已存在的
# 父类字段;跨父类重名字段(不同父类各自声明)仍全部保留。
_cur_member_names_pre: set = {n for n, _ in Gen.class_members.get(ClassName, [])}
_filtered_inherited: list[tuple] = [
(nm2, tp2) for nm2, tp2 in _all_inherited if nm2 not in _cur_member_names_pre
]
if _filtered_inherited:
Gen.class_members[ClassName] = _filtered_inherited + Gen.class_members[ClassName]
# 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加
_any_parent_loaded = True
if not _any_parent_loaded and _ParentClasses:
# 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value],
# 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。
@@ -1910,7 +1923,53 @@ class ImportHandle(BaseHandle):
if _num_elements > _num_members:
_num_inherited = _num_elements - _num_members
_inherited_ph: list[tuple] = [(f'__inherited_{i}', _st.elements[i]) for i in range(_num_inherited)]
# 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致
Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName]
# ============================================================
# 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位),
# 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只
# 注册自身方法(_has_methods_prescan),vtable 槽位会错位 →
# 虚分派调用错误函数(如 MemBuddy.alloc 被解析到槽 13=_fl_find_and_remove
# → 返回 None → "Generator alloc failed")。
# ============================================================
if _ParentClasses:
_all_parent_methods: list[str] = []
_parent_method_names_seen: set[str] = set()
for _pcls in _ParentClasses:
for _pm in list(Gen.class_methods.get(_pcls, [])):
_pm_name: str = _pm.split('.')[-1] if '.' in _pm else _pm
if _pm_name in ('__new__', '__init__', '__before_init__'):
continue
if _pm_name in _parent_method_names_seen:
continue
_parent_method_names_seen.add(_pm_name)
_all_parent_methods.append(_pm)
if _all_parent_methods:
_child_method_map: dict[str, str] = {}
for _m in Gen.class_methods.get(ClassName, []):
_mn: str = _m.split('.')[-1] if '.' in _m else _m
_child_method_map[_mn] = _m
# 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)
for _item_m in getattr(Node, 'body', []):
if isinstance(_item_m, ast.FunctionDef):
_mn2: str = _item_m.name
if _mn2 in ('__new__', '__init__', '__before_init__'):
continue
_child_method_map[_mn2] = f"{ClassName}.{_mn2}"
_new_methods: list[str] = []
_covered_names: set[str] = set()
for _pm2 in _all_parent_methods:
_mname2: str = _pm2.split('.')[-1] if '.' in _pm2 else _pm2
if _mname2 in _child_method_map:
_new_methods.append(_child_method_map[_mname2])
else:
_new_methods.append(_pm2)
_covered_names.add(_mname2)
for _m3 in list(Gen.class_methods.get(ClassName, [])):
_mn3: str = _m3.split('.')[-1] if '.' in _m3 else _m3
if _mn3 not in _covered_names:
_new_methods.append(_m3)
Gen.class_methods[ClassName] = _new_methods
if has_methods:
if IsCVTable:
Gen._cross_module_vtable_classes.add(ClassName)