fix(match): REnum 变体字段绑定不再静默跳过(__types.vp bits NameError)

根因:_handle_renum_match step6 用 renum_name 查 struct 且不校验 Ty;拿不到 entry/Ty 时 step7 的 _bind_variant_fields 被整段静默跳过 -> case 绑定名在 body 里未定义,最终只报 NameError(bits),真因藏到下游

1) 名字回退:变体表与 struct 注册都按裸类名,而 renum_name 来自注解 SourceName(Attribute 可带模块限定名)-> 精确查找失配;与 tag 查表同一裸名推导(tag_cls_bare)

2) 新增 StructGenMixin.find_struct_entry_with_ty:跳过 D30 重注册时就地中和的旧 entry(Name[0]=0/Ty=None/FieldCount=0),避免命中它拿到 Ty=None

3) 禁止静默:仍有 REnum 判据为真却无法绑定时 fail-fast,打印 class/查表名/entry/FieldCount/ty_present(对齐 D30/D32 的响亮失败口径)
This commit is contained in:
TermiNexus committed 2026-09-27 23:58:15 +08:00
1 parent a79e723e8d
commit 5e100e2195
2 files changed
+66 -4

No files matched your search

+40 -4
View File
@@ -572,12 +572,25 @@ novirtual class MatchGenMixin:
llvmlite.build_switch(builder, tag_val, default_bb, sw_head, sw_count)
# 6. 查找 REnum 结构体类型(用于变体字段绑定的 GEP)
# 两条健壮性要求——否则 renum_entry/renum_ty 取不到,step 7 的变体字段绑定会被
# **整段静默跳过** ⇒ case 绑定名在 body 里未定义(NameError:__types.vp
# `_TypePrintDepth` 的 `bits` 根因,真因因此藏到下游):
# (1) 名字:变体表与 struct 注册都按**裸类名**(ClassDef.name),而 renum_name
# 来自注解书写形态(Spec.SourceName,Attribute 可带模块限定名,如
# "c93534fe43c0ee56.LLVMType")⇒ 精确查找可能失配 → 回退 tag_cls_bare。
# (2) 条目有效性:find_struct 是"首注册命中",可能命中 D30 重注册时被就地
# 中和的旧 entry(Ty=None)⇒ 扫表取 Ty 有效者。
renum_entry: GenTypes.StructEntry* = None
renum_ty: llvmlite.LLVMType* = None
se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(renum_name)
if se is not None:
if se is not None and se.Ty is not None:
renum_entry = se
renum_ty = se.Ty
else:
se_v: GenTypes.StructEntry* = self.Gen.StructGen.find_struct_entry_with_ty(renum_name, tag_cls_bare)
if se_v is not None:
renum_entry = se_v
renum_ty = se_v.Ty
# 7. 第二遍:翻译每个 case body
i = 0
@@ -607,9 +620,32 @@ novirtual class MatchGenMixin:
self.Gen.VarGen.enter_scope(trans.SymTab, SCOPE_MATCH)
# 变体字段绑定(MatchClass)
if pat is not None and pat.kind() == ast.ASTKind.MatchClass \
and renum_entry is not None and renum_ty is not None:
self._bind_variant_fields(pat, renum_name, renum_entry, renum_ty, subject_ptr, trans, pool, builder)
if pat is not None and pat.kind() == ast.ASTKind.MatchClass:
if renum_entry is not None and renum_ty is not None:
self._bind_variant_fields(pat, renum_name, renum_entry, renum_ty, subject_ptr, trans, pool, builder)
elif (self.Gen.ClassGenEnum._is_renum_class_name(tag_cls_bare) != 0
or self.Gen.ClassGenEnum._is_renum_class_name(renum_name) != 0
or self.Gen.ClassGenEnum._is_renum_by_entry(tag_cls_bare) != 0
or self.Gen.ClassGenEnum._is_renum_by_entry(renum_name) != 0):
# 走到这里 ⇒ 分派判定为 REnum match(同一组判据至少一条为真),而
# MatchClass 却拿不到可用的 struct entry/类型:此前**静默跳过**绑定
# ⇒ 绑定名在 body 里未定义,最终报 NameError(__types.vp `bits`),
# 真因被藏到下游。此处 fail-fast 并打印现场,便于定位是"变体表缺该类"
# 还是"条目被中和/Ty 缺失"。
ent_nm: str = "None"
ent_fc: int = -1
if renum_entry is not None:
ent_nm = renum_entry.Name
ent_fc = renum_entry.FieldCount
ty_present: int = 0
if renum_ty is not None:
ty_present = 1
self.Gen.TypeGenErr.fatal_error(pat,
f"REnum match 变体字段绑定失败:class='{tag_cls_bare}'(查表名='{renum_name}')"
f" entry='{ent_nm}' FieldCount={ent_fc} ty_present={ty_present};"
f"变体表已登记该类,说明结构体条目或 Ty 缺失(已不再静默跳过绑定,"
f"否则绑定名未定义只会在下游报 NameError)",
"InternalCompilerError")
# 翻译 case body
body_list: list[ast.AST*]* = mc.children
+26
View File
@@ -733,6 +733,32 @@ novirtual class StructGenMixin:
s_rm = StubMergerStore.LookupSha1ByModNameSuffix(mod_name)
return s_rm
def find_struct_entry_with_ty(self, name: str, alt_name: str) -> GenTypes.StructEntry*:
"""按名字(含备选名)查**带有效 Ty** 的结构体条目;找不到返回 None。
为什么需要它:`find_struct` 的语义是"首注册命中"。D30 的 REnum 重注册把旧
entry **就地中和**(`Name[0]='\\0'` / `Ty=None` / `FieldCount=0`)后再追加新
entry;若名索引/线性扫描先命中那条已中和的旧条目,调用方拿到的 `Ty` 是 None ——
依赖 `Ty` 的代码(如 match 的 REnum 变体字段绑定)会**静默跳过**,症状表现为
case 绑定名未定义(NameError,真因被藏到下游)。本函数跳过 `Ty` 无效的同名条目,
返回首个 `Ty` 有效的同名(或备选名)条目;都没有则返回 None,由调用方决定报错。
name / alt_name:主名与备选名(如模块限定名与裸类名),任一命中即可。
"""
if self.StructTable is None:
return None
for i in range(self.StructCount):
entry: GenTypes.StructEntry* = self._get_struct_entry(i)
if entry is None or entry.Name is None or entry.Ty is None:
continue
if entry.Name[0] == '\0':
continue
if string.strcmp(entry.Name, name) == 0:
return entry
if alt_name is not None and string.strcmp(entry.Name, alt_name) == 0:
return entry
return None
def find_struct(self, name: str) -> GenTypes.StructEntry*:
"""按类名查找结构体,返回 StructEntry 或 None