fix(LLVMCG/Handles): GEP 基址 opaque 时按同短名找已具化孪生体并 bitcast;守卫报文补实例清单
现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)` 的基址 pointee 是 opaque 的 `%"<sha1>.Keyword"*` —— 同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名 '<sha1>.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份" 并不保证一致 ⇒ GEP 无法在编译期算元素类型(llvmlite 抛裸 TypeError), _GuardIrBuilder 的 opaque 守卫先行 fail-fast。 改动: 1) 新增 BaseGen._FindConcreteTwin:按**同短名**(IR 全名相同者优先)在 structs 里取一份 已具化实例(元素最多者),与既有"同名实例取字段最多版本"(HandlesAssign:1156-1163) 同一判据;返回 None 表示确无孪生体(真因 = "body 从未 set_body")。 2) 写入路径 HandlesAssign(`self.字段 = ...`):pointee opaque 时 bitcast 到孪生体, 并把它同时用作 struct_type 与 `_get_member_offset` 的 hint ⇒ 偏移与 GEP 同源。 3) 读取路径 HandlesExprAttr._gep_and_Load_member 共用同一工具函数(读写两路同判据, 避免"写修了读没修"再吃一轮)。 4) opaque-GEP 守卫报文补"同短名 struct 实例(key(IR 名)=槽数)"清单:清单含已具化项 ⇒ 属"偏移与 GEP 未同源";全是 opaque ⇒ 属"body 从未 set_body"——一次报文即可判定是哪一侧。 找不到孪生体时**不兜底**:仍由守卫 fail-fast 并报出实例清单(本仓"不许静默兜底"约定)。 验证:py_compile 通过(BaseGen / HandlesAssign / HandlesExprAttr 三文件)。
This commit is contained in:
1 parent
55cbf1d261
commit
2aa55f36b9
3 files changed
+110
No files matched your search
@@ -878,6 +878,29 @@ class AssignHandle(BaseHandle):
|
|||||||
SelfPtr, ir.PointerType(struct_type),
|
SelfPtr, ir.PointerType(struct_type),
|
||||||
name=f"concrete_self_{ClassName}")
|
name=f"concrete_self_{ClassName}")
|
||||||
_SelfPointee = struct_type
|
_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"),**不静默兜底**。
|
||||||
|
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
|
||||||
offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee)
|
offset: int = Gen._get_member_offset(AttrName, ClassName, _SelfPointee)
|
||||||
max_offset: int = (len(struct_type.elements)
|
max_offset: int = (len(struct_type.elements)
|
||||||
if isinstance(struct_type, ir.IdentifiedStructType)
|
if isinstance(struct_type, ir.IdentifiedStructType)
|
||||||
|
|||||||
@@ -29,6 +29,24 @@ class ExprAttrHandle(BaseHandle):
|
|||||||
return None
|
return None
|
||||||
if isinstance(ObjVal.type, ir.PointerType):
|
if isinstance(ObjVal.type, ir.PointerType):
|
||||||
pointee: Any = ObjVal.type.pointee
|
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):
|
if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset >= len(pointee.elements):
|
||||||
# sha1 冲突修复:pointee 字段不足时,尝试 bitcast 到 ST(更完整的版本)
|
# 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):
|
if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements):
|
||||||
|
|||||||
@@ -64,6 +64,30 @@ def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str
|
|||||||
"\"该类 body 从未 set_body\"的根因(不做兜底)")
|
"\"该类 body 从未 set_body\"的根因(不做兜底)")
|
||||||
if Gen is not None and hasattr(Gen, '_get_node_info'):
|
if Gen is not None and hasattr(Gen, '_get_node_info'):
|
||||||
_MsgP += "\n " + Gen._get_node_info()
|
_MsgP += "\n " + Gen._get_node_info()
|
||||||
|
# 同短名 struct 实例清单(key(IR 名)=槽数):**直接**区分两种根因——
|
||||||
|
# 清单里存在已具化项 ⇒ "偏移与 GEP 未同源"(应 bitcast 到该项,见
|
||||||
|
# HandlesAssign 的孪生体搜索);全是 opaque ⇒ "该类 body 从未 set_body"
|
||||||
|
# (往上查 StructGen 的 set_body 为何没跑)。不必再靠猜。
|
||||||
|
if Gen is not None and isinstance(getattr(Gen, 'structs', None), dict):
|
||||||
|
_PShort: str = ''
|
||||||
|
try:
|
||||||
|
_PShort = Gen._extract_short_name(getattr(_Pointee, 'name', '') or '')
|
||||||
|
except Exception:
|
||||||
|
_PShort = ''
|
||||||
|
_Kin: list[str] = []
|
||||||
|
for _k_n, _k_v in list(Gen.structs.items()):
|
||||||
|
if not isinstance(_k_v, ir.IdentifiedStructType):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
_k_s: str = Gen._extract_short_name(getattr(_k_v, 'name', '') or '')
|
||||||
|
except Exception:
|
||||||
|
_k_s = ''
|
||||||
|
if _PShort and _k_s != _PShort:
|
||||||
|
continue
|
||||||
|
_k_cnt: Any = (len(_k_v.elements) if _k_v.elements is not None else 'opaque')
|
||||||
|
_Kin.append(f"{_k_n}({getattr(_k_v, 'name', '?')})={_k_cnt}")
|
||||||
|
if _Kin:
|
||||||
|
_MsgP += "\n 同短名 struct 实例(key(IR 名)=槽数): " + ", ".join(_Kin)
|
||||||
raise FailFastError(_MsgP)
|
raise FailFastError(_MsgP)
|
||||||
return
|
return
|
||||||
# 失败路径才导入:热路径(每次 gep/load/store)不承担导入开销,也避免循环导入。
|
# 失败路径才导入:热路径(每次 gep/load/store)不承担导入开销,也避免循环导入。
|
||||||
@@ -1218,6 +1242,51 @@ class BaseGenMixin:
|
|||||||
return self._current_source_lines[lineno - 1].rstrip('\r\n')
|
return self._current_source_lines[lineno - 1].rstrip('\r\n')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def _FindConcreteTwin(self, StructType: Any) -> Any:
|
||||||
|
"""按**同短名**在 `structs` 里找一份**已具化**(elements 非 None)的 struct 实例。
|
||||||
|
|
||||||
|
为什么需要:同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名
|
||||||
|
'<sha1>.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份"
|
||||||
|
并不保证一致;一旦被 GEP 的那份是 opaque,llvmlite 在编译期算元素类型即抛裸
|
||||||
|
TypeError(现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)`
|
||||||
|
的基址 pointee 是 opaque 的 `%"<sha1>.Keyword"*`)。调用方把指针 bitcast 到
|
||||||
|
本函数返回的实例即可让"偏移与 GEP 同源"——与 HandlesAssign 里
|
||||||
|
"同短名取字段最多版本"是同一判据。
|
||||||
|
返回 None 表示**没有**已具化孪生体(真因是"body 从未 set_body")⇒ 调用方不得
|
||||||
|
兜底,交由 RequirePointerOperand 的 opaque 守卫 fail-fast 并报出实例清单。
|
||||||
|
"""
|
||||||
|
if not isinstance(StructType, ir.IdentifiedStructType):
|
||||||
|
return None
|
||||||
|
_Short: str = ''
|
||||||
|
try:
|
||||||
|
_Short = self._extract_short_name(getattr(StructType, 'name', '') or '')
|
||||||
|
except Exception:
|
||||||
|
_Short = ''
|
||||||
|
if not _Short:
|
||||||
|
return None
|
||||||
|
# 第一优先:**IR 全名相同**的实例(同类名两份实例的情形,必为同一类);
|
||||||
|
# 第二优先:同短名里元素最多的一份(跨模块同名类;与 HandlesAssign
|
||||||
|
# "同短名取字段最多版本"同判据)。全名相同优先可避免"短名相同但实为另一个
|
||||||
|
# 模块的同名类"被误选而算出错的偏移。
|
||||||
|
_Full: str = getattr(StructType, 'name', '') or ''
|
||||||
|
_Best: Any = None
|
||||||
|
_BestN: int = -1
|
||||||
|
for _v in list(getattr(self, 'structs', {}).values()):
|
||||||
|
if not isinstance(_v, ir.IdentifiedStructType) or _v.elements is None:
|
||||||
|
continue
|
||||||
|
if _Full and getattr(_v, 'name', '') == _Full:
|
||||||
|
return _v
|
||||||
|
try:
|
||||||
|
_s: str = self._extract_short_name(getattr(_v, 'name', '') or '')
|
||||||
|
except Exception:
|
||||||
|
_s = ''
|
||||||
|
if _s != _Short:
|
||||||
|
continue
|
||||||
|
if len(_v.elements) > _BestN:
|
||||||
|
_BestN = len(_v.elements)
|
||||||
|
_Best = _v
|
||||||
|
return _Best
|
||||||
|
|
||||||
def _DiskLineno(self, Lineno: int) -> int:
|
def _DiskLineno(self, Lineno: int) -> int:
|
||||||
"""AST 行号 → 磁盘真实行号(减掉文件头自动注入 import 的行数)。
|
"""AST 行号 → 磁盘真实行号(减掉文件头自动注入 import 的行数)。
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user