fix(type): 未注册类指针改前向声明(具名不透明占位)+ 定义侧原位填充
对齐 TPC 语义(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充)。
此前 TPV 对"指向尚未注册类的指针"回退匿名 Ptr(i8),丢类身份且被冻结后
无法修复:现场 CallGenBase.Gen: Generator.Generator* 与 Generator 构成
循环依赖(Gen→Generator,Generator.CallGenBase→CallGenBase),Gen 先于
Generator 注册 ⇒ 冻结成 i8* ⇒ self.Gen.FuncGenBase 属性访问失败
(1.log 取证:Generator 条目本身正确 fcount=47,但 lookup_field 收到
struct_ty=<non-struct>)。
- GenTypes.StructEntry 新增 IsForwardDecl 标记(占位条目)。
- StructGenMixin.ensure_forward_class:按"类名 + 定义模块 SHA1"登记具名
不透明结构体 {sha}.Class 占位(名称与定义侧 ClassGenDef 同口径,持久池
分配),幂等;定义侧据此按名定位原位填充。
- TypeGenResolve._forward_declare_ptr_from_node(Attribute/Name 两形,经
from_imports 与模块名表求 sha1,再 _class_true_def_sha1 校正)+ BinOp
(X|t.CPtr) 未注册回退:优先返回 Ptr(占位.Ty);仅在无法确定类名/模块时
才退回 Ptr(i8) 由 AnnotClassName 恢复。
- ClassGenDef:命中前向声明占位时不早退,把新构建的字段体**原位写入同一
Ty 对象**(*existing.Ty = Struct(...),指针身份不变),登记条目沿用占位
条目;declare_only 1/0 均适用。所有已发出的 Ptr(占位.Ty) 引用在填充后
自动指向完整定义。
TPC 侧核对:TPC 本就是"具名占位 + set_body"模型(TypeAnnotationResolver
.CreateFromTypeName / BaseGen._get_or_create_struct / StructGen op 占位),
并有 fail-fast 拒绝类引用降级为 void**,无需改动。
This commit is contained in:
1 parent
d1c0f6754c
commit
c0d3231e02
4 files changed
+159
-6
No files matched your search
@@ -108,15 +108,23 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# 标记 _deps_pending 延迟会让空 entry 持久存在、多遍死锁不收敛 ⇒ 改为中和旧 entry
|
||||
# (置空 Name + Ty=None,使 find_struct_by_module 不再命中),走下方完整注册路径重建。
|
||||
needs_rereg: int = 0
|
||||
# 前向声明占位命中:本类此前被"指向未注册类的指针"注解按名字前向声明过
|
||||
# (StructGenMixin.ensure_forward_class 建了 {sha}.Class 不透明占位)。
|
||||
# 此处**不早退**,走下方"原位填充"分支——把字段体写进同一个 Ty 对象,
|
||||
# 保住指针身份,使所有已发出的 Ptr(占位.Ty) 引用(如 CallGenBase.Gen)
|
||||
# 在填充后自动指向完整定义(对齐 TPC 的 opaque 占位 + set_body 填充)。
|
||||
fill_fwd: int = 0
|
||||
if existing is not None:
|
||||
if existing.FieldCount == 0 and trans._declare_only == 0:
|
||||
if existing.IsForwardDecl != 0:
|
||||
fill_fwd = 1
|
||||
elif existing.FieldCount == 0 and trans._declare_only == 0:
|
||||
if self.Gen.ClassGenBase._class_body_has_field(cd) != 0:
|
||||
if existing.Name is not None:
|
||||
existing.Name[0] = '\0'
|
||||
existing.Ty = None
|
||||
existing.FieldCount = 0
|
||||
needs_rereg = 1
|
||||
if existing is not None and needs_rereg == 0:
|
||||
if existing is not None and needs_rereg == 0 and fill_fwd == 0:
|
||||
# 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法
|
||||
if existing.Ty is not None:
|
||||
# Phase 1b(declare_only=0):existing.Ty 是 Phase 1a 注册的 struct_ty,
|
||||
@@ -555,6 +563,20 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
struct_ty = st_copy
|
||||
type_name = llvmlite.get_struct_name(struct_ty)
|
||||
|
||||
# 前向声明占位命中:**原位填充**——保持 existing.Ty 指针身份不变,仅把刚构建
|
||||
# 的字段体写进同一个类型对象(名称沿用占位的 "{sha}.Class")。此后所有已发出的
|
||||
# Ptr(占位.Ty) 引用(如 CallGenBase.Gen)自动指向完整定义。这一步必须早于
|
||||
# module_add_named_type:避免同一名字先以占位名注册、又以新对象重复注册。
|
||||
if fill_fwd != 0 and existing is not None and existing.Ty is not None:
|
||||
_fwd_name: str = llvmlite.get_struct_name(existing.Ty)
|
||||
_fwd_fields: rawptr[llvmlite.ParamNode] = llvmlite.get_struct_fields(struct_ty)
|
||||
_fwd_fc: int = llvmlite.get_struct_fcount(struct_ty)
|
||||
*existing.Ty = llvmlite.LLVMType.Struct(_fwd_fields, _fwd_fc, _fwd_name)
|
||||
struct_ty = existing.Ty
|
||||
type_name = _fwd_name
|
||||
existing.IsForwardDecl = 0
|
||||
existing.FieldCount = 0
|
||||
|
||||
# 注册命名结构体到模块(输出 %"name" = type { ... } 定义行)
|
||||
llvmlite.module_add_named_type(trans.Module, pool, struct_ty)
|
||||
|
||||
@@ -577,7 +599,13 @@ novirtual class ClassGenDef(ClassGenBase.ClassGenBase):
|
||||
# ============================================================
|
||||
# 3. 注册到 HandlesStruct
|
||||
# ============================================================
|
||||
entry: GenTypes.StructEntry* = self.Gen.StructGen.register_struct(pool, class_name, struct_ty, trans.ModuleSha1)
|
||||
entry: GenTypes.StructEntry* = None
|
||||
if fill_fwd != 0 and existing is not None:
|
||||
# 前向声明占位:字段体已原位写入 existing.Ty,登记条目沿用 existing
|
||||
# (不新建条目/Ty,否则已发出的 Ptr(占位.Ty) 引用会指向旧空对象)。
|
||||
entry = existing
|
||||
else:
|
||||
entry = self.Gen.StructGen.register_struct(pool, class_name, struct_ty, trans.ModuleSha1)
|
||||
if entry is None:
|
||||
VLogger.error(f"failed to register {class_name}", "CLASS")
|
||||
return 0
|
||||
|
||||
@@ -167,6 +167,9 @@ novirtual class StructEntry:
|
||||
# sha1(方法名带模板前缀),作废重译时 reset_module_methods_emitted
|
||||
# 按触发文件 sha1 精确重置 MethodsEmitted,防特化方法体残留 1 不补译
|
||||
# → 链接 undefined symbol(list[ast.ClassDef|t.CPtr].* 根因)
|
||||
IsForwardDecl: int # 1=前向声明占位条目(Ty=不透明具名结构体 {sha}.Class,FieldCount=0;
|
||||
# 定义侧随后**原位填充**同一 Ty 对象并清零本标记)。
|
||||
# 语义对齐 TPC:未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充。
|
||||
|
||||
|
||||
# ============================================================
|
||||
|
||||
@@ -665,6 +665,58 @@ novirtual class StructGenMixin:
|
||||
self._struct_idx_add(self.StructCount - 1, entry)
|
||||
return entry
|
||||
|
||||
# ============================================================
|
||||
# ensure_forward_class - 按"类名 + 定义模块 SHA1"前向声明一个类
|
||||
#
|
||||
# 语义对齐 TPC(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充):
|
||||
# 注解里出现"指向尚未注册的类的指针"时,返回 Ptr(本占位.Ty) 而不是匿名的
|
||||
# Ptr(i8)——后者会丢类身份、冻结成 i8* 且无法修复(Gen↔Generator 循环依赖
|
||||
# 使"按顺序先注册被指类"不可能)。定义侧命中该占位条目时**原位填充**同一
|
||||
# Ty 对象(保持 Ty 指针不变),所有已发出的 Ptr 引用继续有效。
|
||||
#
|
||||
# 幂等:已存在 (class_name, sha1) 条目则直接返回,不重复登记。
|
||||
# 返回占位/既有条目;参数非法或表满时返回 None。
|
||||
# ============================================================
|
||||
def ensure_forward_class(self, pool: memhub.MemBuddy*, sha1: str, class_name: str) -> GenTypes.StructEntry*:
|
||||
if pool is None or sha1 is None or class_name is None:
|
||||
return None
|
||||
if sha1[0] == '\0' or class_name[0] == '\0':
|
||||
return None
|
||||
existing: GenTypes.StructEntry* = self.find_struct_by_module(class_name, sha1)
|
||||
if existing is not None:
|
||||
return existing
|
||||
if self.init_struct_table(pool) == 0:
|
||||
return None
|
||||
if self.StructCount >= STRUCT_MAX:
|
||||
VLogger.error(f"table full, cannot forward-declare {class_name}", "STRUCT")
|
||||
return None
|
||||
# 具名不透明结构体 "%{sha}.{Class}"(fields=None, fcount=0):名称与定义侧
|
||||
# ClassGenDef 的 "sha1.ClassName" 逐字一致,故定义侧能按名定位并原位填充。
|
||||
# 名称与 Ty 必须按 StdlibAlloc 选**持久池**(与 register_struct 同口径):
|
||||
# 占位条目不随本文件 reset 回收,Ty/Name 若在瞬时 pool 会悬空。
|
||||
fwd_pool: memhub.MemBuddy* = pool
|
||||
if self.StdlibAlloc != 0:
|
||||
fwd_pool = CompilerFactory.factory.PermPool
|
||||
if fwd_pool is None:
|
||||
fwd_pool = pool
|
||||
sh_len: sizet = string.strlen(sha1)
|
||||
cn_len: sizet = string.strlen(class_name)
|
||||
name_buf: str = fwd_pool.alloc(sh_len + 1 + cn_len + 1)
|
||||
if name_buf is None:
|
||||
return None
|
||||
string.strcpy(name_buf, sha1)
|
||||
name_buf[sh_len] = '.'
|
||||
string.strcpy(name_buf + sh_len + 1, class_name)
|
||||
fwd_ty: llvmlite.LLVMType* = llvmlite.Struct(fwd_pool, None, 0, name_buf)
|
||||
if fwd_ty is None:
|
||||
return None
|
||||
entry: GenTypes.StructEntry* = self.register_struct(pool, class_name, fwd_ty, sha1)
|
||||
if entry is None:
|
||||
return None
|
||||
entry.IsForwardDecl = 1
|
||||
self.add_visible_struct(pool, class_name)
|
||||
return entry
|
||||
|
||||
def register_struct_alias(self, pool: memhub.MemBuddy*,
|
||||
name: str,
|
||||
sha1: str,
|
||||
|
||||
@@ -1946,6 +1946,68 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _forward_declare_ptr_from_node - 对"指向未注册类"的注解做前向声明
|
||||
#
|
||||
# 语义对齐 TPC(未注册类 → 具名空 CStruct 占位 + 后续 set_body 填充):
|
||||
# 注解里出现"指向尚未注册的类的指针"(该类尚未 register_struct,常见于类间
|
||||
# 循环引用 / 跨文件注册顺序)时,不再退回匿名的 Ptr(i8)——它丢类身份、被冻结
|
||||
# 成 i8* 后无法修复(现场 CallGenBase.Gen: Generator.Generator* 先于 Generator
|
||||
# 注册 ⇒ Gen 冻结 i8* ⇒ self.Gen.FuncGenBase 属性访问失败)。改为按"类名 +
|
||||
# 定义模块 SHA1"登记具名不透明结构体 {sha}.Class 占位,返回 Ptr(占位.Ty);
|
||||
# 定义侧命中占位时**原位填充**同一 Ty 对象(指针身份不变)。
|
||||
#
|
||||
# 仅支持 Attribute(Mod.Class / 别名 Mod.Class)与 Name(from-import 裸类名)两形。
|
||||
# 返回 Ptr(占位.Ty);无法确定类名/模块 SHA1 时返回 None(调用方保持旧行为)。
|
||||
# ============================================================
|
||||
def _forward_declare_ptr_from_node(self, pool: memhub.MemBuddy*,
|
||||
node: ast.AST*,
|
||||
imported_modules: str,
|
||||
from_imports: str) -> llvmlite.LLVMType*:
|
||||
if node is None:
|
||||
return None
|
||||
cls_nm: str = None
|
||||
sha1: str = None
|
||||
k: int = node.kind()
|
||||
if k == ast.ASTKind.Attribute:
|
||||
at: ast.Attribute* = ast.Attribute*(node)
|
||||
if at.attr is None or at.value is None:
|
||||
return None
|
||||
if at.value.kind() != ast.ASTKind.Name:
|
||||
return None
|
||||
mod_nm: ast.Name* = ast.Name*(at.value)
|
||||
if mod_nm.id is None:
|
||||
return None
|
||||
cls_nm = at.attr
|
||||
mod_buf: str = mod_nm.id
|
||||
# 4a. mod 是 import 别名时先经 from_imports 反查完整模块名
|
||||
if from_imports is not None:
|
||||
orig_mod: str = self.Gen.ImportsGen.lookup_from_import(from_imports, mod_buf, 0)
|
||||
if orig_mod is not None:
|
||||
sha1 = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, orig_mod)
|
||||
# 4b. mod 本身是完整模块路径(无别名 import)
|
||||
if sha1 is None:
|
||||
sha1 = self.Gen.CallGenBase._lookup_sha1_from_modname_raw(pool, mod_buf)
|
||||
elif k == ast.ASTKind.Name:
|
||||
nm: ast.Name* = ast.Name*(node)
|
||||
if nm.id is None:
|
||||
return None
|
||||
cls_nm = nm.id
|
||||
if from_imports is not None:
|
||||
sha1 = self.Gen.CallGenBase._lookup_mod_sha1(pool, from_imports, cls_nm, 0)
|
||||
else:
|
||||
return None
|
||||
if cls_nm is None or sha1 is None or sha1[0] == '\0':
|
||||
return None
|
||||
# 校正为类的**真定义模块**(包重导出会给包 sha1)
|
||||
sha1 = self._class_true_def_sha1(pool, cls_nm, sha1)
|
||||
if sha1 is None or sha1[0] == '\0':
|
||||
return None
|
||||
entry: GenTypes.StructEntry* = self.Gen.StructGen.ensure_forward_class(pool, sha1, cls_nm)
|
||||
if entry is None or entry.Ty is None:
|
||||
return None
|
||||
return llvmlite.Ptr(pool, entry.Ty)
|
||||
|
||||
def map_t_type(self, pool: memhub.MemBuddy*,
|
||||
type_name: str) -> llvmlite.LLVMType*:
|
||||
"""将 t.CXxx / c.CXxx 类型名映射到 LLVMType
|
||||
@@ -3311,9 +3373,17 @@ novirtual class TypeGenResolve(TypeGenBase.TypeGenBase):
|
||||
return llvmlite.Ptr(pool, other_cls_ty)
|
||||
# 类名未注册:可能是前向自我引用(如 json 的
|
||||
# 'JsonValue' | t.CPtr 字段引用自身类,类体处理中
|
||||
# 尚未注册)或 Phase A-pre 依赖类延迟到下一遍注册。
|
||||
# 回退 Ptr(i8) 由 AnnotClassName 恢复 + 多遍注册收敛
|
||||
# 处理,禁止 fail-fast(误杀合法前向引用)。
|
||||
# 尚未注册)、或 Phase A-pre 依赖类延迟到下一遍注册、
|
||||
# 或是**循环依赖**(Gen↔Generator)下被指类必然晚于本类。
|
||||
# 首选**前向声明**:按类名+定义模块 SHA1 登记具名不透明
|
||||
# 结构体 {sha}.Class 占位并返回 Ptr(占位.Ty),保住类身份
|
||||
# (定义侧随后原位填充)。无法确定类名/模块时,才退回
|
||||
# Ptr(i8) 由 AnnotClassName 恢复 + 多遍注册收敛,禁止
|
||||
# fail-fast(误杀合法前向引用)。
|
||||
_fwd_ptr_ty: llvmlite.LLVMType* = self._forward_declare_ptr_from_node(
|
||||
pool, other_node, imported_modules, from_imports)
|
||||
if _fwd_ptr_ty is not None:
|
||||
return _fwd_ptr_ty
|
||||
return llvmlite.Ptr(pool, llvmlite.Int8(pool))
|
||||
# `X | t.CPtr` **恒追加恰好 1 层**(用户口径,与 rawptr[T]/t.CPtr[T] 下标形
|
||||
# 以及 `_parse_type_string` 的 `T*` 反解析三处同源):
|
||||
|
||||
Reference in new issue
Block a user