from __future__ import annotations from typing import Any, TYPE_CHECKING if TYPE_CHECKING: from lib.core.translator import Translator from lib.core.LlvmCodeGenerator import LlvmCodeGenerator from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta from lib.core.VLogger import get_logger as _vlog from lib.core.SymbolUtils import DefineDeclTypeValue, GlobalDeclShadowsDefine import vast as ast import llvmlite.ir as ir class ExprAttrHandle(BaseHandle): # ========== 辅助方法 ========== def _apply_bswap_on_Load(self, val: ir.Value, ClassName: str, AttrName: str) -> ir.Value: Gen: LlvmCodeGenerator = self.Trans.LlvmGen byte_order: str = getattr(Gen, 'class_member_byteorders', {}).get(ClassName, {}).get(AttrName, "") return Gen._apply_bswap_if_big(val, byte_order, f"bswap_load_{AttrName}") def _load_arr_elem_with_bswap(self, Gen: LlvmCodeGenerator, ElemPtr: ir.Value, arr_byte_order: str) -> ir.Value: """加载数组元素并按字节序应用 bswap""" val: ir.Value = Gen._load(ElemPtr, name="arr_elem_load") return Gen._apply_bswap_if_big(val, arr_byte_order, "bswap_arr_load") def _RenumVariantPayloadType(self, Gen: LlvmCodeGenerator, ClassName: str | None, AttrName: str) -> Any: """REnum **变体载荷**字段的声明类型(供 `_gep_and_Load_member` 载入后收口)。 变体 struct 的 body 是**主 REnum 镜像** `{i32 __tag, }` (唯一产源:HandlesClassDef._EmitREnumLlvm:1655-1673 与 HandlesImports 的 REnum 跨模块注册 :2024-2031),故载荷槽在 body 里的类型取**所有变体同位置的 最大尺寸**(现场 'Pointee' 槽是 i64),而字段本身声明为指针/更窄整数 ⇒ 载入值须按声明类型收口,否则上游拿到 i64(现场 CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`)。 判据:类键(或它**唯一**的 REnum 变体键,见 FuncGen._RenumVariantFieldOwner) 的成员表**首项是 '__tag'**(REnum 变体不变式)且声明了该字段; 不命中返回 None(调用方原样返回,对既有类零变化)。 """ if not ClassName or not AttrName: return None _Owners: list[str] = [ClassName] _Variant: str | None = None try: _Variant = Gen._RenumVariantFieldOwner(ClassName, AttrName) except Exception: _Variant = None if _Variant: _Owners.append(_Variant) for _cn in _Owners: _Members: list = (getattr(Gen, 'class_members', {}) or {}).get(_cn) or [] if not _Members or _Members[0][0] != '__tag': continue _FieldTy: Any = next((t for n, t in _Members if n == AttrName), None) if _FieldTy is not None: return _FieldTy return None @staticmethod def _CoerceVariantPayload(Gen: LlvmCodeGenerator, Value: Any, Want: Any) -> Any: """payload 槽载入值 → 字段声明类型(REnum 变体载荷收口用)。 body 里载荷槽常是 i64(max-variant 镜像),而字段声明可能是指针/更窄整数: int → ptr:inttoptr;ptr → ptr:bitcast;int → int:trunc/zext;其余原样返回 (不静默造值:类型不可转换时返回原值,让下游照常 fail-fast)。 """ _got: Any = getattr(Value, 'type', None) try: if _got is Want: return Value if isinstance(Want, ir.PointerType): if isinstance(_got, ir.IntType): return Gen.builder.inttoptr(Value, Want, name="variant_payload_to_ptr") if isinstance(_got, ir.PointerType): return Gen.builder.bitcast(Value, Want, name="variant_payload_ptr_cast") elif isinstance(Want, ir.IntType) and isinstance(_got, ir.IntType): if Want.width < _got.width: return Gen.builder.trunc(Value, Want, name="variant_payload_trunc") if Want.width > _got.width: return Gen.builder.zext(Value, Want, name="variant_payload_zext") elif isinstance(_got, ir.PointerType) and isinstance(Want, ir.PointerType): return Gen.builder.bitcast(Value, Want, name="variant_payload_ptr_cast") except Exception: return Value return Value def _apply_target_struct_hint(self, Gen: LlvmCodeGenerator, FieldName: str, ResolvedKey: str | None) -> str | None: """左值注解**靶型提示**:解析出的类**不声明**该字段时,用赋值左侧注解指向的类兜一次。 与 VPC `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:赋值左侧的 注解是**源码级权威意图**(RHS 必须能赋给它)。仅在**两个条件同时成立**时改判: ① 当前解析出的类(或解析不出)**不声明**该字段 —— 此刻原本会 fail-fast; ② 靶型类**确实声明**了该字段。 任一不满足即原样返回(**不猜**);改判时告警留痕。 现场:CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee` —— `.Pointee` 属 LLVMType(REnum 变体镜像 LLVMType.Ptr.Pointee),却因接收者 类名被判成 ParamNode(只有 Next/Ty)而 fail-fast 终止编译;靶型 `llvmlite.LLVMType*` 正是该字段的归属类。 提示由 HandlesAnnAssign 在求值 RHS 前置入 `Gen._TargetStructHint`(注解为结构体 指针时取其短类名),求值后复位;提示只在"类不声明该字段"时才生效,故嵌套求值 (如调用的实参链)复用同一提示也不会篡改本来解析正确的链。 """ _TH: str | None = getattr(Gen, '_TargetStructHint', None) if not _TH: return ResolvedKey if ResolvedKey and ResolvedKey == _TH: return ResolvedKey if ResolvedKey and any(_n == FieldName for _n, _ in (Gen.class_members.get(ResolvedKey) or [])): return ResolvedKey _THKey: str | None = None try: _THKey = self._find_struct_key_for_class_hint(Gen, _TH) except Exception: _THKey = None if (not _THKey or _THKey == ResolvedKey or _THKey not in Gen.structs or not any(_n2 == FieldName for _n2, _ in (Gen.class_members.get(_THKey) or []))): return ResolvedKey _vlog().warning( f"[属性类名] 字段 '{FieldName}' 不在解析出的类 {ResolvedKey!r} 中," f"按左值注解靶型类 '{_THKey}' 解析(源码级权威意图:RHS 必须可赋给该注解)", "AttrClass") return _THKey @staticmethod def _PointeeMatchesStruct(Pointee: Any, ST: Any) -> bool: """pointee 与该 struct 实例是否算**同一个类**。 判据只有两条:① 同一个对象;② IR 全名逐字相同(IR 名形如 `.<短名>`, 全名相同即同一类的同一份实例)。 **只比 sha1 前缀是错的(2026-09-30 修正)**:前缀标识的是**模块**而不是类—— 同一模块里的兄弟类(`50741ad4b4b0934d.ParamNode` 与 `50741ad4b4b0934d.LLVMType` 同出 includes/llvmlite/__types.vp)前缀相同,只比前缀会把接收者锚到**错误的类**, 随后按那份 body 取字段 ⇒ 报出与真因无关的类名。现场:CallGenDispatch.vp:787 `len_val.Ty.Pointee` 的基座 pointee 是 LLVMType,却被锚到 ParamNode(2 字段 Next/Ty,且 ParamNode 在 __types.vp 里声明更早 ⇒ 迭代先命中)⇒ "属性 'Pointee' 在类 'ParamNode' 中找不到"。 同**短名**不同前缀(同一类的另一模块实例)不在此放行,由调用点在 "同短名候选唯一"时单独处理(见 `_UniqShortCN`);都判不出时返回 False (调用点留痕,不兜底造类型)。 """ if Pointee is ST: return True if not (isinstance(Pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType)): return False _pn: str = getattr(Pointee, 'name', '') or '' _sn: str = getattr(ST, 'name', '') or '' return bool(_pn and _pn == _sn) def _load_bitfield_member(self, ObjPtr: ir.Value, ClassName: str, AttrName: str) -> ir.Value: """位域成员**读取**——与 `HandlesAssign._store_bitfield_member` 严格对称。 布局约定(唯一真源:StructGen 的位域折叠分支):位域类的整个 struct body 只有 **一个 storage 槽** `elements[0]`(该类位域总位宽 ≤ 该整数宽度);每个字段的 **位**偏移 / 位宽 / 符号性分别记在 `class_member_bitoffsets` / `class_member_bitfields` / `class_member_signeds`(都不占独立槽位)。 读取 = `(storage >> 位偏移) & ((1 << 位宽) - 1)`;有符号字段再在 storage 宽度内 做符号扩展(与存储侧的 sext/zext 对称),结果类型 = storage 整数类型(= 该类位域 注解的基类型)。 此前本方法**全仓无定义**而读路径有 7 处调用(如 `_get_attr_ptr` 的位域分支)—— 任何对位域字段的读取都会 `AttributeError`(与 `_PreRegisterClassMembers` 同族)。 判据缺失(无位偏移 / 位宽 ≤ 0 / struct 未具化 / storage 非整数)一律 fail-fast, **不返回 i32 之类的兜底值**:走到这些分支只能是"布局或元数据不一致", 静默降级会把错值写进程序语义。 """ Gen: LlvmCodeGenerator = self.Trans.LlvmGen _Offsets: dict = Gen.class_member_bitoffsets.get(ClassName, {}) _Widths: dict = Gen.class_member_bitfields.get(ClassName, {}) if AttrName not in _Offsets: raise TypeError( f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 缺位偏移" f"(class_member_bitoffsets['{ClassName}'] = {sorted(_Offsets)!r})") BitOffset: int = _Offsets[AttrName] BitWidth: int = _Widths.get(AttrName, 0) if BitWidth <= 0: raise TypeError( f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 位宽非法({BitWidth})" f"(class_member_bitfields['{ClassName}'] = {_Widths!r})") StructType: Any = Gen.structs.get(ClassName) if (not isinstance(StructType, ir.IdentifiedStructType) or StructType.elements is None or len(StructType.elements) == 0): raise TypeError( f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 的 struct 未具化" f"(structs['{ClassName}'] = {getattr(StructType, 'name', StructType)!r})" f"⇒ storage 槽未知") StorageType: ir.Type = StructType.elements[0] if not isinstance(StorageType, ir.IntType): raise TypeError( f"[fast-fail] 位域读取 '{ClassName}.{AttrName}' 的 storage 槽不是整数" f"(elements[0] = {StorageType})") StoragePtr: ir.Value = Gen.builder.gep( ObjPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_bf_storage_ptr") RawVal: ir.Value = Gen._load(StoragePtr, name=f"{AttrName}_bf_storage") Shifted: ir.Value = Gen.builder.lshr( RawVal, ir.Constant(StorageType, BitOffset), name=f"{AttrName}_bf_shr") Masked: ir.Value = Gen.builder.and_( Shifted, ir.Constant(StorageType, (1 << BitWidth) - 1), name=f"{AttrName}_bf_and") _Signeds: Any = (getattr(Gen, 'class_member_signeds', {}) or {}).get(ClassName) or {} if _Signeds.get(AttrName) and BitWidth < StorageType.width: _Rest: int = StorageType.width - BitWidth _LShift: ir.Value = Gen.builder.shl( Masked, ir.Constant(StorageType, _Rest), name=f"{AttrName}_bf_sign_shl") Masked = Gen.builder.ashr( _LShift, ir.Constant(StorageType, _Rest), name=f"{AttrName}_bf_sign_ashr") return Masked def _gep_and_Load_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, offset: int | None, AttrName: str, ClassName: str, ST: ir.Type | None = None) -> ir.Value | None: """从结构体指针通过 GEP 取成员,处理 array/struct pointer/class_members 特殊情况""" if offset is None: self._NoteAttrNone(Gen, ClassName, AttrName, f"偏移为 None(字段 '{AttrName}' 未进成员表)") return None if isinstance(ObjVal.type, ir.PointerType): pointee: Any = ObjVal.type.pointee # opaque pointee:GEP 无法在编译期算元素类型(llvmlite 抛裸 TypeError, # _GuardIrBuilder 会先 fail-fast)。先按**同短名**找一份已具化实例并 bitcast # (与写入路径 HandlesAssign 的孪生体搜索共用 _FindConcreteTwin); # 找不到则保持原样、由守卫报出同短名实例清单(暴露"body 从未 set_body"), # **不静默兜底**。 if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: _TwinR: Any = None try: _TwinR = Gen._FindConcreteTwin(pointee) if _TwinR is None: _TwinR = Gen._FindConcreteTwin(Gen.structs.get(ClassName)) except Exception: _TwinR = None if _TwinR is not None: ObjVal = Gen.builder.bitcast( ObjVal, ir.PointerType(_TwinR), name=f"concrete_twin_read_{AttrName}") pointee = _TwinR if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset >= len(pointee.elements): # sha1 冲突修复:pointee 字段不足时,尝试 bitcast 到 ST(更完整的版本) if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_gep_{ClassName}") else: _st_len: Any = (len(ST.elements) if isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None else 'None/opaque') self._NoteAttrNone( Gen, ClassName, AttrName, f"偏移越界:字段 '{AttrName}' 需槽位 {offset}," f"但 body 只有 {len(pointee.elements)} 个元素," f"stub 版结构体(st)也仅 {_st_len} 个 ⇒ 布局与成员表不同源" f"(成员表 {len(Gen.class_members.get(ClassName, []))} 项)") return None MemberPtr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.ArrayType): return MemberPtr if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.IdentifiedStructType): return MemberPtr # 检查 pointer-to-pointer 且实际元素是 ArrayType if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.PointerType): actual_elem_type: Any = None if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): actual_elem_type = ST.elements[offset] if isinstance(actual_elem_type, ir.ArrayType): return Gen.builder.bitcast(MemberPtr, ir.PointerType(actual_elem_type), name=f"cast_arr_{AttrName}") # 检查 class_members 中的 ArrayType if ClassName in Gen.class_members: for member_name, member_type in Gen.class_members[ClassName]: if member_name == AttrName and isinstance(member_type, ir.ArrayType): return Gen.builder.bitcast(MemberPtr, ir.PointerType(member_type), name=f"cast_arr_{AttrName}") val: Any = Gen._load(MemberPtr, name=AttrName) # REnum 变体载荷槽收口(判据见 _RenumVariantPayloadType):变体 struct 的 body # 是主 REnum 镜像,载荷槽按**最大变体**宽度物化('Pointee' 槽是 i64),而字段 # 声明类型可能是指针 ⇒ 载入后按声明类型转换;非 REnum 变体键逐字不变。 _WantTy: Any = self._RenumVariantPayloadType(Gen, ClassName, AttrName) if _WantTy is not None: val = self._CoerceVariantPayload(Gen, val, _WantTy) return self._apply_bswap_on_Load(val, ClassName, AttrName) def _try_Load_struct_from_stub(self, Gen: LlvmCodeGenerator, ClassName: str) -> tuple[ir.Type | None, bool]: """尝试从 stub 加载结构体定义,返回 (struct_type, ok)""" if ClassName not in Gen.structs: return None, False st: Any = Gen.structs[ClassName] if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) st = Gen.structs.get(ClassName, st) if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): return st, False return st, True def _find_specialized_struct_with_body(self, Gen: LlvmCodeGenerator, opaque_struct: Any) -> tuple[ir.IdentifiedStructType, str] | None: """查找 opaque 非特化泛型结构体的特化版本(如 GSList -> GSList[Param])。 所有特化版本布局相同,可用于 GEP 偏移计算。""" if not isinstance(opaque_struct, ir.IdentifiedStructType): return None base_name: str = opaque_struct.name or "" short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name if not short_name or '[' in short_name: return None for CN2, ST2 in Gen.structs.items(): if not isinstance(ST2, ir.IdentifiedStructType): continue if ST2 is opaque_struct: continue if ST2.elements is None: continue st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") if st2_short.startswith(short_name + '['): return (ST2, CN2) return None def _try_access_opaque_generic_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, opaque_struct: Any, Node: ast.Attribute, orig_CN: str, parent_hint: str | None = None) -> ir.Value | None: """通过特化版本访问 opaque 非特化泛型结构体成员。 遍历所有候选特化版本(如 GSList[Param]/GSList[NameEntry]), 优先选择 class_members 中包含 Node.attr 字段的版本, 避免选到错误特化版本(如 GSList[NameEntry] 而非 GSList[Param]) 导致 _get_member_offset 返回 None 而生成 ret null。 parent_hint: 父上下文提供的特化提示(如 'GSList[BasicBlock]'), 当所有候选都有目标字段(如 Head/Tail/Count)时, 用此提示选择正确特化版本。""" # 收集所有候选特化版本(有 body 的) candidates: list[tuple[ir.IdentifiedStructType, str]] = [] if isinstance(opaque_struct, ir.IdentifiedStructType): base_name: str = opaque_struct.name or "" short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name if short_name and '[' not in short_name: for CN2, ST2 in Gen.structs.items(): if not isinstance(ST2, ir.IdentifiedStructType): continue if ST2 is opaque_struct: continue if ST2.elements is None: continue st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") if st2_short.startswith(short_name + '['): candidates.append((ST2, CN2)) if not candidates: return None # 优先级 1: parent_hint 精确匹配(如 'GSList[BasicBlock]') # 当父对象字段声明记录了特化名时,直接使用,避免 Head/Tail/Count 等公共字段歧义 if parent_hint: hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint for spec_st, spec_CN in candidates: spec_short: str = spec_CN.split('.')[-1] if '.' in spec_CN else spec_CN if spec_short == hint_short: CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") for lookup_CN in (spec_CN, orig_CN): bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) if Node.attr in bitfield_offsets: return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) if offset is not None: return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) break # 优先级 2: class_members 中包含 Node.attr 字段的特化版本 best_candidate: tuple[ir.IdentifiedStructType, str] | None = None for spec_st, spec_CN in candidates: for lookup_CN in (spec_CN, orig_CN): lookup_short: str = lookup_CN.split('.')[-1] if '.' in lookup_CN else lookup_CN for chk_CN in (lookup_CN, lookup_short): if chk_CN in Gen.class_members: if any(name == Node.attr for name, _ in Gen.class_members[chk_CN]): best_candidate = (spec_st, spec_CN) break if best_candidate: break if best_candidate: break if best_candidate is None: best_candidate = candidates[0] spec_st: ir.IdentifiedStructType spec_CN: str spec_st, spec_CN = best_candidate CastedObj = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") for lookup_CN in (spec_CN, orig_CN): bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) if Node.attr in bitfield_offsets: return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) if offset is not None: return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) return None def _make_define_constant(self, Gen: LlvmCodeGenerator, val: int | float | str, Name: str | None = None) -> ir.Value | None: """将 CDefine 值转为 LLVM 常量。 带类型注册优先:若该 define 登记了**声明整数类型**(`Gen._define_decl_types`), 按该类型的位宽与符号性物化(u32 `DWORD(-11)` → `i32 0xFFFFFFF5`),而不是按 数值大小猜 i32/i64(后者会把 `4294967285` 误判成 i64,与 i32/ULONG 使用点不符)。 """ if isinstance(val, int) and not isinstance(val, bool): _dt: tuple[int, int] | None = DefineDeclTypeValue( getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), Name, val) if _dt is not None: return ir.Constant(ir.IntType(_dt[0]), _dt[1]) if isinstance(val, int): # 位宽按值推断(与 VPC ExprGenCdef / define_min_width 同口径):能装进 32 位 # 位模式(含无符号高半区 [2^31, 2^32-1],如 GENERIC_READ / SHARED 掩码)就用 i32, # 否则 i64。此前用 abs(val) > 2^31-1 判定,会把无符号高半区误判成 i64,与 32 位 # 使用点(ULONG/DWORD)组合时触发「64→32 隐式窄化」。 if -2147483648 <= val <= 4294967295: return ir.Constant(ir.IntType(32), val if val <= 2147483647 else val - 4294967296) return ir.Constant(ir.IntType(64), val) elif isinstance(val, float): return ir.Constant(ir.DoubleType(), val) elif isinstance(val, str): return Gen._create_string_global(val) return None def _lookup_cdefine(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: """在符号表和模块中查找 CDefine 常量值 注意:不再强制依赖 _ImportedModules。即使该集合为空(例如自举编译 阶段尚未填充),也始终检查符号表与 Gen._define_constants,确保 ast.CONST_INT 这类 CDefine 常量能够被解析为立即数而非全局变量引用。 """ PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) if imported_modules: for mod_name in imported_modules: if mod_name.endswith(f".{VarName}") or mod_name == VarName: key: str = f"{mod_name}.{AttrName}" if key not in PossibleKeys: PossibleKeys.append(key) # 始终遍历符号表(不依赖 imported_modules 是否为空) for lookup_key in PossibleKeys: SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: return self._make_define_constant(Gen, SymInfo.DefineValue, AttrName) # 也检查 _define_constants(LlvmGenerator 预扫描阶段填充) define_constants: Any = getattr(Gen, '_define_constants', {}) for key in (f"{VarName}.{AttrName}", AttrName): if key in define_constants: return self._make_define_constant(Gen, define_constants[key], AttrName) # 回退:_all_define_constants(Phase2Translator 从所有 .pyi 预提取的跨模块 CDefine, # 与 _HandleNameLlvm 的裸名回退同源)。**模块属性访问**(`win32file.STD_OUTPUT_HANDLE`) # 此前只查 SymbolTable/_define_constants → 未命中就落到 _lookup_module_global 生成 # `@STD_OUTPUT_HANDLE` 全局引用(无定义 → 链接期 undefined symbol)。 # 与裸名路径同口径:仅当本模块没有同名变量/(**已定义的**)全局时才用常量。 # 注意:跨模块 stub 注入的 `@X = external global`(无初值)**不算**遮蔽 —— 否则 # 一条错误的 stub declare 会把 .vpi 的 CDefine 常量顶掉,退化成未定义全局符号。 _has_shadow: bool = AttrName in Gen.variables or GlobalDeclShadowsDefine(Gen, AttrName) if not _has_shadow: all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} for key2 in (f"{VarName}.{AttrName}", AttrName): if key2 in all_dc: return self._make_define_constant(Gen, all_dc[key2], AttrName) return None def _NormalizeModuleGlobalValue(self, Gen: LlvmCodeGenerator, Val: Any, AttrName: str) -> Any: """模块限定全局读取归一化:类指针全局的「多一层」元素类型还原为对象指针 `Mod.g`(g 为模块级全局)的取值统一经 _lookup_module_global:注解为 `X | CPtr` 的模块级全局(如 GenApi._g_gen: rawptr[Generator.Generator]、 VLogger.logger: Logger | CPtr)在本编译器里物化成「比对象多一层」的 元素类型 T**(写入端见 main.vp `GenApi._g_gen = gen`:bitcast T*→T** 后 直接存值,未取地址)⇒ 其位形就是对象指针 T*,静态类型多了一层。 裸名形态(`g.Field`)由 _handle_attr_name 的既有同款补偿 bitcast 处理; 模块限定形态(`Mod.g.Field`)此前无补偿 ⇒ 载入值停在 T**,其 pointee 是 T*(指针而非结构体)⇒ _handle_attr_nested 的 char* 分支与 struct 分支都 不命中而返回 None ⇒ 带注解赋值 RHS 求值为空 fail-fast (App/lib/core/Repl.vp:763 `GenApi._g_gen.TypeGenCDef.CDefineNames`)、 调用接收者 None 而静默丢调用(VpiReader.vp:4066 `GenApi._g_gen.ClassGenBase._collect_parents(...)`)。此处做同款逆运算: 只改静态类型、不改位形(bitcast 不产指令)。 判据限定「pointee 仍是指向结构体的指针」⇒ `str | CPtr` 一类真双层指针 全局(@_argv / @_g_pyi_cdef_names = i8**,实参直传 i8** 形参)不受影响。 """ if not isinstance(Val, ir.Value) or not isinstance(Val.type, ir.PointerType): return Val Inner: Any = Val.type.pointee if isinstance(Inner, ir.PointerType) and isinstance(Inner.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): return Gen.builder.bitcast(Val, Inner, name=f"cast_{AttrName}_global") return Val def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: """在模块全局变量中查找 import 的属性""" imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) if not imported_modules: return None resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) if VarName not in imported_modules and resolved_mod not in imported_modules: return None PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] for mod_name in imported_modules: if mod_name.endswith(f".{VarName}") or mod_name == VarName: key: str = f"{mod_name}.{AttrName}" if key not in PossibleKeys: PossibleKeys.append(key) for key in PossibleKeys: if key in Gen.module.globals: GVar: Any = Gen.module.globals[key] if isinstance(GVar, ir.Function): return GVar if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): return GVar return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar, name=AttrName), AttrName) # SHA1 前缀查找 ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) sha1_candidates: list[str] = [VarName, resolved_mod] for mod_name in imported_modules: if mod_name.endswith(f".{VarName}") or mod_name == VarName: if mod_name not in sha1_candidates: sha1_candidates.append(mod_name) for cand in sha1_candidates: sha1: str | None = ModuleSha1Map.get(cand) if sha1: prefixed: str = f"{sha1}.{AttrName}" if prefixed in Gen.module.globals: GVar2: Any = Gen.module.globals[prefixed] if isinstance(GVar2, ir.Function): return GVar2 if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): return GVar2 return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar2, name=AttrName), AttrName) if AttrName in Gen.module.globals: gv: Any = Gen.module.globals[AttrName] if AttrName in Gen.variables and Gen.variables[AttrName] is None: str_gv_name: str = AttrName + '_str' if str_gv_name in Gen.module.globals: str_gv: Any = Gen.module.globals[str_gv_name] return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") return self._NormalizeModuleGlobalValue(Gen, Gen.builder.load(gv, name=AttrName), AttrName) return None def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: """根据变量名解析其结构体类名""" ClassName: str | None = Gen.var_struct_class.get(VarName) if not ClassName and getattr(Gen, 'global_struct_class', None): ClassName = Gen.global_struct_class.get(VarName) if ClassName: Gen.var_struct_class[VarName] = ClassName if not ClassName and VarName in Gen.module.globals: GVar: Any = Gen.module.globals[VarName] if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): found: Any = Gen.find_struct_by_pointee(GVar.type.pointee) if found: ClassName = found[0] Gen.var_struct_class[VarName] = ClassName if getattr(Gen, 'global_struct_class', None): Gen.global_struct_class[VarName] = ClassName if not ClassName and VarName in Gen.variables: VarPtr: Any = Gen.variables[VarName] if VarPtr is not None and isinstance(VarPtr.type, ir.PointerType): pointee: Any = VarPtr.type.pointee if isinstance(pointee, ir.PointerType): pointee = pointee.pointee found = Gen.find_struct_by_pointee(pointee) if found: ClassName = found[0] return ClassName def _check_struct_match(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, ClassName: str) -> bool: """检查 ObjVal 是否指向 ClassName 对应的结构体""" if ClassName not in Gen.structs: return False if isinstance(ObjVal.type, ir.PointerType): ST: Any = Gen.structs[ClassName] if ObjVal.type.pointee == ST: return True if isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType): # 精确名匹配(含 sha1 前缀)或短名匹配(去掉 sha1 前缀) if ObjVal.type.pointee.name == ST.name: return True if Gen._extract_short_name(ObjVal.type.pointee.name) == Gen._extract_short_name(ST.name): return True return False # ========== _HandleAttributeLlvm 子方法 ========== def _handle_attr_self(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: """处理 self.xxx 属性访问""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen ClassName: str | None = self.Trans._CurrentCpythonObjectClass if not ClassName: return None # 检查 property getter PropKey: str = f'{ClassName}.{Node.attr}' PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: SelfVar: Any = Gen._get_var_ptr('self') if SelfVar: SelfPtr: Any = Gen._load(SelfVar, name="self") GetterFunc: Any = Gen._get_function(PropKey) if GetterFunc and SelfPtr: return Gen.builder.call(GetterFunc, [SelfPtr], name=f"prop_{PropKey}") offset: int | None = Gen._get_member_offset(Node.attr, ClassName) SelfVar = Gen._get_var_ptr('self') if not SelfVar: return None SelfPtr = Gen._load(SelfVar, name="self") if not isinstance(SelfPtr.type, ir.PointerType): return None pointee: Any = SelfPtr.type.pointee if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: # 修复 Bug 4/5:NoVTable fix 清除 struct elements 后,_handle_attr_self # 必须主动从 stub 文件重新加载 struct 定义,否则 self.Trans 等属性访问 # 会因 pointee.elements 为 None 返回 None,导致 AnnAssign/Store 不生成 IR。 # 与 _handle_attr_nested 的处理保持一致。 # 策略:先用 ClassName 直接尝试加载;若失败,遍历 Gen.structs 按名字匹配 # 找到对应的 dict key(可能是 sha1 前缀形式),再用该 key 加载。 loaded: bool = False for TryCN in (ClassName,): st, ok = self._try_Load_struct_from_stub(Gen, TryCN) if ok and isinstance(st, ir.IdentifiedStructType) and st.elements is not None: pointee = st loaded = True break if not loaded: for CN2, ST2 in Gen.structs.items(): if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name: st2, ok2 = self._try_Load_struct_from_stub(Gen, CN2) if ok2 and isinstance(st2, ir.IdentifiedStructType) and st2.elements is not None: pointee = st2 loaded = True break if not loaded: # 最后回退:在 Gen.structs 中查找同名非 opaque 版本 for CN2, ST2 in Gen.structs.items(): if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name and ST2.elements is not None: pointee = ST2 break # 当 pointee 元素数不足以容纳 offset 时(常见于跨模块继承:子类 struct # 在父类 class_members 加载前生成,只有部分继承字段),从 Gen.structs # 中查找同名且元素数更多的完整定义。 if (isinstance(pointee, ir.IdentifiedStructType) and offset is not None and pointee.elements is not None and offset >= len(pointee.elements)): _cur_count: int = len(pointee.elements) for CN2, ST2 in Gen.structs.items(): if (isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name and ST2.elements is not None and len(ST2.elements) > _cur_count and offset < len(ST2.elements)): pointee = ST2 break if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset is not None and offset < len(pointee.elements): if isinstance(pointee.elements[offset], ir.VoidType): return None return self._gep_and_Load_member(Gen, SelfPtr, offset, Node.attr, ClassName, pointee) DynamicVarName: str = f"self.{Node.attr}" if DynamicVarName in Gen.variables: return Gen._load(Gen.variables[DynamicVarName], name=Node.attr) return None def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: """处理枚举成员访问 (VarName 是枚举类型名)""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) if not SymInfo or not SymInfo.IsEnum: return None # 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖) FullKey: str = f"{VarName}.{Node.attr}" MemberInfo: Any = self.Trans.SymbolTable.lookup(FullKey) if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: # 回退到短名查找 MemberInfo = self.Trans.SymbolTable.lookup(Node.attr) if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: # 最后遍历符号表 for key, info in self.Trans.SymbolTable.items(): if info.IsEnumMember and info.EnumName == VarName and key == Node.attr: if isinstance(info.value, int): return ir.Constant(Gen._enum_int_type(VarName, info), info.value) return None if isinstance(MemberInfo.value, int): return ir.Constant(Gen._enum_int_type(VarName, MemberInfo), MemberInfo.value) return None def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: """处理 x.attr 形式 (x 是普通 Name)""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen AttrName: str = Node.attr # __doc__ 魔法字符串:函数/结构体的 docstring if AttrName == '__doc__': doc_ptr: ir.Value | None = Gen._get_doc_ptr(VarName) if doc_ptr is not None: return doc_ptr # 先尝试枚举成员 result: Any = self._handle_attr_enum(Node, VarName) if result is not None: return result # 尝试 CDefine 常量 result = self._lookup_cdefine(Gen, VarName, AttrName) if result is not None: return result # 冗余回退:直接以短名/全名查符号表的 CDefine 条目 # 覆盖 _ImportedModules 未填充但符号表已注册 CDefine 的场景 # (例如自举编译期 ast.CONST_INT 在 LlvmGenerator 预扫描阶段 # 已注册到 SymbolTable,但 _ImportedModules 仍为空)。 for _cdefine_key in (AttrName, f"{VarName}.{AttrName}"): _sym: Any = self.Trans.SymbolTable.lookup(_cdefine_key) if _sym and _sym.IsDefine and _sym.DefineValue is not None: _const_val: Any = self._make_define_constant(Gen, _sym.DefineValue, AttrName) if _const_val is not None: return _const_val # 尝试 import 模块全局变量 result = self._lookup_module_global(Gen, VarName, AttrName) if result is not None: return result # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) # 当 a 是 str 变量时, a 本身是 char*, 但 a__meta__ 存储了 __data__/__mbuddy__ 等字段 # 直接通过 a.__field__ 访问影子结构体字段 (语法糖) _fd_meta_name: str = f"{VarName}__meta__" if _fd_meta_name in Gen.variables and '_str' in Gen.structs: _fd_offset = Gen._get_member_offset(AttrName, '_str') if _fd_offset is not None: _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") return Gen._load(_fd_field_ptr, name=f"{VarName}_{AttrName}_val") # 解析结构体类名:若 VarName 是结构体变量,优先走结构体字段访问路径 # 避免结构体字段名(如 stdout)与全局变量名冲突时被误解析为全局变量 ClassName: str | None = self._resolve_var_classname(Gen, VarName) # 查找端键归一:var_struct_class 可能记录裸短名('Tuple'),而 Gen.structs # 的键是带 class_sha1 前缀的全名(如 '43ec967d0517abc1.Tuple'),或反之。 # 单级 Name 字段取值(tp.elts)要求 ClassName 能直接定位 Gen.structs 并 # 加载 stub body 后才能 GEP —— 与 _handle_attr_nested 各分支一致先归一。 if ClassName and ClassName not in Gen.structs: _cn_key: str | None = self._find_struct_key_for_class_hint(Gen, ClassName) if _cn_key: ClassName = _cn_key # 尝试 attr 直接作为全局变量(仅当 VarName 不是结构体变量时) if not ClassName and AttrName in Gen.module.globals: gv: Any = Gen.module.globals[AttrName] if AttrName in Gen.variables and Gen.variables[AttrName] is None: str_gv_name: str = AttrName + '_str' if str_gv_name in Gen.module.globals: str_gv: Any = Gen.module.globals[str_gv_name] return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") return Gen.builder.load(gv, name=AttrName) if not ClassName: # 单级 Name 字段取值的类名恢复(跨模块 stub 下 var_struct_class 缺失的 # 兜底):本地变量(如 `tp: rawptr[Tuple] = (rawptr[Tuple])(node)`)可能 # 因 rawptr[X] 被折叠为 i8* 而漏登记。若变量 alloca / 值本身可辨识为 # 结构体指针,按实际 pointee 恢复真实 Gen.structs 键并回填,避免属性 # 访问求值 None 被 fail-fast 拦截(parser.vp `tp_elts = tp.elts` 根因)。 if (VarName in Gen.variables or VarName in Gen._reg_values or VarName in Gen._direct_values or VarName in Gen.global_vars): _recovered: str | None = self._recover_struct_class(Gen, VarName) if _recovered: ClassName = _recovered Gen.var_struct_class[VarName] = _recovered if getattr(Gen, 'global_struct_class', None) is not None: Gen.global_struct_class[VarName] = _recovered if not ClassName: self._NoteAttrNone(Gen, None, AttrName, f"类名未解析('{VarName}' 的 var_struct_class 为空且无法由 " f"alloca/值类型恢复)") return None # 检查 property getter PropKey: str = f'{ClassName}.{AttrName}' PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: ObjVal: Any = self.HandleExprLlvm(Node.value) GetterFunc: Any = Gen._get_function(PropKey) if GetterFunc and ObjVal: if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") return Gen.builder.call(GetterFunc, [ObjVal], name=f"prop_{PropKey}") # 获取类型信息 TypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName) IsUnion: bool = TypeInfo.IsUnion if TypeInfo else False IsCenum: bool = TypeInfo.IsEnum if TypeInfo else False IsRenum: bool = TypeInfo.IsRenum if TypeInfo else False # REnum 处理 if IsRenum: if AttrName == 'tag': ObjVal = self.HandleExprLlvm(Node.value) if ObjVal: if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") tag_ptr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="tag_ptr") return Gen._load(tag_ptr, name="tag_val") NestedStructName: str = f"{ClassName}_{AttrName}" if NestedStructName in Gen.structs: NestedStructType: Any = Gen.structs[NestedStructName] ObjVal = self.HandleExprLlvm(Node.value) if ObjVal: if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") # CEnum 处理 if IsCenum: for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"): info: Any = self.Trans.SymbolTable.lookup(qname) if info and info.IsEnumMember and isinstance(info.value, int): return ir.Constant(Gen._enum_int_type(ClassName, info), info.value) for key, info in self.Trans.SymbolTable.items(): if key == AttrName: if info.IsEnumMember and info.EnumName == ClassName: if isinstance(info.value, int): return ir.Constant(Gen._enum_int_type(ClassName, info), info.value) break # CUnion 处理 if IsUnion: NestedStructName = f"{ClassName}_{AttrName}" if NestedStructName in Gen.structs: NestedStructType = Gen.structs[NestedStructName] ObjVal = self.HandleExprLlvm(Node.value) if ObjVal: if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") # 普通结构体成员访问 ObjVal = self.HandleExprLlvm(Node.value) if not ObjVal: self._NoteAttrNone(Gen, ClassName, AttrName, f"基座 '{VarName}' 自身求值为空(类名={ClassName!r})") return None # char* → struct* cast if self._is_char_pointer(ObjVal): if ClassName in Gen.structs: ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") # 二级指针解引用。模块级全局指针变量(如 _g_gen: rawptr[Generator.Generator]) # 的全局元素类型比实际存值多一层:写入端(main.vp `GenApi._g_gen = gen`)生成 # 为 bitcast T*→T** 后直接存值,未取地址;读取端必须做其逆运算 bitcast,绝不能 # 再 load——否则把 T* 当 T** 再解引用,接收者被取成对象首字段的位,方法内读 # self->Gen 即野指针(ASan: CallGenBase.SetTempDir 读悬空地址崩溃)。 if (isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType) and isinstance(Node.value, ast.Name) and Node.value.id in Gen.module.globals): ObjVal = Gen.builder.bitcast(ObjVal, ObjVal.type.pointee, name=f"cast_{VarName}_global") elif isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") # 检查结构体匹配 struct_match: bool = self._check_struct_match(Gen, ObjVal, ClassName) if not struct_match and ClassName and VarName: if VarName in Gen.var_struct_class: CN: str = Gen.var_struct_class[VarName] if CN != ClassName and CN in Gen.structs: ClassName = CN struct_match = self._check_struct_match(Gen, ObjVal, ClassName) # 最终回退:var_struct_class 可能被前一个函数污染(如 cur 在不同函数中 # 分别是 Line/Param/BasicBlock),通过 ObjVal 的实际 pointee 类型恢复正确类名。 # 但若 var_struct_class 已记录特化名(如 ndarray[double]),且 ObjVal 的 # pointee 是对应的 opaque 基础类型(如 ndarray),则 bitcast 到特化版本, # 不覆盖为裸模板名(否则后续方法调用会查找 ndarray.xxx 而非 ndarray[double].xxx) if not struct_match and isinstance(ObjVal.type, ir.PointerType): found: Any = Gen.find_struct_by_pointee(ObjVal.type.pointee) if found: found_name: str = found[0] # 检查 var_struct_class 是否已记录 found_name 的特化版本 preserved: bool = False if VarName and VarName in Gen.var_struct_class: cur_spec: str = Gen.var_struct_class[VarName] if ('[' in cur_spec and cur_spec.split('[')[0] == found_name and cur_spec in Gen.structs): # bitcast ObjVal (opaque base*) 到特化类型指针,保留特化名 ObjVal = Gen.builder.bitcast( ObjVal, ir.PointerType(Gen.structs[cur_spec]), name=f"cast_spec_{cur_spec}") ClassName = cur_spec struct_match = True preserved = True if not preserved: ClassName = found_name struct_match = True if VarName: Gen.var_struct_class[VarName] = ClassName elif isinstance(ObjVal.type.pointee, ir.IdentifiedStructType): # sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时, # 用短名匹配,优先选字段最多的完整版本 pointee_short: str = Gen._extract_short_name(ObjVal.type.pointee.name) best_elem_count: int = 0 for CN, ST in Gen.structs.items(): if isinstance(ST, ir.IdentifiedStructType): st_short: str = Gen._extract_short_name(ST.name) if st_short == pointee_short: elem_count: int = len(ST.elements) if ST.elements else 0 if elem_count > best_elem_count: ClassName = CN best_elem_count = elem_count struct_match = True if struct_match and VarName: # 同样保护特化名:若已记录的特化名的短名基础部分与 ClassName 匹配,保留 cur_spec2: str = Gen.var_struct_class.get(VarName, '') if ('[' in cur_spec2 and cur_spec2.split('[')[0] == ClassName and cur_spec2 in Gen.structs): ObjVal = Gen.builder.bitcast( ObjVal, ir.PointerType(Gen.structs[cur_spec2]), name=f"cast_spec_{cur_spec2}") ClassName = cur_spec2 else: Gen.var_struct_class[VarName] = ClassName # 通用指针槽兜底:静态类名(var_struct_class/键归一后)有效且 ObjVal 是指针, # 但 pointee 非 struct(如 cast 局部变量 tp: rawptr[Tuple] 的槽被建为 i32**, # Name 求值 load 一次得到 i32*,上述 struct 匹配/回退均不命中 → return None)。 # 此时按静态类名信任 bitcast 到 struct* 再走成员 GEP(tp.elts 根因: # var_struct_class='Tuple'、structs 有 'Tuple',仅槽位为通用指针形态)。 if (not struct_match and ClassName and ClassName in Gen.structs and isinstance(ObjVal.type, ir.PointerType) and not isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and not isinstance(ObjVal.type.pointee, ir.LiteralStructType)): ObjVal = Gen.builder.bitcast( ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_slot_{ClassName}") struct_match = True if not struct_match: self._NoteAttrNone( Gen, ClassName, AttrName, f"结构体未匹配(ObjVal 类型={ObjVal.type},类名={ClassName!r};" f"pointee 既非该类 struct,静态类名也未能 bitcast 兜底)") return None # 左值注解靶型提示(见 _apply_target_struct_hint):类不声明该字段时改用注解类 ClassName = self._apply_target_struct_hint(Gen, AttrName, ClassName) st: Any ok: bool st, ok = self._try_Load_struct_from_stub(Gen, ClassName) if not ok: self._NoteAttrNone(Gen, ClassName, AttrName, f"stub 无 body(Gen.structs[{ClassName!r}] 存在但 elements 为空/opaque)") return None # bitfield 处理 bitfield_offsets: Any = Gen.class_member_bitoffsets.get(ClassName, {}) bitfields: Any = Gen.class_member_bitfields.get(ClassName, {}) if AttrName in bitfield_offsets and bitfields.get(AttrName, 0) > 0: return self._load_bitfield_member(ObjVal, ClassName, AttrName) if AttrName in bitfields and bitfields[AttrName] > 0: if ClassName not in Gen.class_member_bitoffsets: Gen.class_member_bitoffsets[ClassName] = {} if AttrName not in Gen.class_member_bitoffsets[ClassName]: bo: int = 0 for bf_name, bf_width in bitfields.items(): if bf_name == AttrName: break bo += bf_width Gen.class_member_bitoffsets[ClassName][AttrName] = bo return self._load_bitfield_member(ObjVal, ClassName, AttrName) # 偏移与 GEP 必须**同源**:把"实际将被索引的那个 struct 实例"传给偏移计算。 # 同一类键上可能存在两个实例(同名类 sha1 冲突双注册 / 一方仍 opaque), # 用类键上那份算偏移、用 ObjVal 这份做 GEP ⇒ 整类字段偏移平移 # (现场:`Constant.lineno` 偏移 2 落到 `pool`(MemManager*) → 第 6 个实参被报 # "指针不能隐式转整数 i32")。_member_base 收到不同实例时会报出事实。 _StructHint: Any = None if isinstance(ObjVal.type, ir.PointerType): _ObjPointee: Any = ObjVal.type.pointee if isinstance(_ObjPointee, ir.IdentifiedStructType): _StructHint = _ObjPointee offset = Gen._get_member_offset(AttrName, ClassName, _StructHint) # 检查 offset 是否超出实际结构体 if isinstance(ObjVal.type, ir.PointerType): actual_pointee: Any = ObjVal.type.pointee if isinstance(actual_pointee, ir.IdentifiedStructType): actual_elems: Any = actual_pointee.elements if actual_elems is not None and offset is not None and offset >= len(actual_elems): self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) actual_elems = actual_pointee.elements if actual_elems is not None and offset >= len(actual_elems): # sha1 冲突修复:ObjVal 的 pointee 可能是字段不全的版本(如 2 字段 Function), # 而 st(来自 _try_Load_struct_from_stub)可能是字段完整的版本。 # bitcast ObjVal 到 st 的类型,使 GEP 能正确访问完整字段。 if st is not None and isinstance(st, ir.IdentifiedStructType) and st.elements is not None and offset < len(st.elements): ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_load_{ClassName}") else: self._NoteAttrNone( Gen, ClassName, AttrName, f"槽位越界(_handle_attr_name 侧):字段 '{AttrName}' 需槽位 " f"{offset},但 ObjVal 的 pointee 只有 {len(actual_elems)} 个元素," f"st 版结构体也不够({len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'None/opaque'})") return None result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st) return result def _recover_struct_class(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: """由变量的 LLVM 存储类型恢复结构体类键(var_struct_class 缺失时兜底)。 单级 Name 字段取值(如 tp.elts)的本地变量,若其注解 rawptr[X] 被 TypeMerge builtins 分支折叠成 i8*、且 var_struct_class 未登记 X,属性访问层无法解析 类名。此处直接检查 Gen.variables / _reg_values / _direct_values / global_vars 中该变量的 alloca(或值)指针 pointee:能辨识出 IdentifiedStruct 就按 pointee 恢复真实 Gen.structs 键(精确匹配失败时按 短名找元素最多的完整版本),供调用方回填并继续 GEP 成员访问。不发射任何 额外 IR(纯查表)。 """ if not VarName: return None probe: ir.Type | None = None vp: Any = Gen.variables.get(VarName) if vp is not None and isinstance(vp.type, ir.PointerType): probe = vp.type.pointee if isinstance(probe, ir.PointerType): probe = probe.pointee if probe is None: for reg in (Gen._reg_values.get(VarName), Gen._direct_values.get(VarName)): if reg is not None and isinstance(reg.type, ir.PointerType): probe = reg.type.pointee if isinstance(probe, ir.PointerType): probe = probe.pointee break if probe is None and VarName in Gen.global_vars and hasattr(Gen, 'module') and VarName in Gen.module.globals: gv: Any = Gen.module.globals[VarName] if isinstance(gv, ir.GlobalVariable) and isinstance(gv.type, ir.PointerType): probe = gv.type.pointee if isinstance(probe, ir.PointerType): probe = probe.pointee if probe is None or not isinstance(probe, ir.IdentifiedStructType): return None found: Any = Gen.find_struct_by_pointee(probe) if found: return found[0] return self._pointee_llvm_to_struct_key(Gen, probe) def _handle_attr_nested(self, Node: ast.Attribute) -> ir.Value | None: """处理 a.b.c 嵌套属性访问""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen if isinstance(Node.value.value, ast.Name): ParentVarName: str = Node.value.value.id ParentAttrName: str = Node.value.attr # __doc__ 魔法字符串:ClassName.method.__doc__ 或 module.ClassName.__doc__ if Node.attr == '__doc__': full_symbol: str = f"{ParentVarName}.{ParentAttrName}" doc_ptr: ir.Value | None = Gen._get_doc_ptr(full_symbol) if doc_ptr is not None: return doc_ptr # 回退:module.ClassName.__doc__ → 仅用 ClassName 查找 doc_ptr = Gen._get_doc_ptr(ParentAttrName) if doc_ptr is not None: return doc_ptr enum_result: Any = self._try_resolve_cross_module_enum(ParentVarName, ParentAttrName, Node.attr) if enum_result is not None: return enum_result # 尝试跨模块子模块全局变量解析: a.b.c 其中 a.b 是已知子模块, c 是该模块的全局变量 imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) if imported_modules: full_ModulePath: str = f"{ParentVarName}.{ParentAttrName}" if full_ModulePath in imported_modules: result: Any = self._lookup_module_global(Gen, full_ModulePath, Node.attr) if result is not None: return result elif isinstance(Node.value.value, ast.Attribute) and isinstance(Node.value.value.value, ast.Name): # 三层嵌套:module.ClassName.method.__doc__ if Node.attr == '__doc__': ModuleName: str = Node.value.value.value.id ClassName2: str = Node.value.value.attr MethodName: str = Node.value.attr FullSymbol3: str = f"{ModuleName}.{ClassName2}.{MethodName}" doc_ptr3: ir.Value | None = Gen._get_doc_ptr(FullSymbol3) if doc_ptr3 is not None: return doc_ptr3 # 回退:ClassName.method.__doc__ doc_ptr3 = Gen._get_doc_ptr(f"{ClassName2}.{MethodName}") if doc_ptr3 is not None: return doc_ptr3 # 三层嵌套:module.submodule.EnumClass.MEMBER ModuleAlias3: str = f"{Node.value.value.value.id}.{Node.value.value.attr}" EnumClassName3: str = Node.value.attr enum_result3: Any = self._try_resolve_cross_module_enum(ModuleAlias3, EnumClassName3, Node.attr) if enum_result3 is not None: return enum_result3 cdefine_result: Any = self._try_resolve_nested_cdefine(Node) if cdefine_result is not None: return cdefine_result ObjVal: Any = self.HandleExprLlvm(Node.value) if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): # 取证(纯记账):基座求值为空/非指针时,把**内层链的失败原因**一并挂上—— # 嵌套读取的内层记录随后会被本节点的记录覆盖,报错点(本节点)与真因(内层某级) # 相隔数层的可读性问题由此解决(现场:`GenApi._g_gen.TypeGenCType.CTypedefNames`)。 _prev_an: Any = getattr(Gen, '_LastAttrNone', None) _chain_an: str = '' if (isinstance(_prev_an, tuple) and len(_prev_an) >= 3 and _prev_an[2] and _prev_an[1] and _prev_an[1] != Node.attr): _chain_an = f";内层字段 '{_prev_an[1]}' 的失败原因:{_prev_an[2]}" self._NoteAttrNone( Gen, None, Node.attr, f"基座 {self._ExprShape(Node.value)} 求值为空或非指针" f"(type={getattr(ObjVal, 'type', None)}){_chain_an}") return None pointee: Any = ObjVal.type.pointee # char* → struct* cast + 成员访问 if isinstance(pointee, ir.IntType) and pointee.width == 8: # 两级自动解引用链(变量 = 指针.字段.成员,如 blk = func.Blocks.Head) # 根因修复:跨模块 stub 会把容器/类内指针字段(rawptr[GSList[BasicBlock]] # 等)退化为 i8*,此处 ObjVal 是刚从结构体字段 load 出的 i8*。必须恢复 # 该字段声明的元素/特化类(如 'GSList[BasicBlock]')才能 # bitcast + GEP + load 出成员。否则本函数返回 None → AnnAssign/Assign # 静默跳过 store → 目标 alloca 永不初始化(_peephole_opt 读未初始化 # 栈变量随机崩溃,见 __function.vp L783 注释)。 # 解析顺序:_resolve_chain_result_class 沿链前向解析父表达式(可为任意 # 级数,如 ve.Alloca.Ty)的结果类;失败回退 _get_attr_class 直查 # class_member_element_class;再回退 _resolve_attr_parent_hint(与下方 # struct 分支同一 parent_hint 机制:func 的 var_struct_class + # class_member_element_class 给出 GSList[BasicBlock] 提示);三者都兼容 # 裸名/带 sha1 前缀的键。 AttrClassName: str | None = self._resolve_chain_result_class(Node.value, Gen) if not AttrClassName: AttrClassName = self._get_attr_class(Node.value, Gen) if not AttrClassName: AttrClassName = self._resolve_attr_parent_hint(Node.value, Gen) if not AttrClassName: # 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory): # 上面三个解析器都按「字段所属类」思路找,对「模块别名.变量」返回 None # (attr_class=None 即此处的 key-miss 现场,StructGenMixin.vp:139 与 # ClassGenVTable.vp:99 根因)。而该变量的类已由 HandlesImports 的 # _EmitExternalGlobalDeclLlvm / HandlesAnnAssign 登记在 # var_struct_class / global_struct_class(键为**裸变量名**)⇒ 直读。 _mva: ast.AST | None = Node.value if isinstance(_mva, ast.Attribute) and isinstance(_mva.value, ast.Name): _mname: str = _mva.value.id _sha1_map2 = getattr(Gen, 'ModuleSha1Map', {}) or {} _imported2 = getattr(self.Trans, '_ImportedModules', None) or [] _aliases2 = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {} try: _rmod: str = self.Trans.SymbolTable.resolve_alias(_mname) except Exception: _rmod = _mname _is_mod2: bool = (_mname in _sha1_map2 or _rmod in _sha1_map2 or _mname in _imported2 or _rmod in _imported2 or _mname in _aliases2 or _rmod in _aliases2) if not _is_mod2: for _k2 in _sha1_map2: if _k2 == _mname or _k2.endswith(f'.{_mname}'): _is_mod2 = True break if _is_mod2: AttrClassName = (getattr(Gen, 'var_struct_class', {}).get(_mva.attr) or getattr(Gen, 'global_struct_class', {}).get(_mva.attr)) ResolvedKey: str | None = self._find_struct_key_for_class_hint(Gen, AttrClassName) # 左值注解靶型提示:解析出的类不声明该字段时,用注解类兜一次(两条件同时成立才改判) ResolvedKey = self._apply_target_struct_hint(Gen, Node.attr, ResolvedKey) if ResolvedKey is None or ResolvedKey not in Gen.structs: self._NoteAttrNone( Gen, AttrClassName, Node.attr, f"基座字段的所属类未能在 Gen.structs 命中键(类名提示={AttrClassName!r}," f"structs 键数={len(Gen.structs)})") return None st: Any ok: bool st, ok = self._try_Load_struct_from_stub(Gen, ResolvedKey) if not isinstance(st, ir.IdentifiedStructType): self._NoteAttrNone(Gen, ResolvedKey, Node.attr, f"类 '{ResolvedKey}' 的 struct 实例类型异常({type(st).__name__})") return None if st.elements is None: # stub 仍 opaque(如跨模块泛型仅声明):走泛型特化候选访问 spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st, Node, ResolvedKey, AttrClassName) if spec_val is not None: return spec_val self._NoteAttrNone( Gen, ResolvedKey, Node.attr, f"类 '{ResolvedKey}' 的 body 仍为 opaque(elements=None),且无泛型特化成员可访问") return None CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_{ResolvedKey}") if Node.attr in Gen.class_member_bitoffsets.get(ResolvedKey, {}): return self._load_bitfield_member(CastedObj, ResolvedKey, Node.attr) offset: int | None = Gen._get_member_offset(Node.attr, ResolvedKey) if offset is None: self._NoteAttrNone( Gen, ResolvedKey, Node.attr, f"字段 '{Node.attr}' 在类 '{ResolvedKey}' 中取不到偏移" f"(成员表 {len(Gen.class_members.get(ResolvedKey) or [])} 项 / body {len(st.elements)} 槽)") return None if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType): self._NoteAttrNone( Gen, ResolvedKey, Node.attr, f"字段 '{Node.attr}' 的槽 {offset} 是 void(占位槽:类型信息缺失)") return None return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, ResolvedKey, st) # 已知结构体类型 → 直接成员访问 if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): # 父上下文特化提示:Node.value 是 Attribute(Name, attr)(如 # func.Blocks)时,从 var_struct_class/global_struct_class + # class_member_element_class 取字段声明的特化名(如 'GSList[BasicBlock]'), # 避免在所有 GSList[T] 都有 Head/Tail/Count 字段时选错特化版本。 # 与 char* 分支共用 _resolve_attr_parent_hint(兼容裸名/sha1 前缀键); # 父表达式为多级链(Node.value 是 Attribute)时 _resolve_attr_parent_hint # 无法取提示,用 _resolve_chain_result_class 沿链前向解析补充。 parent_hint: str | None = self._resolve_attr_parent_hint(Node, Gen) if not parent_hint and isinstance(Node.value, ast.Attribute): parent_hint = self._resolve_chain_result_class(Node.value, Gen) or parent_hint # 同短名候选:**唯一**时才放行(两个及以上同名类无法判定是哪一个, # 宁可留痕失败也不能选错布局——同名类冲突在本仓一律以 sha1 全名区分)。 _UniqShortCN: str | None = None if isinstance(pointee, ir.IdentifiedStructType): _PShort2: str = (getattr(pointee, 'name', '') or '').rsplit('.', 1)[-1] _ShortCNs: list[str] = [ _c5 for _c5, _s5 in Gen.structs.items() if isinstance(_s5, ir.IdentifiedStructType) and (getattr(_s5, 'name', '') or '').rsplit('.', 1)[-1] == _PShort2 ] if len(_ShortCNs) == 1: _UniqShortCN = _ShortCNs[0] for CN, ST in Gen.structs.items(): if ((pointee == ST) or self._PointeeMatchesStruct(pointee, ST) or (_UniqShortCN is not None and CN == _UniqShortCN)): st, ok = self._try_Load_struct_from_stub(Gen, CN) if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN, parent_hint) if spec_val is not None: return spec_val break bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) if Node.attr in bitfield_offsets: return self._load_bitfield_member(ObjVal, CN, Node.attr) offset = Gen._get_member_offset(Node.attr, CN) if offset is None: self._NoteAttrNone( Gen, CN, Node.attr, f"字段 '{Node.attr}' 在类 '{CN}' 中取不到偏移" f"(成员表 {len(Gen.class_members.get(CN) or [])} 项 / " f"body {len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'opaque'} 槽)") break # 泛型基础类型字段特化(根本修复): # 当字段声明为特化泛型(如 GSList[BasicBlock])时,基础类型(GSList)的 # Head/Tail/Count 字段类型是泛型占位 T*。直接按基础类型访问会返回 T*, # 导致变量类型被污染(后续字段访问/循环体翻译静默失败,见 # llvmlite __function.vp _find_max_ssa_number 死循环根因)。 # 用 class_member_element_class 记录的特化类型(parent_hint)重新访问, # 返回真实元素类型(如 BasicBlock*)。 if (parent_hint is not None and isinstance(st, ir.IdentifiedStructType) and st.elements is not None and offset < len(st.elements) and isinstance(st.elements[offset], ir.PointerType) and isinstance(st.elements[offset].pointee, ir.IdentifiedStructType)): _field_short: str = (st.elements[offset].pointee.name or '').split('.')[-1] if (len(_field_short) == 1 and _field_short.isalpha() and _field_short.isupper()): _hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint for CN2, ST2 in Gen.structs.items(): if not isinstance(ST2, ir.IdentifiedStructType): continue if ST2 is ST: continue if ST2.elements is None: continue _spec_short: str = CN2.split('.')[-1] if '.' in CN2 else CN2 if _spec_short == _hint_short: _casted: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(ST2), name=f"cast_spec_{CN}") _off2: int | None = Gen._get_member_offset(Node.attr, CN2) if _off2 is not None: return self._gep_and_Load_member(Gen, _casted, _off2, Node.attr, CN2, ST2) break return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) # 结构体分支未命中任何类:pointee 与 Gen.structs 全部键都不等(同名类双实例 / # 该类未注册)——这是"基座类型已知却读不到字段"的最后一种来源,必须留痕。 _PShortN: str = (getattr(pointee, 'name', '') or '').rsplit('.', 1)[-1] _SameShort: list[str] = [] for _cn3, _st3 in Gen.structs.items(): if not isinstance(_st3, ir.IdentifiedStructType): continue if (getattr(_st3, 'name', '') or '').rsplit('.', 1)[-1] == _PShortN: _SameShort.append(_cn3) self._NoteAttrNone( Gen, None, Node.attr, f"基座 pointee {pointee} 未匹配 Gen.structs 中任何 struct(键数={len(Gen.structs)})" f";分级匹配(身份 / IR 全名 / 同 sha1 前缀)与「同短名 '{_PShortN}' 唯一候选」" f"**全部未命中**,同短名候选={_SameShort if _SameShort else '无'}" f"(候选 >1 时会被主动拒绝,避免选错同名类)" f"——即该类在本 Gen 的 structs 里没有可用实例(另一 Gen/缓存的实例漏进来了," f"或该类从未注册)" f";基座形态={self._ExprShape(Node.value)}") return None def _handle_attr_call_result(self, Node: ast.Attribute) -> ir.Value | None: """处理 func().attr 属性访问""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen # Check if this is Deref(...) — we need the pointer, not the Loaded value ObjVal: Any = None is_cderef: bool = (isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name) and Node.value.func.id == 'Deref' and Node.value.args) if is_cderef: deref_arg: ast.expr = Node.value.args[0] inner_val: Any = self.HandleExprLlvm(deref_arg) if inner_val and isinstance(inner_val.type, ir.PointerType): if isinstance(inner_val.type.pointee, ir.PointerType): ObjVal = Gen._load(inner_val, name="deref_load_ptr") else: ObjVal = inner_val if ObjVal is None: ObjVal = self.HandleExprLlvm(Node.value) # 联合类型转换 (X | CPtr)(arg) 的属性访问(治本修复): # TPC 联合转换对非 CType 类(如 ast.Name / ast.Call)目标类型解析失败, # 结果可能退回 Ptr(i8) 或错误类型,导致下方 find_struct_by_pointee 失败、 # 属性访问被静默丢弃——((ast.Name|CPtr)(ov)).id 编译后 store 0 次 # (with 语句 asname 未绑定、pool 未定义根因)。从联合 BinOp 提取真实 # 基础类并 bitcast 到 Ptr(类) 再访问成员。 # # 该形态的每个 return None 都改成 fail-fast(见 _FailFastAttrCallBase): # `(T | CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `T*(x)` 统一 # 产成该 Call),返回 None 会让调用方只报"表达式编译期求值为空",看不出是 # 类名没被当成类、结构体不透明、还是成员偏移取不到(现场:ViperC # ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`,而同一函数前三条 # `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 _IsUnionCastBase: bool = (isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.BinOp) and isinstance(Node.value.func.op, ast.BitOr)) _UnionCastCls: str | None = None if _IsUnionCastBase: _UnionCastCls = self._extract_union_base_class(Node.value.func) if ObjVal is not None and _UnionCastCls and _UnionCastCls in Gen.structs: target_ty: Any = ir.PointerType(Gen.structs[_UnionCastCls]) if isinstance(ObjVal.type, ir.PointerType): if ObjVal.type != target_ty: ObjVal = Gen.builder.bitcast(ObjVal, target_ty, name="cast_union_attr") elif isinstance(ObjVal.type, ir.IntType): ObjVal = Gen.builder.inttoptr(ObjVal, target_ty, name="int2ptr_union_attr") if not ObjVal: self._FailFastAttrCallBase("基座强转调用求值为空(HandleExprLlvm 返回 None)", Node, Gen, _IsUnionCastBase, _UnionCastCls, None) return None if not isinstance(ObjVal.type, ir.PointerType): self._FailFastAttrCallBase("基座强转结果不是指针类型", Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None pointee: Any = ObjVal.type.pointee if isinstance(pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr") pointee = ObjVal.type.pointee found: Any = Gen.find_struct_by_pointee(pointee) if not found: self._FailFastAttrCallBase("被指结构体未在 structs 登记(find_struct_by_pointee 未命中)", Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None CN: str ST: Any CN, ST = found st: Any ok: bool st, ok = self._try_Load_struct_from_stub(Gen, CN) if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) if spec_val is not None: return spec_val self._FailFastAttrCallBase("结构体不透明(stub / .vpi 未加载出字段)", Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) if Node.attr in bitfield_offsets: return self._load_bitfield_member(ObjVal, CN, Node.attr) offset: int | None = Gen._get_member_offset(Node.attr, CN) if offset is None: self._FailFastAttrCallBase("成员偏移取不到(class_members / 继承链均未命中)", Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) return None return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) def _FailFastAttrCallBase(self, Stage: str, Node: ast.Attribute, Gen: Any, IsUnionCastBase: bool, UnionCls: str | None, ObjVal: Any) -> None: """`(T | CPtr)(x).attr` 属性访问失败时的 fail-fast(非该形态时静默返回,行为不变)。 `(T | CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `(T*)(x)` / `T*(x)` 统一产成该 Call),其属性访问必须有值产出。此前一律 `return None`,调用方只能 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",把"类名没被当成类 / 结构体不透明 / 成员偏移取不到"三种完全不同的根因混成一条报文 (现场:ViperC ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`)。 此处逐项列出判定依据,直接指认失败环节。 """ if not IsUnionCastBase: return from lib.core.LLVMCG.FuncGen import FailFastError _Parts: list[str] = [ f"属性 '{Node.attr}' 的基座是强转调用 '{self._ExprText(Node.value)}'," f"失败环节:{Stage}", f"联合基类名={UnionCls!r}" f";短名 in structs={'是' if (UnionCls and UnionCls in Gen.structs) else '否'}" f";structs 同短名键=" f"{[k for k in Gen.structs if UnionCls and k.split('.')[-1] == UnionCls][:6]}" f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(UnionCls)!r}" f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(UnionCls)!r}", ] if ObjVal is None: _Parts.append("基座求值结果=None") else: _Parts.append(f"基座求值类型={ObjVal.type!r}") if isinstance(ObjVal.type, ir.PointerType): _Pt: Any = ObjVal.type.pointee _Parts.append(f"被指类型={_Pt!r};elements=" f"{getattr(_Pt, 'elements', 'n/a')}") _Found: Any = Gen.find_struct_by_pointee(_Pt) _Parts.append( f"find_struct_by_pointee={'命中 ' + str(_Found[0]) if _Found else '未命中'}") if _Found: _CN: str = _Found[0] _Parts.append( f"class_members[{_CN!r}]=" f"{[m[0] for m in Gen.class_members.get(_CN, [])][:12]}" f"({len(Gen.class_members.get(_CN, []))} 项)" f";成员 '{Node.attr}' 偏移={self._SafeOffset(Gen, Node.attr, _CN)!r}") if UnionCls: _Parts.append( f"class_members[{UnionCls!r}]=" f"{[m[0] for m in Gen.class_members.get(UnionCls, [])][:12]}" f"({len(Gen.class_members.get(UnionCls, []))} 项)" f";class_parent={getattr(Gen, 'class_parent', {}).get(UnionCls)!r}") raise FailFastError(";".join(_Parts)) @staticmethod def _SafeOffset(Gen: Any, Attr: str, ClassName: str) -> Any: """取证用成员偏移查询:本身绝不能再抛异常掩盖原始故障。""" try: return Gen._get_member_offset(Attr, ClassName) except Exception as _E: return f"<查询异常 {type(_E).__name__}: {_E}>" def _extract_union_base_class(self, NodeFunc: ast.BinOp) -> str | None: """从 (X | CPtr) 联合 BinOp 提取第一个非指针/非修饰符的类名 X 可以是任意类(如 ast.Name / ast.Call / memhub.MemPool),不限于 CType 子类——联合转换 (X | CPtr)(arg) 的属性访问需要真实类名 才能 bitcast 到 Ptr(类) 并定位成员偏移。 """ if not isinstance(NodeFunc, ast.BinOp) or not isinstance(NodeFunc.op, ast.BitOr): return None skip_names = { 'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', 'CStatic', 'CExtern', 'CExport', 'CAuto', 'CRegister', 'CDefine', 'CPass', 'CTypeDefault', 'State', 'BigEndian', 'LittleEndian', } operands: list = [] def _collect(node: ast.AST) -> None: if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): _collect(node.left) _collect(node.right) else: operands.append(node) _collect(NodeFunc) for op in operands: name: str | None = None if isinstance(op, ast.Attribute): name = op.attr elif isinstance(op, ast.Name): name = op.id if name and name not in skip_names: return name return None def _handle_attr_subscript_result(self, Node: ast.Attribute) -> ir.Value | None: """处理 arr[i].attr 属性访问""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen ObjVal: Any = self.HandleExprLlvm(Node.value) if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): return None pointee: Any = ObjVal.type.pointee if not isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): return None found: Any = Gen.find_struct_by_pointee(pointee) if not found: return None CN: str ST: Any CN, ST = found st: Any ok: bool st, ok = self._try_Load_struct_from_stub(Gen, CN) if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) if spec_val is not None: return spec_val return None offset: int | None = Gen._get_member_offset(Node.attr, CN) if offset is None: return None return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) def _handle_attr_fallback(self, Node: ast.Attribute) -> ir.Value | None: """兜底:对任意值尝试指针解引用后查找结构体成员""" Gen: LlvmCodeGenerator = self.Trans.LlvmGen ObjVal: Any = self.HandleExprLlvm(Node.value) if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): return None pointee: Any = ObjVal.type.pointee if isinstance(pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr_fallback") pointee = ObjVal.type.pointee if not isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): return None found: Any = Gen.find_struct_by_pointee(pointee) if not found: return None CN: str ST: Any CN, ST = found st: Any ok: bool st, ok = self._try_Load_struct_from_stub(Gen, CN) if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) if spec_val is not None: return spec_val return None offset: int | None = Gen._get_member_offset(Node.attr, CN) if offset is None: return None return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) # ========== 主入口 ========== def _HandleAttributeLlvm(self, Node: ast.Attribute) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen if isinstance(Node.value, ast.Name): VarName: str = Node.value.id # 处理 t/c import 的别名重定向 t_c_imported: Any = getattr(self.Trans, '_from_import_aliases', {}) if VarName != 'self' and VarName in t_c_imported: src_module: str src_name: str src_module, src_name = t_c_imported[VarName] virtual_attr: ast.Attribute = ast.Attribute( value=ast.Name(id=src_module, ctx=ast.Load()), attr=src_name, ctx=ast.Load() ) ast.copy_location(virtual_attr, Node) return self._HandleAttributeLlvm(ast.Attribute( value=virtual_attr, attr=Node.attr, ctx=ast.Load() )) if VarName == 'self': return self._handle_attr_self(Node, VarName) return self._handle_attr_name(Node, VarName) elif isinstance(Node.value, ast.Attribute): return self._handle_attr_nested(Node) elif isinstance(Node.value, ast.Call): return self._handle_attr_call_result(Node) elif isinstance(Node.value, ast.Subscript): return self._handle_attr_subscript_result(Node) # 兜底 return self._handle_attr_fallback(Node) # ========== HandleSubscript 及其辅助 ========== def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen # pointee-volatile(rawptr[CVolatile | T])基址:下标 GEP 名带基址名, # 顶层 _load/_store 经 BaseGen._IsPtrVolatile 回查登记名,使其 load/store # 带 LLVM volatile。基址名取不到时后缀为空 ⇒ GEP 名仍是 "subscript", # 既有代码 IR 逐字节不变。 _PVSuffix: str = Gen._SubscriptSuffix(Node.value) # 查询数组元素字节序 arr_byte_order: str = '' if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders: arr_byte_order = Gen.local_var_byteorders[Node.value.id] ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) if ClassName and Gen._has_function(f'{ClassName}.__getitem__'): obj_val: Any = self.HandleExprLlvm(Node.value) idx_val: Any = self.HandleExprLlvm(Node.slice) if obj_val and idx_val: # 处理 obj_val 为 i8(整数)的情况:联合类型 | CPtr 降级后可能被 load 为 i8 # 需要先 inttoptr 转换为 i8*,再 bitcast 到目标 struct 指针 if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64: idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx") _getitem_func: Any = Gen._get_function(f'{ClassName}.__getitem__') # 处理泛型特化类型不匹配:obj_val 可能是通用类型,而函数期望特化类型 if _getitem_func.ftype.args and _getitem_func.ftype.args[0] != obj_val.type: _expected_type = _getitem_func.ftype.args[0] if isinstance(_expected_type, ir.PointerType) and isinstance(obj_val.type, ir.PointerType): obj_val = Gen.builder.bitcast(obj_val, _expected_type, name=f"cast_{ClassName}_getitem") result: Any = Gen.builder.call(_getitem_func, [obj_val, idx_val], name=f"call_{ClassName}.__getitem__") return result ValueVal: Any = self.HandleExprLlvm(Node.value) if not ValueVal: return None IndexVal: Any = self.HandleExprLlvm(Node.slice) if not IndexVal: return None if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx") IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx") if isinstance(ValueVal.type, ir.IntType): var_ptr: Any = self._get_int_ptr(Node.value) if var_ptr: i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") ptr: Any = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") return Gen._load(ptr, name="int_subscript_val") return None # 当变量本身是二级指针(如 elem_ptr: T | CPtr = AST**), # 但 HandleExprLlvm 已自动 load 返回 AST*(一级指针), # 需要检查 Gen.variables 中的原始 alloca 类型, # 若是二级指针且存储的是结构体指针,应 bitcast ValueVal 为 AST** 后 GEP 再 load, # 避免 GEP AST 结构体数组返回错误的 AST*(指向存储位置而非实际对象)。 # 仅当 var_ptr_element 标记为 True 时触发(由 AnnAssign 检测 (S|CPtr)|CPtr 注解设置), # 防止 pts: Point | CPtr(简单结构体指针)错误走二级指针路径。 if (isinstance(Node.value, ast.Name) and isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))): VarName: str = Node.value.id _vpe_load: bool = (getattr(Gen, 'var_ptr_element', {}).get(VarName, False) or getattr(Gen, 'global_var_ptr_element', {}).get(VarName, False)) if _vpe_load and VarName in Gen.variables and Gen.variables[VarName] is not None: VarAlloca: ir.Value = Gen.variables[VarName] if (isinstance(VarAlloca.type, ir.PointerType) and isinstance(VarAlloca.type.pointee, ir.PointerType)): # 原始 alloca 是二级指针,ValueVal 是已 load 的一级结构体指针 # 该指针指向数据缓冲区,缓冲区存储的是 AST* 指针数组, # 需 bitcast ValueVal 为 AST** 后 GEP 指针数组再 load 出 AST* PtrToStruct: ir.Type = ValueVal.type.pointee BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp_load") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_load") ElemPtrLoad: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name="subscript_pp_load") return Gen._load(ElemPtrLoad, name="ptr_elem_load") if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8: return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) if isinstance(ValueVal.type, ir.PointerType): pointee: Any = ValueVal.type.pointee if isinstance(pointee, ir.ArrayType): zero: ir.Constant = ir.Constant(ir.IntType(32), 0) if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") elem_type: Any = pointee.element if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) else: if isinstance(pointee, ir.IntType) and pointee.width == 8: # 指针槽变量优先(8fbba 修复):T|CPtr 指针槽(elem_ptr 指向 # 8 字节指针数组,如 _list.vp 泛型方法体)→ i8** load i8*。 # 必须放在 _infer_element_struct_class 之前——泛型 T 未解析时 # var_struct_class 误注册 llvmlite.Value,若先推断 elem_class # 会 cast 到 Value*,与函数声明 i8* 不匹配(llc 报错)。 if isinstance(Node.value, ast.Name): _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) if _ptr_elem_flag: CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") return Gen._load(ElemPtr, name="str_ptr_load") elem_class: str | None = self._infer_element_struct_class(Node.value, Gen) if elem_class and elem_class in Gen.structs: CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index") ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name=f"subscript{_PVSuffix}") if isinstance(pointee, ir.IntType): return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return ElemPtr # 指针到 str/bytes 的变量 (BinOp 注解如 str | CPtr) # elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] 应加载 8 字节 if isinstance(Node.value, ast.Name): _ptr_elem_flag = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) if _ptr_elem_flag: CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") return Gen._load(ElemPtr, name="str_ptr_load") arr_type: Any = self._infer_array_member_type(Node.value, Gen) if arr_type and isinstance(arr_type, ir.ArrayType): arr_ptr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(arr_type), name=f"cast_arr_subscript") zero = ir.Constant(ir.IntType(32), 0) if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name=f"subscript{_PVSuffix}") arr_elem_type: Any = arr_type.element if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) elif isinstance(ValueVal.type, ir.ArrayType): zero = ir.Constant(ir.IntType(32), 0) if isinstance(Node.value, ast.Name): VarName: str = Node.value.id if VarName in Gen.variables and Gen.variables[VarName] is not None: VarPtr: Any = Gen.variables[VarName] if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") var_arr_elem_type: Any = VarPtr.type.pointee.element if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) if isinstance(Node.value, ast.Attribute): AttrPtr: Any = self._get_attr_ptr(Node.value) if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") attr_arr_elem_type: Any = AttrPtr.type.pointee.element if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) if isinstance(Node.value, ast.Subscript): InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): inner_pointee: Any = InnerPtr.type.pointee if isinstance(inner_pointee, ir.ArrayType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") inner_elem_type: Any = inner_pointee.element if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") Gen._store(ValueVal, arr_alloc) ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") alloc_arr_elem_type: Any = ValueVal.type.element if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): return ElemPtr return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) return None def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen # 同 _HandleSubscriptLlvm:pointee-volatile 基址名后缀(空后缀 ⇒ 名不变) _PVSuffix: str = Gen._SubscriptSuffix(Node.value) ValueVal: Any = self.HandleExprLlvm(Node.value) if not ValueVal: return None IndexVal: Any = self.HandleExprLlvm(Node.slice) if not IndexVal: return None if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx_ptr") IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_ptr") if isinstance(ValueVal.type, ir.PointerType): pointee: Any = ValueVal.type.pointee if isinstance(pointee, ir.ArrayType): zero: ir.Constant = ir.Constant(ir.IntType(32), 0) if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr elif isinstance(pointee, ir.PointerType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr") ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr else: # 指针到 str/bytes 的变量 (BinOp 注解如 str | CPtr, bytes | CPtr) # elem_ptr 是 i8* 但实际指向 i8* (指针数组), 应按 8 字节步长 GEP if isinstance(Node.value, ast.Name): _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) if _ptr_elem_flag: CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr") ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr elif isinstance(ValueVal.type, ir.IntType): var_ptr: Any = self._get_int_ptr(Node.value) if var_ptr: i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") ElemPtr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") return ElemPtr elif isinstance(ValueVal.type, ir.ArrayType): zero = ir.Constant(ir.IntType(32), 0) if isinstance(Node.value, ast.Name): VarName: str = Node.value.id if VarName in Gen.variables and Gen.variables[VarName] is not None: VarPtr: Any = Gen.variables[VarName] if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr if isinstance(Node.value, ast.Subscript): InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) if InnerPtr: if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr if isinstance(Node.value, ast.Attribute): AttrPtr: Any = self._get_attr_ptr(Node.value) if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: if IndexVal.type.width < 32: IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") else: IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") Gen._store(ValueVal, arr_alloc) ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") return ElemPtr return None # ========== _get_attr_ptr / _get_int_ptr / _get_attr_class ========== def _ConcretePointeeFor(self, Gen: LlvmCodeGenerator, ClassName: str | None, Pointee: Any) -> Any: """取该类的**已具化实例**(GEP 基址可用的那份):类键那份 → 同短名孪生体 → None。 为什么需要:同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名 '.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份" 并不保证一致;typed pointer 下 GEP 必须在**编译期**从 pointee 算元素类型, 撞 opaque 即抛裸 TypeError('NoneType' object is not subscriptable)。 判据(全仓同族点同一套,不引入新判据): ① 类键那份(`Gen.structs[ClassName]`)可经 `_TryLoadStructFromStub` 补出 body; ② 否则按**同短名**取一份已具化实例(`Gen._FindConcreteTwin`,槽数最多); ③ 都拿不到 ⇒ None(**不兜底**,交 IR 边界守卫 fail-fast 报同短名实例清单)。 """ _Twin: Any = None if ClassName: _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) if isinstance(_ST, ir.IdentifiedStructType) and _ST.elements: _Twin = _ST if _Twin is None and isinstance(Pointee, ir.IdentifiedStructType): try: _Twin = Gen._FindConcreteTwin(Pointee) except Exception: _Twin = None return _Twin def _ConcretizeGepBase(self, Gen: LlvmCodeGenerator, Ptr: Any, ClassName: str | None, Tag: str) -> tuple[Any, Any]: """GEP 基址收口:pointee 为 **opaque** 具名结构体时 bitcast 到已具化实例。 判据与实例来源见 `_ConcretePointeeFor`;返回 `(基址指针, 该类的已具化实例)`, 第二个返回值同时用作 `_get_member_offset` 的 StructHint —— 偏移与 GEP 必须同源 (hint 只接受同类,见 FuncGen._member_base)。pointee 非 opaque 时原样返回, 对既有正确路径零变化;都取不到实例时同样原样返回(不兜底,由守卫报根因)。 """ if not isinstance(getattr(Ptr, 'type', None), ir.PointerType): return Ptr, None _Pointee: Any = Ptr.type.pointee if not isinstance(_Pointee, ir.IdentifiedStructType) or _Pointee.elements is not None: return Ptr, _Pointee _Twin: Any = self._ConcretePointeeFor(Gen, ClassName, _Pointee) if _Twin is None: return Ptr, _Pointee return (Gen.builder.bitcast(Ptr, ir.PointerType(_Twin), name=Tag), _Twin) def _get_attr_ptr(self, Node: ast.Attribute) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen if not isinstance(Node, ast.Attribute): return None if isinstance(Node.value, ast.Name): VarName: str = Node.value.id if VarName == 'self': ClassName: str | None = self.Trans._CurrentCpythonObjectClass if ClassName: SelfVar: Any = Gen._get_var_ptr('self') if SelfVar: SelfPtr: Any = Gen._load(SelfVar, name="self") if isinstance(SelfPtr.type, ir.PointerType): # 基址收口(见 _ConcretizeGepBase):opaque 基址先补 body/取孪生体 # 再 GEP。现场(2026-09-30):includes/ast/astaux.vp:165 # `self.arg = _copy_str(pool, arg)`,GEP 名 'arg',基址 pointee 是 # opaque 的 %"8312ce86a2fd82c0.Keyword"*,而同短名的 'Keyword' # 键上那份已具化(10 槽)。 SelfPtr, _SelfPointee = self._ConcretizeGepBase( Gen, SelfPtr, ClassName, f"cast_self_{ClassName}") 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) if not ClassName and getattr(Gen, 'global_struct_class', None): ClassName = Gen.global_struct_class.get(VarName) if not ClassName: imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules): AttrName: str = Node.attr ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) candidates: list[str] = [VarName, resolved_mod] for mod_name in imported_modules: if mod_name.endswith(f".{VarName}") or mod_name == VarName: if mod_name not in candidates: candidates.append(mod_name) for cand in candidates: sha1: str | None = ModuleSha1Map.get(cand) if sha1: prefixed: str = f"{sha1}.{AttrName}" if prefixed in Gen.module.globals: return Gen.module.globals[prefixed] if AttrName in Gen.module.globals: gv: Any = Gen.module.globals[AttrName] if isinstance(gv, ir.GlobalVariable): return gv if ClassName and ClassName in Gen.structs: VarPtr: Any = Gen.variables.get(VarName) if VarPtr: ObjVal: Any = VarPtr if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]: # 基址收口(见 _ConcretizeGepBase):类键那份可能仍 opaque ⇒ 补 body / # 取同短名孪生体后再 GEP(偏移同源)。 ObjVal, ST = self._ConcretizeGepBase(Gen, ObjVal, ClassName, f"cast_{ClassName}") offset = Gen._get_member_offset(Node.attr, ClassName, ST) if offset is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) return MemberPtr elif isinstance(Node.value, ast.Attribute): InnerPtr: Any = self._get_attr_ptr(Node.value) if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): pointee: Any = InnerPtr.type.pointee if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): found: Any = Gen.find_struct_by_pointee(pointee) if found: CN: str ST: Any CN, ST = found # 基址收口(见 _ConcretizeGepBase):链尾 pointee 可能仍是类键上那份 # opaque 实例 ⇒ 补 body / 取同短名孪生体后再 GEP(偏移同源)。 InnerPtr, ST = self._ConcretizeGepBase(Gen, InnerPtr, CN, f"cast_{CN}") offset = Gen._get_member_offset(Node.attr, CN, ST) if offset is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: MemberPtr = Gen.builder.gep(InnerPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) return MemberPtr elif isinstance(pointee, ir.IntType) and pointee.width == 8: AttrClassName: str | None = self._get_attr_class(Node.value, Gen) if AttrClassName and AttrClassName in Gen.structs: CastedObj: Any = Gen.builder.bitcast(InnerPtr, ir.PointerType(Gen.structs[AttrClassName]), name=f"cast_{AttrClassName}") # 类键那份可能仍 opaque ⇒ 统一收口(见 _ConcretizeGepBase) CastedObj, ST2 = self._ConcretizeGepBase( Gen, CastedObj, AttrClassName, f"cast_twin_{AttrClassName}") offset = Gen._get_member_offset(Node.attr, AttrClassName, ST2) if offset is not None: MemberPtr = Gen.builder.gep(CastedObj, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) return MemberPtr elif isinstance(Node.value, ast.Subscript): SubPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) if SubPtr and isinstance(SubPtr.type, ir.PointerType): pointee = SubPtr.type.pointee if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): found = Gen.find_struct_by_pointee(pointee) if found: CN, ST = found # 基址收口(本分支此前完全没有这一步:opaque 时 GEP 直接抛裸 TypeError) SubPtr, ST = self._ConcretizeGepBase(Gen, SubPtr, CN, f"cast_{CN}") offset = Gen._get_member_offset(Node.attr, CN, ST) if offset is not None: MemberPtr = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) return MemberPtr elif isinstance(Node.value, ast.Call): # 指针强转基座(`(ast.Call*(vinner)).IsVolatile = 1`):`T*(x)` 被 vast 的 # _ConsumePtrSuffixCall 解析为 `(T | CPtr)(x)` 强转调用,结果类型是联合 # 简化后的 i8*(struct 身份丢失),故须从强转注解(func 是 BitOr 联合) # 提真实类名,bitcast/inttoptr 回 Ptr(类) 后 GEP 成员。 # 与读取侧 _handle_attr_call_result 的联合分支(_extract_union_base_class) # 逐字同口径;此前写入侧只认 Name/Attribute/Subscript,强转基座的字段 # 写入一律静默返回 None。 ObjVal: Any = self.HandleExprLlvm(Node.value) if ObjVal is not None and isinstance(Node.value.func, ast.BinOp) \ and isinstance(Node.value.func.op, ast.BitOr): UnionCls: str | None = self._extract_union_base_class(Node.value.func) if UnionCls and UnionCls in Gen.structs: ST: Any = Gen.structs[UnionCls] if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): self.Trans.ImportHandler._TryLoadStructFromStub(UnionCls, Gen) ST = Gen.structs.get(UnionCls, ST) if isinstance(ST, ir.IdentifiedStructType) and not ST.elements: # 类键那份仍 opaque(body 未 set_body / 另一份实例已具化)⇒ 取已具化 # 实例作目标 pointee,否则 bitcast/inttoptr 出来的基址仍是 opaque、 # GEP 在编译期算不出元素类型(判据见 _ConcretePointeeFor)。 _TwU: Any = self._ConcretePointeeFor(Gen, UnionCls, ST) if _TwU is not None: ST = _TwU TargetPtrTy: Any = ir.PointerType(ST) if isinstance(ObjVal.type, ir.PointerType): if ObjVal.type != TargetPtrTy: ObjVal = Gen.builder.bitcast(ObjVal, TargetPtrTy, name=f"cast_{UnionCls}") elif isinstance(ObjVal.type, ir.IntType): ObjVal = Gen.builder.inttoptr(ObjVal, TargetPtrTy, name=f"int2ptr_{UnionCls}") else: return None offset: int | None = Gen._get_member_offset(Node.attr, UnionCls) if offset is not None: MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) return MemberPtr return None def _HandleSliceLlvm(self, base_val: ir.Value, slice_node: ast.Slice | ast.Index, base_ast: ast.AST) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen lower_val: Any = None upper_val: Any = None step_val: Any = None if isinstance(slice_node, ast.Slice): if slice_node.lower: lower_val = self.HandleExprLlvm(slice_node.lower) if slice_node.upper: upper_val = self.HandleExprLlvm(slice_node.upper) if slice_node.step: step_val = self.HandleExprLlvm(slice_node.step) if isinstance(slice_node, ast.Index): return self.HandleExprLlvm(slice_node.value) return None def _infer_element_struct_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: if isinstance(node, ast.Attribute): attr_name: str = node.attr if isinstance(node.value, ast.Name) and node.value.id == 'self': current_class: str | None = self.Trans._CurrentCpythonObjectClass if current_class: if current_class in Gen.class_member_element_class: elem_class: str | None = Gen.class_member_element_class[current_class].get(attr_name) if elem_class and elem_class in Gen.structs: return elem_class if current_class in Gen.class_members: for member_name, member_type in Gen.class_members[current_class]: if member_name == attr_name: if isinstance(member_type, ir.IdentifiedStructType): found: Any = Gen.find_struct_by_pointee(member_type) if found: return found[0] break class_name: str | None = Gen.var_struct_class.get(attr_name) if class_name: return class_name elif isinstance(node, ast.Name): var_name: str = node.id class_name = Gen.var_struct_class.get(var_name) if class_name: return class_name return None def _get_int_ptr(self, target: ast.AST) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen if isinstance(target, ast.Name): VarName: str = target.id if VarName in Gen.variables and Gen.variables[VarName] is not None: VarPtr: Any = Gen.variables[VarName] if isinstance(VarPtr.type, ir.PointerType): pointee: Any = VarPtr.type.pointee if isinstance(pointee, ir.ArrayType): return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") return VarPtr elif isinstance(target, ast.Attribute): if isinstance(target.value, ast.Name): obj_name: str = target.value.id if obj_name in Gen.variables and Gen.variables[obj_name] is not None: obj_ptr: Any = Gen.variables[obj_name] if isinstance(obj_ptr.type, ir.PointerType): pointee = obj_ptr.type.pointee if isinstance(pointee, ir.PointerType): obj_ptr = Gen._load(obj_ptr, name=f"Load_{obj_name}") pointee = obj_ptr.type.pointee if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): StructName: str | None = None if isinstance(pointee, ir.IdentifiedStructType): StructName = pointee.name if not StructName: found: Any = Gen.find_struct_by_pointee(pointee) if found: StructName = found[0] if StructName and StructName in Gen.structs: offset: int | None = Gen._get_member_offset(target.attr, StructName) if offset is not None: return Gen.builder.gep(obj_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") obj_val: Any = self.HandleExprLlvm(target.value) if obj_val and isinstance(obj_val.type, ir.PointerType): if isinstance(obj_val.type.pointee, ir.PointerType): obj_val = Gen._load(obj_val, name="Load_attr_ptr") pointee = obj_val.type.pointee if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): StructName = None if isinstance(pointee, ir.IdentifiedStructType): StructName = pointee.name if not StructName: found = Gen.find_struct_by_pointee(pointee) if found: StructName = found[0] if StructName and StructName in Gen.structs: offset = Gen._get_member_offset(target.attr, StructName) if offset is not None: return Gen.builder.gep(obj_val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") return None def _resolve_struct_class_for_attr(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: if isinstance(Node, ast.Name): VarName: str = Node.id if VarName in Gen.var_struct_class: return Gen.var_struct_class[VarName] elif isinstance(Node, ast.Attribute): ClassName: str | None = self._resolve_struct_class_for_attr(Node.value, Gen) if ClassName and ClassName in Gen.class_members: for member_name, _ in Gen.class_members[ClassName]: if member_name == Node.attr: return ClassName return None def _infer_array_member_type(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> ir.ArrayType | None: if isinstance(Node, ast.Attribute): parent_class: str | None = self._get_attr_class(Node, Gen) if parent_class and parent_class in Gen.class_members: for m_name, m_type in Gen.class_members[parent_class]: if m_name == Node.attr and isinstance(m_type, ir.ArrayType): return m_type if parent_class and parent_class in Gen.structs: st: Any = Gen.structs[parent_class] if isinstance(st, ir.IdentifiedStructType) and st.elements is not None: offset: int | None = Gen._get_member_offset(Node.attr, parent_class) if offset is not None and offset < len(st.elements): elem: Any = st.elements[offset] if isinstance(elem, ir.ArrayType): return elem return None def _build_attr_path(self, node: ast.AST) -> list[str]: parts: list[str] = [] current: ast.AST = node while isinstance(current, ast.Attribute): parts.append(current.attr) current = current.value if isinstance(current, ast.Name): parts.append(current.id) parts.reverse() return parts def _try_resolve_nested_cdefine(self, Node: ast.Attribute) -> ir.Value | None: """尝试解析嵌套属性路径上的 CDefine 常量 注意:不再强制依赖 _ImportedModules。即使该集合为空(如自举编译 早期阶段),也始终构建属性路径并检查符号表,确保 ast.CONST_INT 这类跨模块 CDefine 常量可被解析为立即数。 """ Gen: LlvmCodeGenerator = self.Trans.LlvmGen parts: list[str] = self._build_attr_path(Node) if len(parts) < 2: return None attr_name: str = parts[-1] module_parts: list[str] = parts[:-1] possible_keys: list[str] = [attr_name] full_path: str = '.'.join(parts) possible_keys.append(full_path) imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) if module_parts: ModulePath: str = '.'.join(module_parts) resolved_first: str = self.Trans.SymbolTable.resolve_alias(module_parts[0]) if resolved_first != module_parts[0]: resolved_parts: list[str] = [resolved_first] + module_parts[1:] resolved_path: str = '.'.join(resolved_parts) possible_keys.append(f"{resolved_path}.{attr_name}") if imported_modules: for mod_name in imported_modules: if mod_name.endswith('.' + ModulePath) or mod_name == ModulePath: key: str = f"{mod_name}.{attr_name}" if key not in possible_keys: possible_keys.append(key) if module_parts and (mod_name.endswith('.' + module_parts[-1]) or mod_name == module_parts[-1]): key = f"{mod_name}.{attr_name}" if key not in possible_keys: possible_keys.append(key) for lookup_key in possible_keys: SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: val: Any = SymInfo.DefineValue if isinstance(val, int): # 复用 _make_define_constant:带声明类型按声明位宽,否则按值推断 # (含无符号 32 位高半区),与本文件其它 CDefine 解析点同口径。 return self._make_define_constant(Gen, val, attr_name) elif isinstance(val, float): return ir.Constant(ir.FloatType(), val) # 回退:检查 Gen._define_constants(LlvmGenerator 预扫描阶段填充) define_constants: Any = getattr(Gen, '_define_constants', {}) for key in (attr_name, full_path): if key in define_constants: val: Any = define_constants[key] if isinstance(val, int): # 复用 _make_define_constant:带声明类型按声明位宽,否则按值推断 # (含无符号 32 位高半区),与本文件其它 CDefine 解析点同口径。 return self._make_define_constant(Gen, val, attr_name) elif isinstance(val, float): return ir.Constant(ir.FloatType(), val) # 回退:_all_define_constants(Phase2Translator 从所有 .pyi 预提取的跨模块 CDefine, # 与 _HandleNameLlvm 的裸名回退同源)。此前只覆盖裸名/from-import,**模块属性访问** # (如 `win32file.STD_OUTPUT_HANDLE`)走到这里就落空 → 转去基座求值 + 字段/全局加载, # 命中 stub 里那条错误的 `@STD_OUTPUT_HANDLE = external global`,最终链接期未定义符号。 # 与裸名路径同口径:仅当本模块没有同名全局时才用常量(避免遮蔽真正的模块全局)。 _mod_globals: Any = getattr(Gen, 'module', None) _has_mod_global: bool = _mod_globals is not None and attr_name in getattr(_mod_globals, 'globals', {}) if not _has_mod_global: all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} for key2 in (attr_name, full_path): if key2 in all_dc: val2: Any = all_dc[key2] if isinstance(val2, int): return self._make_define_constant(Gen, val2, attr_name) elif isinstance(val2, float): return ir.Constant(ir.FloatType(), val2) return None def _try_resolve_cross_module_enum(self, module_alias: str, enum_class_name: str, member_name: str) -> ir.Value | None: Gen: LlvmCodeGenerator = self.Trans.LlvmGen imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) if not imported_modules: return None resolved_module: str = self.Trans.SymbolTable.resolve_alias(module_alias) if resolved_module not in imported_modules and module_alias not in imported_modules: return None enum_keys: list[str] = [enum_class_name, f"{resolved_module}.{enum_class_name}"] for enum_key in enum_keys: SymInfo: Any = self.Trans.SymbolTable.lookup(enum_key) if SymInfo: if SymInfo.IsEnum: for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"): info: Any = self.Trans.SymbolTable.lookup(qname) if info and info.IsEnumMember and isinstance(info.value, int): return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) for key, info in self.Trans.SymbolTable.items(): if info.IsEnumMember and info.EnumName == enum_key and key == member_name: if isinstance(info.value, int): return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) for key, info in self.Trans.SymbolTable.items(): if info.IsEnumMember and key == member_name: if isinstance(info.value, int): for enum_key in enum_keys: if info.EnumName == enum_key: return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) return None def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None: """在 class_member_element_class 中按"属主类 + 字段名"查找元素/特化类。 class_member_element_class 的 key 与调用方持有的类名(var_struct_class / _CurrentCpythonObjectClass 等)可能存在"裸名 vs 带 sha1 前缀"的不一致 (如 key='Function',而持有者是 '732a8c24abb9b516.Function')。先精确 匹配,再按短名(剥离 16 位 hex 前缀)遍历所有 key 匹配,确保能恢复 字段声明的特化类(如 rawptr[GSList[BasicBlock]] → 'GSList[BasicBlock]')。 """ if not OwnerClass or not FieldName: return None elem_map: Any = getattr(Gen, 'class_member_element_class', {}) or {} if OwnerClass in elem_map: sub_map: Any = elem_map[OwnerClass] if FieldName in sub_map: return sub_map[FieldName] owner_short: str = Gen._extract_short_name(OwnerClass) for key, sub_map in elem_map.items(): if Gen._extract_short_name(key) != owner_short: continue if FieldName in sub_map: return sub_map[FieldName] # 继承链回退(对齐 ExprUtils._get_var_class 的同一张表查询):字段声明在 # 基类时 elem_map 的键是**声明类**,而收件人类可能是子类 # (TypeGenBase.Gen: rawptr[Generator.Generator] → 收件人 TypeGenResolve / # TypeGenCType…)→ 上面两路全 miss。rawptr[X] 字段的 LLVM 成员类型被折叠 # 为 i8*(struct 身份丢失,class_members / struct elements 都推不出目标类), # elem_map 是唯一能恢复目标类的来源;miss 即任意级链解析 # (_field_element_key)返回 None → 带注解赋值 fail-fast # (TypeGenResolve.vp:807 self.Gen.TypeGenCType.ImportAliasBuf 根因)。 try: _mro_chain: list[str] = Gen._get_mro_chain(OwnerClass) except Exception: _mro_chain = [OwnerClass] for _mro_cls in _mro_chain: _sub_mro: Any = elem_map.get(_mro_cls) if _sub_mro is None: _sub_mro = elem_map.get(Gen._extract_short_name(_mro_cls)) if _sub_mro and FieldName in _sub_mro: return _sub_mro[FieldName] return None def _resolve_attr_parent_hint(self, Node: ast.Attribute, Gen: LlvmCodeGenerator) -> str | None: """从父对象上下文解析字段的元素/容器特化提示(parent_hint)。 例:func.Blocks.Head —— Node.value 是 func.Blocks,父变量 func 的 var_struct_class/global_struct_class 是 'Function',Blocks 字段声明为 rawptr[GSList[BasicBlock]],class_member_element_class 记录元素提示 'GSList[BasicBlock]'。该提示用于把退化为 i8* 的字段指针恢复成目标 特化类(char* 分支),或在所有 GSList[T] 都有 Head/Tail/Count 时 选择正确特化版本(struct 分支)。兼容裸名/带 sha1 前缀键。 """ if not (isinstance(Node, ast.Attribute) and isinstance(Node.value, ast.Name)): return None p_var: str = Node.value.id p_attr: str = Node.attr p_CN: str | None = Gen.var_struct_class.get(p_var) if not p_CN and getattr(Gen, 'global_struct_class', None): p_CN = Gen.global_struct_class.get(p_var) if not p_CN: return None return self._lookup_member_elem_class(Gen, p_CN, p_attr) def _find_struct_key_for_class_hint(self, Gen: LlvmCodeGenerator, ClassHint: str | None) -> str | None: """把 class_member_element_class 记录的类/特化名解析为 Gen.structs 的真实键。 记录值可能是裸名('GSList[BasicBlock]')或带 sha1 前缀 ('8e92345fd262a343.GSList[BasicBlock]'),且**方括号内元素也可能带模块 前缀**('GSList[29fdb5e25dc949f4.BasicBlock]',跨模块泛型特化登记值)。 Gen.structs 的键可能是另一种形态。比较前统一剥除所有 '<16hex>.' 片段: 依次尝试:精确键 → 归一短名键 → class_sha1_map 重建前缀键 → 遍历 Gen.structs 按归一短名匹配。 """ if not ClassHint: return None if ClassHint in Gen.structs: return ClassHint import re as _re_sha def _strip_sha(s: str) -> str: return _re_sha.sub(r'(?i)[0-9a-f]{16}\.', '', s) short: str = _strip_sha(Gen._extract_short_name(ClassHint)) if short in Gen.structs: return short expected_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(short) if expected_sha1: prefixed: str = f"{expected_sha1}.{short}" if prefixed in Gen.structs: return prefixed for key in Gen.structs: if _strip_sha(Gen._extract_short_name(key)) == short: return key return None def _get_attr_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: if isinstance(Node, ast.Attribute): # 注: 不再硬编码 ZLIB_VERSION / C_OK,它们应从符号表查找 if isinstance(Node.value, ast.Name) and Node.value.id == 'self': ClassName: str | None = self.Trans._CurrentCpythonObjectClass return self._lookup_member_elem_class(Gen, ClassName, Node.attr) if isinstance(Node.value, ast.Name): VarName: str = Node.value.id ClassName: str | None = Gen.var_struct_class.get(VarName) if not ClassName and getattr(Gen, 'global_struct_class', None): ClassName = Gen.global_struct_class.get(VarName) if ClassName: return self._lookup_member_elem_class(Gen, ClassName, Node.attr) if isinstance(Node.value, ast.Attribute): ParentClass: str | None = self._get_attr_class(Node.value, Gen) if ParentClass: return self._lookup_member_elem_class(Gen, ParentClass, Node.attr) return None # ========== 任意级属性链:逐段前向类解析 ========== def _pointee_llvm_to_struct_key(self, Gen: LlvmCodeGenerator, llvm_type: ir.Type) -> str | None: """把 LLVM 成员类型(Ptr->IdentifiedStruct / IdentifiedStruct)解析为 Gen.structs 的真实键。 先精确匹配(find_struct_by_pointee),失败再按短名匹配(优先选择 有完整 elements 的定义),兼容 sha1 前缀冲突/泛型特化场景。 """ cand: ir.Type = llvm_type if isinstance(cand, ir.PointerType): cand = cand.pointee if not isinstance(cand, ir.IdentifiedStructType): return None found: Any = Gen.find_struct_by_pointee(cand) if found: return found[0] cand_short: str = Gen._extract_short_name(cand.name) best_key: str | None = None best_elems: int = -1 for k in Gen.structs: if Gen._extract_short_name(k) != cand_short: continue st_v: Any = Gen.structs[k] if not isinstance(st_v, ir.IdentifiedStructType): continue n_elems: int = len(st_v.elements) if st_v.elements is not None else 0 if n_elems > best_elems: best_elems = n_elems best_key = k return best_key def _owner_member_llvm_types(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[tuple[str, ir.Type]]: """收集 OwnerClass(含继承父类)的成员 (名, LLVM 类型) 列表。 class_members 的键可能是带 sha1 前缀全名,此处按短名/剥 '[' 泛型 参数后的基名匹配,并沿 class_parents 继承链向上补充父类字段。 """ owner_short: str = Gen._extract_short_name(OwnerClass) base_short: str = owner_short.split('[')[0] if '[' in owner_short else owner_short members_map: dict[str, list] = {} for k, v in getattr(Gen, 'class_members', {}).items(): k_short: str = Gen._extract_short_name(k) k_base: str = k_short.split('[')[0] if '[' in k_short else k_short if k_short == owner_short or k_base == base_short: members_map[k] = list(v) ordered: list[tuple[str, ir.Type]] = [] seen: set[str] = set() for m in self._class_and_parents(Gen, OwnerClass): m_short: str = Gen._extract_short_name(m) for k, v in members_map.items(): if Gen._extract_short_name(k) != m_short: continue for name, ty in v: if name not in seen: seen.add(name) ordered.append((name, ty)) return ordered def _class_and_parents(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[str]: """OwnerClass 自身 + 全部父类短名链(子类在前)""" chain: list[str] = [] seen: set[str] = set() cur: str = Gen._extract_short_name(OwnerClass) parents_map: Any = getattr(Gen, 'class_parents', {}) or {} single_parent: Any = getattr(Gen, 'class_parent', {}) or {} while cur and cur not in seen: seen.add(cur) chain.append(cur) nxt: list[str] = parents_map.get(cur, []) if not nxt and cur in single_parent and single_parent[cur]: nxt = [single_parent[cur]] if not nxt: break # 多继承:只取第一条链向上(深度优先声明顺序)保持与 # _get_member_offset 的继承链回退一致 cur = Gen._extract_short_name(nxt[0]) return chain def _field_element_key(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, AttrName: str) -> str | None: """解析 OwnerClass 上 AttrName 字段的"元素 struct 键"(可在 Gen.structs 定位)。 供任意级链(ve.Alloca.Ty.Pointee)逐段恢复目标类:跨模块 stub 会把 rawptr[X] 指针字段退化为 i8*,load 出的值丢失 struct 身份;此处综合 class_member_element_class(登记的元素/特化类)→ class_members LLVM 成员类型 → struct elements 字段类型 推断该字段指向的 struct 键, 兼容裸名/sha1 前缀/泛型特化名。 """ if not OwnerClass or not AttrName: return None # 1) class_member_element_class 直查(含短名模糊匹配) hint: str | None = self._lookup_member_elem_class(Gen, OwnerClass, AttrName) if hint: k: str | None = self._find_struct_key_for_class_hint(Gen, hint) if k: return k # 2) class_members 成员 LLVM 类型 for m_name, m_type in self._owner_member_llvm_types(Gen, OwnerClass): if m_name != AttrName: continue k2: str | None = self._pointee_llvm_to_struct_key(Gen, m_type) if k2: return k2 break # 3) struct elements 字段类型(加载 stub 后按偏移取值) owner_key: str | None = self._find_struct_key_for_class_hint(Gen, OwnerClass) if owner_key and owner_key in Gen.structs: st_v: Any ok_v: bool st_v, ok_v = self._try_Load_struct_from_stub(Gen, owner_key) if isinstance(st_v, ir.IdentifiedStructType) and st_v.elements is not None: try: off: int | None = Gen._get_member_offset(AttrName, owner_key) except KeyError: off = None if off is not None and off < len(st_v.elements): k3: str | None = self._pointee_llvm_to_struct_key(Gen, st_v.elements[off]) if k3: return k3 # 4) 继承父类 class_members 兜底(_owner_member_llvm_types 已含继承链, # 此处仅处理父类 struct elements 的字段类型) for parent_short in self._class_and_parents(Gen, OwnerClass)[1:]: p_key: str | None = self._find_struct_key_for_class_hint(Gen, parent_short) if not p_key or p_key not in Gen.structs: continue p_st: Any p_ok: bool p_st, p_ok = self._try_Load_struct_from_stub(Gen, p_key) if isinstance(p_st, ir.IdentifiedStructType) and p_st.elements is not None: try: p_off: int | None = Gen._get_member_offset(AttrName, p_key) except KeyError: p_off = None if p_off is not None and p_off < len(p_st.elements): k4: str | None = self._pointee_llvm_to_struct_key(Gen, p_st.elements[p_off]) if k4: return k4 return None def _resolve_chain_result_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: """沿链前向解析表达式(Name 或任意级 Attribute 链)的最终 struct 键。 例如 ve.Alloca.Ty.Pointee:最内层 Name ve 查 var_struct_class / global_struct_class / 模块全局 / 变量 alloca pointee 得首个类(Value), 之后每段用 _field_element_key 在当前类上取字段的元素类推进,直到最外层。 对自指/REnum(如 LLVMType.Pointee → LLVMType)或 class_member_element_class 缺项的字段,由 _field_element_key 的 class_members / struct elements 兜底。 """ if isinstance(Node, ast.Name): if Node.id == 'self': return self.Trans._CurrentCpythonObjectClass return self._resolve_var_classname(Gen, Node.id) if isinstance(Node, ast.Attribute): ParentKey: str | None = self._resolve_chain_result_class(Node.value, Gen) if not ParentKey: return None if ParentKey not in Gen.structs: resolved_parent: str | None = self._find_struct_key_for_class_hint(Gen, ParentKey) if not resolved_parent: return None ParentKey = resolved_parent return self._field_element_key(Gen, ParentKey, Node.attr) return None def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: return Gen._get_member_offset(field_name, ClassName)