diff --git a/lib/core/Handles/HandlesAssign.py b/lib/core/Handles/HandlesAssign.py index 72f4a3d..e9cdeb6 100644 --- a/lib/core/Handles/HandlesAssign.py +++ b/lib/core/Handles/HandlesAssign.py @@ -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: diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index bf62eda..7ca84a1 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -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)