From 76f03b5987204513d0571411db752054de33dce3 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Thu, 10 Sep 2026 14:22:02 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpv):=20=E4=BF=AE=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=BA=A7=E6=B5=AE=E7=82=B9=E5=85=A8=E5=B1=80=E9=9B=B6=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E3=80=81=E8=B7=A8=E6=A8=A1=E5=9D=97=20CDefin?= =?UTF-8?q?e=20=E9=87=8D=E5=AF=BC=E5=87=BA=E3=80=81=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E5=BF=AB=E7=85=A7=E3=80=81typedef=20?= =?UTF-8?q?=E8=AF=8A=E6=96=AD=E6=82=AC=E5=9E=82=E6=8C=87=E9=92=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TranslatorGenMixin/AssignGenMixin: 模块级全局补 CONST_FLOAT 初始化分支。 此前无此分支 → init_kind 保持 0 → t.CDouble 全局一律退化成 zeroinitializer (torch 的 LCG_A/LCG_M/_rng_state 全变 0 → _next_rand() 得 0.0/0.0 = NaN → Kaiming 初始化写入全 NaN 权重 → 推理 softmax 分母 NaN → 十个概率全为 0)。 浮点值不走 AST 的 float_val(会被 pool 操作覆写),改为按变量名从当前源文件 重读恢复;_recover_cdefine_float 泛化为 _recover_float_literal。 - ExprGenCdef: 跨模块 CDefine 重导出链支持绝对导入(from pkg.sub import *), 此前只认相对 from .X → vqt6 包(__init__.vp 用 from vqt6._types import *) 里的 CDefine 全查不到,报 "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'"; 相对导入基准改用"当前模块所属包"而非模块名本身,修正子模块 from .X 拼接 (对齐 CallGenBase._lookup_reexport_from_vpi 的包名口径)。 - CallGenLeafKit: 默认参数常量快照支持一元字面量(USub/UAdd/Invert 且操作数为 整型 Constant)。此前只认 Constant/Name → ex3: t.CInt = -1 这类默认值快照为空, 跨模块重建时报 "跨模块默认参数无法重建(快照缺失)"。其余形态仍保持 kinds=0, 重建时 fail-fast,不静默兜底。 - TypeGenCType/TypeGenResolve/TypeGenBase: builtins typedef 诊断串改为自有缓冲 副本(stdlib.malloc,clear_ctypedefs 释放)。调用方常传临时段缓冲,直接存指针 会悬垂,_build_ann_fail_msg 的 snprintf 读已释放内存(ASan heap-use-after-free)。 --- App/lib/core/Gen/AssignGenMixin.vp | 47 ++++++++++++++-------- App/lib/core/Gen/Call/CallGenLeafKit.vp | 20 ++++++++++ App/lib/core/Gen/Expr/ExprGenCdef.vp | 51 +++++++++++++++++++----- App/lib/core/Gen/TranslatorGenMixin.vp | 52 ++++++++++++++++++++++++- App/lib/core/Gen/Type/TypeGenBase.vp | 8 ++++ App/lib/core/Gen/Type/TypeGenCType.vp | 39 ++++++++++++++++++- App/lib/core/Gen/Type/TypeGenResolve.vp | 24 +++++++++--- 7 files changed, 206 insertions(+), 35 deletions(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 1be6c35..2fc8eec 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -214,10 +214,14 @@ class AssignGenMixin: """检查最近一次 _recover_cdefine_float 是否成功""" return self.RecoverFound - def _recover_cdefine_float(self, name: str, out: t.CDouble | t.CPtr) -> int: - """从当前模块源文件恢复浮点 CDefine 值,写入 out[0],成功返回 1 + def _recover_float_literal(self, name: str, out: t.CDouble | t.CPtr, + require_cdefine: int) -> int: + """从当前模块源文件按变量名恢复浮点字面量,写入 out[0],成功返回 1 用输出参数传递 double,避免 TPV 跨模块 double 返回值传递 bug。 + require_cdefine=1:只认带 t.CDefine 的行(CDefine 注册路径); + =0:模块级普通浮点全局也接受(handle_module_level_var 路径, + 如 torch `LCG_M: t.CDouble = 2147483647.0`)。 """ self.RecoverFound = 0 fp: str = self.Gen.TypeGenResolve.get_current_file() @@ -252,7 +256,8 @@ class AssignGenMixin: if pos < total_len: pos += 1 - # 匹配 "name:" + # 匹配行首的 "name",其后(跳过空格)必须是 ':' 或 '=' + # (AnnAssign:`NAME: t.CDouble = 1.0`;无注解赋值:`NAME = 1.0`) if line_len < name_len + 1: continue match_ok: int = 1 @@ -263,21 +268,27 @@ class AssignGenMixin: break if match_ok == 0: continue - if buf[line_start + name_len] != ':': + sep: t.CSizeT = line_start + name_len + while sep < line_start + line_len and buf[sep] == ' ': + sep += 1 + if sep >= line_start + line_len: + continue + if buf[sep] != ':' and buf[sep] != '=': continue - # 行内包含 "t.CDefine" - has_cd: int = 0 - ci: t.CSizeT = line_start + name_len + 1 - while ci + 8 <= line_start + line_len: - if (buf[ci] == 't' and buf[ci + 1] == '.' and buf[ci + 2] == 'C' and - buf[ci + 3] == 'D' and buf[ci + 4] == 'e' and buf[ci + 5] == 'f' and - buf[ci + 6] == 'i' and buf[ci + 7] == 'n' and buf[ci + 8] == 'e'): - has_cd = 1 - break - ci += 1 - if has_cd == 0: - continue + # CDefine 路径要求行内出现 "t.CDefine";普通浮点全局路径跳过该检查 + if require_cdefine != 0: + has_cd: int = 0 + ci: t.CSizeT = line_start + name_len + 1 + while ci + 8 <= line_start + line_len: + if (buf[ci] == 't' and buf[ci + 1] == '.' and buf[ci + 2] == 'C' and + buf[ci + 3] == 'D' and buf[ci + 4] == 'e' and buf[ci + 5] == 'f' and + buf[ci + 6] == 'i' and buf[ci + 7] == 'n' and buf[ci + 8] == 'e'): + has_cd = 1 + break + ci += 1 + if has_cd == 0: + continue # 找 '=' eq: t.CSizeT = line_start @@ -313,6 +324,10 @@ class AssignGenMixin: stdlib.free(buf) return 0 + def _recover_cdefine_float(self, name: str, out: t.CDouble | t.CPtr) -> int: + """CDefine 浮点值恢复(只认带 t.CDefine 的行),见 _recover_float_literal""" + return self._recover_float_literal(name, out, 1) + # ============================================================ # _annotation_contains_name - 递归检查注解中是否包含指定名称 # diff --git a/App/lib/core/Gen/Call/CallGenLeafKit.vp b/App/lib/core/Gen/Call/CallGenLeafKit.vp index d8f804e..a3b7108 100644 --- a/App/lib/core/Gen/Call/CallGenLeafKit.vp +++ b/App/lib/core/Gen/Call/CallGenLeafKit.vp @@ -1242,6 +1242,26 @@ class CallGenLeafKit(CallGenCBuiltin.CallGenCBuiltin): if self.Gen.TypeGenCDef.is_cdefine_found() != 0: kinds[di] = ast.CONST_INT ints[di] = cdef_v + elif dnode is not None and dnode.kind() == ast.ASTKind.UnaryOp: + # 一元字面量默认值(如 ex3: t.CInt = -1):AST 是 + # UnaryOp(USub, Constant(1)),不是 Constant —— 此前无此分支 + # → kinds[di] 保持 0 → 快照重建失败 → "跨模块默认参数无法 + # 重建(快照缺失,默认值需为简单常量)"(AppGui.top_index + # 的 ex3 = -1 根因)。只对"操作数是整型 Constant"求值, + # 其它(浮点取负 / 取负变量)保持 kinds=0 → 重建时 fail-fast。 + du: ast.UnaryOp | t.CPtr = (ast.UnaryOp | t.CPtr)(dnode) + if du.operand is not None and du.operand.kind() == ast.ASTKind.Constant: + doc: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(du.operand) + if doc.const_kind == ast.CONST_INT: + if du.op == ast.OpKind.USub: + kinds[di] = ast.CONST_INT + ints[di] = -doc.int_val + elif du.op == ast.OpKind.UAdd: + kinds[di] = ast.CONST_INT + ints[di] = doc.int_val + elif du.op == ast.OpKind.Invert: + kinds[di] = ast.CONST_INT + ints[di] = ~doc.int_val entry.ConstKinds = kinds entry.ConstInts = ints entry.ConstStrs = strs diff --git a/App/lib/core/Gen/Expr/ExprGenCdef.vp b/App/lib/core/Gen/Expr/ExprGenCdef.vp index 2d7fe89..8382960 100644 --- a/App/lib/core/Gen/Expr/ExprGenCdef.vp +++ b/App/lib/core/Gen/Expr/ExprGenCdef.vp @@ -802,16 +802,28 @@ class ExprGenCdef(ExprGenBase.ExprGenBase): if rc_pos < total_len: rc_pos += 1 - # 检查行是否以 "from ." 开头(相对导入子模块) + # 检查行是否以 "from " 开头(模块导入) if rc_line_len < 12: continue if pyi_buf[rc_line_start] != 'f' or pyi_buf[rc_line_start+1] != 'r' or \ pyi_buf[rc_line_start+2] != 'o' or pyi_buf[rc_line_start+3] != 'm' or \ - pyi_buf[rc_line_start+4] != ' ' or pyi_buf[rc_line_start+5] != '.': + pyi_buf[rc_line_start+4] != ' ': continue - # 提取子模块名(从 rc_line_start+6 开始,到空格/tab 为止) - rc_sub_start: t.CSizeT = rc_line_start + 6 + # 提取模块路径 token(从 rc_line_start+5 到空格/tab 为止,跳过前导点) + # 两种写法都要支持: + # 相对导入 `from .base import X` → 拼 base_mod.base + # 绝对导入 `from vqt6._types import *` → 直接用 token 自身 + # 此前只认相对导入(硬要求 rc_line_start+5 是 '.'),绝对写法被 + # 直接跳过 → vqt6 包(__init__.vp 用 `from vqt6._types import *` + # 重导出 QT_ALIGN_* 等)里的 CDefine 全部查不到 → + # "跨模块 CDefine 未找到: 'vqt6.QT_ALIGN_CENTER'"。 + rc_is_rel: int = 0 + rc_sub_start: t.CSizeT = rc_line_start + 5 + while rc_sub_start < rc_line_start + rc_line_len and \ + pyi_buf[rc_sub_start] == '.': + rc_is_rel = 1 + rc_sub_start += 1 rc_sub_end: t.CSizeT = rc_sub_start while rc_sub_end < rc_line_start + rc_line_len and \ pyi_buf[rc_sub_end] != ' ' and pyi_buf[rc_sub_end] != '\t': @@ -917,12 +929,31 @@ class ExprGenCdef(ExprGenBase.ExprGenBase): rc_sub_buf[rc_si] = pyi_buf[rc_sub_start + rc_si] rc_sub_buf[rc_sub_len] = '\0' - # 构建完整子模块名 base_mod.X - rc_full_len: t.CSizeT = string.strlen(base_mod) + 1 + rc_sub_len + 1 - rc_full_mod: str = pool.alloc(rc_full_len) - if rc_full_mod is None: - continue - viperlib.snprintf(rc_full_mod, rc_full_len, "%s.%s", base_mod, rc_sub_buf) + # 构建完整模块名: + # 相对导入(rc_is_rel=1)→ <当前模块所属包> + "." + token + # 基准必须是"包"(rel_path 的目录部分)而不是模块名本身: + # pkg/sub/mod.vp 里的 `from .X import Y` 指向 pkg.sub.X, + # 用 base_mod("pkg.sub.mod")会拼成 pkg.sub.mod.X(错)。 + # __init__.vp 时包名 == 模块名,此前只在这种情况碰巧正确 + # (CallGenBase._lookup_reexport_from_vpi 用的是包名,此处对齐)。 + # 绝对导入(rc_is_rel=0)→ 直接用 token(vqt6._types) + # 自导入(目标模块 == base_mod)跳过,避免无意义递归 + rc_full_mod: str = None + if rc_is_rel != 0: + rc_base_mod: str = base_mod + rc_pkg: str = self.Gen.CallGenBase._module_package_from_sha1(pool, sha1) + if rc_pkg is not None: + rc_base_mod = rc_pkg + rc_full_len: t.CSizeT = string.strlen(rc_base_mod) + 1 + rc_sub_len + 1 + rc_full_buf: str = pool.alloc(rc_full_len) + if rc_full_buf is None: + continue + viperlib.snprintf(rc_full_buf, rc_full_len, "%s.%s", rc_base_mod, rc_sub_buf) + rc_full_mod = rc_full_buf + else: + if string.strcmp(rc_sub_buf, base_mod) == 0: + continue + rc_full_mod = rc_sub_buf # 防循环:若 base_mod 已以 "."+rc_sub_buf 结尾(如 ast.base 再拼 # .base → ast.base.base),说明子模块递归在重复追加同一模块段 diff --git a/App/lib/core/Gen/TranslatorGenMixin.vp b/App/lib/core/Gen/TranslatorGenMixin.vp index 19ec381..c0ccc04 100644 --- a/App/lib/core/Gen/TranslatorGenMixin.vp +++ b/App/lib/core/Gen/TranslatorGenMixin.vp @@ -536,7 +536,8 @@ class TranslatorGenMixin: resolved_stg_ty: llvmlite.LLVMType | t.CPtr = None # 对象类全局:静态 storage 类型(BSS 实例) resolved_stg_entry: GenTypes.StructEntry | t.CPtr = None # 对象类全局:对应 StructEntry(vtable 初始化用) init_val: t.CInt64T = 0 - init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str + init_fval: t.CDouble = 0.0 # init_kind==4(浮点字面量)时的数值 + init_kind: int = 0 # 0=none, 1=int, 2=list, 3=str, 4=float init_str_val: str = None init_list_node: ast.List | t.CPtr = None # = None 显式初始化(如 tokens.vp `_kw_head: KwEntry | t.CPtr = None`): @@ -605,6 +606,13 @@ class TranslatorGenMixin: elif cn.const_kind == ast.CONST_STR: init_str_val = cn.str_val init_kind = 3 + elif cn.const_kind == ast.CONST_FLOAT: + # 浮点字面量(如 torch `LCG_M: t.CDouble = 2147483647.0`): + # 此前无此分支 → init_kind 保持 0 → 全局退化成 + # zeroinitializer,初始化值整体丢失(负数 -1 补在 UnaryOp + # 分支是同一类漏项)。数值不在此处取 AST 的 float_val, + # 见下方 init_kind==4 的源文件恢复逻辑。 + init_kind = 4 elif val_kind == ast.ASTKind.UnaryOp: # 负数/按位取反字面量(如 `_vblank_ok: t.CInt = -1`): # AST 是 UnaryOp(USub, Constant),此前未处理 → init_kind 保持 0 @@ -639,6 +647,11 @@ class TranslatorGenMixin: elif cn2.const_kind == ast.CONST_STR: init_str_val = cn2.str_val init_kind = 3 + elif cn2.const_kind == ast.CONST_FLOAT: + # 无注解的模块级浮点赋值:字面量本身即类型依据,提升为 double + # (默认 var_ty 是 i32,不提升会把浮点位模式写进 i32 全局) + init_kind = 4 + var_ty = llvmlite.Double(pool) elif val_kind2 == ast.ASTKind.UnaryOp: # 负数字面量(同 AnnAssign 分支,见上):UnaryOp(USub, Constant) init_val = self.Gen.AssignGen.extract_cdefine_int_value(asgn.value) @@ -660,6 +673,30 @@ class TranslatorGenMixin: if string.strcmp(var_name, "__all__") == 0: return 0 + # 浮点字面量初始化(init_kind==4):数值一律从当前源文件按变量名重读, + # 不用 AST 节点的 float_val —— 该字段可能被后续 pool 操作覆写,且跨模块 + # double 返回值传递本身有缺陷(详见 AssignGenMixin._recover_float_literal)。 + # 同时校验注解类型:注解不是浮点却给浮点字面量属类型错误,直接终止, + # 不静默零初始化。 + if init_kind == 4: + is_fty_g: int = 0 + match var_ty: + case llvmlite.LLVMType.Float(_): + is_fty_g = 1 + case _: + pass + if is_fty_g == 0: + self.Gen.TypeGenErr.fatal_error( + node, f"模块级变量 '{var_name}' 用浮点字面量初始化,但注解不是浮点类型", + "TypeError") + return 0 + if self.Gen.AssignGen._recover_float_literal( + var_name, t.CPtr(c.Addr(init_fval)), 0) != 1: + self.Gen.TypeGenErr.fatal_error( + node, f"模块级浮点变量 '{var_name}' 初始化值源文件恢复失败", + "InternalCompilerError") + return 0 + # 检查是否已注册 existing: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var( trans.SymTab, var_name) @@ -748,6 +785,19 @@ class TranslatorGenMixin: else: self.Gen.TypeGenErr.fatal_error( node, f"模块级变量 '{var_name}' 初始化器构建失败", "TypeError") + elif init_kind == 4: + # 浮点初始值:LLVM IR 的浮点常量必须写成位模式文本(0x%016llx / 0x%08x)。 + # 十进制文本经 llc 解析对部分值有 ±1 ulp 误差(见 llvmlite.ConstFloat 注释)。 + # 此前 init_kind 没有浮点分支 → 模块级 t.CDouble 全局一律退化成 + # zeroinitializer(torch 的 LCG_A/LCG_M/_rng_state 全变 0 → + # _next_rand() 得到 0.0/0.0 = NaN → Kaiming 初始化写入全 NaN 权重 → + # 推理 softmax 分母 NaN → 十个概率全为 0 的根因链)。 + fc_g: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, var_ty, init_fval) + if fc_g is not None and fc_g.Name is not None: + gv.Initializer = fc_g.Name + else: + self.Gen.TypeGenErr.fatal_error( + node, f"模块级变量 '{var_name}' 浮点初始化器构建失败", "TypeError") else: # 使用 zeroinitializer 而非 "0":指针类型必须用 null/zeroinitializer, # 整数/聚合类型也兼容 zeroinitializer,避免 "integer constant must have integer type" diff --git a/App/lib/core/Gen/Type/TypeGenBase.vp b/App/lib/core/Gen/Type/TypeGenBase.vp index 06fc587..cb97ebf 100644 --- a/App/lib/core/Gen/Type/TypeGenBase.vp +++ b/App/lib/core/Gen/Type/TypeGenBase.vp @@ -75,8 +75,14 @@ class TypeGenBase: # builtins 最后匹配诊断("变量类型注解解析失败"详细报错用): # LastBuiltinsTypedefName = 最后一次请求的名称;LastBuiltinsTypedefResult = # 展开后的类型表达式(未命中 = None)。随 clear_ctypedefs 复位。 + # 两个被指串由以下自有缓冲承载(stdlib.malloc,clear_ctypedefs 释放): + # 调用方常传临时缓冲(VpiReader._resolve_single_vpi_type_str 的段缓冲 + # malloc 后立刻 free),直接存指针会悬垂 → _build_ann_fail_msg 的 + # snprintf 读已释放内存(ASan heap-use-after-free)。 LastBuiltinsTypedefName: str LastBuiltinsTypedefResult: str + LastBuiltinsTypedefNameBuf: bytes + LastBuiltinsTypedefResultBuf: bytes # builtins import 别名缓冲(stdlib.malloc,格式 "alias\0target\0" 连续段): # _lookup_builtins_typedef 扫描 builtins.vp import 行填充;库 typedef 模块 # 限定引用按别名还原真实模块名(__stdint.size_t → stdint.size_t)。 @@ -120,6 +126,8 @@ class TypeGenBase: self.BuiltinsTypedefScanned = 0 self.LastBuiltinsTypedefName = None self.LastBuiltinsTypedefResult = None + self.LastBuiltinsTypedefNameBuf = None + self.LastBuiltinsTypedefResultBuf = None self.ImportAliasBuf = None self.ConstEvalOk = 0 self.CurrentFile = None diff --git a/App/lib/core/Gen/Type/TypeGenCType.vp b/App/lib/core/Gen/Type/TypeGenCType.vp index 52fc91e..a7515ec 100644 --- a/App/lib/core/Gen/Type/TypeGenCType.vp +++ b/App/lib/core/Gen/Type/TypeGenCType.vp @@ -13,7 +13,10 @@ import lib.Stub.StubMerger as StubMerger import lib.core.CompilerFactory as CompilerFactory -import lib.core.Gen.Type.TypeGenBase as TypeGenBase +import lib.core.Gen.Type.TypeGenBase as TypeGenBase + +# builtins 诊断串自有缓冲长度(仅服务"变量类型注解解析失败"报错文本) +TYPEDEF_DIAG_BUF: t.CDefine = 256 # ============================================================ # TypeGenCType - TypeGenMixin 拆分(阶段 D):CTypedef 表(register/lookup/ScanCTypedefDeclarations/_eval_const_int_expr) @@ -71,6 +74,31 @@ class TypeGenCType(TypeGenBase.TypeGenBase): return self.CTypedefNames.append(name_copy) self.CTypedefTypeStrs.append(type_copy) + def store_builtins_diag(self, which: int, text: str) -> str: + """把 builtins 诊断串复制进自有缓冲并返回该缓冲(which: 0=Name, 1=Result) + + 调用方(VpiReader._resolve_single_vpi_type_str)传的往往是 + stdlib.malloc 的临时段缓冲,返回前即 free——直接存指针会让 + _build_ann_fail_msg 的 snprintf 读已释放内存(ASan + heap-use-after-free)。诊断字段必须持有自己的副本。 + text 为 None 时返回 None("未命中"标记)。 + """ + if text is None: + return None + if which == 0: + if self.LastBuiltinsTypedefNameBuf is None: + self.LastBuiltinsTypedefNameBuf = stdlib.malloc(TYPEDEF_DIAG_BUF) + buf: str = self.LastBuiltinsTypedefNameBuf + else: + if self.LastBuiltinsTypedefResultBuf is None: + self.LastBuiltinsTypedefResultBuf = stdlib.malloc(TYPEDEF_DIAG_BUF) + buf = self.LastBuiltinsTypedefResultBuf + if buf is None: + return None + string.strncpy(buf, text, TYPEDEF_DIAG_BUF - 1) + buf[TYPEDEF_DIAG_BUF - 1] = '\0' + return buf + def lookup_ctypedef(self, name: str) -> str: """查找 CTypedef,返回类型表达式字符串 @@ -105,7 +133,14 @@ class TypeGenCType(TypeGenBase.TypeGenBase): self.BuiltinsTypedefScanned = 0 self.LastBuiltinsTypedefName = None self.LastBuiltinsTypedefResult = None - # 别名缓冲 stdlib.malloc 分配,跨 pool 生命周期;此处释放防泄漏 + # 别名缓冲 / 诊断缓冲均为 stdlib.malloc 分配,跨 pool 生命周期; + # 此处释放防泄漏(诊断缓冲按需再分配) + if self.LastBuiltinsTypedefNameBuf is not None: + stdlib.free(self.LastBuiltinsTypedefNameBuf) + self.LastBuiltinsTypedefNameBuf = None + if self.LastBuiltinsTypedefResultBuf is not None: + stdlib.free(self.LastBuiltinsTypedefResultBuf) + self.LastBuiltinsTypedefResultBuf = None if self.ImportAliasBuf is not None: stdlib.free(self.ImportAliasBuf) self.ImportAliasBuf = None diff --git a/App/lib/core/Gen/Type/TypeGenResolve.vp b/App/lib/core/Gen/Type/TypeGenResolve.vp index 5336ef6..20115ef 100644 --- a/App/lib/core/Gen/Type/TypeGenResolve.vp +++ b/App/lib/core/Gen/Type/TypeGenResolve.vp @@ -853,8 +853,13 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): """ if name is None: return None - # 详细报错诊断:记录本次 builtins 最后匹配请求与结果(未命中 = None) - self.Gen.TypeGenCType.LastBuiltinsTypedefName = name + # 详细报错诊断:记录本次 builtins 最后匹配请求与结果(未命中 = None)。 + # 必须经 store_builtins_diag 复制进自有缓冲——name 常是调用方的临时 + # 段缓冲(VpiReader._resolve_single_vpi_type_str 的 seg_buf malloc 后 + # 立即 free),直接存指针会悬垂,_build_ann_fail_msg 的 snprintf 读已 + # 释放内存(ASan heap-use-after-free)。 + self.Gen.TypeGenCType.LastBuiltinsTypedefName = \ + self.Gen.TypeGenCType.store_builtins_diag(0, name) self.Gen.TypeGenCType.LastBuiltinsTypedefResult = None # rowptr 已内置于编译器(TPC/TPV 双端语法保留字,不可重定义): # 优先于一切外部声明(builtins.vp 已不再声明,用户自行 typedef 也会被 @@ -866,7 +871,8 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): if ts_b is not None and self.Gen.TypeGenCType.CTypedefFound != 0: # 表内值可能仍是模块限定引用(如 size_t → stdint.size_t),展开后返回 b_hit_r: str = self._expand_typedef_ref(pool, ts_b) - self.Gen.TypeGenCType.LastBuiltinsTypedefResult = b_hit_r + self.Gen.TypeGenCType.LastBuiltinsTypedefResult = \ + self.Gen.TypeGenCType.store_builtins_diag(1, b_hit_r) return b_hit_r # 本文件已扫过 builtins 源(表内无 = 非 builtins typedef):不再重复读文件 if self.Gen.TypeGenCType.BuiltinsTypedefScanned != 0: @@ -1023,10 +1029,16 @@ class TypeGenResolve(TypeGenBase.TypeGenBase): b_i += 1 # 置扫描完成标记:同文件内后续 miss 直接查表,不再重复读 builtins 源 self.Gen.TypeGenCType.BuiltinsTypedefScanned = 1 - stdlib.free(b_buf) + # b_res 是 b_buf 内的切片(非副本),必须在 free 之前完成展开与诊断复制: + # _expand_typedef_ref 可能原样返回指向 b_buf 的指针,free 之后再读即 + # heap-use-after-free(诊断字段还会长期持有该悬垂指针)。 + b_final_r: str = None if b_res is not None: - b_final_r: str = self._expand_typedef_ref(pool, b_res) - self.Gen.TypeGenCType.LastBuiltinsTypedefResult = b_final_r + b_final_r = self._expand_typedef_ref(pool, b_res) + self.Gen.TypeGenCType.LastBuiltinsTypedefResult = \ + self.Gen.TypeGenCType.store_builtins_diag(1, b_final_r) + stdlib.free(b_buf) + if b_final_r is not None: return b_final_r return None def _lookup_library_typedef(self, pool: memhub.MemBuddy | t.CPtr,