diff --git a/App/lib/Phase/Phase1/Phase1Main.vp b/App/lib/Phase/Phase1/Phase1Main.vp index a1b4f99..8b0f53f 100644 --- a/App/lib/Phase/Phase1/Phase1Main.vp +++ b/App/lib/Phase/Phase1/Phase1Main.vp @@ -707,6 +707,25 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str, stdlib.free(tr_r._nonlocal_names) stdlib.free(src_buf_r) + # [TEMP-DIAG] 1a 每文件哨兵(定位越界写发生在哪个阶段):校验本文件窗口 + # [p1a_mark, chk_depth) 内所有存活块的块尾 canary。 + # 位置必须在 reset_to 之前:reset_to 会把 chk_depth 截断回 p1a_mark + # (includes/memhub.vp reset_to L540 / 截断循环 L558-559),之后再查同一 + # mark 时 from_mark >= chk_depth 直接 return 0(includes/memhub.vp + # check_alive_blocks L1234-1235)→ 窗口为空、必然漏报。 + # p1a_mark 在循环体首行捕获(本文件 L597),故窗口恰为"本文件 + # parse + translate(declare=1)"期间分配的块。mark 语义与既有 3 处哨兵 + # (L1109/L1135/L1226 均传 mark1 = 各自窗口起点)一致。 + # 仅 --detection-deep(mb.deep_detect != 0)时执行:本哨兵每文件一次、 + # 每次遍历整窗口活块(O(n)),默认全程不进入,回到上一轮之前的开销。 + # 读法同本文件既有 mb.detection(L470):直接读 MemBuddy 实例的公开 + # int 字段(mb: rowptr[memhub.MemBuddy])。 + if mb.deep_detect != 0: + chk_rc_1a: int = mb.check_alive_blocks(p1a_mark) + if chk_rc_1a != 0: + VLogger.error(f"内存损坏(1a 每文件后续, rc={chk_rc_1a}): {file_path_r}", "Phase1") + stdlib.exit(1) + # 回收本文件 AST/lexer/token/IR 等瞬时数据(注册数据已在 perm_pool, # reset 不影响;IncludesScanner result 等在 p1a_mark 前分配同样保留) mb.reset_to(p1a_mark) @@ -845,6 +864,19 @@ def RunPhase1(mb: rowptr[memhub.MemBuddy], includes_dir: str, temp_dir: str, p1bpre_failed += 1 VLogger.error(f"[Phase 1b-pre] {file_path_pv} | pyi 缓冲分配失败(OOM)", "Phase1") stdlib.free(src_buf_pv) + # [TEMP-DIAG] 1b-pre 每文件哨兵(同上,定位越界写发生在哪个阶段): + # 校验本文件窗口 [mark1, chk_depth) 内所有存活块的块尾 canary。 + # 同样必须在 reset_to(mark1) 之前(否则窗口被截断为空,见 memhub.vp + # reset_to L558-559 与 check_alive_blocks L1234-1235)。mark1 在 1b 段前 + # 捕获(本文件 L756),是 1b/1b-pre 的统一窗口起点;本循环无文件级 mark, + # 故与既有 L1109/L1135/L1226 同传 mark1。 + # 仅 --detection-deep(mb.deep_detect != 0)时执行,读法同上方 1a 哨兵。 + if mb.deep_detect != 0: + chk_rc_pv: int = mb.check_alive_blocks(mark1) + if chk_rc_pv != 0: + VLogger.error(f"内存损坏(1b-pre 每文件后续, rc={chk_rc_pv}): {file_path_pv}", "Phase1") + stdlib.exit(1) + # 回收本文件 AST(vpi 已写盘,AST 不再需要;mark1 后无其他持久数据) mb.reset_to(mark1) diff --git a/App/lib/Stub/StubMerger/StubMergerBuild.vp b/App/lib/Stub/StubMerger/StubMergerBuild.vp index b4efcd6..3f91579 100644 --- a/App/lib/Stub/StubMerger/StubMergerBuild.vp +++ b/App/lib/Stub/StubMerger/StubMergerBuild.vp @@ -1231,6 +1231,141 @@ def _BuildReachableSha1Set(mb: rowptr[memhub.MemBuddy], source_dir: str, VLogger.debug(f"可达 SHA1: {reachable_count} 个", "Reachable") return reachable_count +# ============================================================ +# 类型定义前移(llc: "invalid type for function argument") +# +# 来源:TransPyC/lib/Projectrans/Phase2Translator.py:3056-3081(TPC 同语义实现, +# 注释原文即 llc 的该要求)。触发条件:by-value 结构体参数引用的具名类型必须在 +# declare 点之前定义,否则 llc 报 invalid type for function argument。 +# +# 与 TPC 的差异(唯一一处):TPC 额外做一次同名去重(seen_type_names_inc)。 +# 本侧合并阶段 _seen_types(本文件 L1646-1667)已按类型名保证"活跃定义唯一", +# 重复行不会进入 out_buf,故本遍是纯置换(长度不变),天然幂等且行内容逐字不变。 +# ============================================================ +def _IsTypeDefLine(buf: bytes, ls: sizet, le: sizet) -> int: + """buf[ls, le)(不含换行)是否为 `%name = type ...` 类型定义行 + + 对齐 TPC L3064 的 `stripped.startswith('%') and '= type' in stripped`, + 但把裸子串判定收紧为词判定:'= type' 也会出现在函数体字符串常量里 + (如 c"= type\\00"),裸子串判定会把普通指令行当类型定义搬走 → 破坏 IR。 + """ + k: sizet = ls + while k < le and (buf[k] == ' ' or buf[k] == '\t'): + k += 1 + if k >= le or buf[k] != '%': + return 0 + j: sizet = k + while j + 6 <= le: + if string.strncmp(buf + j, "= type", 6) == 0: + if j + 6 == le: + return 1 + e: char = buf[j + 6] + # 'type' 后只能是空白 / '{' / '<'(= type { ... } / opaque / <{...}>) + if e == ' ' or e == '\t' or e == '{' or e == '<': + return 1 + j += 1 + return 0 + + +def _IsIrHeaderLine(buf: bytes, ls: sizet, le: sizet) -> int: + """buf[ls, le)(不含换行)是否属于 ll header(类型定义插在其后) + + 对齐 TPC L3076 的 header 判据:空行 / ';' 注释 / 'target ' / + 'source_filename' / '!' 元数据(行首空白先跳过,等价 TPC 的 stripped)。 + """ + k: sizet = ls + while k < le and (buf[k] == ' ' or buf[k] == '\t' or buf[k] == '\r'): + k += 1 + if k >= le: + return 1 + if buf[k] == ';' or buf[k] == '!': + return 1 + if le - k >= 7 and string.strncmp(buf + k, "target ", 7) == 0: + return 1 + if le - k >= 15 and string.strncmp(buf + k, "source_filename", 15) == 0: + return 1 + return 0 + + +def HoistTypeDefsBeforeDeclares(buf: bytes, length: sizet) -> sizet: + """把所有 `%name = type ...` 行上移到 IR header 之后(见上方注释) + + 纯文本行级重排:行内容逐字不变、类型定义之间相对顺序不变、非类型定义行 + 相对顺序不变;插入点与 TPC 一致(header 之后第一行;若首个非 header 行 + 就在文件首行或无 header 行,按 TPC 同规则追加末尾)。 + 幂等:已是"定义在 header 之后"时结果与输入逐字节相同。 + 返回重排后的长度(有类型定义行时恒为 length)。 + """ + if buf is None or length == 0: + return length + # 1. 统计类型定义行总长(整行含换行) + types_len: sizet = 0 + p: sizet = 0 + while p < length: + le: sizet = p + while le < length and buf[le] != '\n': + le += 1 + step: sizet = le - p + if le < length: + step += 1 + if _IsTypeDefLine(buf, p, le) != 0: + types_len += step + p += step + if types_len == 0: + return length + tmp: bytes = stdlib.malloc(types_len + 1) + if tmp is None: + # VLogger.error 直接终止进程(不静默降级为"未前移") + VLogger.error("类型定义前移暂存缓冲分配失败", "StubMerger") + return length + # 2. 类型定义行抽到 tmp、其余行原地压缩(只删行不增行,w <= p 恒成立) + tmp_pos: sizet = 0 + w: sizet = 0 + p = 0 + while p < length: + le = p + while le < length and buf[le] != '\n': + le += 1 + step = le - p + if le < length: + step += 1 + if _IsTypeDefLine(buf, p, le) != 0: + string.memmove(tmp + tmp_pos, buf + p, step) + tmp_pos += step + else: + if w != p: + string.memmove(buf + w, buf + p, step) + w += step + p += step + tmp[tmp_pos] = '\0' + # 3. 求插入点:过滤后文本里 header 之后的首行起始偏移 + ins: sizet = 0 + hit: int = 0 + q: sizet = 0 + while q < w: + qe: sizet = q + while qe < w and buf[qe] != '\n': + qe += 1 + if _IsIrHeaderLine(buf, q, qe) == 0: + ins = q + hit = 1 + break + if qe >= w: + break + q = qe + 1 + if hit == 0 or ins == 0: + # 对齐 TPC L3079-3080:insert_pos == len(other_lines) + ins = w + # 4. 腾出 tmp_pos 字节空隙并拷入(长度不变;右移用 memmove 保证重叠安全) + tail: sizet = w - ins + if tail > 0: + string.memmove(buf + ins + tmp_pos, buf + ins, tail) + string.memmove(buf + ins, tmp, tmp_pos) + new_len: sizet = w + tmp_pos + buf[new_len] = '\0' + stdlib.free(tmp) + return new_len + # ============================================================ # BuildCombinedIR - 组合本地 stub + 依赖 stubs + 本地 text → 完整 IR # @@ -1674,6 +1809,13 @@ def BuildCombinedIR(temp_dir: str, local_sha1: str, if mod_arr is not None: stdlib.free(mod_arr) + # 类型定义前移:llc 要求 by-value 结构体参数引用的具名类型在 declare 点之前 + # 定义(否则 "invalid type for function argument")。TPC 侧同一遍见 + # TransPyC/lib/Projectrans/Phase2Translator.py:3056-3081。 + # 落点说明:Phase2 的 write_ir_to_file 是逐字节写本缓冲区(BuildPipeline.vp + # L289-314,无任何文本加工),故在此完成即覆盖 llc 的全部输入,无需在 + # Phase2 再复制一份 4MB 缓冲区重排。 + out_pos = HoistTypeDefsBeforeDeclares(out_buf, out_pos) return out_pos # ============================================================ # LoadDepVpiIntoModule - 加载依赖模块的 .vpi 文件,注入 declare 到当前模块 diff --git a/App/lib/core/Gen/Class/ClassGenVTable.vp b/App/lib/core/Gen/Class/ClassGenVTable.vp index f3bd1d3..c7b8796 100644 --- a/App/lib/core/Gen/Class/ClassGenVTable.vp +++ b/App/lib/core/Gen/Class/ClassGenVTable.vp @@ -1012,6 +1012,23 @@ class ClassGenVTable(ClassGenBase.ClassGenBase): _is_void = 1 case _: pass + # 追加91:返回注解层面的"无返回值"判据(与模块级函数 + # FuncGenDef.vp「_is_void 双保险」同款、同语义,追加89 的 + # 方法/函数同步约定)。void 方法无需显式 return,掉出末尾合法; + # 报文里的"(返回类型 void)"来自 TypePrint 的 `case _` 兜底亦可 + # 打印 void,不能当作已识别依据,故补注解判据。 + # ① t.CVoid / ② 裸名 CVoid:经既有判据 + # TypeGenResolve._is_cvoid_marker(TypeGenResolve.vp:1484) + # ③ -> None(CONST_NONE):解析即 Void(TypeGenResolve.vp:1996-1999) + if _is_void == 0 and fd.returns is not None: + if self.Gen.TypeGenResolve._is_cvoid_marker( + fd.returns, imported_modules) != 0: + _is_void = 1 + elif fd.returns.kind() == ast.ASTKind.Constant: + ret_none_cn_m: ast.Constant | t.CPtr = ( + ast.Constant | t.CPtr)(fd.returns) + if ret_none_cn_m.const_kind == ast.CONST_NONE: + _is_void = 1 if _is_void != 0: llvmlite.build_ret_void(func_builder) elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0: diff --git a/App/lib/core/Gen/Expr/ExprGenCdef.vp b/App/lib/core/Gen/Expr/ExprGenCdef.vp index eb281eb..25f1e7e 100644 --- a/App/lib/core/Gen/Expr/ExprGenCdef.vp +++ b/App/lib/core/Gen/Expr/ExprGenCdef.vp @@ -1097,6 +1097,9 @@ class ExprGenCdef(ExprGenBase.ExprGenBase): 规则(与 translate_constant 的 i32 溢出升级一致,但保留 0xFFFFFFFF 语义): 值超 32 位无符号范围(> 0xFFFFFFFF)→ i64 全精度常量 (如 sys.maxsize = 9223372036854775807); + 值低于 i32 下限(< -2147483648)→ i64 全精度常量。符号位为 1 的 64 位 + 模式(如 MEMBUDDY_CANARY_PATTERN = 0xA5A5A5A5A5A5A5A5)以 %lld 文本 + 存回即负数 -6510615555426900571,仅按 u32 截断回比较会漏判; 否则 → i32 常量,内部 int 截断(0xFFFFFFFF → -1,保持既有 INVALID_HANDLE_VALUE / WAIT_FAILED = -1 语义不变,避免 i32 4294967295 生成非法 LLVM IR 文本 "i32 4294967295")。 @@ -1106,7 +1109,16 @@ class ExprGenCdef(ExprGenBase.ExprGenBase): 正数 CDefine 都被升 i64(INVALID_FILE_ATTRIBUTES 0xFFFFFFFF 从 i32 -1 变 i64 4294967295 → 与 _get_attrs 的 sext(-1) 比较失败,SysLibTest exists FAIL 根因)。 改用截断回比较:uint32(value) 回绕后还原,与 value 不等即超 u32 范围。 + + 负数侧漏判的现场:`(0xA5A5A5A5 << 32) | 0xA5A5A5A5`(includes/memhub.vp:123) + 经 CDefine 求值(ExprGenCdef 内 lval << rval 的 int64 回绕)得 + -6510615555426900571,再经 TypeGenCDef.register_cdefine_constant 的 + "%lld" 文本存回(TypeGenCDef.vp:156)仍为负数 → 原判据物化成 i32 + -1515870811:写入路径 inttoptr i32(x86 movl 零扩展)实存 + 0x00000000A5A5A5A5,比较/打印路径 sext i32→i64 期望 0xFFFFFFFFA5A5A5A5 + ⇒ MemBuddy canary 校验 rc=6 假阳性。下限判据与既有 + ExprGenConst.vp:108 / CallGenLeafKit.vp:1477 的写法一致。 """ - if value >= 0 and uint64(uint32(value)) != value: + if (value >= 0 and uint64(uint32(value)) != value) or value < -2147483648: return llvmlite.const_int64(pool, value) return llvmlite.const_int32(pool, int(value)) \ No newline at end of file diff --git a/App/lib/core/Gen/Func/FuncGenDef.vp b/App/lib/core/Gen/Func/FuncGenDef.vp index 4a4a902..cdcb09c 100644 --- a/App/lib/core/Gen/Func/FuncGenDef.vp +++ b/App/lib/core/Gen/Func/FuncGenDef.vp @@ -981,6 +981,41 @@ class FuncGenDef(CallGenBase.CallGenBase): _is_void = 1 case _: pass + # 追加91:返回注解层面的"无返回值"判据(与上面的 ret_ty 类型判定 + # 互为双保险,不改变任何非 void 分支)。 + # 现场(误判):includes/atom.vp:22 + # def __atomic_clear(ptr: t.CUInt64T | t.CPtr, order: t.CInt) -> t.CVoid: + # 函数体为 c.Asm 调用、流程真实掉出末尾;对 void 函数这是合法收尾 + # (Python 语义:void 无需显式 return),却被下方 fail-fast 误判为 + # "缺显式 return"。注意报文的"(返回类型 void)"**不能**当作"已识别 + # 出 void"的依据——llvmlite.TypePrint 的 `case _` 兜底 + # (includes/llvmlite/__types.vp:510-511)同样打印 "void",任何 + # 未命中的类型都会显示 void。故必须由注解判据补足:LLVMType 的 + # REnum class-pattern match 一旦退化(变体表/类名失配 → + # MatchGenMixin._handle_simple_match 不产出 case 值,case 体结构性 + # 不可达),_is_void 恒 0,本检查就会把 void 函数误报成缺 return。 + # 判据与注解→类型解析同口径(注解声明无返回值 ⇔ 该注解解析为 + # LLVMType.Void),逐形式与先例: + # ① t.CVoid(Attribute 形式):TypeGenResolve.map_t_type + # "CVoid" → llvmlite.Void(TypeGenResolve.vp:1351-1352); + # 先例 includes/atom.vp:22、includes/vipersimd.vp:8 + # ② 裸名 CVoid(from t import CVoid):同 TypeGenResolve.vp:1441-1442 + # (_is_t_attr 的裸名分支注释明列 CVoid) + # ①②统一经既有判据 TypeGenResolve._is_cvoid_marker + # (TypeGenResolve.vp:1484-1493)判定——不新增名字白名单, + # 与 FuncGenDef.vp:256/516/625/784 的既有调用方式一致 + # ③ -> None(CONST_NONE):注解解析即 Void + # (TypeGenResolve.vp:1996-1999);先例 + # App/lib/core/Gen/GenApi.vp:25 set_temp_dir + if _is_void == 0 and fd.returns is not None: + if self.Gen.TypeGenResolve._is_cvoid_marker( + fd.returns, imported_modules) != 0: + _is_void = 1 + elif fd.returns.kind() == ast.ASTKind.Constant: + ret_none_cn: ast.Constant | t.CPtr = ( + ast.Constant | t.CPtr)(fd.returns) + if ret_none_cn.const_kind == ast.CONST_NONE: + _is_void = 1 if _is_void != 0: llvmlite.build_ret_void(func_builder) elif last_is_ctrl != 0 and trans._ctrl_merge_reachable == 0: diff --git a/App/lib/core/Gen/MatchGenMixin.vp b/App/lib/core/Gen/MatchGenMixin.vp index a477a0f..9f35a2d 100644 --- a/App/lib/core/Gen/MatchGenMixin.vp +++ b/App/lib/core/Gen/MatchGenMixin.vp @@ -392,6 +392,33 @@ class MatchGenMixin: sw_tail: llvmlite.SwitchCase | t.CPtr = None sw_count: int = 0 + # 修复: 应提交 case 的臂计数(非 default 且模式必须产出 case 值: + # MatchClass 必有变体 tag / MatchValue 必有常量值)。与 sw_count 不等 + # 即"静默丢 case" —— build_switch 前 fail-fast(见 step 5 前的硬校验)。 + need_case: int = 0 + # 修复: 未取到 case 值的臂诊断串(供硬校验报错打印 臂序号/变体名/tag) + # 容量 = 8 条 × 单条上限(diag_tmp 128,strcat 无边界检查 → 必须够大) + miss_buf: str = pool.alloc(1088) + diag_tmp: str = pool.alloc(128) + if miss_buf is None or diag_tmp is None: + # fail-fast: 诊断缓冲区分配失败(与上方 name_buf 同款处理) + self.Gen.TypeGenErr.fatal_error(node, "match 诊断缓冲区分配失败", "TypeError") + miss_buf[0] = '\0' + miss_cnt: int = 0 + # 修复: 变体表 RenumClassNames 按**裸类名**注册 + # (ClassGenEnum.L976-982 存 ClassDef.name,如 "LLVMType"),而 2a 路径的 + # renum_name 来自变量注解的书写形态(Spec.SourceName; + # TypeGenResolve.extract_class_name_from_annotation L1648-1682 对 Attribute + # 返回模块限定名 "llvmlite.LLVMType")。检测侧 _is_renum_by_entry → + # find_struct(StructGenMixin L770-830 支持模块限定名 + 裸名回退)通过, + # 但精确 strcmp 的 _lookup_renum_variant_in_class 失配 → 恒 -1 → + # 该臂被静默丢弃(TPV1 产物空 case 列表根因,FuncGenDef.vp:993-996 同款记载)。 + # 去模块前缀:复用 find_struct 同款 L1078 括号外取点(泛型下标内的点不算)。 + tag_cls_bare: str = renum_name + rn_dot: str = self.Gen.StructGen._last_dot_outside_brackets(renum_name) + if rn_dot is not None and rn_dot[1] != '\0': + tag_cls_bare = rn_dot + 1 + # 4. 第一遍:收集 case 信息 + 创建 BB + 构建链表 i: int = 0 while i < case_count: @@ -407,14 +434,25 @@ class MatchGenMixin: is_def: int = 0 cv: llvmlite.Value | t.CPtr = None + # 修复: 提升到臂作用域(原先声明在 MatchClass 分支内),供下方 + # "未取到 case 值"诊断打印变体名/tag + vnm: str = None + tag: int = -1 if pat is not None: pk: int = pat.kind() if pk == ast.ASTKind.MatchClass: - vnm: str = self._extract_variant_name_from_cls(pat) + vnm = self._extract_variant_name_from_cls(pat) if vnm is not None: - tag: int = self.Gen.ClassGenEnum._lookup_renum_variant_in_class( - renum_name, vnm) + # 修复: 先用裸类名查表(与模式侧 2b 路径同口径,变体表就是 + # 按裸类名注册的);失败再用注解书写形态重试(泛型特化 + # mangled 名等确需带点的场景)。两者都失败 → tag=-1 → + # step 5 前的硬校验 fail-fast,不再静默丢 case。 + tag = self.Gen.ClassGenEnum._lookup_renum_variant_in_class( + tag_cls_bare, vnm) + if tag < 0 and string.strcmp(tag_cls_bare, renum_name) != 0: + tag = self.Gen.ClassGenEnum._lookup_renum_variant_in_class( + renum_name, vnm) if tag >= 0: tag_buf: str = pool.alloc(32) if tag_buf is not None: @@ -468,6 +506,13 @@ class MatchGenMixin: # 缓存 mc 指针(pass2 复用,避免重索引) mc_arr[collected] = sizet(mc) collected = collected + 1 + # 修复: 计入"必须提交 case"的臂(MatchClass 必有变体 tag、 + # MatchValue 必有常量值)。注意必须在 MAX_MATCH_CASES 上限判定 + # 之后计数——超上限的臂已 break,不参与校验(与旧行为一致)。 + if is_def == 0 and pat is not None: + pkm: int = pat.kind() + if pkm == ast.ASTKind.MatchClass or pkm == ast.ASTKind.MatchValue: + need_case = need_case + 1 # 构建 SwitchCase 链表(非 default case 且有值) if is_def == 0 and cv is not None: @@ -481,10 +526,32 @@ class MatchGenMixin: sw_tail.Next = sc sw_tail = sc sw_count = sw_count + 1 + # 修复: 记录"应产出 case 值但未产出"的臂(MatchClass 变体 tag 查表失败 + # / MatchValue 常量翻译失败)。此处只收集诊断信息,是否致命由 step 5 + # 前的硬校验统一判定(保证判定口径唯一,便于将来扩展 MatchOr 等)。 + if is_def == 0 and cv is None and miss_cnt < 8 and pat is not None: + pkm_d: int = pat.kind() + if pkm_d == ast.ASTKind.MatchClass or pkm_d == ast.ASTKind.MatchValue: + viperlib.snprintf(diag_tmp, 128, " #%d(%s,tag=%d)", + i, vnm if vnm is not None else "?", tag) + string.strcat(miss_buf, diag_tmp) + miss_cnt = miss_cnt + 1 i = i + 1 # 5. 发射 switch 指令 + # 修复: **禁止静默丢 case** —— 必须提交 case 的臂数(MatchClass/MatchValue, + # 非 default)必须等于实际提交给 build_switch 的 sw_count。不等即说明该臂 + # 的 case 值没算出来(REnum 变体 tag 查表失败最常见),此时 build_switch + # 会发射**空 case 列表** → 臂体照样生成但结构性不可达(TPV1 产物 57% 空 + # case 列表;FuncGenDef.vp:993-996 记载的 _is_void 恒 0 即此)。 + # 修复前:无任何校验,静默发射;修复后:fail-fast 并打印类名/查表名/ + # 应提交数/实际数/各未取到值臂的 序号(变体名,tag),便于定位变体表失配。 + if need_case != sw_count: + self.Gen.TypeGenErr.fatal_error( + node, + f"REnum match 分派丢失 case:class='{renum_name}'(查表名='{tag_cls_bare}')应提交 {need_case} 个 case,实际提交 {sw_count} 个;未取到值的臂:{miss_buf}", + "InternalCompilerError") llvmlite.build_switch(builder, tag_val, default_bb, sw_head, sw_count) # 6. 查找 REnum 结构体类型(用于变体字段绑定的 GEP) @@ -1224,6 +1291,23 @@ class MatchGenMixin: cv = self.Gen.ExprGen.translate_value( builder, pool, trans.Module, mv2.value, trans._funcs, trans._func_count, trans) + elif pk == ast.ASTKind.MatchClass: + # 修复: 本路径(非 REnum 分派)不为 class-pattern 臂产出任何 + # case 值 → 该臂被静默丢弃、臂体生成但不可达。TPC 侧 + # (HandlesMatch.py:132-160)对"REnum 检测失败但 case 是 + # MatchClass"有变体名→tag 兜底,本侧没有 ⇒ 静默不一致。 + # 若该 class-pattern 确实指向已注册 REnum(_is_renum_class_name / + # _is_renum_by_entry 命中),说明是"REnum 分派失配" + # (FuncGenDef.vp:993-996 记载的 _is_void 恒 0 即此),必须 + # fail-fast 暴露,不得静默丢 case(禁止兜底掩盖)。 + rnm_nr: str = self._extract_renum_name_from_cls(pat) + if rnm_nr is not None and ( + self.Gen.ClassGenEnum._is_renum_class_name(rnm_nr) != 0 + or self.Gen.ClassGenEnum._is_renum_by_entry(rnm_nr) != 0): + self.Gen.TypeGenErr.fatal_error( + pat, + f"非 REnum 路径收到 REnum 变体模式 'case {rnm_nr}.*':case 值无法产出,该臂会被静默丢弃(REnum 分派失配)", + "InternalCompilerError") if is_def == 0: if collected >= MAX_MATCH_CASES: diff --git a/App/main.vp b/App/main.vp index 51ce66a..d30403a 100644 --- a/App/main.vp +++ b/App/main.vp @@ -61,6 +61,16 @@ def main() -> int: if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection") is not None: detect_flag = 1 + # --detection-deep:重诊断(MemBuddy 每次 alloc 都探测 + 周期全表扫描), + # 用于定位越界写/重复分配,默认关(极慢,见 memhub.MEMBUDDY_PERIOD_SCAN 注释)。 + # 取值方式与上一条 --detection 完全一致:MemBuddy 在 parse_args(本函数 L169) + # 之前就要构造,只能用原始命令行 strstr 取值,不能等 args 解析结果。 + # 注意 "--detection-deep" 含 "--detection" 前缀 → 上面那条 strstr 必然命中, + # detect_flag 同时为 1(重诊断依赖 detection 的 0xCDCD 布局与记录表)。 + deep_detect_flag: int = 0 + if cmd_line_d is not None and string.strstr(cmd_line_d, "--detection-deep") is not None: + deep_detect_flag = 1 + # Initialize the mbuddy memory pool arena: bytes = stdlib.malloc(POOL_SIZE) if arena is None: @@ -72,12 +82,12 @@ def main() -> int: stdio.printf("FAIL: malloc for perm_pool arena failed\n") return 1 - mb: rowptr[memhub.MemBuddy] = memhub.MemBuddy(arena, POOL_SIZE, detect_flag) + mb: rowptr[memhub.MemBuddy] = memhub.MemBuddy(arena, POOL_SIZE, detect_flag, deep_detect_flag) if mb is None: stdio.printf("FAIL: MemBuddy init failed\n") return 1 - perm_pool: rowptr[memhub.MemBuddy] = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE, detect_flag) + perm_pool: rowptr[memhub.MemBuddy] = memhub.MemBuddy(perm_arena, PERM_POOL_SIZE, detect_flag, deep_detect_flag) if perm_pool is None: stdio.printf("FAIL: perm_pool init failed\n") @@ -146,6 +156,9 @@ def main() -> int: parser.add_argument("--phase2command", None, argparse.STRING, 0, None, False, argparse.STORE, "Do not run llc/link: write Phase2 llc(opt+llc) and clang link commands to the given .bat path") parser.add_argument("--nowarn-register", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Suppress Phase A dependency WARN") parser.add_argument("--detection", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Enable MemBuddy allocation detection (0xCDCD markers + record-table probe for out-of-bounds writes; applies to both mb and perm_pool)") + # 仅注册以便 argparse 接受该标志(取值不读这里:MemBuddy 在本函数 L85/L90 构造, + # 早于 parse_args,值取自 L70-72 的原始命令行 strstr —— 与 --detection 同范式) + parser.add_argument("--detection-deep", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Deep detection: probe every allocation and enable the periodic full-table scan (implies --detection; extremely slow, diagnostics only)") parser.add_argument("--show-args", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Show all parsed arguments") parser.add_argument("--build-dir", None, argparse.STRING, 0, None, False, argparse.STORE, "Override project.vpj build_dir (default ./.tpv_build; relative paths resolve against the project dir)") parser.add_argument("--no-llvmpeephole", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "Disable all local llvmlite peephole passes (T2 store/load forward & ret-zero, T3 no-op bitcast) in FunctionPrint serialization") diff --git a/project.vpj b/project.vpj index 6e775ef..b19d434 100644 --- a/project.vpj +++ b/project.vpj @@ -11,6 +11,10 @@ "linker": { "cmd": "D:/msys64/mingw64/bin/clang++.exe", "flags": ["-fuse-ld=lld", "-lmsvcrt", "-lucrt", "-lpthread", "-lmingwex", "-lkernel32", "-Wl,--allow-multiple-definition", "-Wl,--stack,0x1000000", "-Wl,--no-insert-timestamp"], + "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": "TransPyV.exe" }, "includes": [