refactor(struct): AST vtable 拷贝改为按 uint64 槽显式读写

原写法 *dst_c = vt_ptr_c 把 i8* 赋给结构体类型的左值,TPC 会以「非基本类型禁止指针隐式转值类型」报错。新增 _copy_ast_vtable(src, dst):把两侧 AST 节点都视作一个 uint64 槽(src_vt[0] -> dst_vt[0]),语义不变(只复制首字段 vptr),Constant 与 List 两条深拷贝路径统一调用它。
This commit is contained in:
TermiNexus committed 2026-10-17 19:13:08 +08:00
1 parent 3e1e1fc1b3
commit d46fdf152a
1 file changed
+20 -10
+20 -10
View File
@@ -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