From 882c78c67a2408f4ca460d299c69dcaf5beac18b Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 11 Aug 2026 21:21:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(gen):=20ListTest=20=E8=81=94=E5=90=88?= =?UTF-8?q?=E4=B8=8B=E6=A0=87=E4=BF=AE=E5=A4=8D=20+=20DocTest=20docstring?= =?UTF-8?q?=20=E5=AE=8C=E6=95=B4=E4=BF=9D=E7=95=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CallGenMixin: 默认参数快照无条件查询、静态类调用识别、len() 走 __len__ - TypeGenMixin/AssignGenMixin: T | t.CPtr 泛型标量元素宽度修复 - ExprGenMixin: / → __truediv__、指针判空走 icmp null、__doc__ 完整保留(登记表+跨模块符号解析) - BuildPipeline: 翻译后提取模块 docstring(预注册阶段跨模块可用) - Generator: DocKeys/DocValues/DocCount 登记表字段 --- App/lib/core/BuildPipeline.vp | 4 + App/lib/core/Gen/AssignGenMixin.vp | 50 ++++- App/lib/core/Gen/CallGenMixin.vp | 30 +-- App/lib/core/Gen/ExprGenMixin.vp | 302 +++++++++++++++++++++++++++++ App/lib/core/Gen/Generator.vp | 4 + App/lib/core/Gen/TypeGenMixin.vp | 20 +- 6 files changed, 394 insertions(+), 16 deletions(-) diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index f2088bf..71018c6 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -201,6 +201,10 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, ret: int = tr.translate(tree) if ret != 0: return None + # 提取 docstring(预注册阶段所有 App/includes 文件都被翻译, + # 保证跨模块 __doc__ 在正式翻译前已登记;查重幂等) + if gen is not None: + gen.extract_module_docstrings(tree, sha1_val) return tr diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 8fabc69..c645518 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -357,10 +357,27 @@ class AssignGenMixin: if self._annotation_contains_name(annot, "bytes") != 0: return 1 # 泛型类型参数/嵌套联合 | t.CPtr(如 _list.vp 的 elem_ptr: T | t.CPtr): - # T 特化后为具体类型(AST|t.CPtr 等指针),联合在解析时可能退化为 i8*, - # 但元素是 8 字节指针,下标必须按 8 字节步长访问—— - # 否则 list.get/append 只读写 1 字节,指针被截断 inttoptr 崩溃。 + # 先经泛型上下文解析 T 的具体类型——纯标量(int/float 等 sizeof≠8)时 + # 元素按标量自身大小(list[int] elem_size=4 匹配 i32),不能按 8 字节 + # 指针槽(否则 8 字节写 / 4 字节步长互相覆盖,pop 读到 0); + # 指针/嵌套联合/64 位标量/double(sizeof==8)才按 8 字节。 if self._is_generic_tp_or_nested(bop.left) != 0 or self._is_generic_tp_or_nested(bop.right) != 0: + gp_nm: str = None + if bop.left is not None and bop.left.kind() == ast.ASTKind.Name: + gp_left_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(bop.left) + gp_nm = gp_left_nm.id + elif bop.right is not None and bop.right.kind() == ast.ASTKind.Name: + gp_right_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(bop.right) + gp_nm = gp_right_nm.id + if gp_nm is not None: + # 经全局 gen 委托解析泛型参数(T → 具体类型名)。不能 self.GenericTpNames + # 直读(Mixin @t.NoVTable 静态绑定:字段声明在 TypeGenMixin,AssignGenMixin + # 访问会生成不存在的属性引用 → TPC fast-fail 编译崩溃) + gp_resolved: str = HandlesType.resolve_generic_type_name(gp_nm) + if gp_resolved is not None and string.strcmp(gp_resolved, gp_nm) != 0: + # 泛型参数解析为纯标量 → 元素按标量自身大小(非 8 字节指针槽) + if self._is_scalar_ty_name(gp_resolved) != 0: + return 0 return 1 # 64 位标量数值类型 | CPtr(如 CUInt64T | CPtr / CSizeT | CPtr): # 联合解析后同样退化为指针表示,元素为 8 字节(i64 或指针)。 @@ -393,6 +410,33 @@ class AssignGenMixin: return 1 return 0 + def _is_scalar_ty_name(self, ty_name: str) -> int: + """判断类型名是否为纯标量(不含指针标记),用于泛型 T 的联合元素类型判定 + + 泛型 T | t.CPtr(如 _list.vp 的 elem_ptr)经 Fix A(TypeGenMixin 联合 + 解析保留泛型标量)后 alloca 变 Ptr(标量):T 为纯标量(int/float/double/ + i64 等)时下标按标量自身大小步长,不能再按 8 字节指针槽——否则 + Ptr(标量) alloca + IsPtrElement 路径(load 标量当指针)产生非法 IR。 + 指针/联合/str/bytes 才保持 8 字节指针槽语义。 + """ + if ty_name is None: + return 0 + if string.strchr(ty_name, '|') is not None: + return 0 + if string.strchr(ty_name, '*') is not None: + return 0 + if string.strcmp(ty_name, "str") == 0: + return 0 + if string.strcmp(ty_name, "bytes") == 0: + return 0 + if string.strcmp(ty_name, "t.CPtr") == 0: + return 0 + if string.strcmp(ty_name, "CPtr") == 0: + return 0 + if string.strstr(ty_name, "Ptr") is not None: + return 0 + return 1 + def _is_generic_tp_or_nested(self, node: ast.AST | t.CPtr) -> int: """判断注解节点是否为泛型类型参数名(单字母大写,如 T)或嵌套联合(BinOp)""" if node is None: diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index a3cb447..0563464 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -2547,12 +2547,15 @@ class CallGenMixin: mdf_defaults = mdf_entry.Defaults mdf_default_count = mdf_entry.DefaultCount mdf_param_count = mdf_entry.ParamCount - if mdf_defaults is None: - mdf_gd: FuncDefaultsEntry | t.CPtr = HandlesExprCall.find_func_defaults(lookup_name) - if mdf_gd is not None: - mdf_defaults = mdf_gd.Defaults - mdf_default_count = mdf_gd.DefaultCount - mdf_param_count = mdf_gd.ParamCount + # 全局默认参数表查询无条件执行:mdf_entry 命中时(泛型特化方法注册进 + # 调用方函数表)Defaults 非 None 会短路跳过,但下方"用快照重建默认值" + # 分支依赖 mdf_gd(唯一持有 stdlib 常量快照处)——缺它必 fatal_error + # (ListTest list[int].__new__ 根因) + mdf_gd: FuncDefaultsEntry | t.CPtr = HandlesExprCall.find_func_defaults(lookup_name) + if mdf_defaults is None and mdf_gd is not None: + mdf_defaults = mdf_gd.Defaults + mdf_default_count = mdf_gd.DefaultCount + mdf_param_count = mdf_gd.ParamCount # 计算需要填充的参数(不含 self) # extra_count = 已提供的非 self 参数数量 @@ -3780,12 +3783,15 @@ class CallGenMixin: mdf_defaults = mdf_entry.Defaults mdf_default_count = mdf_entry.DefaultCount mdf_param_count = mdf_entry.ParamCount - if mdf_defaults is None: - mdf_gd: FuncDefaultsEntry | t.CPtr = HandlesExprCall.find_func_defaults(lookup_name) - if mdf_gd is not None: - mdf_defaults = mdf_gd.Defaults - mdf_default_count = mdf_gd.DefaultCount - mdf_param_count = mdf_gd.ParamCount + # 全局默认参数表查询无条件执行:mdf_entry 命中时(泛型特化方法注册进 + # 调用方函数表)Defaults 非 None 会短路跳过,但下方"用快照重建默认值" + # 分支依赖 mdf_gd(唯一持有 stdlib 常量快照处)——缺它必 fatal_error + # (ListTest list[int].__new__ 根因) + mdf_gd: FuncDefaultsEntry | t.CPtr = HandlesExprCall.find_func_defaults(lookup_name) + if mdf_defaults is None and mdf_gd is not None: + mdf_defaults = mdf_gd.Defaults + mdf_default_count = mdf_gd.DefaultCount + mdf_param_count = mdf_gd.ParamCount # 计算需要填充的参数(不含 self) # arg_count_eff = 已提供的非 self 参数数量(含关键字模式完整构建) diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index 423131a..e9e2f9e 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -26,6 +26,19 @@ import lib.Projectrans.Config as Config import lib.Projectrans.Utils as Utils +# ============================================================ +# docstring 登记表容量(__doc__ 魔法属性支持) +# +# DOC_MAX: 登记条目上限(预注册阶段所有 App/includes 文件的 +# 函数/类/方法 docstring,幂等查重) +# DOC_KEY_SZ: key "{module_sha1}.{symbol}" 缓冲宽度 +# DOC_VAL_SZ: docstring 内容缓冲宽度 +# ============================================================ +DOC_MAX: t.CDefine = 512 +DOC_KEY_SZ: t.CDefine = 192 +DOC_VAL_SZ: t.CDefine = 4096 + + # ============================================================ # ExprGenMixin - 表达式处理 Mixin(对象化 P3,wiki/14-oo-refactor.md) # @@ -51,6 +64,11 @@ import lib.Projectrans.Utils as Utils class ExprGenMixin: """表达式处理 Mixin(纯方法化迁移,无状态字段)""" + # --- docstring 登记表字段(__doc__ 魔法属性支持)--- + DocKeys: bytes + DocValues: bytes + DocCount: int + # ============================================================ # P3 迁移方法(函数体来自 HandlesExpr.vp 模块级函数, # 本模块函数调用改 self.,外部模块调用保留模块前缀) @@ -3169,6 +3187,259 @@ class ExprGenMixin: return inner_cd return None + # ============================================================ + # docstring 提取/登记/查找(__doc__ 魔法属性支持) + # + # 预注册阶段(BuildPipeline.TranslateFileGetTrans)扫描所有 + # App/includes 文件的 AST,提取函数/类/方法的 docstring 内容 + # 登记到全局表(key = "{module_sha1}.{symbol}");__doc__ 属性 + # 访问时查表,命中则当前模块内联生成字符串常量(无跨模块 IR + # 符号依赖),未命中返回 null。幂等查重,多遍翻译安全。 + # ============================================================ + + def _extract_docstring_from_body(self, body: list[ast.AST | t.CPtr] | t.CPtr) -> str: + """从函数/类体提取 docstring:body[0] 为 Expr(Constant str) 时返回内容""" + if body is None or body.__len__() == 0: + return None + first: ast.AST | t.CPtr = body.get(0) + if first is None or first.kind() != ast.ASTKind.Expr: + return None + ex: ast.Expr | t.CPtr = (ast.Expr | t.CPtr)(first) + if ex is None or ex.value is None or ex.value.kind() != ast.ASTKind.Constant: + return None + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(ex.value) + if cn is None or cn.const_kind != ast.CONST_STR: + return None + return cn.str_val + + def register_docstring(self, module_sha1: str, symbol: str, doc: str) -> None: + """登记 docstring(key="{module_sha1}.{symbol}"),查重幂等""" + if module_sha1 is None or symbol is None or doc is None: + return + if string.strlen(doc) == 0: + return + if self.DocKeys is None: + self.DocKeys = stdlib.malloc(DOC_MAX * DOC_KEY_SZ) + self.DocValues = stdlib.malloc(DOC_MAX * DOC_VAL_SZ) + if self.DocKeys is None or self.DocValues is None: + return + string.memset(self.DocKeys, 0, DOC_MAX * DOC_KEY_SZ) + string.memset(self.DocValues, 0, DOC_MAX * DOC_VAL_SZ) + if self.DocCount >= DOC_MAX: + return + ml: t.CSizeT = string.strlen(module_sha1) + sl: t.CSizeT = string.strlen(symbol) + key_buf: bytes = stdlib.malloc(ml + sl + 2) + if key_buf is None: + return + viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol) + i: int + for i in range(self.DocCount): + if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0: + stdlib.free(key_buf) + return + string.strcpy(self.DocKeys + self.DocCount * DOC_KEY_SZ, key_buf) + string.strcpy(self.DocValues + self.DocCount * DOC_VAL_SZ, doc) + self.DocCount += 1 + stdlib.free(key_buf) + + def lookup_docstring(self, module_sha1: str, symbol: str) -> str: + """查找 docstring 内容(key="{module_sha1}.{symbol}"),未找到返回 None""" + if self.DocKeys is None or module_sha1 is None or symbol is None: + return None + ml: t.CSizeT = string.strlen(module_sha1) + sl: t.CSizeT = string.strlen(symbol) + key_buf: bytes = stdlib.malloc(ml + sl + 2) + if key_buf is None: + return None + viperlib.snprintf(key_buf, ml + sl + 2, "%s.%s", module_sha1, symbol) + i: int + for i in range(self.DocCount): + if string.strcmp(self.DocKeys + i * DOC_KEY_SZ, key_buf) == 0: + stdlib.free(key_buf) + return self.DocValues + i * DOC_VAL_SZ + stdlib.free(key_buf) + return None + + def extract_module_docstrings(self, tree: ast.AST | t.CPtr, module_sha1: str) -> None: + """扫描模块 AST:登记模块级函数/类/方法的 docstring(幂等)""" + if tree is None or module_sha1 is None: + return + ch: list[ast.AST | t.CPtr] | t.CPtr = tree.children + if ch is None: + return + cn: t.CSizeT = ch.__len__() + for ci in range(cn): + child: ast.AST | t.CPtr = ch.get(ci) + if child is None: + continue + kd: int = child.kind() + if kd == ast.ASTKind.FunctionDef: + fd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(child) + if fd is not None and fd.name is not None: + fd_body: list[ast.AST | t.CPtr] | t.CPtr = fd.body + fdoc: str = self._extract_docstring_from_body(fd_body) + if fdoc is not None: + self.register_docstring(module_sha1, fd.name, fdoc) + elif kd == ast.ASTKind.ClassDef: + cd: ast.ClassDef | t.CPtr = (ast.ClassDef | t.CPtr)(child) + if cd is None or cd.name is None: + continue + cd_body0: list[ast.AST | t.CPtr] | t.CPtr = cd.body + cdoc: str = self._extract_docstring_from_body(cd_body0) + if cdoc is not None: + self.register_docstring(module_sha1, cd.name, cdoc) + # 类方法 docstring(key="{ClassName}.{method}") + cd_body: list[ast.AST | t.CPtr] | t.CPtr = cd.body + if cd_body is not None: + cbn: t.CSizeT = cd_body.__len__() + for mi in range(cbn): + m: ast.AST | t.CPtr = cd_body.get(mi) + if m is None or m.kind() != ast.ASTKind.FunctionDef: + continue + mfd: ast.FunctionDef | t.CPtr = (ast.FunctionDef | t.CPtr)(m) + if mfd is None or mfd.name is None: + continue + mdoc: str = self._extract_docstring_from_body(mfd.body) + if mdoc is not None: + cl_nl: t.CSizeT = string.strlen(cd.name) + mt_nl: t.CSizeT = string.strlen(mfd.name) + sym_buf: bytes = stdlib.malloc(cl_nl + mt_nl + 2) + if sym_buf is not None: + viperlib.snprintf(sym_buf, cl_nl + mt_nl + 2, "%s.%s", cd.name, mfd.name) + self.register_docstring(module_sha1, sym_buf, mdoc) + stdlib.free(sym_buf) + + def _resolve_doc_symbol_name(self, trans: HT.Translator | t.CPtr, name_id: str, + out_mod: str | t.CPtr, out_sym: str | t.CPtr) -> int: + """解析 Name 符号的 docstring 归属,写入 out_mod/out_sym,成功返回 1 + + 判定顺序:本地变量(函数变量)→ 结构体类(含跨模块预注册)→ 模块名。 + """ + if trans is None or name_id is None or out_mod is None or out_sym is None: + return 0 + if trans.ModuleSha1 is None: + return 0 + # 1. 本地变量(函数变量/全局变量) + if HandlesVar.lookup_var(trans.SymTab, name_id) is not None: + string.strcpy(out_mod, trans.ModuleSha1) + string.strcpy(out_sym, name_id) + return 1 + # 2. 结构体类(entry.ModuleSha1 区分跨模块预注册的类) + se: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(name_id) + if se is not None: + if se.ModuleSha1 is not None: + string.strcpy(out_mod, se.ModuleSha1) + else: + string.strcpy(out_mod, trans.ModuleSha1) + string.strcpy(out_sym, name_id) + return 1 + # 3. 模块名(模块本身,少见);用 StubMerger 模块级函数(避免 + # 跨 Mixin self 调用生成不存在符号) + mod_sha1: str = StubMerger.LookupSha1ByModName(name_id) + if mod_sha1 is None: + mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(name_id) + if mod_sha1 is not None: + string.strcpy(out_mod, mod_sha1) + string.strcpy(out_sym, name_id) + return 1 + # 4. 兜底当前模块 + string.strcpy(out_mod, trans.ModuleSha1) + string.strcpy(out_sym, name_id) + return 1 + + def _resolve_doc_symbol_attr(self, trans: HT.Translator | t.CPtr, + at: ast.Attribute | t.CPtr, + out_mod: str | t.CPtr, out_sym: str | t.CPtr) -> int: + """解析嵌套 Attribute 链的 docstring 归属,写入 out_mod/out_sym + + 支持形态:remote.remote_func / remote.RemoteClass / + remote.RemoteClass.remote_method / LocalClass.local_method 等。 + 最左 Name 判定:模块名 → 该模块 sha1;变量/类 → 类所属模块 sha1。 + """ + if trans is None or at is None or out_mod is None or out_sym is None: + return 0 + if trans.ModuleSha1 is None: + return 0 + # 收集 Attribute 链(最外层 attr 在前) + DOC_CHAIN_MAX: t.CInt = 8 + items: bytes = stdlib.malloc(DOC_CHAIN_MAX * 128) + if items is None: + return 0 + n_items: int = 0 + cur: ast.AST | t.CPtr = (ast.AST | t.CPtr)(at) + while cur is not None and cur.kind() == ast.ASTKind.Attribute and n_items < DOC_CHAIN_MAX: + cat: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cur) + if cat.attr is None: + break + string.strcpy(items + n_items * 128, cat.attr) + n_items += 1 + cur = cat.value + # 最左应为 Name + head_id: str = None + if cur is not None and cur.kind() == ast.ASTKind.Name: + hnm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(cur) + head_id = hnm.id + if head_id is None: + stdlib.free(items) + return 0 + # head 判定:模块名 or 变量/类 + is_mod: int = 0 + mod_sha1: str = None + if HandlesVar.lookup_var(trans.SymTab, head_id) is None: + mod_sha1 = StubMerger.LookupSha1ByModName(head_id) + if mod_sha1 is None: + mod_sha1 = StubMerger.LookupSha1ByModNameSuffix(head_id) + if mod_sha1 is not None: + is_mod = 1 + if is_mod != 0: + # 模块限定:symbol = items 反转(不含 head_id) + if n_items == 0: + string.strcpy(out_mod, mod_sha1) + string.strcpy(out_sym, head_id) + stdlib.free(items) + return 1 + total: t.CSizeT = 0 + j: int + for j in range(n_items): + total += string.strlen(items + j * 128) + 1 + sym_buf: bytes = stdlib.malloc(total + 1) + if sym_buf is None: + stdlib.free(items) + return 0 + sym_buf[0] = '\0' + for j in range(n_items): + if j > 0: + string.strcat(sym_buf, ".") + string.strcat(sym_buf, items + (n_items - 1 - j) * 128) + string.strcpy(out_mod, mod_sha1) + string.strcpy(out_sym, sym_buf) + stdlib.free(sym_buf) + stdlib.free(items) + return 1 + # 变量/类:symbol = head_id + '.' + items 反转 + total2: t.CSizeT = string.strlen(head_id) + for j in range(n_items): + total2 += string.strlen(items + j * 128) + 1 + sym2: bytes = stdlib.malloc(total2 + 1) + if sym2 is None: + stdlib.free(items) + return 0 + sym2[0] = '\0' + string.strcat(sym2, head_id) + for j in range(n_items): + string.strcat(sym2, ".") + string.strcat(sym2, items + (n_items - 1 - j) * 128) + se2: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct(head_id) + if se2 is not None and se2.ModuleSha1 is not None: + string.strcpy(out_mod, se2.ModuleSha1) + else: + string.strcpy(out_mod, trans.ModuleSha1) + string.strcpy(out_sym, sym2) + stdlib.free(sym2) + stdlib.free(items) + return 1 + def translate_attribute(self, builder: llvmlite.IRBuilder | t.CPtr, pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -3196,6 +3467,37 @@ class ExprGenMixin: VLogger.error(diag_buf_an, "ATTR") return None + # 魔法属性 __doc__:查询 docstring 登记表(预注册阶段从 AST 提取), + # 命中则当前模块内联生成字符串常量返回;未命中(无 docstring 的 + # 函数/类/方法)返回 null 指针(i8*)。支持本模块与跨模块符号: + # local_func / LocalClass / LocalClass.method / remote.remote_func / + # remote.RemoteClass / remote.RemoteClass.remote_method + if string.strcmp(at.attr, "__doc__") == 0: + doc_mod_buf: bytes = pool.alloc(64) + doc_sym_buf: bytes = pool.alloc(256) + if doc_mod_buf is None or doc_sym_buf is None: + return None + doc_mod_buf[0] = '\0' + doc_sym_buf[0] = '\0' + if at.value is not None and at.value.kind() == ast.ASTKind.Name: + nm_doc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if nm_doc.id is not None: + self._resolve_doc_symbol_name(trans, nm_doc.id, doc_mod_buf, doc_sym_buf) + elif at.value is not None and at.value.kind() == ast.ASTKind.Attribute: + self._resolve_doc_symbol_attr(trans, (ast.Attribute | t.CPtr)(at.value), doc_mod_buf, doc_sym_buf) + doc_val: str = None + if doc_mod_buf[0] != '\0' and doc_sym_buf[0] != '\0': + doc_val = self.lookup_docstring(doc_mod_buf, doc_sym_buf) + if doc_val is None: + i8_ty_doc: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + if i8_ty_doc is None: + return None + i8_ptr_ty_doc: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty_doc) + if i8_ptr_ty_doc is None: + return None + return llvmlite.ConstNull(pool, i8_ptr_ty_doc, "null") + return self.create_global_string(builder, pool, mod, doc_val, trans) + # 枚举成员访问: EnumName.MemberName → 常量值 # 当 value 是 Name 且 Name.id 是已注册枚举时,查找成员并返回常量 if at.value.kind() == ast.ASTKind.Name: diff --git a/App/lib/core/Gen/Generator.vp b/App/lib/core/Gen/Generator.vp index c0b69ca..04d15b5 100644 --- a/App/lib/core/Gen/Generator.vp +++ b/App/lib/core/Gen/Generator.vp @@ -130,6 +130,10 @@ class Generator(BaseGenMixin.BaseGenMixin, TypeGenMixin.TypeGenMixin, self.EnumCount = 0 # ---- AssignGenMixin 字段 ---- self.RecoverFound = 0 + # ---- ExprGenMixin docstring 字段(__doc__ 魔法属性支持)---- + self.DocKeys = None + self.DocValues = None + self.DocCount = 0 # ============================================================ diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index e480f05..45d3c79 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -1757,7 +1757,25 @@ class TypeGenMixin: other_is_name = 1 if other_ty is not None and other_is_name != 0: if HandlesStruct.find_struct_by_type(other_ty) is None: - other_ty = None + # 泛型类型参数(T→int 等)经上下文替换解析出的合法标量 + # 必须保留——"Name 且未注册 struct 即丢弃"本为拦截损坏 + # 节点解析出基础类型垃圾,但会误杀泛型替换的合法标量, + # 导致 T | t.CPtr(T=int)退化为 i8*:list[int] elem_ptr + # 按 8 字节槽写、elem_size=4 步长读 → 元素互相覆盖, + # pop 读到 0(ListTest 根因)。泛型参数名放行。 + keep_other_ty: int = 0 + if self.GenericTpNames is not None: + other_nm_x: ast.Name | t.CPtr = (ast.Name | t.CPtr)(other_node) + gtn_nx: t.CSizeT = self.GenericTpNames.__len__() + gtn_ix: t.CSizeT + for gtn_ix in range(gtn_nx): + gtn_nmx: str = self.GenericTpNames.get(gtn_ix) + if gtn_nmx is not None and other_nm_x.id is not None: + if string.strcmp(gtn_nmx, other_nm_x.id) == 0: + keep_other_ty = 1 + break + if keep_other_ty == 0: + other_ty = None if other_ty is None: # 严格模式(App 文件)下裸类名不可见时,回退到全局 # struct 注册表按类名查找。X | t.CPtr 是明确的指针包装