From 31b8dccffd36297c7eef0aac700530191eff7d67 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 15 Sep 2026 23:58:39 +0800 Subject: [PATCH] =?UTF-8?q?fix(stub):=20declare=20=E4=BE=A7=E8=AF=86?= =?UTF-8?q?=E5=88=AB=20t.CPtr[...]=20=E6=AE=B5=EF=BC=8C=E4=B8=8E=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E4=BE=A7=E6=8C=87=E9=92=88=E5=B1=82=E7=BA=A7=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=E5=AF=B9=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit t.CUInt8T | t.CPtr[t.CPtr] 在定义侧(TypeGenResolve._is_cptr_subscript)按指针层级合并得 i8**,但声明侧段判据只认整段等于 t.CPtr/CPtr,下标段落入普通段解析后取首段 => i8,造成跨模块 define/declare 签名不一致(实参 1 字节 vs 写入 8 字节) 新增 t.CPtr[/c.CPtr[ 段的识别与递归解析(与同文件 rowptr[...] 分支同构),数外层 Ptr 层数 N 后按 Ptr^N 合并;裸 t.CPtr 段路径未动,未放宽 fail-fast 注: TPC 侧 LLVMTypeMapper._resolve_bitor 与 SymbolUtils.IsCPtrNode 同源缺陷未改,两套编译器需同时修以免产物分叉 --- App/lib/Stub/StubGen/VpiReader.vp | 82 ++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index d05de4a..e75a703 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1198,6 +1198,31 @@ def _resolve_single_vpi_type_str(pool: rowptr[memhub.MemBuddy], if inner_ty_rp is not None: return llvmlite.Ptr(pool, inner_ty_rp) return None + # t.CPtr[T] / c.CPtr[T] 下标指针(含嵌套,如 t.CPtr[t.CPtr] → Ptr(Ptr(i8))): + # 与定义侧 TypeGenResolve.resolve_annotation_type 的 Subscript(CPtr) 分支 + # (TypeGenResolve.vp L2769-2790)同口径 Ptr(resolve(T)),T 自身可含指针层级。 + # 先例:上方 rowptr[T] 分支(L1187-1200)同形(前缀 + 尾 ']' 判定 + 递归 + Ptr 包装)。 + # vpi 注解文本按源码原样缓存(如 `out: t.CUInt8T | t.CPtr[t.CPtr]`),本函数 + # 此前对该形态一律返回 None → 段被丢弃 → 联合坍缩到首段(i8),与定义侧 + # i8** 不一致(跨模块 declare/define ABI 错位;见 _resolve_vpi_ret_annot_str + # 段循环内的同源判据)。 + cptr_sub_pfx: sizet = 0 + if string.strncmp(seg, "t.CPtr[", 7) == 0 or string.strncmp(seg, "c.CPtr[", 7) == 0: + cptr_sub_pfx = 7 + if cptr_sub_pfx > 0: + seg_len_cp: sizet = string.strlen(seg) + if seg_len_cp > cptr_sub_pfx + 1 and seg[seg_len_cp - 1] == ']': + inner_len_cp: sizet = seg_len_cp - cptr_sub_pfx - 1 + inner_buf_cp: bytes = stdlib.malloc(inner_len_cp + 1) + if inner_buf_cp is not None: + string.strncpy(inner_buf_cp, seg + cptr_sub_pfx, inner_len_cp) + inner_buf_cp[inner_len_cp] = '\0' + inner_ty_cp: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str( + pool, inner_buf_cp) + stdlib.free(inner_buf_cp) + if inner_ty_cp is not None: + return llvmlite.Ptr(pool, inner_ty_cp) + return None # t.XXX / c.XXX 内置类型映射 if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: attr_nm: str = seg + 2 @@ -1326,6 +1351,16 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy], first_qual: int = 0 first_wl: int = 0 first_td: int = 0 + # CPtr 下标段(t.CPtr[T]/c.CPtr[T],含嵌套)的联合合并参数: + # cptr_sub_lvl = 该段解析后的外层 Ptr 层数 N(如 t.CPtr[t.CPtr] ⇒ 2); + # cptr_sub_ty = 该段解析出的类型本身(无另一侧基类型时即结果)。 + # 判据与定义侧 TypeGenResolve.vp 的 `X | t.CPtr[T]` 分支(L2596-2639)同口径: + # CPtr 侧贡献 N 层指针、另一侧提供基类型 ⇒ 结果 Ptr^N(基类型)。 + # 与 has_cptr(裸 t.CPtr 段)分开记录:裸标记路径恒"追加 1 层"且有 struct + # 吸收特例(定义侧 L2540-2594),下标路径无吸收(定义侧 L2610-2639), + # 两者在定义侧分属互斥分支(_is_cptr_marker / _is_cptr_subscript)。 + cptr_sub_lvl: int = 0 + cptr_sub_ty: llvmlite.LLVMType | t.CPtr = None buf_len: sizet = string.strlen(ret_annot) pos: sizet = 0 @@ -1347,7 +1382,35 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy], break string.strncpy(seg_buf, ret_annot + seg_start, seg_len2) seg_buf[seg_len2] = '\0' - if string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: + # CPtr 下标段(t.CPtr[T]/c.CPtr[T],含嵌套如 t.CPtr[t.CPtr]): + # 必须先行识别,不能落普通段分支取 first_ty——定义侧 + # TypeGenResolve.vp L2596-2639 对 `X | t.CPtr[T]` 是"CPtr 侧贡献 + # N 层指针、另一侧给基类型 ⇒ Ptr^N(X)",与"把该段本身当联合取值" + # 语义不同(如 X=struct S、t.CPtr[t.CPtr]:定义侧 Ptr(Ptr(S)), + # 取该段本身则得 i8**)。此处只记录 N 与段类型,末尾统一合并。 + if string.strncmp(seg_buf, "t.CPtr[", 7) == 0 or string.strncmp(seg_buf, "c.CPtr[", 7) == 0: + seg_ty_cs: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf) + if seg_ty_cs is not None: + # 计算外层 Ptr 嵌套数(定义侧 L2616-2632 同口径)。注意不能在 + # case body 内更新 while 条件变量(自举编译的 match 会写到 subject + # 快照槽 → 条件读旧槽死循环,见 TypeGenResolve.vp L2620-2624): + # case 绑定 pointee 到局部变量,match 外更新循环变量。 + lvl_cs: int = 0 + cur_cs: llvmlite.LLVMType | t.CPtr = seg_ty_cs + while cur_cs is not None: + nxt_cs: llvmlite.LLVMType | t.CPtr = None + match cur_cs: + case llvmlite.LLVMType.Ptr(pointee_cs): + lvl_cs += 1 + nxt_cs = pointee_cs + case _: + break + cur_cs = nxt_cs + # 多下标段取最大层数(定义侧按最外层联合右值合并,等价于层数累加) + if lvl_cs > cptr_sub_lvl: + cptr_sub_lvl = lvl_cs + cptr_sub_ty = seg_ty_cs + elif string.strcmp(seg_buf, "t.CPtr") == 0 or string.strcmp(seg_buf, "CPtr") == 0: has_cptr = 1 elif string.strcmp(seg_buf, "t.CVoid") == 0 or string.strcmp(seg_buf, "CVoid") == 0: has_cvoid = 1 @@ -1425,6 +1488,23 @@ def _resolve_vpi_ret_annot_str(pool: rowptr[memhub.MemBuddy], return llvmlite.Ptr(pool, first_ty) return i8_ptr_ty return i8_ptr_ty + # CPtr 下标段联合合并(定义侧 TypeGenResolve.vp L2596-2639 同口径): + # 有另一侧基类型 first_ty → 结果 = Ptr^N(first_ty)(N=cptr_sub_lvl) + # 仅 CPtr 下标段自身 → 结果 = 该段解析类型本身(等价定义侧 + # Subscript(CPtr) 分支 L2769-2790 的 Ptr^N(inner)) + # 下标段无 struct 吸收(定义侧该分支不吸收),故不与上方 has_cptr 分支的 + # 吸收判据混用。has_cptr(裸 t.CPtr 段)在上一分支已全部返回,二者不叠加: + # 定义侧裸标记(_is_cptr_marker)与下标标记(_is_cptr_subscript)亦互斥, + # `X | t.CPtr | t.CPtr[...]` 这类混合写法本函数不建模(保持 has_cptr 旧行为)。 + if cptr_sub_lvl > 0: + if first_ty is None: + return cptr_sub_ty + res_ty_cs: llvmlite.LLVMType | t.CPtr = first_ty + wrap_i_cs: int = 0 + while wrap_i_cs < cptr_sub_lvl: + res_ty_cs = llvmlite.Ptr(pool, res_ty_cs) + wrap_i_cs += 1 + return res_ty_cs if has_cvoid != 0: # t.CVoid | t.State:State 是装饰器标记(编译侧忽略),t.CVoid → void。 # 此前缺此分支 → first_ty=None → 返回 None → 跨模块函数返回类型