From d46fdf152a8edfc8b07bdcc74f8cfb2a9d4abbfb Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 17 Oct 2026 19:13:08 +0800 Subject: [PATCH] =?UTF-8?q?refactor(struct):=20AST=20vtable=20=E6=8B=B7?= =?UTF-8?q?=E8=B4=9D=E6=94=B9=E4=B8=BA=E6=8C=89=20uint64=20=E6=A7=BD?= =?UTF-8?q?=E6=98=BE=E5=BC=8F=E8=AF=BB=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原写法 *dst_c = vt_ptr_c 把 i8* 赋给结构体类型的左值,TPC 会以「非基本类型禁止指针隐式转值类型」报错。新增 _copy_ast_vtable(src, dst):把两侧 AST 节点都视作一个 uint64 槽(src_vt[0] -> dst_vt[0]),语义不变(只复制首字段 vptr),Constant 与 List 两条深拷贝路径统一调用它。 --- App/lib/core/Gen/StructGenMixin.vp | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 8842409..f4f396c 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -1,4 +1,3 @@ -import t, c from stdint import * import memhub import string @@ -2498,6 +2497,20 @@ novirtual class StructGenMixin: struct_entry.FieldCount = idx + 1 return idx + def _copy_ast_vtable(self, src: ast.AST*, dst: ast.AST*) -> None: + """Copy the vtable word (field 0) from one AST node to another. + + AST classes are @t.CVTable: virtual calls such as kind() dispatch through the first + field. Both nodes are viewed as a single uint64 slot, which reads better than assigning + a pointer to a struct-typed lvalue (TPC rejects that implicit pointer -> struct value + conversion; it also once emitted no load/store at all, leaving vptr=0 and crashing + kind() with `load i8*, i8** [0 + 0]`). + """ + if src is None or dst is None: return + src_vt: uint64* = (uint64*)(src) + dst_vt: uint64* = (uint64*)(dst) + dst_vt[0] = src_vt[0] + def _deep_copy_default_ast(self, pool: memhub.MemBuddy*, default_val: ast.AST*) -> ast.AST*: """Deep-copy a field default AST (constants and lists) into the perm pool.""" if default_val is None: return None @@ -2507,10 +2520,8 @@ novirtual class StructGenMixin: dst_c: ast.Constant* = ast.Constant*(pool.alloc_tagged(ast.Constant.__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_641)) if dst_c is None: return None string.memset(dst_c, 0, ast.Constant.__sizeof__()) - # Copy the vtable pointer (AST is @t.CVTable; kind() virtual calls depend on the first-field vptr); must match _deep_copy_list_ast (c.Deref read + c.DerefAs write). - # The old form silently failed under TPC: no load-vptr instruction and no store emitted, leaving vptr=0, so a later node.kind() reads `load i8*, i8** [0 + 0]` -> ASan READ at 0x0. Evidence: ViperC\output\b5883dc9c114a604.ll:10382-10384. - vt_ptr_c: t.CPtr = t.CPtr(*((uint64*)(default_val))) - c.DerefAs(dst_c, vt_ptr_c) + # Copy the vtable pointer (AST is @t.CVTable; virtual kind() dispatches through field 0). + self._copy_ast_vtable(default_val, dst_c) # Copy position fields (for error location). dst_c.lineno = src_c.lineno dst_c.col_offset = src_c.col_offset @@ -2534,9 +2545,8 @@ novirtual class StructGenMixin: dst_l: ast.List* = ast.List*(pool.alloc(ast.List.__sizeof__())) if dst_l is None: return None string.memset(dst_l, 0, ast.List.__sizeof__()) - # Copy the vtable pointer (AST is @t.CVTable; kind() virtual calls depend on the first-field vptr). - vt_l: t.CPtr = t.CPtr(*((uint64*)(default_val))) - c.DerefAs(dst_l, vt_l) + # Copy the vtable pointer (same requirement as the Constant copy above). + self._copy_ast_vtable(default_val, dst_l) # Copy position fields and ctx. dst_l.lineno = src_l.lineno dst_l.col_offset = src_l.col_offset @@ -2547,7 +2557,7 @@ novirtual class StructGenMixin: src_elts_l: list[ast.AST*]* = src_l.elts if src_elts_l is not None: cnt_l: sizet = len(src_elts_l) - lst_copy_l: list[ast.AST*]* = (list[ast.AST*] | t.CPtr)(pool.alloc_tagged(list[ast.AST*].__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_693)) + lst_copy_l: list[ast.AST*]* = list[ast.AST*]*(pool.alloc_tagged(list[ast.AST*].__sizeof__(), memhub.DETSITE_STRUCTGENMIXIN_693)) if lst_copy_l is not None: string.memset(lst_copy_l, 0, list[ast.AST*].__sizeof__()) # The count must never be fixed from the source count up front; it must be settled by the actual written slot count (written_l below). @@ -2557,7 +2567,7 @@ novirtual class StructGenMixin: elem_sz_l: sizet = 8 lst_copy_l.__elem_size__ = elem_sz_l lst_copy_l.__pool__ = None - data_l: t.CPtr = pool.alloc_tagged(cnt_l * elem_sz_l, memhub.DETSITE_STRUCTGENMIXIN_698) + data_l: rawptr = pool.alloc_tagged(cnt_l * elem_sz_l, memhub.DETSITE_STRUCTGENMIXIN_698) if data_l is not None: string.memset(data_l, 0, cnt_l * elem_sz_l) lst_copy_l.__data__ = data_l