fix(tpc): 布局校验/修复排除 REnum 镜像布局 + set_body 失败带回滚与上下文
a 编译器缺陷(承接 b8ed33a2 / 7fb0e498):REnum 的 struct body 是 **max-variant
镜像布局** —— class_members 里的名字是*变体*而非字段,变体嵌套结构体也必须逐字
镜像主结构体。此前两处按"声明类型 vs 槽类型"的校验/修复会把这类布局当成错位:
- Step 4 前置校验可能据此清 _elements 重建;
- _SyncStructBodyTypes 可能据此改写布局(跨模块 GEP 偏移随即与定义端失配)。
新增 _IsRenumLayoutClass(主 REnum 自身 / `<REnum>_<Variant>`,判据走 SymbolTable 的
IsRenum)并在两处排除。
另外:就地修复的 `_elements = None` + `set_body` 一旦被 llvmlite 拒绝
(`<name> is already defined`,现场 fd4cd0ec583f42b9.Generator),原来会抛出这句
毫无上下文的裸 RuntimeError。现改为:捕获后**直接写回原 _elements 回滚**,再抛
FailFastError 带上类名 / struct / 原异常 / 元素数前后值,既不留中间状态也不再含糊。
This commit is contained in:
1 parent
dab0823f5c
commit
07ea8e2fd9
1 file changed
+51
-2
@@ -319,6 +319,35 @@ class StructGenMixin:
|
||||
# 判据不可用(缺上下文)时保守放行,避免误清已正确的布局。
|
||||
return True
|
||||
|
||||
def _IsRenumLayoutClass(self, ClassName: str | None) -> bool:
|
||||
"""该类是否属 **REnum 布局**(主 REnum 自身,或其变体嵌套结构体 `<REnum>_<Variant>`)。
|
||||
|
||||
REnum 的 struct body 是 **max-variant 镜像布局**:`class_members` 里那些名字是
|
||||
*变体*(不是字段),变体嵌套结构体也必须逐字镜像主结构体。因此**一律不得**用
|
||||
class_members 的"声明类型"去改写或校验它们的布局 —— 否则跨模块 GEP 偏移与
|
||||
定义端失配(变体载荷槽本就允许与 max-variant 槽类型不同,取值处靠 bitcast)。
|
||||
"""
|
||||
if not ClassName:
|
||||
return False
|
||||
_tbl: Any = getattr(self, 'SymbolTable', None)
|
||||
if _tbl is None:
|
||||
return False
|
||||
try:
|
||||
_e: Any = _tbl.lookup(ClassName)
|
||||
except Exception:
|
||||
return False
|
||||
if _e is not None and getattr(_e, 'IsRenum', False):
|
||||
return True
|
||||
if '_' in ClassName:
|
||||
_prefix: str = ClassName.rsplit('_', 1)[0]
|
||||
try:
|
||||
_pe: Any = _tbl.lookup(_prefix)
|
||||
except Exception:
|
||||
_pe = None
|
||||
if _pe is not None and getattr(_pe, 'IsRenum', False):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _SyncStructBodyTypes(self, ClassName: str | None) -> None:
|
||||
"""把 struct body 中与 `class_members` 声明**不兼容**的槽改回声明类型(就地修复)。
|
||||
|
||||
@@ -339,6 +368,10 @@ class StructGenMixin:
|
||||
"""
|
||||
if not ClassName:
|
||||
return
|
||||
# REnum 布局(主 REnum 及其变体嵌套结构体)是 max-variant 镜像布局,声明成员
|
||||
# 不是字段 —— 一律不参与本修复,避免改写其布局导致跨模块偏移失配。
|
||||
if self._IsRenumLayoutClass(ClassName):
|
||||
return
|
||||
_st: Any = self.structs.get(ClassName)
|
||||
if not isinstance(_st, ir.IdentifiedStructType) or _st.elements is None:
|
||||
return
|
||||
@@ -358,8 +391,21 @@ class StructGenMixin:
|
||||
_changed = True
|
||||
if not _changed:
|
||||
return
|
||||
_backup: list = list(_st.elements)
|
||||
_st._elements = None
|
||||
_st.set_body(*_elems)
|
||||
try:
|
||||
_st.set_body(*_elems)
|
||||
except Exception as _e:
|
||||
# 回滚到原布局(直接写回私有字段,不再经 set_body),再响亮失败:把 llvmlite
|
||||
# 那句含糊的 "<name> is already defined" 变成带类名/元素数/原异常的错误,
|
||||
# 且保证不留下被改坏的中间状态。
|
||||
_st._elements = _backup
|
||||
from lib.core.LLVMCG.FuncGen import FailFastError
|
||||
raise FailFastError(
|
||||
f"struct body 就地修复失败(类 '{ClassName}',struct='{_st}'):"
|
||||
f"{type(_e).__name__}: {_e};元素数={len(_backup)} → 目标元素数={len(_elems)},"
|
||||
f"已回滚原布局"
|
||||
) from _e
|
||||
|
||||
def _generate_structs(self) -> None:
|
||||
all_classes: set[str] = set(self.class_methods.keys()) | set(self.class_members.keys())
|
||||
@@ -548,7 +594,10 @@ class StructGenMixin:
|
||||
# 声明类型**能隐式写进该槽**才算一致 —— 通用指针槽(i8*)照旧放行,不误清。
|
||||
_slot_types_ok: bool = True
|
||||
_members_pre: list = self.class_members.get(ClassName, [])
|
||||
if _members_pre:
|
||||
# REnum 布局(主 REnum 及变体嵌套结构体)是 max-variant 镜像布局,
|
||||
# class_members 里是*变体*不是字段 ⇒ 不参与逐元素类型校验(见
|
||||
# _IsRenumLayoutClass)。
|
||||
if _members_pre and not self._IsRenumLayoutClass(ClassName):
|
||||
_base_pre: int = 1 if _has_vtable_precheck else 0
|
||||
_elems_pre: list = list(existing_st.elements)
|
||||
for _i_pre, _item_pre in enumerate(_members_pre):
|
||||
|
||||
Reference in new issue
Block a user