From f0309e49048e2280f95f5ed6071865d29f00b4fd Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sun, 27 Sep 2026 00:19:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(gen,renum):=20=E6=8C=87=E9=92=88=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E7=AC=A6=E7=9B=AE=E6=A0=87=E5=86=99=E5=85=A5=E8=90=BD?= =?UTF-8?q?=E7=9B=98=20+=20REnum=20tag=20=E4=B8=89=E4=BE=A7=E5=8F=A3?= =?UTF-8?q?=E5=BE=84=E7=BB=9F=E4=B8=80=EF=BC=8C=E4=BF=AE=20kind()=20CastEr?= =?UTF-8?q?ror?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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: 字段读取探针 --- App/lib/core/Gen/AssignGenMixin.vp | 78 ++++++++++- App/lib/core/Gen/Call/CallGenCtor.vp | 15 ++- App/lib/core/Gen/Call/CallGenDispatch.vp | 163 ++++++++++++++++++++--- App/lib/core/Gen/MatchGenMixin.vp | 22 ++- Test/FieldReadTest/App/main.vp | 47 +++++++ Test/FieldReadTest/project.json | 33 +++++ 6 files changed, 323 insertions(+), 35 deletions(-) create mode 100644 Test/FieldReadTest/App/main.vp create mode 100644 Test/FieldReadTest/project.json diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 1e83f9a..63fef14 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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 方法时, diff --git a/App/lib/core/Gen/Call/CallGenCtor.vp b/App/lib/core/Gen/Call/CallGenCtor.vp index 9b5bc6d..06788c4 100644 --- a/App/lib/core/Gen/Call/CallGenCtor.vp +++ b/App/lib/core/Gen/Call/CallGenCtor.vp @@ -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) diff --git a/App/lib/core/Gen/Call/CallGenDispatch.vp b/App/lib/core/Gen/Call/CallGenDispatch.vp index 8e194c5..d2d1140 100644 --- a/App/lib/core/Gen/Call/CallGenDispatch.vp +++ b/App/lib/core/Gen/Call/CallGenDispatch.vp @@ -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 = ty)。 + # rk_ty 是 Ptr(Ptr(X)),要取内层指针 ⇒ ty 必须是 rk_ty.Pointee; + # 传 rk_ty 会生成 `load , %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 元数据,走静态调用路径 diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index d03b304..e1d6bb9 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -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) diff --git a/Test/FieldReadTest/App/main.vp b/Test/FieldReadTest/App/main.vp new file mode 100644 index 0000000..e70783d --- /dev/null +++ b/Test/FieldReadTest/App/main.vp @@ -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 diff --git a/Test/FieldReadTest/project.json b/Test/FieldReadTest/project.json new file mode 100644 index 0000000..ce6035c --- /dev/null +++ b/Test/FieldReadTest/project.json @@ -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 + } +}