From eef84657a12945036bdd601c275cb97ee89b1dda Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 12 Sep 2026 23:14:23 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpv):=20=E4=BF=AE=E6=AD=A3=20REnum=20?= =?UTF-8?q?=E8=A1=A8=E6=8C=89=E5=90=8D=E6=9F=A5=E6=89=BE=E4=B8=8E=20match?= =?UTF-8?q?=20=E5=88=86=E6=B4=BE=EF=BC=8C=E6=81=A2=E5=A4=8D=20str()=20?= =?UTF-8?q?=E6=8C=87=E9=92=88=E5=BC=BA=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit str() 对整数(sizet/i64)走 snprintf 十进制格式化(Python 语义),不是 i8* 强转;用它读 sizet|t.CPtr 联合表元素再 strcmp 永不相等 => _is_renum_class_name 恒 0、变体名查不到(__types.vp:407 bits / :159 Void 根因)。IR 实证 output/a8891afb66d3eda4.text.ll L5551-5558。 - ClassGenEnum: RenumClassNames/RenumVariantNames 读侧统一改 (str)(t.CVoid(x, t.CPtr));新增 _is_renum_by_entry 与 RenumTableDump;REnum 注册静默早退改 fail-fast - MatchGenMixin/CallGenMixin: match 分派 2a/2b 与变体构造入口并上 by_entry 判据 - TypeGenResolve: 泛型下标 Phase B 解析不出特化时 fail-fast - CallGenProp: 泛型特化名末段按括号外点切分 - CompilerFactory/ClassGenBase: REnum 注册表改挂编译期单例 - ClassGenDef: CEnum/REnum 分派歧义 fail-fast --- App/lib/Stub/StubGen/VpiReader.vp | 37 ++++++++ App/lib/core/CompilerFactory.vp | 27 ++++++ App/lib/core/Gen/AssignGenMixin.vp | 66 ++++++++++++++- App/lib/core/Gen/Call/CallGenMixin.vp | 16 +++- App/lib/core/Gen/Call/CallGenProp.vp | 23 ++++- App/lib/core/Gen/Class/ClassGenBase.vp | 25 +++--- App/lib/core/Gen/Class/ClassGenDef.vp | 11 +++ App/lib/core/Gen/Class/ClassGenEnum.vp | 107 +++++++++++++++++++++--- App/lib/core/Gen/MatchGenMixin.vp | 9 +- App/lib/core/Gen/Type/TypeGenResolve.vp | 27 ++++++ 10 files changed, 318 insertions(+), 30 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 686f531..23e2528 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -2095,6 +2095,36 @@ def _vpi_class_has_packed(decorator_list: list[ast.AST | t.CPtr] | t.CPtr) -> in return 0 +def _vpi_class_is_renum(class_cd: ast.ClassDef | t.CPtr) -> int: + """检查 vpi ClassDef 是否为 REnum(基类含 t.REnum),返回 1=是 / 0=否 + + vpi 文本保留原始基类(`class LLVMType(t.REnum):`),故可据此识别。 + REnum 布局 {i32 __tag, } 由 ClassGenEnum. + _translate_renum_def 在正式翻译时按变体计算;vpi 侧按普通 struct 注册 + 会得到错误布局(变体字段都在嵌套类里,直接 AnnAssign 扫描得到 0 字段)。 + """ + if class_cd is None or class_cd.bases is None: + return 0 + bases: list[ast.AST | t.CPtr] | t.CPtr = class_cd.bases + bn: sizet = len(bases) + for bi in range(bn): + base_node: ast.AST | t.CPtr = bases.get(bi) + if base_node is None: + continue + bk: int = base_node.kind() + # Attribute(t.REnum) 形式 + if bk == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(base_node) + if at.attr is not None and string.strcmp(at.attr, "REnum") == 0: + return 1 + # Name(REnum) 形式(from t import REnum) + elif bk == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(base_node) + if nm.id is not None and string.strcmp(nm.id, "REnum") == 0: + return 1 + return 0 + + # ============================================================ # _inject_class_struct_declare - 从 vpi ClassDef 注册类为 struct 类型 # @@ -2129,6 +2159,13 @@ def _inject_class_struct_declare(pool: rowptr[memhub.MemBuddy], if existing_c is not None and existing_c.Ty is not None: GenApi.add_visible_struct(pool, cls_name) return 1 + # REnum 类(class X(t.REnum))跳过:布局 {i32 __tag, } 由 + # ClassGenEnum._translate_renum_def 按变体计算;vpi 侧按普通 struct 注册 + # 会得到错误布局(变体字段都在嵌套类里,直接 AnnAssign 扫描得 0 字段)。 + # 留给源模块正式翻译注册。LLVMType 本就因 field_count==0 早退,此守卫把 + # 该行为显式化,避免将来 REnum 带直接字段时被误注册为普通 struct。 + if _vpi_class_is_renum(class_cd) != 0: + return 0 # 2. 收集字段类型(AnnAssign 子节点,仿 ClassGenDef.translate_class_def) field_names_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX) field_types_buf: sizet | t.CPtr = pool.alloc(8 * GenTypes.FIELD_MAX) diff --git a/App/lib/core/CompilerFactory.vp b/App/lib/core/CompilerFactory.vp index 2e7b933..be97066 100644 --- a/App/lib/core/CompilerFactory.vp +++ b/App/lib/core/CompilerFactory.vp @@ -10,6 +10,33 @@ class CompilerFactory: """编译器结构体工厂(持有主池/永久池等共享对象)""" Pool: rowptr[memhub.MemBuddy] # 主内存池(瞬时数据,mark/reset 回收) PermPool: rowptr[memhub.MemBuddy] # 永久内存池(持久数据,从不 reset) + + # ---- REnum 变体注册表(编译期单例持有,跨 Generator/Phase 存活)---- + # [D30 根治] 此前这 4 张表是 ClassGenBase 的**实例字段**,被 InitClassGen + # 在每个 Generator 构造时清零;而 struct entry 在 perm_pool 跨 Phase 存活。 + # 两者寿命不对称 ⇒ 新 Generator 建好后表被清空、struct 仍在 ⇒ + # _translate_renum_def 命中 existing 早退 → 变体表永久缺该类 → + # _is_renum_class_name 恒 0 → match 走 _handle_simple_match(无变体绑定)→ + # case 绑定名 NameError(includes/llvmlite/__types.vp L407 `bits` 根因)。 + # 表内存本就是 perm_pool 分配(设计上就是持久的),故把**指针与计数** + # 一并挂到编译期单例 factory 上,与 struct entry 同寿命。 + RenumClassNames: sizet | t.CPtr + RenumClassVariantCounts: sizet | t.CPtr + RenumVariantNames: sizet | t.CPtr + RenumVariantTags: sizet | t.CPtr + RenumClassCount: int + + # ---- REnum 变体字段别名表(同上,必须同寿命)---- + # _bind_variant_fields 靠 _lookup_renum_member_ty 查这些表拿到字段真实类型; + # 表被清 → 类型槽 0 → 绑定回退 payload 槽位 i64 → 指针字段被当整数读 → + # 递归传参/赋值 int→ptr 被拦(llvmlite __types.vp L448 CastError 根因)。 + RenumAliasClass: sizet | t.CPtr + RenumAliasField: sizet | t.CPtr + RenumAliasIndex: sizet | t.CPtr + RenumAliasTy: sizet | t.CPtr + RenumAliasVariant: sizet | t.CPtr + RenumAliasCount: int + def __init__(self, pool: rowptr[memhub.MemBuddy], perm_pool: rowptr[memhub.MemBuddy]): if self is None: return self.Pool = pool diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index d03f8b7..1e5d971 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -1239,7 +1239,9 @@ class AssignGenMixin: # 单参 t.CArray[elem_ty] + 列表字面量 → 静态数组(与 Handle 推断一致)。 # PreScan 预创建的 alloca 类型必须与 Handle 期望一致,否则 Handle 的 # get_or_create_sym 复用旧 alloca,数组推断失效仍走动态 list → _mbuddy。 - var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty) + var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty) + # [D31 fail-fast] 泛型注解退化成裸基类 → 立即终止(暴露退化点) + self._fail_on_degenerated_generic_annotation(aa, nm.id, annot, var_ty) else: # fast-fail: 注解类型解析失败,Python traceback 立即终止(不静默回退 i32) self.Gen.TypeGenErr.fatal_error( @@ -1341,6 +1343,66 @@ class AssignGenMixin: # # 返回新增的变量数 # ============================================================ + def _fail_on_degenerated_generic_annotation(self, + aa: ast.AnnAssign | t.CPtr, + var_name: str, + annot: ast.AST | t.CPtr, + var_ty: llvmlite.LLVMType | t.CPtr) -> int: + """fail-fast:泛型注解解析成了「裸泛型基类指针」(丢失类型实参) + + 场景:注解是泛型下标 `list[X]` 或 `list[X] | t.CPtr`,但 + `resolve_annotation_type` 退化成裸基类 `list*`(没有 `[...]`) + ⇒ 之后该变量的任何泛型方法调用(`.get()`/`.set()`/...)都会按**错误** + 元素类型特化(实测 `list[int].get` 返回 i32)⇒ `inttoptr` 把 i32 当 + 指针 ⇒ 读垃圾 AST 节点(D21/D25/D31 崩溃族,ASan 现场 + `MatchGenMixin._handle_simple_match → StmtGenMixin._translate_stmt_impl`)。 + + 与 D25 既有守卫的区别:那条守卫判「复用到的**旧 alloca** 是裸泛型基类」; + 本守卫判「**本次注解解析结果本身**就是裸泛型基类」,覆盖「旧 alloca 恰好 + 也是裸类型 → 两边一致 → 既有守卫不触发」的漏网路径。 + + 返回 0=未退化(继续);退化则 fatal_error 终止(不返回)。 + """ + if aa is None or annot is None or var_ty is None: + return 0 + # 取注解里的泛型 Subscript 一侧:Subscript 本身,或 A | t.CPtr 的 Subscript 分支 + ann_sub: ast.Subscript | t.CPtr = None + if annot.kind() == ast.ASTKind.Subscript: + ann_sub = (ast.Subscript | t.CPtr)(annot) + elif annot.kind() == ast.ASTKind.BinOp: + bop_ff: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot) + if bop_ff.op == ast.OpKind.BitOr: + if bop_ff.left is not None and bop_ff.left.kind() == ast.ASTKind.Subscript: + ann_sub = (ast.Subscript | t.CPtr)(bop_ff.left) + elif bop_ff.right is not None and bop_ff.right.kind() == ast.ASTKind.Subscript: + ann_sub = (ast.Subscript | t.CPtr)(bop_ff.right) + if ann_sub is None or ann_sub.value is None \ + or ann_sub.value.kind() != ast.ASTKind.Name: + return 0 + base_nm_ff: ast.Name | t.CPtr = (ast.Name | t.CPtr)(ann_sub.value) + if base_nm_ff.id is None: + return 0 + # 仅当该基名确实是泛型模板(list/GSList/...)才判定为退化 + if self.Gen.ClassGenGeneric._find_generic_template(base_nm_ff.id) is None: + return 0 + if var_ty.Pointee is None: + return 0 + vt_nm_ff: str = llvmlite.get_struct_name(var_ty.Pointee) + if vt_nm_ff is None: + return 0 + vt_pf_ff: int = GenApi.sha1_prefix_len(vt_nm_ff) + vt_short_ff: str = vt_nm_ff + vt_pf_ff if vt_pf_ff != 0 else vt_nm_ff + if string.strchr(vt_short_ff, '[') is not None: + return 0 # 带类型实参:未退化 + self.Gen.TypeGenErr.fatal_error( + aa, + f"变量 '{var_name}' 的泛型注解 '{base_nm_ff.id}[...]' 解析成了裸基类 " + f"'{vt_nm_ff}*'(丢失类型实参);后续 .get()/.set() 会按错误元素类型" + f"特化(如 list[int].get 返回 i32)→ inttoptr 截断指针崩溃。" + f"禁止静默继续——请检查该注解在 resolve_annotation_type 中的退化点。", + "TypeError") + return 0 + def translate_annassign(self, trans: HT.Translator | t.CPtr, node: ast.AST | t.CPtr) -> int: """翻译 AnnAssign(target=Name, annotation=..., value=expr)""" @@ -1551,6 +1613,8 @@ class AssignGenMixin: # 与 PreScan 推断一致:get_or_create_sym 复用 PreScan 创建的 alloca, # 类型一致才能命中局部数组快路径,避免 _find_pool_var 兜底引用 @_mbuddy。 var_ty = self._infer_static_array_ty_from_literal(pool, annot, aa.value, var_ty) + # [D31 fail-fast] 泛型注解退化成裸基类 → 立即终止(暴露退化点) + self._fail_on_degenerated_generic_annotation(aa, nm.id, annot, var_ty) # 普通局部变量 # 创建 alloca diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index 064d44e..f1854ed 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -457,10 +457,24 @@ class CallGenMixin(CallGenBase.CallGenBase): if rc_cls_node.id is not None: rc_cls_name: str = rc_cls_node.id # 检查 ClassName 是否是已注册的 REnum 类 - if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0: + # [D32] 并上 _is_renum_by_entry(struct 带 __tag 即判 REnum): + # 变体表因任何原因缺失时,入口至少不被表的空状态挡住; + # 若 tag 仍查不到则下方 fail-fast 报出表状态(不再落到 + # "外部函数返回类型未登记 ... Metadata" 这种误导报错)。 + if self.Gen.ClassGenEnum._is_renum_class_name(rc_cls_name) != 0 \ + or self.Gen.ClassGenEnum._is_renum_by_entry(rc_cls_name) != 0: # 在该 REnum 类中查找变体名 func_name rc_tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class( rc_cls_name, func_name) + if rc_tag < 0: + # [D32 fail-fast] 是 REnum 却查不到变体名 ⇒ 变体表不完整, + # 继续走外部函数路径会报无关的 "外部函数返回类型未登记"。 + self.Gen.TypeGenErr.fatal_error( + cl, + f"REnum 类 '{rc_cls_name}' 的变体 '{func_name}' 未在变体表中找到" + f"({self.Gen.ClassGenEnum.RenumTableDump()});" + f"变体表未注册或变体名不匹配", + "NameError") if rc_tag >= 0: return self.Gen.CallGenCtor._translate_renum_construct( pool, builder, mod, rc_cls_name, func_name, diff --git a/App/lib/core/Gen/Call/CallGenProp.vp b/App/lib/core/Gen/Call/CallGenProp.vp index 44381ef..35d59e9 100644 --- a/App/lib/core/Gen/Call/CallGenProp.vp +++ b/App/lib/core/Gen/Call/CallGenProp.vp @@ -170,9 +170,26 @@ class CallGenProp(CallGenBase.CallGenBase): trans.SymTab, obj_nm.id) annot_val: str = self.Gen.VarGen.var_annot_class_name(var_entry) if annot_val is not None: - dot_ptr: str = string.strrchr(annot_val, '.') - if dot_ptr is not None: - return dot_ptr + 1 + # [D32] 取「模块限定名」末段时只按**括号外**的 '.' 切分。 + # 此前用 string.strrchr 在整个串上找最后一个 '.':泛型特化名 + # "list[e94ef9e139461915.AST|t.CPtr]" 的 '.' 落在 '[' 内 ⇒ + # 返回 "AST|t.CPtr]" 这种垃圾短名 ⇒ 后续按它查表必落空, + # 进而可能被兜底成任意特化(.get() 误分派)。 + n_av: sizet = string.strlen(annot_val) + last_dot_av: sizet = n_av + depth_av: int = 0 + ai_av: sizet = 0 + while ai_av < n_av: + ch_av: int = annot_val[ai_av] + if ch_av == '[': + depth_av += 1 + elif ch_av == ']': + depth_av -= 1 + elif ch_av == '.' and depth_av == 0: + last_dot_av = ai_av + ai_av += 1 + if last_dot_av < n_av: + return annot_val + last_dot_av + 1 return annot_val return None # ============================================================ diff --git a/App/lib/core/Gen/Class/ClassGenBase.vp b/App/lib/core/Gen/Class/ClassGenBase.vp index 412bf70..9932649 100644 --- a/App/lib/core/Gen/Class/ClassGenBase.vp +++ b/App/lib/core/Gen/Class/ClassGenBase.vp @@ -1226,15 +1226,18 @@ class ClassGenBase: self.MethodMetaKeys = None self.MethodMetaFlags = None self.MethodMetaCount = 0 - self.RenumClassNames = None - self.RenumClassVariantCounts = None - self.RenumVariantNames = None - self.RenumVariantTags = None - self.RenumClassCount = 0 - self.RenumAliasClass = None - self.RenumAliasField = None - self.RenumAliasIndex = None - self.RenumAliasCount = 0 + # [D30 根治] REnum 变体注册表挂在编译期单例 factory 上(perm_pool 后端), + # 与 struct entry 同寿命:此处**恢复**而非清零。清零会让新 Generator + # 丢掉前序已注册的 REnum 变体(`bits` NameError 根因),见 CompilerFactory。 + self.RenumClassNames = CompilerFactory.factory.RenumClassNames + self.RenumClassVariantCounts = CompilerFactory.factory.RenumClassVariantCounts + self.RenumVariantNames = CompilerFactory.factory.RenumVariantNames + self.RenumVariantTags = CompilerFactory.factory.RenumVariantTags + self.RenumClassCount = CompilerFactory.factory.RenumClassCount + self.RenumAliasClass = CompilerFactory.factory.RenumAliasClass + self.RenumAliasField = CompilerFactory.factory.RenumAliasField + self.RenumAliasIndex = CompilerFactory.factory.RenumAliasIndex + self.RenumAliasCount = CompilerFactory.factory.RenumAliasCount self.SpecKeys = None self.SpecNames = None self.SpecCount = 0 @@ -1247,8 +1250,8 @@ class ClassGenBase: self.VtMethodNames = None self.VtParentSha1s = None self.VtMethodCount = 0 - self.RenumAliasTy = None - self.RenumAliasVariant = None + self.RenumAliasTy = CompilerFactory.factory.RenumAliasTy + self.RenumAliasVariant = CompilerFactory.factory.RenumAliasVariant self.ClassMetaKinds = None self.ClassMetaClss = None self.ClassMetaFields = None diff --git a/App/lib/core/Gen/Class/ClassGenDef.vp b/App/lib/core/Gen/Class/ClassGenDef.vp index 140dc04..c690489 100644 --- a/App/lib/core/Gen/Class/ClassGenDef.vp +++ b/App/lib/core/Gen/Class/ClassGenDef.vp @@ -51,7 +51,18 @@ class ClassGenDef(ClassGenBase.ClassGenBase): self.Gen.StructGen.add_visible_struct(pool, class_name) # 枚举类:交给 HandlesEnum 处理 + # [D32 fail-fast] 分派歧义检测:REnum 优先于 CEnum/CUnion。若某类同时被判 + # 为枚举,说明它被 `_is_enum_class` 抢先截胡 → 走 _translate_enum_def ⇒ + # **变体表永不注册** ⇒ `_is_renum_class_name` 恒 0、变体构造拿不到 tag + # (includes/llvmlite/__types.vp L703 `LLVMType.Metadata()` 被当外部函数 + # 根因)。两者语义互斥,禁止静默按枚举处理。 if self.Gen.ClassGenBase._is_enum_class(cd, trans) == 1: + if self.Gen.ClassGenBase._is_renum_class(cd, trans) == 1: + self.Gen.TypeGenErr.fatal_error( + cd, + f"类 '{class_name}' 同时被判定为 CEnum 与 REnum(分派歧义)," + f"REnum 变体表将无法注册", + "TypeError") return self.Gen.ClassGenEnum._translate_enum_def(trans, cd) # 联合体类:交给联合体处理 diff --git a/App/lib/core/Gen/Class/ClassGenEnum.vp b/App/lib/core/Gen/Class/ClassGenEnum.vp index 2dfaa2a..d30eb59 100644 --- a/App/lib/core/Gen/Class/ClassGenEnum.vp +++ b/App/lib/core/Gen/Class/ClassGenEnum.vp @@ -633,13 +633,34 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): existing: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct_by_module( class_name, trans.ModuleSha1) if existing is not None: - if existing.Ty is not None: - llvmlite.module_add_named_type(trans.Module, pool, existing.Ty) - return 0 + # [D30 契约修复] existing 只保证 struct entry 在,不保证 REnum 语义(变体表)在。 + # struct entry 由 register_struct 写 StructTable,跨 Phase 存活(perm_pool); + # 而变体表是 gen 实例字段(InitClassGen 每个 Generator 重置)。 + # 若此处无条件早退,下方 L819 的 _register_renum_variants 永不再执行 ⇒ + # 变体表缺该类 ⇒ _is_renum_class_name 恒 0 ⇒ _translate_match_impl 走 + # _handle_simple_match(无变体绑定)⇒ case 绑定名未定义 + # (includes/llvmlite/__types.vp L407 `case LLVMType.Int(bits):` 的 bits 根因, + # [RN] LLVMType -> 0 (scan count=1) 实证)。 + if self._is_renum_class_name(class_name) != 0: + if existing.Ty is not None: + llvmlite.module_add_named_type(trans.Module, pool, existing.Ty) + return 0 + # 变体表缺失:中和旧 entry(对齐 ClassGenDef needs_rereg 先例),使 + # find_struct_by_module 不再命中(按 Name 精确匹配),走下方完整注册路径 + # 以同时修正 struct 布局 + 变体表。 + if existing.Name is not None: + existing.Name[0] = '\0' + existing.Ty = None + existing.FieldCount = 0 children: list[ast.AST | t.CPtr] | t.CPtr = cd.children if children is None: - VLogger.warning(f"{class_name} has no body", "RENUM") + # [D32 fail-fast] 此前只 warning + return 0:变体表**静默不注册** → + # _is_renum_class_name 恒 0、变体构造拿不到 tag(__types.vp L703 + # `LLVMType.Metadata()` 被当外部函数根因)。禁止静默跳过。 + self.Gen.TypeGenErr.fatal_error( + cd, f"REnum 类 '{class_name}' 缺少类体(children 为 None),无法注册变体表", + "TypeError") return 0 cn: sizet = len(children) @@ -711,7 +732,11 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): variant_count += 1 if variant_count == 0: - VLogger.warning(f"{class_name} has no variants", "RENUM") + # [D32 fail-fast] 同上:变体数为 0 时此前静默 return → 变体表不注册。 + # 对 `class X(t.REnum)` 而言变体数必 > 0,为 0 说明 AST 变体类丢失。 + self.Gen.TypeGenErr.fatal_error( + cd, f"REnum 类 '{class_name}' 的变体数为 0(嵌套变体类丢失),无法注册变体表", + "TypeError") return 0 # 计算每个位置的最大字段大小 @@ -923,6 +948,13 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): string.memset(self.RenumVariantNames, 0, 8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) string.memset(self.RenumVariantTags, 0, 8 * RENUM_CLASS_MAX * RENUM_VARIANT_PER_CLASS) self.RenumClassCount = 0 + # [D30 根治] 首次分配后把指针登记到编译期单例 factory(与 struct entry + # 同寿命);后续新建的 Generator 由 InitClassGen 从此处恢复,不再清零。 + CompilerFactory.factory.RenumClassNames = self.RenumClassNames + CompilerFactory.factory.RenumClassVariantCounts = self.RenumClassVariantCounts + CompilerFactory.factory.RenumVariantNames = self.RenumVariantNames + CompilerFactory.factory.RenumVariantTags = self.RenumVariantTags + CompilerFactory.factory.RenumClassCount = self.RenumClassCount #rcv_chk: uint64 = uint64(self.RenumClassVariantCounts) #if rcv_chk == 0 or rcv_chk < 4096: @@ -931,7 +963,7 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): # 检查是否已注册(避免重复注册) for i in range(self.RenumClassCount): - existing: str = self.RenumClassNames[i] + existing: str = (str)(t.CVoid(self.RenumClassNames[i], t.CPtr)) if existing is not None and string.strcmp(existing, class_name) == 0: return @@ -980,6 +1012,9 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): tag += 1 self.RenumClassCount = idx + 1 + # [D30 根治] 回写计数到编译期单例(新建 Generator 靠它恢复;不回写会导致 + # 新 Generator 只看到旧计数 → 后续注册的变体不可见) + CompilerFactory.factory.RenumClassCount = self.RenumClassCount def _lookup_renum_variant(self, variant_name: str) -> str: """查找变体名所属的 REnum 类名 @@ -992,17 +1027,58 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): vcount: int = self.RenumClassVariantCounts[i] for j in range(vcount): name_offset: sizet = i * RENUM_VARIANT_PER_CLASS + j - vname: str = self.RenumVariantNames[name_offset] + vname: str = (str)(t.CVoid(self.RenumVariantNames[name_offset], t.CPtr)) if vname is not None and string.strcmp(vname, variant_name) == 0: - cls_nm: str = self.RenumClassNames[i] + cls_nm: str = (str)(t.CVoid(self.RenumClassNames[i], t.CPtr)) return cls_nm return None + def RenumTableDump(self) -> str: + """诊断用:变体表摘要(类数 / 首类名 / 该变体数 / 变体首项) + + 用于区分「类根本没注册」与「类在但变体名没注册」两类失败。 + """ + buf: str = CompilerFactory.factory.PermPool.alloc(256) + if buf is None: + return "-" + buf[0] = '\0' + cls_first: str = "-" + v_first: str = "-" + n_first_v: int = 0 + if self.RenumClassNames is not None and self.RenumClassCount > 0: + cls_first = (str)(t.CVoid(self.RenumClassNames[0], t.CPtr)) + if self.RenumVariantNames is not None \ + and self.RenumClassVariantCounts is not None: + n_first_v = self.RenumClassVariantCounts[0] + if n_first_v > 0: + v_first = (str)(t.CVoid(self.RenumVariantNames[0], t.CPtr)) + viperlib.snprintf(buf, 256, "count=%d first=%s vcount=%d vfirst=%s", + self.RenumClassCount, cls_first, n_first_v, v_first) + return buf + + def _is_renum_by_entry(self, class_name: str) -> int: + """不依赖变体表的 REnum 判定:struct 定义里带 `__tag` 字段即为 REnum + + `_translate_renum_def` 必定 `add_field(entry, "__tag", i32)`,故 struct + 定义本身即持久凭证(perm_pool)。用途:变体表因**任何**原因缺失时, + match 分派仍能走带变体绑定的 `_handle_renum_match` 路径,避免退化为 + `_handle_simple_match`(不绑定)→ case 绑定名 NameError + (includes/llvmlite/__types.vp L407 `bits` 根因)。 + """ + if class_name is None: + return 0 + st: llvmlite.LLVMType | t.CPtr = self.Gen.StructGen.get_struct_type(class_name) + if st is None: + return 0 + if self.Gen.StructGen.lookup_field(st, "__tag") is None: + return 0 + return 1 + def _is_renum_class_name(self, class_name: str) -> int: """检查名称是否是已注册的 REnum 类名,返回 1=是 / 0=否""" if class_name is None or self.RenumClassNames is None: return 0 for i in range(self.RenumClassCount): - existing: str = self.RenumClassNames[i] + existing: str = (str)(t.CVoid(self.RenumClassNames[i], t.CPtr)) if existing is not None and string.strcmp(existing, class_name) == 0: return 1 return 0 @@ -1011,13 +1087,13 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): if class_name is None or variant_name is None or self.RenumClassNames is None: return -1 for i in range(self.RenumClassCount): - existing: str = self.RenumClassNames[i] + existing: str = (str)(t.CVoid(self.RenumClassNames[i], t.CPtr)) if existing is None or string.strcmp(existing, class_name) != 0: continue vcount: int = self.RenumClassVariantCounts[i] for j in range(vcount): name_offset: sizet = i * RENUM_VARIANT_PER_CLASS + j - vname: str = self.RenumVariantNames[name_offset] + vname: str = (str)(t.CVoid(self.RenumVariantNames[name_offset], t.CPtr)) if vname is not None and string.strcmp(vname, variant_name) == 0: return self.RenumVariantTags[name_offset] return -1 @@ -1058,6 +1134,13 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): string.memset(self.RenumAliasTy, 0, 8 * RENUM_ALIAS_MAX) string.memset(self.RenumAliasVariant, 0, 8 * RENUM_ALIAS_MAX) self.RenumAliasCount = 0 + # [D30 根治] 同上:别名表指针登记到编译期单例,跨 Generator 存活 + CompilerFactory.factory.RenumAliasClass = self.RenumAliasClass + CompilerFactory.factory.RenumAliasField = self.RenumAliasField + CompilerFactory.factory.RenumAliasIndex = self.RenumAliasIndex + CompilerFactory.factory.RenumAliasTy = self.RenumAliasTy + CompilerFactory.factory.RenumAliasVariant = self.RenumAliasVariant + CompilerFactory.factory.RenumAliasCount = self.RenumAliasCount if self.RenumAliasCount >= RENUM_ALIAS_MAX: return @@ -1117,6 +1200,8 @@ class ClassGenEnum(ClassGenBase.ClassGenBase): string.strcpy(vn_buf_rfa, variant_name) self.RenumAliasVariant[idx] = sizet(vn_buf_rfa) self.RenumAliasCount = idx + 1 + # [D30 根治] 回写计数到编译期单例(新建 Generator 靠它恢复) + CompilerFactory.factory.RenumAliasCount = self.RenumAliasCount def _lookup_renum_field_alias(self, class_name: str, field_name: str) -> int: """查找 REnum 变体字段名的 payload 索引,返回索引或 -1 diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index c181cdc..d4a7f8a 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -136,8 +136,10 @@ class MatchGenMixin: trans.SymTab, nm.id) entry_annot: str = self.Gen.VarGen.var_annot_class_name(entry) if entry_annot is not None \ - and self.Gen.ClassGenEnum._is_renum_class_name( - entry_annot) != 0: + and (self.Gen.ClassGenEnum._is_renum_class_name( + entry_annot) != 0 + or self.Gen.ClassGenEnum._is_renum_by_entry( + entry_annot) != 0): is_renum = 1 renum_name = entry_annot subject_ptr = self.Gen.VarGen.lookup_var(trans.SymTab, nm.id) @@ -159,7 +161,8 @@ class MatchGenMixin: and pat.kind() == ast.ASTKind.MatchClass: rnm: str = self._extract_renum_name_from_cls(pat) if rnm is not None \ - and self.Gen.ClassGenEnum._is_renum_class_name(rnm) != 0: + and (self.Gen.ClassGenEnum._is_renum_class_name(rnm) != 0 + or self.Gen.ClassGenEnum._is_renum_by_entry(rnm) != 0): is_renum = 1 renum_name = rnm if subject_ptr is None: diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 743b14d..3cfeb10 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -1825,6 +1825,18 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): self.Gen.TypeGenBase.pop_trace() if spec_name_chk is not None: gen_spec_ty = self.Gen.StructGen.get_struct_type(gen_mangled) + # [D31] gen_mangled 由本函数自行拼接(_get_type_name_from_annotation + # + _norm_type_arg_name),而 _specialize_generic_class 内部还会 + # 对每个实参再走一次规范化,两处命名可能不一致:如元素注解 + # `ast.AST | t.CPtr` 本函数拼出 "list[.AST|t.CPtr]", + # 特化侧规范化为 "list[.ASTptr]"(联合折叠为指针名)。 + # 此时按 gen_mangled 重查恒 miss → 静默退化为裸 list* → + # 之后 list.get() 误分派(返回 i32)→ inttoptr 截断指针 → + # 读垃圾 AST 节点崩溃(D21/D25 崩溃族根因之一)。 + # spec_name_chk 是特化侧返回的**权威特化名**,必须按它重查。 + # 注:非联合实参(如 list[int])两侧命名一致,本步恒 miss 不改变行为。 + if gen_spec_ty is None: + gen_spec_ty = self.Gen.StructGen.get_struct_type(spec_name_chk) if gen_spec_ty is not None: return gen_spec_ty else: @@ -1835,6 +1847,21 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): VLogger.info(f"[GENERIC-DBG] 模板未注册: {gen_cls_name}", "Phase1") else: VLogger.info(f"[GENERIC-DBG] trans 为 None: {gen_cls_name}", "Phase1") + # [D32 治本 fail-fast] Phase B(declare_only==0)全量翻译时,**泛型模板类**的 + # 下标仍解析不出特化结构体 ⇒ 上游注解必然退化: + # * BinOp(`list[X] | t.CPtr`) 分支会落到 `Ptr(i8)`(丢类型); + # * 其它路径可能退化到裸基类 `list*`。 + # 二者后续的 `.get()` 都会按错误元素类型分派(实测 list[int].get 返回 i32) + # → inttoptr 截断指针 → 读垃圾 AST 节点崩溃(D21/D25/D31/D32 崩溃族)。 + # 禁止静默退化:报出查表键与实际模板名,直接暴露退化点。 + # 仅当基名确为泛型模板时才触发——避免误伤 rowptr/CArray 等 builtin 下标。 + if trans is not None and trans._declare_only == 0 \ + and self.Gen.ClassGenGeneric._find_generic_template(gen_cls_name) is not None: + self.Gen.TypeGenErr.fatal_error( + sub, + f"泛型下标 '{gen_mangled}' 未解析出特化结构体(泛型模板='{gen_cls_name}');" + f"该注解将退化为裸基类/i8* → 后续 .get() 按错误元素类型分派并截断指针", + "TypeError") # 未特化:返回 None(不报错,让后续流程处理) return None def resolve_annotation_type(self, pool: rowptr[memhub.MemBuddy],