fix(tpc): 自指针指向 opaque 同名实例时归一为具化实例;GEP 边界拦下 opaque pointee

类别 a(编译器缺陷):llvmlite 的 typed pointer GEP 必须在编译期算出元素类型——
`GEPInstr` → `ptr.type.gep(i)` → `IdentifiedStructType.gep` → `self.elements[i]`,
对 **opaque**(elements is None)pointee 抛裸 TypeError('NoneType' object is not
subscriptable)。现场:includes/ast/astaux.vp `Keyword.__init__` 的
`self.arg = _copy_str(pool, arg)`,经 HandlesAssign._HandleAttributeStoreLlvm:869
的 GEP 崩溃。成因:同名 struct 存在**两份实例**——函数签名/变量里那份仍 opaque,
类键 `Gen.structs[ClassName]` 上那份已具化;偏移取自类键那份、GEP 却用 self 那份。

- HandlesAssign._HandleAttributeStoreLlvm:GEP 前把 self 指针 bitcast 到**有 body 的
  那份实例**(仅当 self 的 pointee opaque 且类键实例已具化),并把该实例作为
  StructHint 传给 _get_member_offset —— 偏移与 GEP 必须同源(与属性读取路径
  _gep_and_Load_member 的 415916b3 同口径);max_offset 的取值也补上 elements 为
  None 的判据(原实现会对 opaque 实例 len() 触发断言)。
- BaseGen.RequirePointerOperand 新增 RequireConcretePointee(GEP 专用):基址指向
  opaque 具名结构体时 fail-fast,报文给出基址类型、结构体名、GEP 名与当前节点位置,
  不再落到 llvmlite 的裸 TypeError(typed pointer 下"GEP 指向 opaque 结构体"无任何
  合法场景:元素类型必须编译期可知)。

本地冒烟:opaque pointee 被拦下(FailFastError,而非 llvmlite 的 TypeError);
具化 pointee 正常通过。
This commit is contained in:
TermiNexus committed 2026-09-29 22:10:11 +08:00
1 parent 4734a54aed
commit 90d81df4f8
2 files changed
+52 -4

No files matched your search

+25 -2
View File
@@ -856,9 +856,32 @@ class AssignHandle(BaseHandle):
FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}")
Gen._store(FuncPtrI8, MethodPtrAddr)
return
offset: int = Gen._get_member_offset(AttrName, ClassName)
# 自指针的 pointee 可能是**另一份同名 struct 实例**:函数签名里那份
# 仍 opaque(`_get_or_create_struct` 早期创建、从未 set_body),而类键
# `Gen.structs[ClassName]` 上那份已具化。typed pointer 的 GEP 必须在
# 编译期算出元素类型 ⇒ 走 opaque pointee 时 llvmlite 在
# `types.py:633 self.elements[i.constant]` 抛裸 TypeError
# ('NoneType' object is not subscriptable;现场:astaux.vp
# `Keyword.__init__` 的 `self.arg = _copy_str(pool, arg)`)。
# 修法:先 bitcast 到**有 body 的那份实例**,再把该实例作为
# StructHint 交给 `_get_member_offset` —— 偏移与 GEP 必须同源
# (与属性读取路径 _gep_and_Load_member 的 415916b3 同口径)。
_SelfPointee: ir.Type | None = None
if isinstance(SelfPtr.type, ir.PointerType):
_SelfPointee = SelfPtr.type.pointee
struct_type: ir.Type = Gen.structs[ClassName]
max_offset: int = len(struct_type.elements) if isinstance(struct_type, ir.IdentifiedStructType) else 0
if (isinstance(_SelfPointee, ir.IdentifiedStructType)
and _SelfPointee.elements is None
and isinstance(struct_type, ir.IdentifiedStructType)
and struct_type.elements is not None):
SelfPtr = Gen.builder.bitcast(
SelfPtr, ir.PointerType(struct_type),
name=f"concrete_self_{ClassName}")
_SelfPointee = struct_type
offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee)
max_offset: int = (len(struct_type.elements)
if isinstance(struct_type, ir.IdentifiedStructType)
and struct_type.elements is not None else 0)
if offset >= max_offset:
VarName: str = f"self.{AttrName}"
if VarName not in Gen.variables:
+27 -2
View File
@@ -17,7 +17,8 @@ class VaArgInstruction(ir.Instruction):
ptr.type, ptr.get_reference(), self.type))
def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str = '') -> None:
def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str = '',
RequireConcretePointee: bool = False) -> None:
"""IR 边界守卫:指针类指令(GEP/load/store)的**指针操作数**必须是指针类型。
为什么需要本守卫:llvmlite 的 `IRBuilder.gep` **不做任何类型校验**,
@@ -36,6 +37,29 @@ def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str
合法路径下(操作数确实是指针)零副作用,只多一次 isinstance 判定。
"""
if isinstance(getattr(Value, 'type', None), ir.PointerType):
if RequireConcretePointee:
# GEP 的第二层守卫:被指结构体**未具化**(opaque,elements is None)。
# llvmlite 的 GEPInstr 走 `ptr.type.gep(i)` → `IdentifiedStructType.gep`
# → `self.elements[i.constant]`,对 opaque pointee 抛裸 TypeError
# ('NoneType' object is not subscriptable;现场:astaux.vp `Keyword.__init__`
# 的 `self.arg = ...`)。typed pointer 下 GEP 必须能在编译期算出元素类型,
# 故"指向 opaque 结构体的 GEP"**无任何合法场景** ⇒ 直接 fail-fast 报出
# 结构体名与 GEP 名,暴露"同名 struct 两份实例(一份已具化、一份仍 opaque)
# / body 从未 set_body"的根因。
_Pointee: Any = getattr(Value.type, 'pointee', None)
if isinstance(_Pointee, ir.IdentifiedStructType) and _Pointee.elements is None:
from lib.core.LLVMCG.FuncGen import FailFastError
_MsgP: str = (
f"{OpName} 的基址指向**未具化**的具名结构体:基址类型={Value.type},"
f"被指结构体={getattr(_Pointee, 'name', '?')!r}(opaque,elements 未设置)"
+ (f",{Detail}" if Detail else "")
+ "\n llvmlite 会在此处内部对 opaque pointee 计算元素类型并抛裸 TypeError"
"('NoneType' object is not subscriptable);本守卫在 IR 边界 fail-fast,"
"暴露\"该类存在另一份已具化实例(偏移与 GEP 未同源)\"或"
"\"该类 body 从未 set_body\"的根因(不做兜底)")
if Gen is not None and hasattr(Gen, '_get_node_info'):
_MsgP += "\n " + Gen._get_node_info()
raise FailFastError(_MsgP)
return
# 失败路径才导入:热路径(每次 gep/load/store)不承担导入开销,也避免循环导入。
from lib.core.LLVMCG.FuncGen import FailFastError
@@ -75,7 +99,8 @@ class _GuardIrBuilder(ir.IRBuilder):
def gep(self, ptr: Any, indices: Any, inbounds: bool = False,
name: str = '', source_etype: Any = None) -> Any:
RequirePointerOperand(ptr, 'getelementptr(GEP)', self._GuardGen,
f"GEP 名='{name}',索引 {len(indices)} 项")
f"GEP 名='{name}',索引 {len(indices)} 项",
RequireConcretePointee=True)
return super().gep(ptr, indices, inbounds=inbounds, name=name,
source_etype=source_etype)