fix(gen): 修 REnum match case 静默丢弃与 CDefine 负 64 位常量截断,补 void 注解放行、移植类型定义前移

- MatchGenMixin: 2a 路径查变体 tag 改用裸类名(注解书写形态带模块前缀,与变体表裸名注册口径不一致致 tag=-1、case 被静默丢弃、match 全走 default),并在 build_switch 前硬校验 应提交 case 数==sw_count,不再静默丢

- ExprGenCdef: _const_int_for_cdefine 补 value < -2147483648 下限判据,修正 (0xA5A5A5A5<<32)|0xA5A5A5A5 等负 64 位模式被物化为 i32(写入 inttoptr 零扩展 vs 比较 sext 不一致,致 MemBuddy canary 假阳性 rc=6)

- FuncGenDef/ClassGenVTable: 掉尾检查新增注解级 void 判据(t.CVoid/裸名 CVoid/None),不再因 REnum match 退化误判 void 函数缺 return

- StubMergerBuild: 移植 TPC 的 类型定义前移 遍,满足 llc 对 by-value 具名类型需先定义的要求(invalid type for function argument)

- main.vp/Phase1Main.vp/project.vpj: 新增 --detection-deep 深诊断开关与 canary 哨兵、ASan 静态库声明
This commit is contained in:
TermiNexus committed 2026-09-15 19:11:54 +08:00
1 parent 60100fa6af
commit 31df298341
8 files changed
+345 -6

No files matched your search

+32
View File
@@ -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)
+142
View File
@@ -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 到当前模块
+17
View File
@@ -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:
+13 -1
View File
@@ -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))
+35
View File
@@ -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:
+87 -3
View File
@@ -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:
+15 -2
View File
@@ -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")
+4
View File
@@ -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": [