fix(tpv): 修正 REnum 表按名查找与 match 分派,恢复 str() 指针强转

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
This commit is contained in:
TermiNexus committed 2026-09-12 23:14:23 +08:00
1 parent e0e024ee93
commit eef84657a1
10 files changed
+318 -30

No files matched your search

+37
View File
@@ -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, <max_variant_payload>} 由 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, <payload>} 由
# 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)
+27
View File
@@ -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
+65 -1
View File
@@ -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
+15 -1
View File
@@ -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,
+20 -3
View File
@@ -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
# ============================================================
+14 -11
View File
@@ -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
+11
View File
@@ -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)
# 联合体类:交给联合体处理
+96 -11
View File
@@ -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
+6 -3
View File
@@ -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:
+27
View File
@@ -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[<sha1>.AST|t.CPtr]",
# 特化侧规范化为 "list[<sha1>.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],