From 3329c9beaea8dace255fe07abfa5ddbe1e7336a6 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 30 Sep 2026 23:53:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(TPV/CallGenBase):=20text.ll=20=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E7=B1=BB=E5=9E=8B=E7=9A=84=E5=91=BD=E5=90=8D=E6=8C=87?= =?UTF-8?q?=E9=92=88=E4=B8=8D=E5=86=8D=E5=9D=8D=E7=BC=A9=E6=88=90=E9=80=9A?= =?UTF-8?q?=E7=94=A8=20i8*=EF=BC=8C=E4=BF=AE=20FakeDuck=20parts[0]=20?= =?UTF-8?q?=E6=8A=A5=E6=95=B4=E6=95=B0=E9=9A=90=E5=BC=8F=E8=BD=AC=E6=8C=87?= =?UTF-8?q?=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现象(TransPyV 编译 Test/FakeDuckTest): main.vp:115 p0: str = parts[0] CastError: 整数隐式转指针已禁止…出错表达式: 下标表达式 Subscript (parts = s1.split(','),s1: str,split 由 includes/_fakeduck.vp 的 _str 提供) 根因链(逐环取证,均落在产物文件上): 1. _fakeduck.vp:273 `def split(self, delimiter: str) -> list[str]*`。 2. 定义侧产物正确:temp/88/884eaabef9bd161c.text.ll:1775 define %"b0798adca340002b.list[str]"* @"884eaabef9bd161c._str.split"(...) (884eaabef9bd161c = includes/_fakeduck.vp,b0798adca340002b = includes/_list.vp) 3. 调用侧 declare 走了 vpi 注解:temp/88/884eaabef9bd161c.vpi:27 def split(self, delimiter: str) -> list[str] | t.CPtr: pass 该联合在 stub 侧解析不出 `list[str]` 这个泛型特化名 ⇒ 坍缩成 i8*(既有设计已知的 「跨模块 declare 的 list[T] 返回类型被简化」),故 _translate_fakeduck_call 里那条 「从定义模块 text.ll 读精确返回类型」的兜底(CallGenFd.vp:134-142)必须生效。 4. 但该兜底读到的 `%"b0798adca340002b.list[str]"*` 又被本函数坍缩回 i8*: CallGenBase._ParseLLVMRetTyStr 的「含 * 者一律返回通用 Ptr(Int8)」分支 (原注释:"包括 i8*, i32*, %"name"*, { i32, i8 }* 等")。 5. 于是 parts 的 alloca 退化为 i8* ⇒ parts[0] 走字节下标取到整数 ⇒ 赋给 str 报 「整数隐式转指针已禁止」(报文指不到真因,正是本问题最难查的地方)。 改动(CallGenBase._ParseLLVMRetTyStr): - 新增「命名类型 `%"name"*` / `%name*`」分支:剥引号/空格与尾随 `*`(支持多级), 先 GenApi.get_struct_type(name)、未命中再 GenApi.get_enum_base_type(name),命中即 按星号层数包 Ptr 返回真实结构体/枚举指针;查不到登记项时才保持原来的通用 Ptr(Int8) 行为(名字可能来自尚未登记的模块,不引入新的静默降级面)。 - 同步更新该函数顶部说明(原写「含 * 者返回通用 Ptr(Int8)」,现区分字面量结构体 / 命名类型 / 其余)。 - 收益面不限 FakeDuck:所有经 text.ll 取返回类型的方法调用(CallGenCall / CallGenDispatch / CallGenProp / CallGenFd 等)都从「指针一律 i8*」变为「命名类指针精确」, 调用方 alloca 与下标/字段语义不再退化。 待验证(不自行执行编译命令):用 TransPyV.exe 重跑 --project ./Test/FakeDuckTest/project.json --clean --clear-cache --rebuild-includes --traceback 预期 115 行不再报错;若仍在 parts2.get(0)(131 行)等处冒新错,请把栈贴回来。 --- App/lib/core/Gen/Call/CallGenBase.vp | 44 +++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/App/lib/core/Gen/Call/CallGenBase.vp b/App/lib/core/Gen/Call/CallGenBase.vp index 821fc60..15a0d1e 100644 --- a/App/lib/core/Gen/Call/CallGenBase.vp +++ b/App/lib/core/Gen/Call/CallGenBase.vp @@ -927,8 +927,50 @@ novirtual class CallGenBase: return llvmlite.Ptr(pool, st_ty_st) return st_ty_st + # 命名类型(%"name" / %name)带指针:先解析到真实结构体/枚举类型再包 Ptr。 + # 为什么不许坍缩成通用 i8*:返回类型是「指向类对象」时,坍缩后调用方变量 + # 的 alloca 变成 i8*,下标语义随之从「类的 __getitem__」掉到「字节下标」。 + # 现场(Test/FakeDuckTest/App/main.vp:115 `p0: str = parts[0]`): + # includes/_fakeduck.vp `def split(self, delimiter: str) -> list[str]*`, + # 定义侧 text.ll 为 + # define %".list[str]"* @"._str.split"(...) + # 本函数若把 `%".list[str]"*` 当通用指针,parts 的 alloca 即 + # 退化为 i8* ⇒ parts[0] 取到整数 ⇒ 赋给 str 报 + # 「整数隐式转指针已禁止」(报文指不到真因)。 + # 解析不到登记项时保持原「通用指针」行为(名字可能来自尚未登记的模块)。 + if s < e and buf[s] == '%': + nm_s: sizet = s + 1 + nm_e: sizet = e + nm_star: int = 0 + while nm_e > nm_s and buf[nm_e - 1] == '*': + nm_star += 1 + nm_e -= 1 + while nm_e > nm_s and (buf[nm_e - 1] == ' ' or buf[nm_e - 1] == '\t'): + nm_e -= 1 + if nm_star > 0 and nm_e > nm_s: + if buf[nm_s] == '"' and buf[nm_e - 1] == '"': + nm_s += 1 + nm_e -= 1 + if nm_e > nm_s: + nm_len: sizet = nm_e - nm_s + nm_buf: bytes = stdlib.malloc(nm_len + 1) + if nm_buf is not None: + string.strncpy(nm_buf, buf + nm_s, nm_len) + nm_buf[nm_len] = '\0' + nm_ty: llvmlite.LLVMType* = GenApi.get_struct_type(nm_buf) + if nm_ty is None: + nm_ty = GenApi.get_enum_base_type(nm_buf) + stdlib.free(nm_buf) + if nm_ty is not None: + nm_wrap: llvmlite.LLVMType* = nm_ty + nm_i: int = 0 + while nm_i < nm_star: + nm_wrap = llvmlite.Ptr(pool, nm_wrap) + nm_i += 1 + return nm_wrap + # 检查是否含 * (指针类型) → 返回 i8* (通用指针) - # 包括 i8*, i32*, %"name"*, { i32, i8 }* 等 + # 包括 i8*, i32*, { i32, i8 }*,以及上面未登记命名的 %name* ci: sizet = s while ci < e: if buf[ci] == '*':