diff --git a/App/lib/Stub/StubGen/VpiReader.vp b/App/lib/Stub/StubGen/VpiReader.vp index f7d9553..21d7c1a 100644 --- a/App/lib/Stub/StubGen/VpiReader.vp +++ b/App/lib/Stub/StubGen/VpiReader.vp @@ -1281,6 +1281,47 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy*, """解析单个类型段为 LLVMType,无法识别返回 None""" if seg is None or pool is None: return None + # 【指针实参唯一规范形 `T*`(用户定口径,2026-09-30)】`rawptr[T]` ≡ + # `t.CPtr[T]` ≡ `T | t.CPtr` ⇒ 一律折叠为 `T*`(见 TypeGenResolve + # ._norm_type_arg_name 规则 1.0/1.5)。为什么必须在此补:_subst_template_arg + # 会用**特化类名 `[...]` 内的已归一实参文本**(如 "{sha}.AST*")替换模板注解 + # 里的 T,替换结果再送到本函数解析;本函数此前只认 `rawptr[` / `t.CPtr[` / + # 裸 `t.CPtr`,遇到 `{sha}.AST*` 会落到下方 strrchr('.') 取末段得 "AST*" → + # 查 struct miss → **静默**返回 None(跨模块参数/返回宽度被悄悄收窄)。 + # 现场:Test/AstTest/App/main.vp:21 的 `list[ast.AST | t.CPtr] | t.CPtr` 特化链。 + # 最小改法:把尾随 `*`(先跳尾随空格)按层数重写回**已存在**的 `rawptr[...]` + # 文本形再走原分支(与 `rawptr[` 分支逐字等价,不新写 Ptr 包装逻辑): + # "{sha}.AST*" → "rawptr[{sha}.AST]";"i8**" → "rawptr[rawptr[i8]]"。 + seg_len_ps: sizet = string.strlen(seg) + star_end_ps: sizet = seg_len_ps + while star_end_ps > 0 and seg[star_end_ps - 1] == ' ': + star_end_ps -= 1 + star_cnt_ps: sizet = 0 + star_base_end_ps: sizet = star_end_ps + while star_base_end_ps > 0 and seg[star_base_end_ps - 1] == '*': + star_cnt_ps += 1 + star_base_end_ps -= 1 + if star_cnt_ps > 0 and star_base_end_ps > 0: + # 基数段长度 = star_base_end_ps;每层 "rawptr[" + "]" 共 8 字符 + star_buf_len_ps: sizet = star_base_end_ps + 8 * star_cnt_ps + 1 + star_buf_ps: bytes = stdlib.malloc(star_buf_len_ps) + if star_buf_ps is None: + return None + star_buf_ps[0] = '\0' + star_k_ps: sizet = 0 + while star_k_ps < star_cnt_ps: + string.strcat(star_buf_ps, "rawptr[") + star_k_ps += 1 + star_off_ps: sizet = string.strlen(star_buf_ps) + string.strncpy(star_buf_ps + star_off_ps, seg, star_base_end_ps) + star_buf_ps[star_off_ps + star_base_end_ps] = '\0' + star_k_ps = 0 + while star_k_ps < star_cnt_ps: + string.strcat(star_buf_ps, "]") + star_k_ps += 1 + star_ty_ps: llvmlite.LLVMType* = _resolve_single_vpi_type_str(pool, star_buf_ps) + stdlib.free(star_buf_ps) + return star_ty_ps i8_ty: llvmlite.LLVMType* = llvmlite.Int8(pool) i8_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i8_ty) # 内置别名 diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index ed9b438..6e3477f 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -3495,7 +3495,11 @@ novirtual class AssignGenMixin: e: sizet = i while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1 - while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'): + # 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态 + # `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与 + # ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径; + # 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。 + while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1 seglen = e - s if seglen > 0 and seglen < 256: @@ -3510,7 +3514,11 @@ novirtual class AssignGenMixin: e: sizet = alen while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1 - while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'): + # 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态 + # `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与 + # ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径; + # 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。 + while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1 seglen: sizet = e - s if seglen > 0 and seglen < 256: diff --git a/App/lib/core/Gen/Call/CallGenCtor.vp b/App/lib/core/Gen/Call/CallGenCtor.vp index 2d72794..381f3d1 100644 --- a/App/lib/core/Gen/Call/CallGenCtor.vp +++ b/App/lib/core/Gen/Call/CallGenCtor.vp @@ -776,6 +776,50 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase): return result # 泛型类构造调用辅助 list[str](pool, 8):复用 translate_list_literal 的构造逻辑——alloca tmp → 翻译 cl.args 为参数指针数组 → __new__(tmp,*args) → __before_init__ → __init__ → 返回 storage_ptr。必须独立于 translate_call 普通函数路径:spec_name 是类名不是函数名,走裸名会被 _lookup_mod_sha1 的 star import 回退误导 + # ============================================================ + # _ptr_layer_type_arg - 构造推断类型实参的指针层补足(与注解侧同票名) + # + # 口径来源(用户定,2026-09-30):指针类型实参**唯一规范形是 `T*`** + # (rawptr[T] ≡ t.CPtr[T] ≡ T | t.CPtr;见 TypeGenResolve._norm_type_arg_name)。 + # 为什么必须补:_infer_generic_ctor_type_arg 的 Name/Attribute 分支把 + # 「注解类名」(AnnotClassName)直接当泛型类型实参返回,而 + # TypeGenResolve.extract_class_name_from_annotation 对 `rawptr[AST]` 只返回 + # pointee "AST"、对 `AST | t.CPtr` 也只返回 "AST"(丢指针层)⇒ 构造侧推出 + # `{sha}.AST`,注解侧(Box[rawptr[AST]] / Box[AST | t.CPtr])归一形却是 + # `{sha}.AST*`,同一 `Box(x)` 隐式特化会分裂成两个结构体(undefined symbol / + # 同布局两 struct)。 + # 判据:变量/字段的**值类型**指针层级由 Spec.PtrLevel 承载(`x: AST` 为 0; + # `x: rawptr[AST]` / `x: AST | t.CPtr` 为 1),即"注解本身是指针形"。 + # 顺序:**先经 _norm_type_arg_name 归一再加 `*`** —— 裸名 "AST*" 不再是可查 + # 类名,_norm 无法给它补 SHA1 前缀 ⇒ 会与 "{sha}.AST*" 分裂;故必须先归一成 + # `{定义模块SHA1}.{类}` 再补层。仅当归一结果确为类名(带 SHA1 前缀)时补; + # 非类名(typedef/内置标量/裸指针标记 CPtr/泛型形参,名域即自身、无 `X*` + # 归一形)原样返回,不引入新分裂。 + # ============================================================ + def _ptr_layer_type_arg(self, pool: memhub.MemBuddy*, + cls_nm: str, ptr_level: int, + from_imports: str) -> str: + """注解类名 → 与注解侧同票名的类型实参文本(按 PtrLevel 补 `*`,非类名原样)""" + if cls_nm is None: + return None + res_pl: str = self.Gen.TypeGenResolve._norm_type_arg_name(pool, cls_nm, from_imports) + if res_pl is None: + res_pl = cls_nm + if ptr_level <= 0: + return res_pl + # 非类名(无 SHA1 前缀,如 typedef "VOIDPTR" / 裸标记 "CPtr")不补指针层 + if self.Gen.TypeGenResolve._is_sha1_prefixed(res_pl) == 0: + return res_pl + # 已带尾随 `*`(如泛型上下文 resolve_generic_type_name 已给指针层)不再叠加 + res_len_pl: sizet = string.strlen(res_pl) + if res_len_pl > 0 and res_pl[res_len_pl - 1] == '*': + return res_pl + lvl_pl: int = ptr_level + while lvl_pl > 0: + res_pl = self.Gen.TypeGenResolve._ptr_star_text(pool, res_pl) + lvl_pl -= 1 + return res_pl + # ============================================================ def _infer_generic_ctor_type_arg(self, pool: memhub.MemBuddy*, arg_node: ast.AST*, @@ -847,12 +891,30 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase): if nm_i.id is not None and trans is not None: ve_i: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm_i.id) if ve_i is not None: - return self.Gen.VarGen.var_annot_class_name(ve_i) + # 注解类名经 _ptr_layer_type_arg:走 _norm_type_arg_name 归一后, + # 变量值类型为指针(Spec.PtrLevel>0,如 `x: rawptr[AST]` / + # `x: AST | t.CPtr`)时补足 `*`,与注解侧归一形 `{sha}.AST*` 同票名。 + cn_nm_i: str = self.Gen.VarGen.var_annot_class_name(ve_i) + pl_nm_i: int = 0 + if ve_i.Spec is not None: + pl_nm_i = ve_i.Spec.PtrLevel + return self._ptr_layer_type_arg(pool, cn_nm_i, pl_nm_i, trans._from_imports) return None # 属性(Attribute)实参:取字段注解类名(ExprGenAttr 沿属性链递归解析, # 同样只查表)。取不到 ⇒ None ⇒ 调用方 fail-fast。 if k == ast.ASTKind.Attribute: - return self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, arg_node) + # 同上:字段值类型为指针时(FieldEntry.Spec.PtrLevel>0,如字段注解 + # `AST | t.CPtr`)补足 `*`。_resolve_attr_field_entry 为只读非 fatal + # 解析,仅支持单层基座;更深的属性链取不到 PtrLevel 时保持原样(原行为)。 + cn_at_i: str = self.Gen.ExprGenAttr._resolve_attr_field_class(pool, trans, arg_node) + pl_at_i: int = 0 + fe_at_i: GenTypes.FieldEntry* = self.Gen.ExprGenAttr._resolve_attr_field_entry(pool, trans, arg_node) + if fe_at_i is not None and fe_at_i.Spec is not None: + pl_at_i = fe_at_i.Spec.PtrLevel + fi_at_i: str = None + if trans is not None: + fi_at_i = trans._from_imports + return self._ptr_layer_type_arg(pool, cn_at_i, pl_at_i, fi_at_i) return None def _annotation_refers_type_param(self, node: ast.AST*, diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index 03d6de6..0ba744f 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -610,12 +610,18 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase): return llvmlite.const_int64(pool, 8) # 归一后的指针唯一规范形 `T*`(TypeGenResolve._norm_type_arg_name # 的指针书写归一:rawptr[T] ≡ t.CPtr[T] ≡ T|t.CPtr ≡ T*) - # ⇒ 尾随 '*' 一律按指针大小 8。缺这条时 `T.__sizeof__()` - # 的实参串 `{sha}.AST*` 既不命中 '|' 也不命中 'rawptr[', - # 落回把接收者 Name(T) 当实例翻译 → NameError: 未定义名称 'T' - # (现场 Test/AstTest/App/main.vp:21 特化 list[ast.AST|t.CPtr] - # → includes/_list.vp:36 `self.__elem_size__ = T.__sizeof__()`)。 - if string.strrchr(sz_cls_name, '*') is not None: + # ⇒ **尾随** '*' 一律按指针大小 8(多级 `**` 同为 8)。 + # 判据必须是"尾随",不能用 strrchr:接收者是 + # `list[{sha}.AST*]`(元素为指针的容器)时串内也含 '*', + # strrchr 会把它误判成指针并返回 8(容器大小 ≠ 8)。 + # 缺本判据时 `T.__sizeof__()` 的实参串 `{sha}.AST*` 既不命中 + # '|' 也不命中 'rawptr[',落回把接收者 Name(T) 当实例翻译 → + # NameError: 未定义名称 'T'(现场 Test/AstTest/App/main.vp:21 + # 特化 list[ast.AST|t.CPtr] → includes/_list.vp:36)。 + _sz_len_s: sizet = string.strlen(sz_cls_name) + while _sz_len_s > 0 and sz_cls_name[_sz_len_s - 1] == ' ': + _sz_len_s -= 1 + if _sz_len_s > 0 and sz_cls_name[_sz_len_s - 1] == '*': return llvmlite.const_int64(pool, 8) # 保存完整名(含模块前缀)供模块归属精确匹配:去前缀后 # get_struct_type 裸名全局匹配会误命中跨模块同名类 diff --git a/App/lib/core/Gen/Expr/ExprGenCls.vp b/App/lib/core/Gen/Expr/ExprGenCls.vp index 9024ce3..6ba331e 100644 --- a/App/lib/core/Gen/Expr/ExprGenCls.vp +++ b/App/lib/core/Gen/Expr/ExprGenCls.vp @@ -414,7 +414,11 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase): e: sizet = i while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1 - while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'): + # 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态 + # `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与 + # ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径; + # 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。 + while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1 seglen: sizet = e - s if seglen > 0 and seglen < 256: @@ -435,7 +439,11 @@ novirtual class ExprGenCls(ExprGenBase.ExprGenBase): e: sizet = alen while s < e and (annot[s] == ' ' or annot[s] == '\'' or annot[s] == '"'): s += 1 - while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"'): + # 段尾另需剥 '*':实参归一后段文本是 `{sha}.AST*`(旧形态 + # `{sha}.AST | t.CPtr` 按 '|' 分段能落到 `{sha}.AST` 命中),与 + # ExprGenName._list_elem_obj_ty_from_struct_name 的尾剔除同口径; + # 不剥则 find_struct 落空 → 返回 None → 链式 .get(0).kind() 分派退化。 + while e > s and (annot[e - 1] == ' ' or annot[e - 1] == '\'' or annot[e - 1] == '"' or annot[e - 1] == '*'): e -= 1 seglen: sizet = e - s if seglen > 0 and seglen < 256: