fix(tpc): 字段偏移改为「布局真源优先」,成员表漂移不再整体平移

类别 a(编译器缺陷):_get_member_offset 返回 `base + 成员表下标`,但 class_members
是**可变**表(继承展平会在父类就绪后前置前缀、跨模块可按需重载/重跑),而 struct
body 一旦 set_body 即**永久冻结** ⇒ 表与布局的序位一旦漂移,整类字段偏移整体平移。
现场(IR 实证):Converter 模块读 `Alias.name` 落在 struct 槽 9(= asname),而定义
模块 Alias.__init__ 写入槽 8;`import t` 无 as 子句 ⇒ asname=NULL ⇒ TransPyV StubGen
误报「import 别名 name 为空(_copy_str 返回 None)」并终止 includes/stdint.vp 的 vpi
生成。同族旧现场:ast.Constant.lineno 取到 pool 槽(已由成员偏移同源修复过)。

- StructGen._generate_structs:set_body 时把「成员名 → 槽位」映射冻结到 struct
  实例上(_tpc_member_slots / _tpc_member_base)。用**实例属性**而非全局表:同一
  类名可能存在不止一个 struct 实例,偏移必须与被 GEP 的那一份布局同源;仅当
  member_types 恰为 `[vtable?] + 全部成员` 时记录(位域类折叠为单一 storage 槽,
  其字段位置另由 class_member_bitoffsets 表达;stub 已置 body 的情形不记录)。
- FuncGen._get_member_offset:新增两条「只看布局」的路径,优先于原下标法——
  ① 冻结映射命中即返回;
  ② 用**类型**把成员表逐项配对到 elements(类型不匹配、重名的条目视为方法/占位/
     漂移项,不占槽位),配对中类型相等的槽位即字段真实槽位。
  表与布局自洽时两者给出与旧下标法逐字相同的结果;都覆盖不到才落回下标法(对既有
  正确行为零变化)。另在「配对结果 ≠ 下标法」时每类每字段告警一次,报出成员表项数
  与 struct 槽数,把漂移来源变成可见事实。

本地冒烟(三类,均通过):自洽表 8 / 漂移表(类型不匹配)8(旧下标法 9)/
全同型漂移且命中冻结映射 8。
This commit is contained in:
TermiNexus committed 2026-09-29 19:57:57 +08:00
1 parent d305d3369b
commit f28ff1b414
2 files changed
+89

No files matched your search

+22
View File
@@ -755,6 +755,28 @@ class StructGenMixin:
# 让后续 _TryLoadStructFromStub 能正确设置 body。
if member_types:
struct_type.set_body(*member_types)
# ---- 冻结"成员名 → 槽位下标"映射(布局真源,挂在 struct 实例上)----
# struct body 一旦 set_body 即**永久冻结**,而 class_members 是**可变**
# 表(继承展平会在父类就绪后前置前缀、跨模块可按需重载/重跑),
# 两者序位会漂移。而字段访问用的是 `_get_member_offset` 的
# `base + 成员表下标`,漂移即"整类字段偏移平移"——现场:
# Converter 模块读 `Alias.name` 取到槽 9(= asname),`import t`
# 无 as 子句 ⇒ asname=NULL ⇒ vpi 生成报"import 别名 name 为空"。
# 这里把"建 body 那一刻"的真实槽位表记到 struct 实例上
# (用实例属性而非全局表:同一类名可能存在不止一个 struct 实例,
# 偏移必须与被 GEP 的那一份同源)。仅当 member_types 恰为
# `[vtable?] + 全部成员` 时记录(位域类折叠成单一 storage 槽,
# 其字段下标另由 class_member_bitoffsets 表达,不在此记录)。
_ExpectN: int = (1 if has_vtable else 0) + len(all_members)
if len(member_types) == _ExpectN:
_SlotMap: dict[str, int] = {}
_SlotIdx: int = 1 if has_vtable else 0
for _mnS, _ in all_members:
if _mnS not in _SlotMap:
_SlotMap[_mnS] = _SlotIdx
_SlotIdx += 1
struct_type._tpc_member_slots = _SlotMap
struct_type._tpc_member_base = 1 if has_vtable else 0
def _create_Vtable_globals(self) -> None:
# 「本模块有定义(非 declare)」的函数名所蕴含的全部**点分前缀**集合。