fix(exprattr): 包子模块属性访问支持全局注册表回退+裸名回退+数组退化

import intr 只注册包名 intr,intr.idt.idt[32] 的嵌套路径 is_module_imported
('intr.idt')=0 → 落入结构体字段访问报 '属性访问失败: .idt'(main.vp L98 根因)。
修复:1) 嵌套路径模块已知性增加 Sha1Store 全局注册表回退(包 __init__.vp
from . import X 注册的 pkg.X);2) 未显式 import 的已知子模块先经
LoadDepVpiIntoModule 注入其 .vpi 声明(含类完整结构体定义 + CExport 裸名
全局声明);3) 全局查找补 CExport 裸名回退;4) 模块级数组全局按 C 语义
返回基址指针(对齐读路径 L629-634),供下标/字段 GEP 继续。
This commit is contained in:
TermiNexus committed 2026-08-26 18:51:13 +08:00
1 parent 50a4772a7d
commit 23ef8c94fa
1 file changed
+53 -8
+53 -8
View File
@@ -16,6 +16,7 @@ import lib.core.Handles.HandlesExpr as HandlesExpr
import lib.core.Handles.HandlesExprCall as HandlesExprCall
import lib.Stub.StubMerger as StubMerger
import lib.Stub.StubMerger.StubMergerStore as StubMergerStore
import lib.Stub.StubMerger.StubMergerBuild as StubMergerBuild
import lib.core.VLogger as VLogger
import lib.Projectrans.Config as Config
import lib.Projectrans.Utils as Utils
@@ -263,6 +264,13 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
gv_q: str = self.Gen.FuncGenBase._mangle_name_with_sha1(
self._lookup_module_sha1(mod_path_w), at.attr)
gv: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, gv_q)
# CExport 全局(定义端裸名 @_sched_ptr):mangled 名查不到时回退裸名精确
# 查找。此前只查 mangled 名 → 找不到 → 新建 mangled external global 声明
# (@"ef5b0ba0dc895058._sched_ptr")→ 与定义端裸名不匹配 → ld.lld
# undefined symbol: ef5b0ba0dc895058._sched_ptr(idt.vp L134
# sched._sched_ptr 根因)。
if gv is None:
gv = self.find_global_in_module(mod, at.attr)
if gv is not None and gv.Ty is not None:
# 用 gv.Name(完整名,带 SHA1 前缀时与定义端一致)构造引用,
# 避免裸名 @_mbuddy 在多模块各自带前缀定义下 undefined symbol
@@ -642,18 +650,41 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
qual_buf: str = pool.alloc(qual_len)
if qual_buf is not None:
viperlib.snprintf(qual_buf, qual_len, "%s.%s", inner_nm.id, inner_at.attr)
if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, qual_buf) == 1:
# 模块已知性:显式 import(w32.win32file)或全局模块注册表登记
#(包 __init__.vp 内 from . import X 注册的 pkg.X,如 intr.idt)
mod_known_n: int = self.Gen.ImportsGen.is_module_imported(
trans._imported_modules, qual_buf)
if mod_known_n == 0 and self._lookup_module_sha1(qual_buf) is not None:
# 未显式 import 的已知子模块:先注入其 .vpi 声明到当前模块,
# 否则 find_global_in_module 找不到该模块的 CExport 全局(@idt)
#(main.vp intr.idt.idt[32] 根因)
StubMergerBuild.LoadDepVpiIntoModule(
pool, mod, qual_buf, trans._from_imports,
self.Gen.TypeGenBase.get_temp_dir(), trans)
mod_known_n = 1
if mod_known_n != 0:
# 查找全局变量 at.attr(带限定模块 SHA1 前缀精确匹配,
# 避免多模块同名全局后缀匹配串扰;sha1 未知退化裸名)
gv_nested_q: str = self.Gen.FuncGenBase._mangle_name_with_sha1(
self._lookup_module_sha1(qual_buf), at.attr)
gv_nested: llvmlite.GlobalVariable | t.CPtr = self.find_global_in_module(mod, gv_nested_q)
# CExport 全局(定义端裸名 @idt)mangled 名查不到时回退
# 裸名精确查找(对齐读路径 _resolve_module_attribute_global)
if gv_nested is None:
gv_nested = self.find_global_in_module(mod, at.attr)
if gv_nested is not None and gv_nested.Ty is not None:
gv_ref_nested: llvmlite.Value | t.CPtr = self.make_global_ref(
pool, gv_nested.Name, gv_nested.Ty)
if gv_ref_nested is not None and gv_ref_nested.Ty is not None:
load_ty_nested: llvmlite.LLVMType | t.CPtr = gv_ref_nested.Ty.Pointee
if load_ty_nested is not None:
# 模块级全局数组(如 idt.idt: CArray[T, N])按 C 语义
# 返回基址指针(对齐读路径 L629-634),下标/取址继续 GEP
match load_ty_nested:
case llvmlite.LLVMType.Array(_, _):
return gv_ref_nested
case _:
pass
return llvmlite.build_load(builder, load_ty_nested, gv_ref_nested)
# 未找到全局: 尝试 CDefine 常量查找
# 对于 w32.win32base.INVALID_HANDLE_VALUE 等嵌套模块属性访问,
@@ -801,7 +832,7 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
if base_class is not None:
cur_sha1: str = trans.ModuleSha1
field_info = self.Gen.StructGen.lookup_field_by_class(
base_class, at.attr, cur_sha1)
base_class, at.attr, cur_sha1, at, trans._from_imports)
# REnum 变体字段别名回退:base_class 是 REnum 类时,变体
# 字段名(如 LLVMType.Pointee → _p2 槽位)不是普通字段,
# lookup_field_by_class 查不到(LLVMType"字段 Pointee 未找到"
@@ -868,6 +899,15 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
annot_struct: GenTypes.StructEntry | t.CPtr = self.Gen.StructGen.find_struct(
field_info.AnnotClassName)
if annot_struct is not None and annot_struct.Ty is not None:
# 确保目标结构体类型在当前模块有声明(可能未定义/未 opaque):
# 联合字段 AnnotClassName 可能解析到本模块未 import 的类
# (如 __types 的 ParamNode.Next 误解析到 __function.Param),
# bitcast 引用未定义类型 → opt ASan 报 "use of undefined type"。
# _scan_type_for_cross_module_refs 只扫 Ptr(Struct) 模式,
# 因此传 Ptr(Ty) 让结构体名本身被 opaque 声明。
# opaque 声明足以支撑指针 bitcast(不访问字段),行为不变。
llvmlite.module_ensure_opaque_for_type(
mod, pool, llvmlite.Ptr(pool, annot_struct.Ty))
annot_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, annot_struct.Ty)
return llvmlite.build_bitcast(builder, loaded_val, annot_ptr_ty)
return loaded_val
@@ -962,11 +1002,14 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
if parent_class is None:
return None
# 在父类中查 at.attr 字段,返回其注解类名
cur_sha1: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field_by_class(
parent_class, at.attr, cur_sha1)
cur_sha1: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
lfi_pc: str = None
if trans is not None:
lfi_pc = trans._from_imports
fe: GenTypes.FieldEntry | t.CPtr = self.Gen.StructGen.lookup_field_by_class(
parent_class, at.attr, cur_sha1, at, lfi_pc)
if fe is None:
return None
return self.Gen.StructGen.get_field_annot_class_name(fe)
@@ -1098,10 +1141,12 @@ class ExprGenAttr(ExprGenBase.ExprGenBase):
base_class = self._resolve_attr_field_class(pool, trans, at.value)
if base_class is not None:
cur_sha1: str = None
lfi_gp: str = None
if trans is not None:
cur_sha1 = trans.ModuleSha1
lfi_gp = trans._from_imports
field_info = self.Gen.StructGen.lookup_field_by_class(
base_class, at.attr, cur_sha1)
base_class, at.attr, cur_sha1, at, lfi_gp)
# REnum 变体字段别名回退(同 translate_attribute L5046-5062):
# get_attribute_ptr 赋值路径的按需过滤同样需要——struct_ty 简化
# i8 时主路径 REnum 别名查找被跳过(LLVMType.Pointee 报错根因)