feat(stub): vpi 注入侧递归登记嵌套类为扁平名 {外类}_{内类}

VpiReader 此前只登记顶层 ClassDef,跨模块引用 `外类.内类` 拿不到类型
(TPC 侧 stub.ll 已递归声明,双端不对齐)。现按定义端
ClassGenDef._translate_nested_classdefs 的命名口径补上:

- _inject_class_struct_declare 增加 name_prefix 形参,非空时算出扁平名
  real_name;REPL 分支、已注册检查、命名结构体类型名、register_struct、
  add_visible_struct 一律改用 real_name。
- 新增 _inject_nested_class_declares:遍历类体内缩进 ClassDef 子节点,
  以 real_name 为前缀递归调用(多层嵌套自然展开为 A_B_C)。
- 嵌套登记后再次调用 mark_module_class 复原"正在定义类"窗口:否则内层
  类名会顶掉 CurDefClassName,使外层字段注解的规则 5.5 自引用证据判错、
  且 `外类.内类` 的守卫①失效。
This commit is contained in:
TermiNexus committed 2026-10-01 23:39:11 +08:00
1 parent c3f554f1f5
commit b91dfef777
1 file changed
+92 -14
+92 -14
View File
@@ -2680,10 +2680,28 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*,
trans: HT.Translator*,
src_sha1: str,
imported_modules: str,
from_imports: str) -> int:
"""从 vpi ClassDef 注册类为 struct 类型(含字段),使同 vpi 内裸类名注解可解析"""
from_imports: str,
name_prefix: str = None) -> int:
"""从 vpi ClassDef 注册类为 struct 类型(含字段),使同 vpi 内裸类名注解可解析
name_prefix:非 None 表示"本类是 prefix 类的嵌套类",注册名取扁平名
`{prefix}_{cls_name}`(多层嵌套自然递归为 A_B_C)——与定义端
ClassGenDef._translate_nested_classdefs 的命名口径逐字一致。
"""
if pool is None or mod is None or class_cd is None or cls_name is None:
return 0
# 扁平名(嵌套类):`{外类}_{内类}`。父类未注册时也照常登记(本函数自带
# "字段解析失败即不注册"的既有口径,交给多遍收敛)。
real_name: str = cls_name
if name_prefix is not None and name_prefix[0] != '\0':
p_len: sizet = string.strlen(name_prefix)
c_len: sizet = string.strlen(cls_name)
flat_buf_c: str = pool.alloc(p_len + 1 + c_len + 1)
if flat_buf_c is not None:
string.strcpy(flat_buf_c, name_prefix)
flat_buf_c[p_len] = '_'
string.strcpy(flat_buf_c + p_len + 1, cls_name)
real_name = flat_buf_c
# REPL(ReplStubLayoutMode,见文件末"REPL:includes 类完整布局与虚表恢复"):
# 本类若已由 .stub.ll 驱动的路径接管 ⇒ 这里整类让位——已登记(或本遍就地
# 登记成功)只标可见;父类未就绪 ⇒ 本遍不登记(收尾遍补齐);布局不可信 ⇒
@@ -2691,18 +2709,18 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*,
# State=EMPTY 的类(无父类/无虚表/字段类型不可解析)不在此列,仍走下方既有口径。
if ReplStubLayoutMode != 0:
se: rawptr[ReplStubClass] = _repl_stub_find(
_repl_stub_key(pool, src_sha1, cls_name))
_repl_stub_key(pool, src_sha1, real_name))
if se is not None and se.State != _REPL_SS_EMPTY:
if se.State == _REPL_SS_PENDING:
_repl_stub_try(se, mod)
if se.State == _REPL_SS_OK:
GenApi.add_visible_struct(pool, cls_name)
GenApi.add_visible_struct(pool, real_name)
return 1
# 1. 已注册检查(含源模块编译时注册的 entry):仅标记可见,避免重复注册
if src_sha1 is not None and src_sha1[0] != '\0':
existing_c: GenTypes.StructEntry* = GenApi.find_struct_by_module(cls_name, src_sha1)
existing_c: GenTypes.StructEntry* = GenApi.find_struct_by_module(real_name, src_sha1)
if existing_c is not None and existing_c.Ty is not None:
GenApi.add_visible_struct(pool, cls_name)
GenApi.add_visible_struct(pool, real_name)
return 1
# REnum 类(class X(t.REnum))跳过:布局 {i32 __tag, <payload>} 由
# ClassGenEnum._translate_renum_def 按变体计算;vpi 侧按普通 struct 注册
@@ -2722,9 +2740,21 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*,
# includes/ast/base.vp:359 `_append_child(self.children, node, self.pool)`
# 源 %"…list[rawptr[AST]]"* vs 目标 %"…list[rawptr<定义模块SHA1>.AST]"*。
# mark_module_class 一次调用同时完成两件事:
# ① 登记"{src_sha1}.{cls_name}"(名字归属,与 struct 注册状态无关);
# ① 登记"{src_sha1}.{real_name}"(名字归属,与 struct 注册状态无关);
# ② 把 CurDefModuleSha1/CurDefClassName 指向本次注入的模块与类(规则 5.5 的闸门)。
GenApi.mark_module_class(pool, src_sha1, cls_name)
GenApi.mark_module_class(pool, src_sha1, real_name)
# 1.5 嵌套类 / 嵌套 struct:vpi 里的嵌套类是**缩进 ClassDef**(Converter 原样写出,
# 见 Converter._PyiClass 的嵌套类分支),定义端以扁平名 `{外类}_{内类}` 发射
# (ClassGenDef._translate_nested_classdefs)⇒ 此处按同一命名**先**递归登记。
# 必须早于下方字段注解解析:外层字段可能写成 `外类.内类`,只有扁平名条目已在
# 结构体表里,TypeGenResolve._resolve_nested_class_type 才解析得出类型。
_inject_nested_class_declares(pool, mod, class_cd, real_name, trans,
src_sha1, imported_modules, from_imports)
# 还原"正在定义类"窗口:嵌套递归内的 mark_module_class 会把 CurDefClassName 改成
# 嵌套类名(A_B)。不还原会让外层字段注解的规则 5.5 自引用证据判错(裸类名票名冻结
# 成裸名版本、与定义侧带前缀形式分裂),也会让 `外类.内类` 的守卫①(CurDefClassName
# == 外类名)失效。
GenApi.mark_module_class(pool, src_sha1, real_name)
# 2. 收集字段类型(AnnAssign 子节点,仿 ClassGenDef.translate_class_def)
field_names_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
field_types_buf: sizet* = pool.alloc(8 * GenTypes.FIELD_MAX)
@@ -2778,20 +2808,22 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*,
if prev_node_c is not None:
prev_node_c.Next = pnode_c
prev_node_c = pnode_c
# 4. 命名结构体类型名 "sha1.ClassName"(与定义侧 ClassGenDef 一致)
type_name_c: str = cls_name
# 4. 命名结构体类型名 "sha1.ClassName"(与定义侧 ClassGenDef 一致)。
# 嵌套类用扁平名 real_name(`外类_内类`),与定义端
# ClassGenDef._translate_nested_classdefs 发射的 "sha1.外类_内类" 逐字一致。
type_name_c: str = real_name
if src_sha1 is not None and src_sha1[0] != '\0':
# [D7·截断纪律] 旧实现固定 64 缓冲 + snprintf("%s.%s"):sha1(16)+'.' 占 17
# 字节,类名超过 46 字符即被**静默截断** ⇒ 命名结构体名与定义侧
# ClassGenDef 的 "sha1.ClassName" 不一致 → find_struct_by_module /
# lookup_field_by_class 查不到(误判"未注册")。改为精确长度分配。
sh_len_c: sizet = string.strlen(src_sha1)
cn_len_c: sizet = string.strlen(cls_name)
cn_len_c: sizet = string.strlen(real_name)
name_buf_c: str = pool.alloc(sh_len_c + 1 + cn_len_c + 1)
if name_buf_c is not None:
string.strcpy(name_buf_c, src_sha1)
name_buf_c[sh_len_c] = '.'
string.strcpy(name_buf_c + sh_len_c + 1, cls_name)
string.strcpy(name_buf_c + sh_len_c + 1, real_name)
type_name_c = name_buf_c
struct_ty_c: llvmlite.LLVMType* = llvmlite.Struct(
pool, first_node_c, field_count, type_name_c)
@@ -2811,8 +2843,10 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*,
# 算偏移,与定义侧 packed 布局错位(gdt/elf/fat32 二进制格式字段错读)。
if _vpi_class_has_packed(class_cd.decorator_list) != 0:
llvmlite.module_set_named_type_packed(mod, pool, struct_ty_c, 1)
# 注册名用扁平名 real_name:跨模块引用 `外类.内类` 时 TypeGenResolve 查的是
# 扁平名条目(与定义端 StructGen 的登记名同源)。
entry_c: GenTypes.StructEntry* = GenApi.register_struct(
pool, cls_name, struct_ty_c, src_sha1)
pool, real_name, struct_ty_c, src_sha1)
if entry_c is None:
return 0
# B5:vpi 注入注册的 entry 同步 IsPacked——后续跨模块 stub 注入
@@ -2828,10 +2862,54 @@ def _inject_class_struct_declare(pool: memhub.MemBuddy*,
af_name_c: str = str(field_names_buf[afi_c])
af_ty_c: llvmlite.LLVMType* = (llvmlite.LLVMType*)(field_types_buf[afi_c])
GenApi.add_field(pool, entry_c, af_name_c, af_ty_c)
GenApi.add_visible_struct(pool, cls_name)
GenApi.add_visible_struct(pool, real_name)
return 1
# ============================================================
# _inject_nested_class_declares - 递归登记 vpi 类里的嵌套类
#
# vpi 里的嵌套类是缩进的 ClassDef(Converter._PyiClass 原样写出),定义端以
# 扁平名 `{外类}_{内类}` 发射(ClassGenDef._translate_nested_classdefs)⇒ 注入侧
# 必须按同一命名登记,跨模块引用 `外类.内类` 才解析得出类型(否则与 TPC 侧
# stub.ll 的递归声明不对齐,引用模块拿到 None)。
#
# 多层嵌套靠递归自然展开(A.B.C → prefix=A_B、cls=C ⇒ A_B_C)。这里**不**过滤
# REnum / 空字段:把判定统一交给 _inject_class_struct_declare 既有的守卫
# (REnum 跳过、field_count==0 不注册、已注册只标可见)。
# 每个内层类登记后,_inject_class_struct_declare 会把自己的
# mark_module_class 窗口留在嵌套类上;故外层调用点在其后**再调一次**
# mark_module_class 还原(见 _inject_class_struct_declare 内注释)。
# ============================================================
def _inject_nested_class_declares(pool: memhub.MemBuddy*,
mod: llvmlite.LLVMModule*,
class_cd: ast.ClassDef*,
prefix_name: str,
trans: HT.Translator*,
src_sha1: str,
imported_modules: str,
from_imports: str) -> None:
"""递归登记 class_cd 的所有嵌套 ClassDef 为扁平名类型(前缀 = prefix_name)"""
if pool is None or mod is None or class_cd is None or prefix_name is None:
return
children: list[ast.AST*]* = class_cd.children
if children is None:
return
cn: sizet = len(children)
for ci in range(cn):
child_ast: ast.AST* = children.get(ci)
if child_ast is None:
continue
if child_ast.kind() != ast.ASTKind.ClassDef:
continue
child_cd: ast.ClassDef* = ast.ClassDef*(child_ast)
if child_cd is None or child_cd.name is None or child_cd.name[0] == '\0':
continue
_inject_class_struct_declare(pool, mod, child_cd, child_cd.name, trans,
src_sha1, imported_modules, from_imports,
prefix_name)
# ============================================================
# _ensure_full_value_types - 跨模块「值类型」引用注册完整定义
#