Files
TransPyC/lib/core/Handles/HandlesExprAttr.py
T
TermiNexus f19d25782f refactor(tpc): 退役 t/c 模块并将定宽类型升级为内建
删除 lib/includes/t.py、c.py,类型模型迁入 lib/core/TypeSystem.py;移除 IsTModule/_T_MODULE_NAMES 等名称判据,语法糖与内部标记统一为裸名;builtins 的 sizet/intN/uintN/fast/least/uchar/short/long 等改为编译器内建短名。
2026-10-18 13:00:58 +08:00

2568 lines
156 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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, <max_variant_payload>}`
(唯一产源: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>.<短名>`,
全名相同即同一类的同一份实例)。
**只比 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 全名
'<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)