fix(Handles*): GEP 基址 opaque pointee 收口统一为单一判据源,补齐同族 12 处缺口

【a 编译器缺陷】7aa12263 修好 `_get_attr_ptr` 的 self 分支后,同族其余 GEP 点仍会在
"基址 pointee 是另一份 opaque 同名实例"时抛裸 TypeError(被 IR 边界守卫拦下,报文为
"GEP 的基址指向**未具化**的具名结构体")。根因是同一套收口逻辑被各点各写一份、且
**多数点根本没写**:真正需要的判据只有一条。

新增唯一判据源(HandlesExprAttr):
  · `_ConcretePointeeFor(Gen, ClassName, Pointee)`:取该类的**已具化实例** ——
    ① 类键那份先 `_TryLoadStructFromStub` 补 body;② 否则 `Gen._FindConcreteTwin`
    (同短名、槽数最多);③ 都取不到 ⇒ None(**不兜底**,交守卫 fail-fast 报同短名
    实例清单 + TPC 发射点)。
  · `_ConcretizeGepBase(Gen, Ptr, ClassName, Tag)`:pointee 非 opaque ⇒ 原样返回
    (对既有正确路径零变化);opaque ⇒ bitcast 到已具化实例并**同时返回该实例**,
    供 `_get_member_offset` 作 StructHint(偏移与 GEP 必须同源,与 a43513c9 同口径)。

接入点(全部改为调用上述判据源,逐点行为只在"原本必崩"的情形下变化):
  · HandlesExprAttr._get_attr_ptr — self / 变量 / 属性链 / i8* / 下标 / 强转基座 六条分支
    (self 分支已由 7aa12263 修,此处并入统一判据;下标分支此前**完全没有**收口);
  · HandlesAnnAssign._HandleAttributeStoreLlvm — self / 变量 / 属性链 三条分支;
  · HandlesBase._get_int_ptr — 属性分支;
  · HandlesExprBuiltin(len() 的两个接收者 GEP:`obj.len` 与 list 特化字段);
  · HandlesMatch — REnum 变体 match 的载荷 GEP(变体 struct 可能未 set_body)。

冒烟(临时脚本已删,鸭子类型 Gen + 真实方法):
  · `_get_attr_ptr` self 分支 ⇒ 基址 `%"fef783da4d24330d.Keyword"*`(经 cast_self_Keyword)PASS;
  · `_get_attr_ptr` i8* 分支 ⇒ 同上 PASS;
  · `HandlesAnnAssign` self 分支 ⇒ GEP 基址为已具化实例 PASS;
  · 回归:基址本就具化时必须**原样**(不得多出 bitcast)PASS。
另:`python -m compileall TransPyC/lib` 全绿。
This commit is contained in:
TermiNexus committed 2026-09-30 14:42:56 +08:00
1 parent 7aa12263f5
commit 00edd90c28
5 files changed
+133 -60

No files matched your search

+28 -9
View File
@@ -96,9 +96,16 @@ class AnnAssignHandle(BaseHandle):
SelfVar: ir.Value | None = Gen._get_var_ptr('self')
if SelfVar:
SelfPtr: ir.Value = Gen._load(SelfVar, name="self")
offset: int = Gen._get_member_offset(AttrName, ClassName)
MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):self 的 pointee
# 可能是**另一份 opaque 同名实例**(函数签名那份),typed pointer 下
# GEP 必须编译期算出元素类型 ⇒ 先补 body / 取同短名孪生体再 GEP。
_SP: Any
SelfPtr, _SP = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, SelfPtr, ClassName, f"cast_self_{ClassName}")
offset: int = Gen._get_member_offset(AttrName, ClassName, _SP)
if offset is not None:
MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
elif isinstance(Target.value, ast.Name):
VarName: str = Target.value.id
@@ -193,6 +200,12 @@ class AnnAssignHandle(BaseHandle):
and '[' in ClassName):
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_spec_{ClassName}")
if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]:
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):类键那份可能
# 仍是 opaque(body 未 set_body / 另一份实例已具化)⇒ 先补 body /
# 取同短名孪生体,偏移与 GEP 同源。
_STa: Any
ObjVal, _STa = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, ObjVal, ClassName, f"cast_{ClassName}")
if IsUnion:
NestedStructName: str = f"{ClassName}_{AttrName}"
if NestedStructName in Gen.structs:
@@ -203,9 +216,10 @@ class AnnAssignHandle(BaseHandle):
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
return
offset: int = Gen._get_member_offset(AttrName, ClassName)
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
offset: int = Gen._get_member_offset(AttrName, ClassName, _STa)
if offset is not None:
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
elif isinstance(Target.value, ast.Attribute):
ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value)
if ObjVal and isinstance(ObjVal.type, ir.PointerType):
@@ -213,9 +227,14 @@ class AnnAssignHandle(BaseHandle):
if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)):
for CN, ST in Gen.structs.items():
if pointee == ST:
offset: int = Gen._get_member_offset(AttrName, CN)
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase)
_STb: Any
ObjVal, _STb = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, ObjVal, CN, f"cast_{CN}")
offset: int = Gen._get_member_offset(AttrName, CN, _STb)
if offset is not None:
MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName)
self._StoreWithCoerce(Value, MemberPtr)
break
def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None:
+6 -1
View File
@@ -810,7 +810,12 @@ class BaseHandle:
StructName = ClassName
break
if StructName and StructName in Gen.structs:
offset: int = self.Trans.ExprHandler._get_llvm_member_offset(Node.attr, StructName, Gen)
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):pointee 可能是
# **另一份 opaque 同名实例**,typed pointer 下 GEP 必须编译期算出
# 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。
obj_val, _STk = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, obj_val, StructName, f"cast_{StructName}")
offset: int = Gen._get_member_offset(Node.attr, StructName, _STk)
member_ptr: Any = Gen.builder.gep(obj_val, [_ir.Constant(_ir.IntType(32), 0), _ir.Constant(_ir.IntType(32), offset)], name=f"{Node.attr}_ptr")
return member_ptr
return None
+79 -47
View File
@@ -1772,6 +1772,54 @@ class ExprAttrHandle(BaseHandle):
# ========== _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):
@@ -1785,36 +1833,13 @@ class ExprAttrHandle(BaseHandle):
if SelfVar:
SelfPtr: Any = Gen._load(SelfVar, name="self")
if isinstance(SelfPtr.type, ir.PointerType):
# 基址 pointee 若**未具化**(opaque):typed pointer 下 GEP 必须
# 在编译期算出元素类型,否则 llvmlite 内部抛裸 TypeError
# ('NoneType' object is not subscriptable)。与**同函数下方
# 各分支**(属性链分支 / i8* 分支:均先 _TryLoadStructFromStub
# 补 body、再 bitcast 到已具化实例)同口径 —— 偏移与 GEP 必须
# 同源;本分支此前是唯一漏了这套处理的分支。
# 现场(2026-09-30):includes/ast/astaux.vp:165
# `self.arg = _copy_str(pool, arg)`,GEP 名 'arg',基址 pointee
# 是 opaque 的 `%"8312ce86a2fd82c0.Keyword"*`,而同短名的
# 'Keyword' 键上那份已具化(10 槽)。
_SelfPointee: Any = SelfPtr.type.pointee
if (isinstance(_SelfPointee, ir.IdentifiedStructType)
and _SelfPointee.elements is None):
_ST: Any = Gen.structs.get(ClassName)
if isinstance(_ST, ir.IdentifiedStructType) and not _ST.elements:
self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen)
_ST = Gen.structs.get(ClassName, _ST)
_Twin: Any = None
if isinstance(_ST, ir.IdentifiedStructType) and _ST.elements:
_Twin = _ST
else:
try:
_Twin = Gen._FindConcreteTwin(_SelfPointee)
except Exception:
_Twin = None
if _Twin is not None:
SelfPtr = Gen.builder.bitcast(
SelfPtr, ir.PointerType(_Twin),
name=f"cast_self_{ClassName}")
_SelfPointee = _Twin
# 基址收口(见 _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
@@ -1851,14 +1876,11 @@ class ExprAttrHandle(BaseHandle):
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]:
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 ST.elements is not None and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and ObjVal.type.pointee.elements is None:
ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_{ClassName}")
offset = Gen._get_member_offset(Node.attr, ClassName)
if offset is not None and ST.elements is not None:
# 基址收口(见 _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):
@@ -1871,20 +1893,21 @@ class ExprAttrHandle(BaseHandle):
CN: str
ST: Any
CN, ST = found
if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0):
self.Trans.ImportHandler._TryLoadStructFromStub(CN, Gen)
ST = Gen.structs.get(CN, ST)
if ST.elements is not None and isinstance(InnerPtr.type, ir.PointerType) and isinstance(InnerPtr.type.pointee, ir.IdentifiedStructType) and InnerPtr.type.pointee.elements is None:
InnerPtr = Gen.builder.bitcast(InnerPtr, ir.PointerType(ST), name=f"cast_{CN}")
offset = Gen._get_member_offset(Node.attr, CN)
if offset is not None and ST.elements is not None:
# 基址收口(见 _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}")
offset = Gen._get_member_offset(Node.attr, 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
@@ -1896,7 +1919,9 @@ class ExprAttrHandle(BaseHandle):
found = Gen.find_struct_by_pointee(pointee)
if found:
CN, ST = found
offset = Gen._get_member_offset(Node.attr, CN)
# 基址收口(本分支此前完全没有这一步: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
@@ -1917,6 +1942,13 @@ class ExprAttrHandle(BaseHandle):
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:
+12 -2
View File
@@ -648,7 +648,13 @@ class ExprBuiltinHandle(BaseHandle):
self_val = Gen._get_var_ptr(obj_name)
if self_val:
self_ptr = Gen._load(self_val, name="obj_for_len")
offset = Gen._get_member_offset(len_member, bare_cls)
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):接收者的
# pointee 可能是**另一份 opaque 同名实例**,GEP 需要在编译期算出
# 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。
_STl: Any
self_ptr, _STl = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, self_ptr, bare_cls, f"cast_{bare_cls}")
offset = Gen._get_member_offset(len_member, bare_cls, _STl)
len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{len_member}_ptr")
return Gen._load(len_ptr, name=f"{obj_name}_{len_member}")
# 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段
@@ -672,7 +678,11 @@ class ExprBuiltinHandle(BaseHandle):
self_val = Gen._get_var_ptr(obj_name)
if self_val:
self_ptr = Gen._load(self_val, name="obj_for_len")
offset = Gen._get_member_offset(attr_name, bare_cls)
# 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase)
_STl2: Any
self_ptr, _STl2 = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, self_ptr, bare_cls, f"cast_{bare_cls}")
offset = Gen._get_member_offset(attr_name, bare_cls, _STl2)
if offset is not None:
list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{attr_name}_ptr")
list_val = Gen._load(list_ptr, name=f"{obj_name}_{attr_name}")
+8 -1
View File
@@ -1,5 +1,5 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle
@@ -439,6 +439,13 @@ class MatchHandle(BaseHandle):
NestedStructName: str = f"{RenumName}_{VariantName}"
if NestedStructName in Gen.structs:
NestedStructType: ir.Type = Gen.structs[NestedStructName]
# 变体 struct 可能仍是 opaque(body 未 set_body / 同名两份实例)
# ⇒ 先取已具化实例,否则 GEP 在编译期算不出元素类型(判据见
# ExprAttrHandle._ConcretePointeeFor)。
_TwV: Any = self.Trans.ExprAttrHandle._ConcretePointeeFor(
Gen, NestedStructName, NestedStructType)
if isinstance(_TwV, ir.IdentifiedStructType):
NestedStructType = _TwV
NestedStructPtrType: ir.PointerType = ir.PointerType(NestedStructType)
variant_ptr: ir.Value = Gen.builder.bitcast(SubjectPtr, NestedStructPtrType, name=f"match_cast_{VariantName}")
for bind_name, member_name, member_type, member_idx in bindings: