fix(tpc): 类型静默降级改 fail-fast + 成员槽写入失败附来源明细

a 编译器缺陷:类型信息缺失时静默兜底会掩盖上游根因,兜出来的"看着合理"的类型
(如 i8**)又会让下游报出与真因无关的错。按既定铁律改为响亮失败:

1) HandlesImports._parse_llvm_type:被指类型是具名聚合(%...)却在
   create_structs=True 下取不到结构体时,原静默 `return PointerType(i8)` ——
   结构体指针被悄悄变成通用指针,外层再套一层即成 i8**。改 fail-fast。
   (create_structs=False 属"尚未加载",非异常,保持原样。)

2) HandlesTypeMerge._HandleSubscript:新增判据自洽守卫 —— base 是指针标记
   (rawptr / t.CPtr / 裸名 CPtr / 别名)而 IsPtrSubscriptNode 未命中时,
   历史上会落到 CollectAll 当"数组下标"解析、静默产出「基座 void + 多层指针」
   (i8**)。现 fail-fast(判据分裂即内部不变量被破坏)。

3) HandlesAssign._HandleAttributeStoreLlvm(`self.字段 = 值`):成员槽写入失败
   时追加「成员槽来源」明细 —— class_members 声明侧(项数/base/下标/声明槽类型)
   + 布局侧(struct IR 名/元素数/elements[offset])+ structs 键数与是否同一对象,
   使"声明侧退化"与"布局侧退化"一眼可分,杜绝再靠猜。

现场:TransPyV/App/lib/core/Gen/Generator.vp:193 `self.ExprGenConst = SubExprGenConst`
  → 指针隐式转换已禁止(层数不同):不能把指针类型
    %"cfd1f3d0d7e87fea.ExprGenConst"* 隐式转为 i8**
This commit is contained in:
TermiNexus committed 2026-09-28 23:59:29 +08:00
1 parent 49f0386c33
commit c49dc1d915
3 files changed
+90 -2

No files matched your search

+62 -1
View File
@@ -847,7 +847,19 @@ class AssignHandle(BaseHandle):
Gen._store(Value, Gen.variables[VarName])
return
MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
# 成员槽写入失败时补「来源明细」:槽类型的两个来源(class_members
# 声明侧 / struct body 布局侧)是两条独立链,不一致时会静默写进
# 类型完全不同的槽。报文里逐项列出两侧数据,直接判定谁退化了
# (现场:Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。
try:
self._StoreWithCoerce(Value, MemberPtr)
except Exception as _Me:
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
if not isinstance(_Me, _FFE):
raise
raise _FFE(
f"{_Me}{self._DescribeMemberSlot(Gen, ClassName, AttrName, offset, SelfPtr)}"
) from _Me
elif isinstance(Target.value, ast.Name):
VarName: str = Target.value.id
@@ -1533,6 +1545,55 @@ class AssignHandle(BaseHandle):
f"(`rawptr[T] | t.CPtr` 或 `rawptr[rawptr[T]]`)后再赋值;"
f"或把基座显式转为 t.CPtr(无类型指针)后按 8 字节指针语义写入。{SrcInfo}")
def _DescribeMemberSlot(self, Gen: "Translator.LlvmGen", ClassName: str | None,
FieldName: str, Offset: int, SelfPtr: ir.Value) -> str:
"""`self.字段 = 值` 成员槽写入失败的**来源明细**(供 fail-fast 报文引用)。
槽类型有两个**互相独立**的来源,必须自洽:
- 声明侧:`Gen.class_members[类][下标]`(CollectAnnAssignMember / .vpi 加载);
- 布局侧:`SelfPtr` 所指 struct 的 `elements[offset]`(StructGen / stub 解析)。
两者不一致时,GEP 会把值写进**类型完全不同**的槽(不是"转换",是越界/错位),
现场:Generator.ExprGenConst 声明 `%"...ExprGenConst"*` 却落到 `i8**`。
此处把两侧数据逐项列出,直接判定是声明侧退化还是布局侧退化(含 structs
键空间大小与"是否同一对象",用于识别裸名 / sha1 全名双条目)。
"""
Lines: list[str] = []
try:
Members: list = Gen.class_members.get(ClassName) or []
Base: int = Gen._member_base(ClassName)
Idx: int = Offset - Base
Declared: Any = Members[Idx][1] if 0 <= Idx < len(Members) else None
Lines.append(
f" class_members[{ClassName}] 项数={len(Members)} base={Base} "
f"下标={Idx} 声明槽类型={Declared}")
except Exception as _e1:
Lines.append(f" class_members 读取失败: {type(_e1).__name__}: {_e1}")
Layout: Any = None
try:
Layout = getattr(SelfPtr.type, 'pointee', None)
Elems: Any = getattr(Layout, 'elements', None)
try:
LayoutName: Any = getattr(Layout, 'name', None)
except Exception:
LayoutName = '<未命名>'
Elem: Any = None
if Elems is not None and 0 <= Offset < len(Elems):
Elem = Elems[Offset]
Lines.append(
f" 布局 struct name={LayoutName!r} 元素数="
f"{len(Elems) if Elems is not None else None} elements[{Offset}]={Elem}")
except Exception as _e2:
Lines.append(f" 布局 struct 读取失败: {type(_e2).__name__}: {_e2}")
try:
_Structs: dict = getattr(Gen, 'structs', {}) or {}
_ByName: Any = _Structs.get(ClassName)
Lines.append(
f" structs 键数={len(_Structs)} structs[{ClassName}] 与布局同一对象="
f"{_ByName is Layout}")
except Exception:
pass
return f"\n [成员槽来源] self.{FieldName} = ...\n" + "\n".join(Lines)
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None:
Gen: "Translator.LlvmGen" = self.Trans.LlvmGen
if isinstance(Ptr.type, ir.PointerType):
+12
View File
@@ -3585,6 +3585,18 @@ class ImportHandle(BaseHandle):
pointee_type = self._parse_llvm_type(pointee, Gen, source_sha1=source_sha1, create_structs=create_structs)
if pointee_type is not None:
return ir.PointerType(pointee_type)
# 被指类型是**具名聚合**(`%...`)却在"允许建结构体"模式下解析不出来:
# 具名类型必然可由 _get_or_create_struct 兜出,取不到即内部不变量已被破坏
# (成员表/布局两链不同源、名字带多余前缀等)。静默退化成 i8* 会把
# 「结构体指针」悄悄变成通用指针,外层再套一层即成 i8**(现场:
# Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。
# create_structs=False 时未加载不属异常 ⇒ 只在该模式下响亮失败。
if create_structs and pointee.startswith('%'):
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
raise _FFE(
f"具名类型指针的被指类型解析失败:'{type_str}' 的被指部分 '{pointee}'"
f" 在 create_structs=True 下仍取不到结构体(source_sha1={source_sha1})"
f"—— 拒绝静默退化为 i8*(会使结构体指针变通用指针,再套一层即 i8**)")
return ir.PointerType(ir.IntType(8))
if type_str.startswith('[') and ']' in type_str:
+16 -1
View File
@@ -5,7 +5,7 @@ if TYPE_CHECKING:
from typing import List
from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo
from lib.core.SymbolUtils import (
IsTModule, IsListAnnotation, IsPtrSubscriptNode,
IsTModule, IsListAnnotation, IsPtrSubscriptNode, IsCPtrMarkerNode,
DesugarPtrSubscriptToUnion)
from lib.includes import t
import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes)
@@ -495,6 +495,21 @@ class HandlesTypeMerge(BaseHandle):
if IsPtrSubscriptNode(Node, self.Trans.SymbolTable):
return self.MergeTypes(DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable))
# 判据自洽守卫(fail-fast):base 是指针标记(`rawptr` / `t.CPtr` / 裸名
# `CPtr` / 别名 `X[...]`,见 IsPtrSubscriptNode)却被上面那条判据漏掉 ⇒
# 两处判据分裂(内部不变量被破坏)。历史行为是落到下方 CollectAll 当成
# 「数组下标」解析,**静默**产出"基座 void + 多层指针"(如 i8**),与
# declare 端的结构体指针分裂(现场:Generator.ExprGenConst 声明
# `%"...ExprGenConst"*` 却落到 i8**)。按「不许静默降级」改为响亮失败,
# 暴露上游根因。
if (isinstance(Node.value, ast.Name) and Node.value.id == 'rawptr') \
or IsCPtrMarkerNode(Node.value, self.Trans.SymbolTable):
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"指针下标判据分裂:注解 '{ast.unparse(Node)}' 的 base 是指针标记,"
f"但 IsPtrSubscriptNode 未命中 —— 拒绝落到 CollectAll 静默产出"
f"「基座 void 的多层指针」(结构体指针会被悄悄变成 i8**)")
def CollectAll(node: ast.AST, ParentIsArrayPtr: bool = False) -> CTypeInfo:
"""鏀堕泦绫诲瀷淇℃伅锛岃繑鍥?CTypeInfo"""
if isinstance(node, ast.Subscript):