fix(HandlesAssign): self 属性写入的基址收口统一为 _ConcretizeGepBase;收口失败不再静默丢写

类别:a(编译器缺陷)。

1) 收口统一:`self.字段 = v` 的基址此前走**内联**孪生体搜索,缺
   `_TryLoadStructFromStub` 一步 —— 类键那份仍 opaque 且 Gen.structs 里尚无已具化
   孪生体时(body 只在 stub 文件里待加载),内联版会一路走到 GEP 撞 opaque 守卫
   (现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)`,基址 pointee
   是 opaque 的 %"8312ce86a2fd82c0.Keyword"*,而同短名 'Keyword' 键那份已具化 10 槽)。
   现改为调用读路径的**同一判据源** HandlesExprAttr._ConcretizeGepBase /
   _ConcretePointeeFor:① 类键那份 + stub 补 body → ② 同短名已具化孪生体 →
   ③ 都拿不到则原样返回、由守卫报根因。偏移与 GEP 同源(返回实例同时作为
   _get_member_offset 的 StructHint)。

2) 收口失败改 fail-fast:三条路都拿不到 elements 时,此前会落回
   `offset >= max_offset` 的「存回临时变量」分支 —— 该临时变量不在对象布局里,
   后续 `self.attr` 读路径按 GEP 取值,写入被**静默丢弃**。现直接 FailFastError,
   并报出三次尝试的结果(不兜底)。

静态核验:py_compile 通过;全仓 symtable 未定义名扫描 0 条;以 _GuardIrBuilder +
真实 _ConcretizeGepBase 复现现场实例形状(opaque '8312ce86a2fd82c0.Keyword' +
已具化 'fef783da4d24330d.Keyword'(10 槽))→ 解析成功、GEP 正常发出(索引 3);
未收口的裸 opaque 基址仍被守卫拦下。
This commit is contained in:
TermiNexus committed 2026-09-30 15:14:31 +08:00
1 parent 7ee6ebe6b0
commit 393ffefcd5
1 file changed
+28 -43
+28 -43
View File
@@ -856,51 +856,36 @@ class AssignHandle(BaseHandle):
FuncPtrI8: ir.Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"func_ptr_i8_{AttrName}")
Gen._store(FuncPtrI8, MethodPtrAddr)
return
# 自指针的 pointee 可能是**另一份同名 struct 实例**:函数签名里那份
# 仍 opaque(`_get_or_create_struct` 早期创建、从未 set_body),而类键
# `Gen.structs[ClassName]` 上那份已具化。typed pointer 的 GEP 必须在
# 编译期算出元素类型 ⇒ 走 opaque pointee 时 llvmlite 在
# `types.py:633 self.elements[i.constant]` 抛裸 TypeError
# ('NoneType' object is not subscriptable;现场:astaux.vp
# `Keyword.__init__` 的 `self.arg = _copy_str(pool, arg)`)。
# 修法:先 bitcast 到**有 body 的那份实例**,再把该实例作为
# StructHint 交给 `_get_member_offset` —— 偏移与 GEP 必须同源
# (与属性读取路径 _gep_and_Load_member 的 415916b3 同口径)。
_SelfPointee: ir.Type | None = None
if isinstance(SelfPtr.type, ir.PointerType):
_SelfPointee = SelfPtr.type.pointee
struct_type: ir.Type = Gen.structs[ClassName]
if (isinstance(_SelfPointee, ir.IdentifiedStructType)
and _SelfPointee.elements is None
and isinstance(struct_type, ir.IdentifiedStructType)
and struct_type.elements is not None):
SelfPtr = Gen.builder.bitcast(
SelfPtr, ir.PointerType(struct_type),
name=f"concrete_self_{ClassName}")
_SelfPointee = struct_type
# 孪生体搜索(治本,2026-09-30):类键实例可能**也是** opaque —— 同一类名
# 常有两份 struct 实例(短名 'Keyword' / sha1 全名 '<sha1>.Keyword'),
# 函数签名里的 self 用哪一份、StructGen 具化的是哪一份**并不保证一致**。
# 现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)` 的
# 基址 pointee 是 opaque 的 `%"<sha1>.Keyword"*`,GEP 无法在编译期算元素类型。
# 修法:按**同短名**取一份已具化实例(工具函数 _FindConcreteTwin,读路径
# HandlesExprAttr 共用同一判据)并 bitcast —— 偏移与 GEP 由此同源。
# 找不到 ⇒ 保持现状,交由 RequirePointerOperand 的 opaque 守卫 fail-fast
# (报出同短名实例清单,暴露"body 从未 set_body"),**不静默兜底**。
# 自指针基址收口:与**读路径同一判据源**(HandlesExprAttr
# `_ConcretizeGepBase` / `_ConcretePointeeFor`),三步同源:
# ① 类键那份 opaque ⇒ 先 `_TryLoadStructFromStub` 补 body;
# ② 仍不可用 ⇒ 按同短名取一份已具化实例(`_FindConcreteTwin`);
# ③ 都拿不到 ⇒ 原样返回,由下方 opaque fail-fast 报根因。
# 写路径此前是**内联**版本、缺 ①:类键那份仍 opaque 且 `Gen.structs`
# 里也还没有孪生体时(body 只在 stub 文件里待加载),内联版会一路走到
# GEP 撞 opaque(现场 2026-09-30:includes/ast/astaux.vp:165
# `self.arg = _copy_str(pool, arg)` 的基址 pointee 是 opaque 的
# %"8312ce86a2fd82c0.Keyword"*,而同短名 'Keyword' 键那份已具化 10 槽)。
# 偏移与 GEP 必须同源:返回的实例同时作为 `_get_member_offset` 的 StructHint。
SelfPtr, _SelfPointee = self.Trans.ExprAttrHandle._ConcretizeGepBase(
Gen, SelfPtr, ClassName, f"concrete_self_{ClassName}")
struct_type: ir.Type = (_SelfPointee
if isinstance(_SelfPointee, ir.IdentifiedStructType)
else Gen.structs[ClassName])
if (isinstance(_SelfPointee, ir.IdentifiedStructType)
and _SelfPointee.elements is None):
_Twin: ir.IdentifiedStructType | None = Gen._FindConcreteTwin(_SelfPointee)
if _Twin is None:
try:
_Twin = Gen._FindConcreteTwin(Gen.structs.get(ClassName))
except Exception:
_Twin = None
if _Twin is not None:
SelfPtr = Gen.builder.bitcast(
SelfPtr, ir.PointerType(_Twin),
name=f"concrete_twin_self_{ClassName}")
_SelfPointee = _Twin
struct_type = _Twin
# 收口失败:类键那份、同短名孪生体、以及 `_TryLoadStructFromStub`
# 三条路都没拿到 body。**不得**再落回下方 `offset >= max_offset`
# 的「存回临时变量」分支 —— 该临时变量不在对象布局里,后续
# `self.attr` 读路径按 GEP 取值,写入会被**静默丢弃**。此处 fail-fast。
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE0
raise _FFE0(
f"属性写入 '{ClassName}.{AttrName}' 的 self 基址指向**未具化**"
f"结构体:'{getattr(_SelfPointee, 'name', '?')}'(opaque)。"
f"已尝试:类键 {ClassName!r} 的实例 → 同短名孪生体 → "
f"_TryLoadStructFromStub 补 body,均未拿到 elements。"
f"请查该类 body 为何从未 set_body(StructGen._generate_structs)。"
+ (f"\n {Gen._get_node_info()}" if hasattr(Gen, '_get_node_info') else ""))
offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee)
max_offset: int = (len(struct_type.elements)
if isinstance(struct_type, ir.IdentifiedStructType)