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:
1 parent
4734a54aed
commit
90d81df4f8
2 files changed
+52
-4
No files matched your search
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in new issue
Block a user