fix(gen,renum): 指针操作符目标写入落盘 + REnum tag 三侧口径统一,修 kind() CastError

- AssignGenMixin: 补 UnaryOp(Deref) 赋值目标分支(*p = v / *&x = *&y),非 Deref 的 &x/-x/~x fail-fast;*p += v 真正实现;Tuple/List 目标合并

- CallGenDispatch: kind() 改四级 REnum 判据(变体表 / struct __tag / pointee 带 __tag / X.field 声明类型反推);修正 build_load 第一参数误传指针类型 rk_tag_p(生成 load i32*, i32* 使返回值 Ty 为 i32*,导致 CallGenLib.vp:355 vk_a: int = val.Ty.kind() 报指针隐式转整数的 CastError),rk_load 分支同步改 rk_ty.Pointee

- CallGenCtor / MatchGenMixin: __tag 读/写/比统一取字段声明类型 FieldEntry.Ty

- Test/FieldReadTest: 字段读取探针
This commit is contained in:
TermiNexus committed 2026-09-27 00:19:44 +08:00
1 parent bcf66edebe
commit f0309e4904
6 files changed
+323 -35

No files matched your search

+75 -3
View File
@@ -1534,9 +1534,47 @@ novirtual class AssignGenMixin:
llvmlite.build_store(builder, result, elem_ptr, vol_sa)
return 0
# UnaryOp 目标: *p += v —— 解引用增强赋值。
# 目标节点是 UnaryOp(Deref)(SetStoreContext 不改 UnaryOp 的 ctx),
# 与 `*p = v` 同一形态;流程同 Subscript 分支:
# 操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。
if tk == ast.ASTKind.UnaryOp:
uo_ag: ast.UnaryOp* = ast.UnaryOp*(target)
if uo_ag is None or uo_ag.operand is None:
self.Gen.TypeGenErr.fatal_error(target, "增量赋值目标单目运算缺少操作数", "TypeError")
if uo_ag.op != ast.OpKind.Deref:
self.Gen.TypeGenErr.fatal_error(target,
"增量赋值目标只支持单目 '*' 解引用(&x / -x / ~x 不是合法左值)", "SyntaxError")
pool: memhub.MemBuddy* = trans.Pool
builder: llvmlite.IRBuilder* = trans._cur_builder
mod: llvmlite.LLVMModule* = trans.Module
ptr_ag: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_ag.operand, None, 0, trans)
if ptr_ag is None or ptr_ag.Ty is None \
or self.Gen.ExprGenBase.is_ptr_type(ptr_ag.Ty) == 0:
self.Gen.TypeGenErr.fatal_error(target,
"增量赋值 '*' 的操作数必须是指针类型(类型推断退化)", "TypeError")
ty_ag: llvmlite.LLVMType* = ptr_ag.Ty.Pointee
if ty_ag is None:
self.Gen.TypeGenErr.fatal_error(target,
"解引用增量赋值的目标类型缺失(pointee 为空)", "TypeError")
old_ag: llvmlite.Value* = llvmlite.build_load(builder, ty_ag, ptr_ag, 0)
if old_ag is None:
self.Gen.TypeGenErr.fatal_error(target, "无法加载解引用当前值", "TypeError")
rhs_ag: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, aug.value, None, 0, trans)
if rhs_ag is None:
self.Gen.TypeGenErr.fatal_error(target, "rhs 翻译失败", "TypeError")
res_ag: llvmlite.Value* = self._apply_aug_op(pool, builder, aug.op, old_ag, rhs_ag, node, trans, target)
if res_ag is None:
self.Gen.TypeGenErr.fatal_error(target, f"解引用二元运算失败 op={aug.op}", "TypeError")
res_ag = self.Gen.ExprGenBase.coerce_to_type(builder, res_ag, ty_ag, node, trans)
if res_ag is None:
self.Gen.TypeGenErr.fatal_error(target, "类型对齐失败", "TypeError")
llvmlite.build_store(builder, res_ag, ptr_ag, 0)
return 0
if tk != ast.ASTKind.Name:
# fail-fast: 不支持的 AugAssign 目标类型
self.Gen.TypeGenErr.fatal_error(target, "AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript", "TypeError")
self.Gen.TypeGenErr.fatal_error(target, "AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用", "TypeError")
nm: ast.Name* = ast.Name*(target)
if nm is None or nm.id is None:
@@ -2700,10 +2738,13 @@ novirtual class AssignGenMixin:
tk: int = target.kind()
# Tuple 赋值: a, b = f() 多返回值解包
# Tuple/List 赋值: a, b = f() / [a, b] = f() 多返回值解包
# rhs_val 是匿名 struct 值(函数返回 tuple[T1,T2] 打包)→ alloca +
# store + GEP 逐字段 load → store 到各 Name 目标。
if tk == ast.ASTKind.Tuple:
# ast.List 与 ast.Tuple 字段表逐字相同(Nodes.py:119-120 均为
# ('elts','ctx')),故按 Tuple 视图统一取 elts;此前只认 Tuple ⇒
# `[a, b] = f()` 落入无分支区被静默丢弃(不落盘、不报错)。
if tk == ast.ASTKind.Tuple or tk == ast.ASTKind.List:
tgt_tup: ast.Tuple* = ast.Tuple*(target)
tgt_elts: list[ast.AST*]* = tgt_tup.elts
# 守卫:rhs 不是 struct(tuple 打包)时跳过本 target
@@ -2887,6 +2928,37 @@ novirtual class AssignGenMixin:
self.Gen.TypeGenErr.fatal_error(target, "subscript ptr is None")
continue
# UnaryOp 目标: *p = val —— 解引用写入(与读侧同一节点形态)。
# 解析链:语句级 Assign 走 vast_c.c ParseSimpleStmt 的
# ParseExprList + SetStoreContext;`*p` 由 ParseFactor 产出
# UnaryOp(Deref)(与 `*p` 读侧完全同形),而 SetStoreContext 只改
# Attribute/Subscript/Name/Tuple/List/Starred 的 ctx,**不改 UnaryOp**
# ⇒ 赋值目标就是 ctx=Load 的 UnaryOp(Deref)。
# 此前目标分派(Tuple/Subscript/Attribute/Name)无 UnaryOp 分支且无兜底
# ⇒ 该语句被整条静默丢弃(既不落盘也不报错):memhub 的块头
# `(order<<1)|1`、空闲链 next、canary 三处写入全不发射 ⇒ 块头恒为 0
# (free 被拒)、free_lists 链读到未初始化 0xBE、池单调漏满 ⇒ 1 GiB OOM。
# `&x`/`-x` 作目标不是合法左值,故除 Deref 外一律 fail-fast。
if tk == ast.ASTKind.UnaryOp:
uo_w: ast.UnaryOp* = ast.UnaryOp*(target)
if uo_w is None or uo_w.operand is None:
self.Gen.TypeGenErr.fatal_error(target, "赋值目标单目运算缺少操作数", "TypeError")
if uo_w.op != ast.OpKind.Deref:
self.Gen.TypeGenErr.fatal_error(target,
"赋值目标只支持单目 '*' 解引用(&x / -x / ~x 不是合法左值)", "SyntaxError")
ptr_w: llvmlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_w.operand, None, 0, trans)
if ptr_w is None or ptr_w.Ty is None \
or self.Gen.ExprGenBase.is_ptr_type(ptr_w.Ty) == 0:
self.Gen.TypeGenErr.fatal_error(target,
"单目 '*' 赋值的操作数必须是指针类型(类型推断退化)", "TypeError")
pointee_w: llvmlite.LLVMType* = ptr_w.Ty.Pointee
if pointee_w is None:
self.Gen.TypeGenErr.fatal_error(target,
"解引用写入的目标类型缺失(pointee 为空)", "TypeError")
sv_w: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, pointee_w, node, trans)
llvmlite.build_store(builder, sv_w, ptr_w)
continue
# Attribute 赋值: obj.field = val
if tk == ast.ASTKind.Attribute:
# property setter 拦截: obj.attr = v 且 attr 是 @x.setter 方法时,
+9 -6
View File
@@ -363,16 +363,19 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
llvmlite.build_store(builder, zero, tmp)
# 写入 __tag 字段(索引 0)
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
# tag 的类型取 __tag 的**字段声明类型**(FieldEntry.Ty),与 kind()
# (CallGenDispatch)和 match 读 tag(MatchGenMixin)共用同一真源:
# 常量与 GEP 必须同类型,否则将来 __tag 换宽度会出现"i32 常量存进 i64 槽"
# 这种静默错配(此前常量硬编码 i32、GEP 用字段类型,两者口径不一致)。
tag_field: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(renum_entry, 0)
tag_field_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
if tag_field is not None and tag_field.Ty is not None:
tag_field_ty = tag_field.Ty
tag_name_buf: str = pool.alloc(32)
if tag_name_buf is not None:
viperlib.snprintf(tag_name_buf, 32, "%d", tag_value)
tag_const: llvmlite.Value* = llvmlite.ConstInt(pool, i32_ty, tag_value,
tag_const: llvmlite.Value* = llvmlite.ConstInt(pool, tag_field_ty, tag_value,
tag_name_buf if tag_name_buf is not None else "0")
tag_field: GenTypes.FieldEntry* = self.Gen.StructGen._get_field_entry(renum_entry, 0)
tag_field_ty: llvmlite.LLVMType* = i32_ty
if tag_field is not None and tag_field.Ty is not None:
tag_field_ty = tag_field.Ty
tag_ptr: llvmlite.Value* = llvmlite.build_gep_struct(builder, renum_ty, tag_field_ty, tmp, 0)
if tag_ptr is not None:
llvmlite.build_store(builder, tag_const, tag_ptr)
+143 -20
View File
@@ -8,7 +8,6 @@ import ast
import llvmlite
import memhub
import string
import stdio
import stdlib
import sys
import viperlib
@@ -19,10 +18,10 @@ import lib.core.Gen.Type.TypeSpec as TSpec
import lib.Stub.StubMerger as StubMerger
import lib.core.CompilerFactory as CompilerFactory
from . import CallGenBase
import lib.core.Gen.GenApi as GenApi
# ============================================================
import lib.core.Gen.GenApi as GenApi
# ============================================================
# CallGenDispatch - CallGenMixin 拆分(阶段 C):translate_call 检测段
#
# 继承 CallGenBase(1 级,TPV self 调用仅支持 1 级父类)。
@@ -941,10 +940,18 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
translate_call 会误判"未命中"继续走跨模块函数路径(把对象名
当模块名查 SHA1 → "跨模块调用 'buf.__before_init__' 找不到模块 SHA1")。
"""
# REnum.kind() 编译期转 __tag 读取(位置 0 i32):
# REnum.kind() 编译期转 __tag 读取(字段偏移 0;类型取 __tag 的字段声明类型):
# REnum 无 kind 方法(源码无、编译器不生成),此前 .kind() 被方法查找
# 兜底误绑到 ast.AST.kind(this 偏移,语义错误)。此处直接翻译为
# bitcast obj 到 i32* + load —— 零函数调用,返回变体 tag。
# bitcast obj 到 __tag* + load —— 零函数调用,返回变体 tag。
# REnum 判据四条并列(缺一即整条分支被跳过 ⇒ .kind() 掉进通用调用路径、
# 返回类型落空 ⇒ 报「指针隐式转整数」):
# ① 变体表 _is_renum_class_name ② struct 带 __tag:_is_renum_by_entry
# ③ pointee 结构体带 __tag(类型版,不需要类名)
# ④ 接收者 `X.field` 时按 X 的注解 + 字段声明类型反推 pointee 类名
# ③④ 的存在理由:变体表是 per-Generator 实例字段(ClassGenEnum:811-816:
# InitClassGen 每个 Generator 重置),跨模块 stub 路径又明确跳过 REnum 注册
# (VpiReader:2637-2643);且字段访问可能把类型整体退化掉。
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
rk_at: ast.Attribute* = ast.Attribute*(cl.func)
if rk_at is not None and rk_at.attr is not None and string.strcmp(rk_at.attr, "kind") == 0:
@@ -953,20 +960,54 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
rk_ty: llvmlite.LLVMType* = rk_obj.Ty
rk_load: int = 0
rk_cls: str = None
rk_pointee: llvmlite.LLVMType* = None
# rk_is_rn:是否已判定为 REnum。与 rk_cls 解耦——判据不再只依赖类名/变体表。
rk_is_rn: int = 0
# rk_tag_ty:kind() 的返回类型真源 = __tag 的**字段声明类型**
# (FieldEntry.Ty)。默认先放 i32 只为"取不到字段条目"时的等效值
# ——__tag 全仓只有一处写入(ClassGenEnum:1009
# add_field(entry, "__tag", Int32)),故取不到时 i32 必等价;
# 取到则以字段声明类型为准,将来 __tag 换宽度不会静默失配。
rk_tag_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
if self.Gen.ExprGenBase.is_ptr_type(rk_ty) != 0:
rk_pointee: llvmlite.LLVMType* = rk_ty.Pointee
rk_pointee = rk_ty.Pointee
# 联合简化双指针(Ptr(Ptr(Struct)))→ 解一层 + load
if rk_pointee is not None and self.Gen.ExprGenBase.is_ptr_type(rk_pointee) != 0:
rk_pointee = rk_pointee.Pointee
rk_load = 1
rk_cls = self.Gen.StructGen.get_class_name_by_type(pool, rk_pointee)
if rk_cls is not None and self.Gen.ClassGenEnum._is_renum_class_name(rk_cls) == 0:
rk_cls = None
# 判据三条并列(此前只认变体表这一条):
# 1) 变体表命中:_is_renum_class_name
# 2) struct 带 __tag:_is_renum_by_entry(表缺失时的既有判据,
# 与 CallGenMixin:426-427 同口径)
# 3) 类名反查不到(跨模块 struct 未命名/名称退化)但 pointee 结构体
# 带 __tag ⇒ 必是 REnum(_is_renum_by_entry 的类型版,不需要类名)
# 背景:变体表是 per-Generator 实例字段(ClassGenEnum:811-816 注释:
# InitClassGen 每个 Generator 重置),跨模块 stub 路径又明确跳过
# REnum 注册(VpiReader:2637-2643)⇒「表里没有 LLVMType」是常态。
# 只认表会让整条 kind() 分支被跳过 ⇒ .kind() 掉进通用调用路径、返回
# 类型落空(实况:CallGenLib.vp:355 `vk_a: int = val.Ty.kind()` 报
# 「指针隐式转整数已禁止」)。
if rk_cls is not None:
if self.Gen.ClassGenEnum._is_renum_class_name(rk_cls) != 0 \
or self.Gen.ClassGenEnum._is_renum_by_entry(rk_cls) != 0:
rk_is_rn = 1
else:
rk_cls = None
# ③:类名反查不到也不要紧——直接看 pointee 结构体带不带 __tag。
# 顺带把 __tag 的字段声明类型记为 kind() 返回类型真源(第 2 层)。
rk_tag_fe: GenTypes.FieldEntry* = None
if rk_pointee is not None:
rk_tag_fe = self.Gen.StructGen.lookup_field(rk_pointee, "__tag")
if rk_is_rn == 0 and rk_tag_fe is not None:
rk_is_rn = 1
if rk_tag_fe is not None and rk_tag_fe.Ty is not None:
rk_tag_ty = rk_tag_fe.Ty
# 注解兜底:联合简化/退化后运行时类型丢失(如
# resolved: llvmlite.LLVMType* 存 i8*,pointee 非
# 结构体),用变量注解类名识别 REnum 类(kind() 语义:
# 读 __tag,bitcast 对象到 i32* + load)。
if rk_cls is None and rk_at.value is not None and rk_at.value.kind() == ast.ASTKind.Name and trans is not None:
if rk_is_rn == 0 and rk_at.value is not None and rk_at.value.kind() == ast.ASTKind.Name and trans is not None:
rk_nm: ast.Name* = ast.Name*(rk_at.value)
if rk_nm.id is not None:
rk_ve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, rk_nm.id)
@@ -986,18 +1027,100 @@ novirtual class CallGenDispatch(CallGenBase.CallGenBase):
rk_cls = rk_ad + 1
else:
rk_cls = rk_ab
if self.Gen.ClassGenEnum._is_renum_class_name(rk_cls) == 0:
# 判据同上(表 或 struct 带 __tag),不再只认变体表
if self.Gen.ClassGenEnum._is_renum_class_name(rk_cls) != 0 \
or self.Gen.ClassGenEnum._is_renum_by_entry(rk_cls) != 0:
rk_is_rn = 1
# 第 2 层:按类名取 struct 的 __tag 声明类型
rk_st_b: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(rk_cls)
if rk_st_b is not None:
rk_fe_b: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(rk_st_b, "__tag")
if rk_fe_b is not None and rk_fe_b.Ty is not None:
rk_tag_ty = rk_fe_b.Ty
else:
rk_cls = None
if rk_cls is not None:
# 第 4 层兜底:接收者是 `X.field` 形状时,运行时类型可能整体退化
# (rk_ty 不是"指向 REnum 结构体"的指针 ⇒ 上面三条判据都拿不到
# struct,只能用**声明**信息判):X 的注解类名 → struct → 该字段的
# 声明类型 → 解一层(字段访问取地址形态为 Ptr(Ptr(T)),故必要时再解
# 一层)→ pointee 类名 → 再走 表/__tag 判据。
# CallGenLib.vp:355 `vk_a: int = val.Ty.kind()` 正是此形态:
# Value.Ty 声明为 `rawptr[LLVMType]`(__values.vp:22)。
if rk_is_rn == 0 and rk_at.value is not None \
and rk_at.value.kind() == ast.ASTKind.Attribute and trans is not None:
rk_bat: ast.Attribute* = ast.Attribute*(rk_at.value)
if rk_bat is not None and rk_bat.attr is not None and rk_bat.value is not None \
and rk_bat.value.kind() == ast.ASTKind.Name:
rk_bnm: ast.Name* = ast.Name*(rk_bat.value)
if rk_bnm.id is not None:
rk_bve: GenTypes.VarEntry* = self.Gen.VarGen.lookup_var_entry(trans.SymTab, rk_bnm.id)
rk_bannot: str = self.Gen.VarGen.var_annot_class_name(rk_bve)
if rk_bannot is not None:
rk_blen: sizet = 0
while rk_bannot[rk_blen] != '\0' and rk_bannot[rk_blen] != ' ' \
and rk_bannot[rk_blen] != '|' and rk_bannot[rk_blen] != '[':
rk_blen += 1
rk_bbuf: str = pool.alloc(rk_blen + 1)
if rk_bbuf is not None:
string.strncpy(rk_bbuf, rk_bannot, rk_blen)
rk_bbuf[rk_blen] = '\0'
# 去模块前缀("llvmlite.Value" → "Value")
rk_bdot: str = string.strrchr(rk_bbuf, '.')
rk_bcls: str = rk_bbuf
if rk_bdot is not None and rk_bdot[1] != '\0':
rk_bcls = rk_bdot + 1
rk_bst: llvmlite.LLVMType* = self.Gen.StructGen.get_struct_type(rk_bcls)
if rk_bst is not None:
rk_bfe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(rk_bst, rk_bat.attr)
if rk_bfe is not None and rk_bfe.Ty is not None:
rk_fpt: llvmlite.LLVMType* = rk_bfe.Ty
if self.Gen.ExprGenBase.is_ptr_type(rk_fpt) != 0:
rk_fpt = rk_fpt.Pointee
if rk_fpt is not None \
and self.Gen.ExprGenBase.is_ptr_type(rk_fpt) != 0:
rk_fpt = rk_fpt.Pointee
if rk_fpt is not None:
rk_fcls: str = self.Gen.StructGen.get_class_name_by_type(pool, rk_fpt)
if rk_fcls is not None:
if self.Gen.ClassGenEnum._is_renum_class_name(rk_fcls) != 0 \
or self.Gen.ClassGenEnum._is_renum_by_entry(rk_fcls) != 0:
rk_is_rn = 1
rk_cls = rk_fcls
rk_ffe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(rk_fpt, "__tag")
if rk_ffe is not None:
if rk_is_rn == 0:
rk_is_rn = 1
if rk_ffe.Ty is not None:
rk_tag_ty = rk_ffe.Ty
if rk_is_rn != 0:
rk_val: llvmlite.Value* = rk_obj
if rk_load != 0:
rk_val = llvmlite.build_load(builder, rk_ty, rk_obj)
if rk_val is not None:
if hit_box is not None:
hit_box[0] = 1
rk_i32p: llvmlite.LLVMType* = llvmlite.Ptr(pool, llvmlite.Int32(pool))
rk_cast: llvmlite.Value* = llvmlite.build_bitcast(builder, rk_val, rk_i32p)
return llvmlite.build_load(builder, rk_i32p, rk_cast)
# build_load(builder, ty, ptr):ty 是**载入值**的类型
# (生成 `load <ty>, <ptr.Ty> <ptr>`,返回值 Ty = ty)。
# rk_ty 是 Ptr(Ptr(X)),要取内层指针 ⇒ ty 必须是 rk_ty.Pointee;
# 传 rk_ty 会生成 `load <ptr>, <ptr> %p` 且返回值 Ty 仍是指针。
rk_val = llvmlite.build_load(builder, rk_ty.Pointee, rk_obj)
if rk_val is None:
# fail-fast:已判定为 REnum(判据命中),双指针解一层 load 却拿不到
# 值——绝不静默跳过:跳过会让 .kind() 掉进通用调用路径、返回类型落空,
# 报错点漂到几百行外的赋值处(如 CallGenLib.vp:355)。
self.Gen.TypeGenErr.fatal_error(cl,
f"REnum.kind() 翻译失败:接收者已判定为 REnum(变体表/__tag 判据命中),"
f"但接收者指针解一层 load 失败(rk_load={rk_load})", "TypeError")
if hit_box is not None:
hit_box[0] = 1
# 第 2 层:按 __tag 的**字段声明类型**取址并 load,不再硬编码 i32。
# 宽度口径交给赋值侧的隐式转换规则(coerce_to_type):整型↔整型
# (低向高无损 / 高向低可安全降级)自动提升降级,规则不覆盖的组合
# (如 tag 为浮点/指针)才由该规则 fail-fast。
rk_tag_p: llvmlite.LLVMType* = llvmlite.Ptr(pool, rk_tag_ty)
rk_cast: llvmlite.Value* = llvmlite.build_bitcast(builder, rk_val, rk_tag_p)
# 第一参数是**载入值**类型 rk_tag_ty(不是指针 rk_tag_p):
# 传 rk_tag_p 会生成 `load i32*, i32* %p`,返回值 Ty = i32*,
# 到赋值侧 coerce_to_type 就报「指针隐式转整数已禁止」
# (CallGenLib.vp:355 `vk_a: int = val.Ty.kind()` 根因)。
# 对照 MatchGenMixin.vp:327 读 __tag 的正确写法。
return llvmlite.build_load(builder, rk_tag_ty, rk_cast)
# 检测类名静态/类方法调用: ClassName.method(args)
# 当 cl.func 是 Attribute、value 是已注册结构体类名(非普通变量)时,
# 且方法带 @staticmethod/@classmethod 元数据,走静态调用路径
+16 -6
View File
@@ -307,14 +307,24 @@ novirtual class MatchGenMixin:
if subj_ptr0 is not None:
subject_ptr = subj_ptr0
# 1. bitcast subject 到 i32* 读 __tag
i32_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
i32_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, i32_ty)
tag_ptr: llvmlite.Value* = llvmlite.build_bitcast(builder, subject_ptr, i32_ptr_ty)
# 1. bitcast subject 到 __tag* 读 __tag
# tag 的**类型**取 __tag 的字段声明类型(FieldEntry.Ty),与 kind()
# (CallGenDispatch)和变体构造共用同一真源,不再各自硬编码 i32;
# 下方 case 常量(ConstInt)同用 tag_ty ⇒ 将来 __tag 换宽度时
# 读 / 写 / 比较三侧自动跟随。(默认 i32 仅当字段条目取不到时的等效值:
# __tag 全仓唯一写入点是 ClassGenEnum:1009 add_field(entry,"__tag",Int32)。)
tag_ty: llvmlite.LLVMType* = llvmlite.Int32(pool)
tag_se: GenTypes.StructEntry* = self.Gen.StructGen.find_struct(renum_name)
if tag_se is not None and tag_se.Ty is not None:
tag_fe: GenTypes.FieldEntry* = self.Gen.StructGen.lookup_field(tag_se.Ty, "__tag")
if tag_fe is not None and tag_fe.Ty is not None:
tag_ty = tag_fe.Ty
tag_ptr_ty: llvmlite.LLVMType* = llvmlite.Ptr(pool, tag_ty)
tag_ptr: llvmlite.Value* = llvmlite.build_bitcast(builder, subject_ptr, tag_ptr_ty)
if tag_ptr is None:
# fail-fast: REnum tag 指针转换失败
self.Gen.TypeGenErr.fatal_error(node, "REnum tag 指针转换失败", "TypeError")
tag_val: llvmlite.Value* = llvmlite.build_load(builder, i32_ty, tag_ptr)
tag_val: llvmlite.Value* = llvmlite.build_load(builder, tag_ty, tag_ptr)
if tag_val is None:
# fail-fast: REnum tag 读取失败
self.Gen.TypeGenErr.fatal_error(node, "REnum tag 读取失败", "TypeError")
@@ -415,7 +425,7 @@ novirtual class MatchGenMixin:
tag_buf: str = pool.alloc(32)
if tag_buf is not None:
viperlib.snprintf(tag_buf, 32, "%d", tag)
cv = llvmlite.ConstInt(pool, i32_ty, tag,
cv = llvmlite.ConstInt(pool, tag_ty, tag,
tag_buf if tag_buf is not None else "0")
elif pk == ast.ASTKind.MatchAs:
ma: rawptr[ast.MatchAs] = rawptr[ast.MatchAs](pat)
+47
View File
@@ -0,0 +1,47 @@
import t, c
from stdint import *
import memhub
import stdio
import stdlib
# ============================================================
# 最小复现:memhub.alloc 返回的指针与块头的位置关系是否正确。
#
# 期望:alloc 返回 block + 8(块头在 p-8),块头 = (order<<1)|1(奇数);
# free(p) 后该块回到空闲链(free_count 增 1)。
# 若 p-8 处是 0、而 p 处是奇数 → alloc 返回了 block 本身(+8 没生效),
# 调用方的 memset 会正好把块头清 0(并越过 8 字节踩邻居块头)。
# ============================================================
def main() -> int:
sz: sizet = 1048576
arena: t.CPtr = stdlib.malloc(sz)
if arena == None:
stdio.printf("FAIL: arena malloc 失败\n")
return 1
mb: memhub.MemBuddy* = memhub.MemBuddy(arena, sz)
if mb == None:
stdio.printf("FAIL: MemBuddy 构造失败\n")
return 1
p: t.CPtr = mb.alloc(64)
if p == None:
stdio.printf("FAIL: alloc 失败\n")
return 1
w_m8: uint64* = (uint64*)(t.CPtr(uint64(p) - 8))
w_0: uint64* = (uint64*)(p)
hdr_m8: uint64 = w_m8[0]
hdr_0: uint64 = w_0[0]
c0: sizet = mb.free_count()
rc: int = mb.free(p)
c1: sizet = mb.free_count()
stdio.printf("p-8=0x%llX p=0x%llX free_before=%zu free_after=%zu free_rc=%d\n",
hdr_m8, hdr_0, c0, c1, rc)
if (hdr_m8 & 1) == 0:
stdio.printf("FAIL: p-8 不是奇数块头(0x%llX)——alloc 返回的不是 block+8\n", hdr_m8)
return 1
if c1 != c0 + 1:
stdio.printf("FAIL: free 未把块放回空闲链(%zu -> %zu)——块头被破坏/被拒\n", c0, c1)
return 1
stdio.printf("OK: p-8 是奇数块头,free 正常回收\n")
return 0
+33
View File
@@ -0,0 +1,33 @@
{
"$schema": "https://raw.githubusercontent.com/TermiNexus/TransPyC/main/schemas/project-schema.json",
"name": "FieldReadTest",
"version": "1.0.0",
"source_dir": "./App",
"build_dir": "./.tpv_build",
"compiler": {
"cmd": "llc",
"flags": ["-filetype=obj", "-relocation-model=pic"]
},
"linker": {
"cmd": "clang++",
"flags": ["-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition"],
"static_libs": [
"D:/msys64/clang64/lib/clang/22/lib/windows/libclang_rt.asan_dynamic-x86_64.dll.a",
"D:/msys64/clang64/lib/clang/22/lib/windows/libclang_rt.asan_dynamic_runtime_thunk-x86_64.a"
],
"output": "FieldReadTest.exe"
},
"includes": [
"../../../includes"
],
"target": {
"triple": "x86_64-pc-windows-gnu",
"datalayout": "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
},
"options": {
"slice_level": 3,
"sha1_slice_level": 1,
"target": "llvm",
"strict_mode": true
}
}