diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index b388871..875061a 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -1781,11 +1781,43 @@ class ExprAttrHandle(BaseHandle): if VarName == 'self': ClassName: str | None = self.Trans._CurrentCpythonObjectClass if ClassName: - offset: int | None = Gen._get_member_offset(Node.attr, ClassName) SelfVar: Any = Gen._get_var_ptr('self') if SelfVar: SelfPtr: Any = Gen._load(SelfVar, name="self") if isinstance(SelfPtr.type, ir.PointerType): + # 基址 pointee 若**未具化**(opaque):typed pointer 下 GEP 必须 + # 在编译期算出元素类型,否则 llvmlite 内部抛裸 TypeError + # ('NoneType' object is not subscriptable)。与**同函数下方 + # 各分支**(属性链分支 / i8* 分支:均先 _TryLoadStructFromStub + # 补 body、再 bitcast 到已具化实例)同口径 —— 偏移与 GEP 必须 + # 同源;本分支此前是唯一漏了这套处理的分支。 + # 现场(2026-09-30):includes/ast/astaux.vp:165 + # `self.arg = _copy_str(pool, arg)`,GEP 名 'arg',基址 pointee + # 是 opaque 的 `%"8312ce86a2fd82c0.Keyword"*`,而同短名的 + # 'Keyword' 键上那份已具化(10 槽)。 + _SelfPointee: Any = SelfPtr.type.pointee + if (isinstance(_SelfPointee, ir.IdentifiedStructType) + and _SelfPointee.elements is None): + _ST: Any = Gen.structs.get(ClassName) + if isinstance(_ST, ir.IdentifiedStructType) and not _ST.elements: + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + _ST = Gen.structs.get(ClassName, _ST) + _Twin: Any = None + if isinstance(_ST, ir.IdentifiedStructType) and _ST.elements: + _Twin = _ST + else: + try: + _Twin = Gen._FindConcreteTwin(_SelfPointee) + except Exception: + _Twin = None + if _Twin is not None: + SelfPtr = Gen.builder.bitcast( + SelfPtr, ir.PointerType(_Twin), + name=f"cast_self_{ClassName}") + _SelfPointee = _Twin + offset: int | None = Gen._get_member_offset(Node.attr, ClassName, _SelfPointee) + if offset is None: + return None MemberPtr: Any = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) return MemberPtr ClassName = Gen.var_struct_class.get(VarName) diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index 0af055d..7c3157a 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -88,6 +88,25 @@ def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str _Kin.append(f"{_k_n}({getattr(_k_v, 'name', '?')})={_k_cnt}") if _Kin: _MsgP += "\n 同短名 struct 实例(key(IR 名)=槽数): " + ", ".join(_Kin) + # TPC 发射点(由内向外,最内 3 帧):行号只能指到**语句**,无法区分 + # 同一语句里是哪个 GEP 点(现场 astaux.vp:165 `self.arg = _copy_str(...)` + # 在读写两侧共有多个候选发射点,逐个读代码无法判定)。取栈中位于本仓 + # lib/、且不在本文件(守卫自身)的最内 3 帧,报出 文件:行(函数)。 + try: + import traceback as _tb + _Sites: list[str] = [] + for _fr in reversed(_tb.extract_stack()): + _fn: str = (getattr(_fr, 'filename', '') or '').replace('\\', '/') + if '/lib/' not in _fn or _fn.endswith('/LLVMCG/BaseGen.py'): + continue + _Sites.append(f"{_fn.split('/lib/', 1)[-1]}:{getattr(_fr, 'lineno', 0)}" + f"({getattr(_fr, 'name', '?')})") + if len(_Sites) >= 3: + break + if _Sites: + _MsgP += "\n TPC 发射点(由内向外): " + " ← ".join(_Sites) + except Exception: + pass raise FailFastError(_MsgP) return # 失败路径才导入:热路径(每次 gep/load/store)不承担导入开销,也避免循环导入。