diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index c053df7..b388871 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -22,6 +22,68 @@ class ExprAttrHandle(BaseHandle): val: ir.Value = Gen._load(ElemPtr, name="arr_elem_load") return Gen._apply_bswap_if_big(val, arr_byte_order, "bswap_arr_load") + def _RenumVariantPayloadType(self, Gen: LlvmCodeGenerator, ClassName: str | None, + AttrName: str) -> Any: + """REnum **变体载荷**字段的声明类型(供 `_gep_and_Load_member` 载入后收口)。 + + 变体 struct 的 body 是**主 REnum 镜像** `{i32 __tag, }` + (唯一产源:HandlesClassDef._EmitREnumLlvm:1655-1673 与 HandlesImports 的 + REnum 跨模块注册 :2024-2031),故载荷槽在 body 里的类型取**所有变体同位置的 + 最大尺寸**(现场 'Pointee' 槽是 i64),而字段本身声明为指针/更窄整数 ⇒ + 载入值须按声明类型收口,否则上游拿到 i64(现场 CallGenDispatch.vp:787 + `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`)。 + + 判据:类键(或它**唯一**的 REnum 变体键,见 FuncGen._RenumVariantFieldOwner) + 的成员表**首项是 '__tag'**(REnum 变体不变式)且声明了该字段; + 不命中返回 None(调用方原样返回,对既有类零变化)。 + """ + if not ClassName or not AttrName: + return None + _Owners: list[str] = [ClassName] + _Variant: str | None = None + try: + _Variant = Gen._RenumVariantFieldOwner(ClassName, AttrName) + except Exception: + _Variant = None + if _Variant: + _Owners.append(_Variant) + for _cn in _Owners: + _Members: list = (getattr(Gen, 'class_members', {}) or {}).get(_cn) or [] + if not _Members or _Members[0][0] != '__tag': + continue + _FieldTy: Any = next((t for n, t in _Members if n == AttrName), None) + if _FieldTy is not None: + return _FieldTy + return None + + @staticmethod + def _CoerceVariantPayload(Gen: LlvmCodeGenerator, Value: Any, Want: Any) -> Any: + """payload 槽载入值 → 字段声明类型(REnum 变体载荷收口用)。 + + body 里载荷槽常是 i64(max-variant 镜像),而字段声明可能是指针/更窄整数: + int → ptr:inttoptr;ptr → ptr:bitcast;int → int:trunc/zext;其余原样返回 + (不静默造值:类型不可转换时返回原值,让下游照常 fail-fast)。 + """ + _got: Any = getattr(Value, 'type', None) + try: + if _got is Want: + return Value + if isinstance(Want, ir.PointerType): + if isinstance(_got, ir.IntType): + return Gen.builder.inttoptr(Value, Want, name="variant_payload_to_ptr") + if isinstance(_got, ir.PointerType): + return Gen.builder.bitcast(Value, Want, name="variant_payload_ptr_cast") + elif isinstance(Want, ir.IntType) and isinstance(_got, ir.IntType): + if Want.width < _got.width: + return Gen.builder.trunc(Value, Want, name="variant_payload_trunc") + if Want.width > _got.width: + return Gen.builder.zext(Value, Want, name="variant_payload_zext") + elif isinstance(_got, ir.PointerType) and isinstance(Want, ir.PointerType): + return Gen.builder.bitcast(Value, Want, name="variant_payload_ptr_cast") + except Exception: + return Value + return Value + def _apply_target_struct_hint(self, Gen: LlvmCodeGenerator, FieldName: str, ResolvedKey: str | None) -> str | None: """左值注解**靶型提示**:解析出的类**不声明**该字段时,用赋值左侧注解指向的类兜一次。 @@ -66,20 +128,22 @@ class ExprAttrHandle(BaseHandle): @staticmethod def _PointeeMatchesStruct(Pointee: Any, ST: Any) -> bool: - """pointee 与该 struct 实例是否算**同一个类**(判据分级,宽到窄)。 + """pointee 与该 struct 实例是否算**同一个类**。 - 原始判据只有"身份相同 / IR 全名逐字相同",遇到"同名类的另一份实例"就整条链放弃 - (现场:Repl.vp:802 `GenApi._g_gen.TypeGenCType.CTypedefNames` 的基座 pointee 是 - `%"750b8c10137d273e.TypeGenCType"*`,而 Gen.structs(250 键)里没有该 IR 名的实例 - ⇒ 身份与全名都不等 ⇒ 属性链求值为空终止编译)。故补两级退化判据: + 判据只有两条:① 同一个对象;② IR 全名逐字相同(IR 名形如 `.<短名>`, + 全名相同即同一类的同一份实例)。 - ① 同 IR 全名(原判据); - ② 同 sha1 前缀 —— 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' 中找不到"。 - 同**短名**但不同 sha1 前缀的情形**不放行**(那可能是别模块的同名类,选错即静默 - 错布局):它由调用点在"同短名候选唯一"时单独放行(见 `_UniqShortCN`)。 - 找不到时仍返回 False(由调用点留痕,不兜底造类型)。 + 同**短名**不同前缀(同一类的另一模块实例)不在此放行,由调用点在 + "同短名候选唯一"时单独处理(见 `_UniqShortCN`);都判不出时返回 False + (调用点留痕,不兜底造类型)。 """ if Pointee is ST: return True @@ -88,11 +152,7 @@ class ExprAttrHandle(BaseHandle): return False _pn: str = getattr(Pointee, 'name', '') or '' _sn: str = getattr(ST, 'name', '') or '' - if _pn and _pn == _sn: - return True - _pp: str = _pn.rsplit('.', 1)[0] if '.' in _pn else '' - _sp: str = _sn.rsplit('.', 1)[0] if '.' in _sn else '' - return bool(_pp and _sp and _pp == _sp) + return bool(_pn and _pn == _sn) 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 特殊情况""" @@ -153,6 +213,12 @@ class ExprAttrHandle(BaseHandle): 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]: diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 3da0daa..25173c1 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -492,6 +492,49 @@ class FuncGenMixin: base = _BestB return base + def _RenumVariantFieldOwner(self, ClassName: str | None, field_name: str) -> str | None: + """REnum **变体字段**的归属类键:主 REnum 不声明该字段时,返回**唯一**声明它的变体键。 + + 判据(逐条可查,不是猜): + ① 类短名按 `_IsRenumLayoutClass`(全仓唯一判据源)判为 REnum —— 只有 REnum + 才有变体层; + ② 变体类键固定为 `<主类短名>_<变体名>`,且成员表**首项恒为 `__tag`** + (唯一产源:HandlesClassDef._EmitREnumLlvm:1659-1673 与 HandlesImports + 的 REnum 跨模块注册 :1975-2015); + ③ 恰有**一个**变体声明该字段(多个变体同名字段 ⇒ 无法判定 ⇒ None,由调用方 + 照常 fail-fast); + ④ 该变体键的 struct 已注册(布局可用)。 + + 用变体键算偏移为什么正确:变体 struct 的 body 是**主 REnum 镜像** + `{i32 __tag, }`,而变体成员表 `[__tag] + 载荷` 与主 REnum + 槽位一一对应 ⇒ `base + 表内下标` 即真实槽位。 + 现场(2026-09-30):CallGenDispatch.vp:787 `len_val.Ty.Pointee` —— 'Pointee' 只属 + 变体键 'LLVMType_Ptr',主类 'LLVMType' 的成员表为空(HandlesClassDef:716)。 + """ + if not ClassName or not field_name: + return None + _Short: str | None = self._extract_short_name(ClassName) + if not _Short: + return None + if not self._IsRenumLayoutClass(_Short): + return None + _Owner: str | None = None + _Prefix: str = f"{_Short}_" + for _Key, _Members in self.class_members.items(): + if not _Key.startswith(_Prefix): + continue + _List: list = _Members or [] + if not _List or _List[0][0] != '__tag': + continue + if not any(_n == field_name for _n, _ in _List): + continue + if _Key not in self.structs: + continue + if _Owner is not None: + return None + _Owner = _Key + return _Owner + def _get_member_offset(self, field_name: str, ClassName: str | None = None, StructHint: Any = None) -> int | None: # base 判定用未归一的原始类名:_member_base 内部自行做同一套归一 @@ -567,6 +610,37 @@ class FuncGenMixin: # 依赖补齐(上面的懒加载)后再确认一次展平(幂等);偏移基址由 _BaseNow() # 在真正 return 处现算,不在此缓存。 _BaseNow() + # ---- REnum 变体字段:主 REnum 不声明该字段时,改用它**唯一**的变体键继续 ---- + # 现场(2026-09-30):CallGenDispatch.vp:787 `len_val.Ty.Pointee` —— 'Pointee' 属 + # LLVMType 的 REnum 变体键 'LLVMType_Ptr',主类 'LLVMType' 成员表为空 ⇒ 旧路径 + # 直接 fast-fail "属性 'Pointee' 在类 'LLVMType' 中找不到" 终止整个编译。 + # 判据与槽位正确性见 _RenumVariantFieldOwner(变体 struct body = 主 REnum 镜像)。 + if field_name and ClassName: + _MainMembers: list = (self.class_members.get(_ResolvedClass) + or self.class_members.get(ClassName) or []) + if not any(_n == field_name for _n, _ in _MainMembers): + _VariantOwner: str | None = None + try: + _VariantOwner = self._RenumVariantFieldOwner(ClassName, field_name) + except Exception: + _VariantOwner = None + if _VariantOwner: + _RepRK: set = getattr(self, '_RenumVariantFieldReported', None) + if _RepRK is None: + _RepRK = set() + self._RenumVariantFieldReported = _RepRK + _RKey: tuple = (_VariantOwner, field_name) + if _RKey not in _RepRK: + _RepRK.add(_RKey) + _vlog().warning( + f"[REnum 变体字段] '{field_name}' 不在主 REnum 类 " + f"{_ResolvedClass!r} 中,按唯一变体类 '{_VariantOwner}' 取偏移" + f"(变体 struct body = 主 REnum 镜像,槽位一一对应)", + "MemberBase") + ClassName = _VariantOwner + _ResolvedClass = _VariantOwner + _MemberBaseKey = _VariantOwner + _BaseNow() # ---- 布局真源优先(配对式偏移):偏移只认"运行时真正被 GEP 的那份 struct body" ---- # class_members 是**可变**表(继承展平会在父类就绪后前置前缀、跨模块可按需重载), # 而 struct body 一旦 set_body 即**永久冻结** ⇒ "base + 成员表下标"会随表漂移