fix(HandlesImports): 跨模块展平父类无条件 force 重载,消除并行 worker 布局不确定
并行 worker 中 class_members[Generator] 曾出现 83/85/88/89/105/106 vs 114 的不确定变体:父类在某次展平时祖先未加载完成即被冻结为部分成员,guard (成员非空即跳过)又阻止重载 → this_adjust 按不完整 members 计算字节偏移 → 继承方法 self 错位 → 运行时越界崩溃(FuncTest is_exception_name strcmp, 错位 68 字节)。 修复: - 父类加载循环无条件 force 重载:_TryLoadClassMembersFromPyi(force=True) 先清空该类 class_members/defaults/signeds/bitfields/byteorders/bitoffsets 再重建,字段顺序与定义模块 stub 一致;对其祖先递归同样强制重载, 最终所有 worker 展平结果确定性一致。 - 已加载父类经 class_sha1_map 直连定义模块 sha1 强制重载(O(1)), 避免对每个父类全量扫描 ModuleSha1Map。 - _TryLoadClassMembersFromPyi 返回 bool(找到即停),移除 [DIAG-EXT] 诊断。
This commit is contained in:
1 parent
858aba46d0
commit
bdc5ec3e88
1 file changed
+86
-25
@@ -1931,26 +1931,39 @@ class ImportHandle(BaseHandle):
|
||||
_parent_all_placeholders: bool = bool(_parent_members_check) and all(
|
||||
n.startswith('__inherited_') for n, _ in _parent_members_check
|
||||
)
|
||||
if not _parent_members_check or _parent_all_placeholders:
|
||||
# 清除占位条目以便 _TryLoadClassMembersFromPyi 的 guard 通过
|
||||
_old_parent_members: list = _parent_members_check
|
||||
if _parent_all_placeholders:
|
||||
Gen.class_members[_ParentClass] = []
|
||||
_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {})
|
||||
_parent_loaded: bool = False
|
||||
# 2026-08-18 关键修复:无条件 force 重载父类成员(此前仅当成员为空/
|
||||
# 全占位时才加载)。并行 worker 中父类可能已存在"不完整"的成员表
|
||||
# (此前某次展平时祖先未加载完成即冻结部分字段,guard 又阻止重载),
|
||||
# 导致 class_members[Generator] 83~106 vs 114 不确定 → this_adjust
|
||||
# 错位 → 继承方法 self 偏移错误 → 运行时越界崩溃。force=True 会先
|
||||
# 清空再重建(缺啥补啥、顺序与定义模块一致),且 _EmitExternalClassDeclLlvm
|
||||
# 对其祖先递归同样强制重载,最终所有 worker 展平结果确定性一致。
|
||||
_old_parent_members: list = _parent_members_check
|
||||
if _parent_all_placeholders:
|
||||
Gen.class_members[_ParentClass] = []
|
||||
_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) or {}
|
||||
_parent_found: bool = False
|
||||
# 已加载过的父类直接用其定义模块 sha1 强制重载(O(1)),
|
||||
# 避免对每个父类线性扫描全部 ModuleSha1Map。
|
||||
_parent_sha1: str | None = getattr(Gen, 'class_sha1_map', {}).get(_ParentClass)
|
||||
if _parent_sha1:
|
||||
try:
|
||||
_parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, f"{_parent_sha1}.stub.ll", Gen, force=True)
|
||||
except Exception:
|
||||
_parent_found = False
|
||||
else:
|
||||
for _mod_name, _mod_sha1 in _sha1_map.items():
|
||||
_stub_name: str = f"{_mod_sha1}.stub.ll"
|
||||
try:
|
||||
self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen)
|
||||
_parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen, force=True)
|
||||
except Exception:
|
||||
pass
|
||||
_parent_members_check = Gen.class_members.get(_ParentClass, [])
|
||||
if _parent_members_check and not all(n.startswith('__inherited_') for n, _ in _parent_members_check):
|
||||
_parent_loaded = True
|
||||
_parent_found = False
|
||||
if _parent_found:
|
||||
break
|
||||
if not _parent_loaded and _old_parent_members:
|
||||
# 加载失败,回滚到旧占位条目(有总比没有好)
|
||||
Gen.class_members[_ParentClass] = _old_parent_members
|
||||
_parent_members_check = Gen.class_members.get(_ParentClass, [])
|
||||
if not _parent_found and _old_parent_members:
|
||||
# 加载失败(父类不在任何 pyi 中,如泛型特化基类),回滚旧条目(有总比没有好)
|
||||
Gen.class_members[_ParentClass] = _old_parent_members
|
||||
if _ParentClass in Gen.class_members:
|
||||
# 按继承源去重(与 HandlesClassDef._FlattenInherited 一致,2026-08-18):
|
||||
# 菱形继承(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase,
|
||||
@@ -2257,11 +2270,48 @@ class ImportHandle(BaseHandle):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin) -> None:
|
||||
def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin,
|
||||
force: bool = False) -> bool:
|
||||
"""从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。
|
||||
|
||||
force=True 时(跨模块展平父类的强制重载):先清空该类的成员数据再重建,
|
||||
保证并行 worker 间展平结果确定性一致(缺啥补啥、字段顺序与定义模块一致),
|
||||
避免此前"部分成员被冻结、guard 阻止重载"导致的 class_members[Generator]
|
||||
83~106 vs 114 不确定变体(this_adjust 错位崩溃)。
|
||||
"""
|
||||
ProjectRoot = self._find_project_root()
|
||||
temp_dir = os.path.join(ProjectRoot, 'temp')
|
||||
if class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0:
|
||||
return
|
||||
if (not force) and class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0:
|
||||
# guard 命中:class_members 已存在(可能从别处预置、不完整),但
|
||||
# class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names
|
||||
# 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114)
|
||||
# → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载
|
||||
# class_members,避免覆盖可能更完整的已有数据)。
|
||||
try:
|
||||
_pyi_b0: str = stub_filename.replace('.stub.ll', '.vpi')
|
||||
_path0: str = os.path.join(temp_dir, _pyi_b0)
|
||||
if os.path.isfile(_path0):
|
||||
_tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0)
|
||||
if _tree0 is None:
|
||||
with open(_path0, 'r', encoding='utf-8') as _fh0:
|
||||
_tree0 = ast.parse(_fh0.read())
|
||||
getattr(self, '_pyi_cache', {})[_path0] = _tree0
|
||||
if _tree0 is not None:
|
||||
for _n0 in _tree0.body:
|
||||
if isinstance(_n0, ast.ClassDef) and _n0.name == class_name:
|
||||
_b0: list[str] = []
|
||||
for _bb in _n0.bases:
|
||||
_bn0: str | None = getattr(_bb, 'id', None) or getattr(_bb, 'attr', None)
|
||||
if _bn0 and _bn0 not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'):
|
||||
_b0.append(_bn0)
|
||||
if _b0:
|
||||
if not hasattr(Gen, 'class_parents'):
|
||||
Gen.class_parents = {}
|
||||
Gen.class_parents[class_name] = _b0
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
pyi_basename = stub_filename.replace('.stub.ll', '.vpi')
|
||||
pyi_path = os.path.join(temp_dir, pyi_basename)
|
||||
if not os.path.isfile(pyi_path):
|
||||
@@ -2276,18 +2326,18 @@ class ImportHandle(BaseHandle):
|
||||
if pyi_path not in self._pyi_cache:
|
||||
if not os.path.isfile(pyi_path):
|
||||
self._pyi_cache[pyi_path] = None
|
||||
return
|
||||
return False
|
||||
try:
|
||||
with open(pyi_path, 'r', encoding='utf-8') as f:
|
||||
self._pyi_cache[pyi_path] = ast.parse(f.read())
|
||||
except Exception as e:
|
||||
_vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e)
|
||||
self._pyi_cache[pyi_path] = None
|
||||
return
|
||||
return False
|
||||
|
||||
pyi_tree = self._pyi_cache[pyi_path]
|
||||
if pyi_tree is None:
|
||||
return
|
||||
return False
|
||||
source_sha1 = stub_filename.replace('.stub.ll', '')
|
||||
module_name = None
|
||||
if hasattr(Gen, 'ModuleSha1Map'):
|
||||
@@ -2319,11 +2369,22 @@ class ImportHandle(BaseHandle):
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
return
|
||||
return False
|
||||
if force:
|
||||
# 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。
|
||||
# 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。
|
||||
Gen.class_members[class_name] = []
|
||||
for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields',
|
||||
'class_member_byteorders', 'class_member_bitoffsets'):
|
||||
_gd: dict | None = getattr(Gen, _gd_attr, None)
|
||||
if _gd is not None:
|
||||
_gd.pop(class_name, None)
|
||||
if hasattr(Gen, 'class_parents'):
|
||||
Gen.class_parents.pop(class_name, None)
|
||||
if hasattr(Gen, 'class_parent'):
|
||||
Gen.class_parent.pop(class_name, None)
|
||||
self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name, source_sha1=source_sha1)
|
||||
if class_name in Gen.class_members and len(Gen.class_members[class_name]) > 0:
|
||||
return
|
||||
return
|
||||
return True
|
||||
|
||||
def _BuildScalarConstant(self, value_node: ast.AST, target_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Constant | None:
|
||||
"""Build a scalar constant value from AST node, delegated to unified method."""
|
||||
|
||||
Reference in new issue
Block a user