From 60100fa6af25ce76d0eeecfb35c4bfdf49dd8b1d Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 15 Sep 2026 13:52:21 +0800 Subject: [PATCH] =?UTF-8?q?fix(type):=20=E8=81=94=E5=90=88=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=20A|t.CPtr=20=E5=88=A4=E6=8D=AE=E7=94=B1=E7=99=BD?= =?UTF-8?q?=E5=90=8D=E5=8D=95=E6=94=B9=E4=B8=BA=E8=A7=A3=E6=9E=90=E6=88=90?= =?UTF-8?q?=E5=8A=9F=E5=8D=B3=E4=BF=9D=E7=95=99=EF=BC=8Cstub=20=E4=BE=A7?= =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 定义侧 TypeGenResolve.resolve_annotation_type 的 BinOp(A | t.CPtr) 分支:裸名 pointee 的保留判据由“硬编码名字白名单 + 必须是已注册 struct”改为“以解析是否 成功为准”——命中统一口径 resolve_typedef_or_map(CTypedef 动态表 → builtins → stdint/w32.win32base 库源 → map_t_type)且结果非指针即保留该已解析类型。 于是 sizet | t.CPtr ⇒ i64*(原静默退化为 i8*)。 修复前的故障:TPV2 Phase1 的 includes/ast/tokens.vp _kw_index_put 按 i8* 处理 哈希槽,slot[0] 只做 1 字节读写,指针被截断成低字节 0xd0,随后被当 KwEntry* 解引用 → ASan 读地址 0xd0 崩溃(TPC 侧同 sha 为 alloca i64*/load i64/store i64)。 stub/declare 侧 VpiReader._resolve_vpi_ret_annot_str 同步同一判据:新增 first_td 标记,对裸名(无 '.')、非白名单、非指针的解析结果复用同一个 GenApi.resolve_typedef_or_map 判定,命中即保留 pointee。声明侧与定义侧现在 同型(sizet/uint64/INT/u32 等别名两侧都得到 i64*/i32*),同型即收敛, 不再需要把 declare 侧坍缩为 i8* 去迁就定义侧。 既有路径行为不变:泛型参数名、内建标量白名单(int/char/str/bytes/bool/float/ double)、解析失败与裸枚举名的 i8* 兜底;指针别名(BYTEPTR/HANDLE/VOIDPTR) 因解析结果为 Ptr(...) 不置位 first_td,仍坍缩 i8*,与定义侧 td_res_is_ptr 守卫对称。 --- App/lib/Stub/StubGen/VpiReader.vp | 40 +++++++++++++++++++++---- App/lib/core/Gen/Type/TypeGenResolve.vp | 31 +++++++++++++++++++ 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index 8210b49..9798098 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1317,10 +1317,15 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy], has_cvoid: int = 0 first_ty: llvmlite.LLVMType | t.CPtr = None # 产生 first_ty 那一段文本的属性(供末尾 has_cptr 分支复刻定义侧规则,N12): - # first_qual=1 段是限定名(含 '.',如 uint64 / mod.Class) + # first_qual=1 段是限定名(含 '.',如 mod.Class / t.CInt64T) # first_wl =1 段是白名单裸标量名(int/char/str/bytes/bool/float/double) + # first_td =1 段是裸 typedef 别名且已成功解析出**非指针**类型(N13, + # 如 sizet/uint64 ⇒ i64):判据=“解析是否成功”,与定义侧 + # TypeGenResolve.resolve_annotation_type 的 BinOp(A | t.CPtr) + # 分支同源(同一 resolve_typedef_or_map 口径)。 first_qual: int = 0 first_wl: int = 0 + first_td: int = 0 buf_len: sizet = string.strlen(ret_annot) pos: sizet = 0 @@ -1364,6 +1369,22 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy], string.strcmp(seg_buf, "float") == 0 or \ string.strcmp(seg_buf, "double") == 0: first_wl = 1 + # 裸名 typedef 别名保真(N13,与定义侧同源):sizet/uint64/INT/u32 + # 等别名经上面 _resolve_single_vpi_type_str 已成功解析出标量 + # (sizet ⇒ i64、INT/u32 ⇒ i32),只是"不是 struct 且不在白名单" + # 就被下方坍缩成 i8* → declare 侧 i8* 而定义侧(已同步改为 + # "解析成功即保留")为 i64* → 跨模块同型收敛被破坏。 + # 判据直接用与定义侧同一个 API GenApi.resolve_typedef_or_map + # (委托 TypeGenResolve.resolve_typedef_or_map:CTypedef 动态表 → + # builtins → stdint/w32.win32base 库源 → map_t_type), + # 命中即置位保留;不另立手写名字白名单。 + # 仅对**非指针**解析结果置位:指针别名(BYTEPTR/HANDLE/VOIDPTR + # 解析为 Ptr(...))不置位 → 仍坍缩 i8*,与定义侧守卫 + # (td_res_is_ptr != 0 时不套用 typedef 保真)对称。 + if first_qual == 0 and first_wl == 0: + if first_ty.kind() != llvmlite.LLVMType.Ptr: + if GenApi.resolve_typedef_or_map(pool, seg_buf, None, None) is not None: + first_td = 1 stdlib.free(seg_buf) pos += 1 # 跳过 '|' @@ -1371,11 +1392,18 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy], if has_cvoid != 0: return i8_ptr_ty if first_ty is not None: - # 复刻定义侧规则(N12):限定名 / 白名单裸标量 / 已注册结构体 → 保留 - # pointee;其余**裸名**(如 uint64/uint32 等 typedef 别名、裸枚举名) - # → 联合坍缩为 i8*,与 resolve_annotation_type 一致,避免 - # `save_buf: uint64 | t.CPtr` 定义侧 i8* 而 stub 侧 i64* 跨模块不收敛。 - if first_qual != 0 or first_wl != 0: + # 复刻定义侧规则(N12/N13):限定名 / 白名单裸标量 / 已注册结构体 / + # 可解析的裸 typedef 别名(非指针)→ 保留 pointee;其余(解析失败、 + # 裸枚举名、指针别名)→ 联合坍缩为 i8*,与 resolve_annotation_type 一致。 + # 收敛依据(替换旧理由"避免定义侧 i8* 而 declare i64* 跨模块不收敛"): + # 定义侧同一分支已改为"解析成功即保留(非指针)",`sizet | t.CPtr` 定义侧 + # ⇒ i64*(见 TypeGenResolve.vp BinOp(A | t.CPtr) 的 typedef 别名保真判定), + # 本侧经同一 GenApi.resolve_typedef_or_map 命中后亦 ⇒ Ptr(i64) = i64*; + # **两侧同型即收敛**,不再需要把 declare 侧坍缩成 i8* 去迁就定义侧。 + # 仍坍缩 i8* 的三类与定义侧对称:解析失败兜底;裸枚举名(定义侧 + # get_struct_type miss 同样落 i8*);指针别名(first_td 不置位,对应定义侧 + # td_res_is_ptr != 0 的守卫)。 + if first_qual != 0 or first_wl != 0 or first_td != 0: return llvmlite.Ptr(pool, first_ty) if first_ty.kind() == llvmlite.LLVMType.Struct: return llvmlite.Ptr(pool, first_ty) diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index e1ae1a6..3d7feba 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -2500,6 +2500,37 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): string.strcmp(other_nm_b.id, "float") == 0 or \ string.strcmp(other_nm_b.id, "double") == 0: keep_other_ty = 1 + # typedef 别名保真(判据=“解析是否成功”,非白名单扩名): + # sizet/uint64/CSizeT 等别名经 resolve_annotation_type 已 + # 成功解析出标量,却因“不是已注册 struct 且不在白名单”被 + # 丢弃 → other_ty=None → 下方 get_struct_type("sizet") miss + # → 联合静默退化为 i8*:哈希槽 slot[0] 只做 1 字节读写、 + # 指针被截断成低字节 0xd0,随后被当 KwEntry* 解引用(TPV2 + # Phase1 includes/ast/tokens.vp _kw_index_put ASan 读地址 + # 0xd0 崩根因;TPC 侧同 sha 为 alloca i64*/load i64/store i64)。 + # 用统一口径 resolve_typedef_or_map(CTypedef 动态表 → + # builtins → stdint/w32.win32base 库源 → map_t_type)判定 + # 该裸名是否是可解析的 typedef/别名;命中即保留本次已解析出 + # 的类型,参与“基类型 + 指针层数”合并(对齐 TPC + # HandlesTypeMerge._MergeAllComponentTypes L887-946: + # sizet | t.CPtr ⇒ 基类型 i64 + 1 层指针 = i64*)。 + # 仅保留非指针结果:Ptr(...)(类 __new__ 的 Ptr(struct)、 + # 指针别名)继续走下方 get_struct_type 回退与解析失败兜底, + # 该两条既有路径行为逐字节不变。 + if keep_other_ty == 0 and other_node is not None and other_node.kind() == ast.ASTKind.Name: + other_nm_td: ast.Name | t.CPtr = (ast.Name | t.CPtr)(other_node) + if other_nm_td.id is not None: + td_res_is_ptr: int = 0 + match other_ty: + case llvmlite.LLVMType.Ptr(_): + td_res_is_ptr = 1 + case _: + td_res_is_ptr = 0 + if td_res_is_ptr == 0: + td_alias_ty: llvmlite.LLVMType | t.CPtr = self.resolve_typedef_or_map( + pool, other_nm_td.id, imported_modules, from_imports, trans) + if td_alias_ty is not None: + keep_other_ty = 1 if keep_other_ty == 0: other_ty = None if other_ty is None: