Commit Graph
832 Commits
Author SHA1 Message Date
TermiNexus 41674d125c fix(gen): 修 vpi 生成阶段 is_file_func 读到已回收池的悬垂表(ASan 野读崩溃)
现场:TransPyV.exe(ASan)编译 EscapeOkTest 崩在 Phase A-pre-vpi →
_GeneratePyiFromAst → _PyiFunction → compute_escape_summary → _ScanEscapes →
_PushInductionEntry → is_file_func → HashTable._slot_hash
(access-violation on unknown address 0xffffffffffffffff)。

根因(潜伏已久,非本阶段语义改动引起):
- FileClassNames / FileFuncNames / FileVarNames / FileModuleSha1 是按文件重建的表,
  表与键共用该文件的瞬时池;契约要求跨文件/跨阶段先清空再重建
  (原写在 reset_visible_structs 里那四行)。
- Phase A-pre-vpi 为生成 vpi 会重新 lex/parse 每个文件,但不走 register_file_*、
  也不清表 → _PushInductionEntry 的 is_file_func 只认 range 的判据,落在 Phase1
  最后一个文件的表上,而那个池已被 reset_to(mark1) 回收。非 ASan 下常表现为读到
  0/旧值 的静默错答,ASan 下 0xfd 填充 → 垃圾容量/垃圾 __slots__ → 野读崩溃。

修法:
- 抽出 reset_file_name_window() 作为文件级名字窗口的唯一开启入口(原四行清空搬进去,
  reset_visible_structs 改为调它),并在注释里写死契约。
- 新增轻量 register_file_func_names(pool, tree):只重建本文件顶层函数名(核对过 vpi
  路径只需这一张;FileClassNames/FileModuleSha1/FileVarNames 的读取点全在翻译期)。
- Phase A-pre-vpi 循环里,在 _GeneratePyiFromAst 之前调用
  reset_file_name_window() + register_file_func_names(mb, tree_vp2)。
- register_file_classes / register_file_funcs 改为无条件重建(原来 没有才建 的写法在
  表已被别的阶段建过时会静默复用悬垂表)。
2026-10-10 09:41:15 +08:00
TermiNexus 79d1c86982 fix(gen): return 本帧存储改为声明感知 fail-fast(A2 方案 A)
A2 原本整体移除定义端 return <本帧地址> 的 fail-fast,导致无 @life(ret(...))
声明的函数返回本帧存储时静默通过(调用点只拿到 LIFE_UNKNOWN,悬垂一路无声)。
现改为按被调者自己的 ret 声明判定:
- ret(frame) / ret(borrows=<该形参>) → 放行;
- ret(heap|static|borrows=none) → 报错(声明与返回值矛盾);
- 无声明 → 报错(提示补 @life(ret(frame)) 或 ret(borrows=<param>))。
落点:AssignGenMixin.check_return_frame_decl(判据复用 life_of_expr,target_ty 传
返回类型,与移除前的口径一致);ret_kind 由 StructGenMixin.get_ret_kind_by_func
反查 EscFuncTable → EscDirTable(顶层函数/方法/嵌套 def 一次覆盖,无需猜类名),
当前 FunctionDef 取 StructGenMixin.NllCurFunc。
新检查是旧检查的严格子集 ⇒ 自举无回归。
2026-10-10 09:16:26 +08:00
TermiNexus 6da8e2e095 feat(gen): 落地 @life(ret(...)) 返回值生命期契约(A2)
- GenApi:新增 compute_life_anno / _RetKindFromTag 与 RET_KIND_* 编码,
  提供 get_ret_kind / get_ret_kind_method / lookup_ret_kind_via_vpi;
  _WillFreeMergeTag 不再拦截 ret(改由 compute_life_anno 收集校验)。
- StructGenMixin:EscDirTable 由 3 槽扩到 4 槽(新增 ret_kind 列),
  compute_escape_summary 把契约落在 box[5] 并登记。
- Stub:.vpi 行尾新增 escr=<ret_kind>(Converter 写、VpiReader 读 _g_vpi_esd_r)。
- AssignGenMixin.life_of_expr(Call):@life(ret(...)) 声明优先于任何推断;
  frame/heap/static/borrows=none 直接映射,borrows=x 取对应实参生命期,
  方法调用按接收者静态类型解析类名。
- StmtGenCifMixin:同批移除定义端 return <本帧地址> 的 fail-fast,
  返回值去向改由调用点按被调者契约判定。
2026-10-09 22:58:33 +08:00
TermiNexus 4907e64714 feat(lifetime): _g_new_life 透传——new stack/slife 的构造也算 FRAME(参与构造器同命裁剪)
新增模块级 _g_new_life 与 _g_new_storage 同步维护(set/restore 支持嵌套 ctor),口径与 life_of_expr 的
New 分支一致:stack(含 stack slife)/slife → LIFE_FRAME;heap·memgr + 缺省/hlife → LIFE_HEAP;-1 = 未知。
_translate_struct_ctor 两份副本的 recv_life_ct 改为:裸 T() → FRAME;有 new → 取 _g_new_life。
于是 new stack S(&l) / new heap slife S(&l) 不再误报,new heap hlife S(&l) 仍报。
2026-10-09 22:43:43 +08:00
TermiNexus 953c829e9f feat(lifetime): 构造器路径填 recv_life——裸 T() 构造(入口块 alloca)算 FRAME
_translate_struct_ctor 两份副本都算 recv_life_ct:_g_new_storage == None(无 new 描述符提供存储)→
裸 T() 的存储是入口块 alloca → LIFE_FRAME(参与构造器同命裁剪);非空(存储来自 new …)→ -1 不裁剪。
两处 __init__ 调用点(:171 / :675)均传入。覆盖 CtorHolder(&l) / BoxCtor(&l) 这类裸构造误报。
2026-10-09 22:40:30 +08:00
TermiNexus 357a8bba05 feat(lifetime): 构造器路径同命裁剪的透传管道(recv_life,默认 -1 = 现状不变)
_call_method_on_ptr 新增 recv_life: int = -1(被构造对象的生命期,由调用方 CallGenCtor 算出);
构造器分支在指令最终裁决之后,若 recv_life ∈ {FRAME, VALUE} 则复用 _FrameArgsEscapeClearsMethod
(recv_node=None,接收者已由 recv_life 判定)清"写进形参对象字段"那类位。
下一轮只需在 3 个 __init__ 调用点(CallGenCtor:171/665、ExprGenConst:257)填 recv_life;未填 = 完全现状。
2026-10-09 22:36:09 +08:00
TermiNexus bf5ecd7668 fix(lifetime): 方法路径补上"同命裁剪"(此前缺失)—— h.set_q(l) 不该报
现场:h: S* = new heap slife S()(生命期本就是 FRAME:作用域出口释放)+ l 是栈数组;
h.set_q(l) 却报"escaping method parameter"。根因:同命裁剪只做在模块级函数路径,方法路径一直没接
(此前已明确告知"还差一半")。

新增 CallGenMixin._FrameArgsEscapeClearsMethod:位 0 = 接收者(self)、位 (i+1) = 用户第 i 个实参;
某位可清 ⇔ 该位对应实参是 FRAME/VALUE 且其余全部实参(含接收者)也在本帧。
CallGenCall 方法路径在 refine 之后、指令最终裁决之前套用;硬位仍用 get_param_escapes_hard_method 排除。
构造器路径暂未接(__init__ 的 self 是"正在构造的对象",此处拿不到接收者 AST 变量)。
2026-10-09 22:30:35 +08:00
TermiNexus 6000734704 feat(life): A1——@life 新增 stores(x)/borrows(x)(复用 escape/noescape 契约位);ret/owns 显式报未实现
§14 路线 A 第一步(零语义变化):
- stores(x) 归到"强制逃逸"位、borrows(x) 归到"强制不逃逸"位(同一对契约位,调用点已在最后套用、.vpi 已有 esce=/escn= 通道)
- _WillFreeMergeTag 白名单同步扩 stores/borrows;ret/owns 显式 fail-fast "not implemented yet (planned A2/A3)",不做静默忽略
- 矛盾契约报文改为 escape/stores 与 noescape/borrows 两两对立
2026-10-09 22:14:26 +08:00
TermiNexus 71730d920c chore(dbg): 删除 NLL 临时诊断(§13 已在 EscapeTest 上验证通过)
实测:var=h confined=1,g(l,h) 不再报错;报错前移到真正的泄漏点 `if 1==0 or a == 1: return l`(where=a return value)。
2026-10-09 21:59:48 +08:00
TermiNexus 0f504a9010 chore(dbg): 给 NLL 谓词插桩([NLL-ST] 逐条语句 + [NLL-UNSAFE]/[NLL-MENTION] 拒绝原因),定位后删除 2026-10-09 21:53:44 +08:00
TermiNexus 960ecdfd74 fix(nll): 补 BoolOp/Compare 的字段递归(if 1==0 or a == 1 曾把整条判据否掉)
_init_ast 里 node.children = None 是**默认值**(只有语句体这类显式赋值的才有子树),所以"认不出的表达式
一律保守"这条策略会被任何未列举的表达式种类触发。新用例的 if 条件 BoolOp(values) 与 Compare(left/comparators)
就是这种。补上后 h 应能降级,报错位置回到真正的泄漏点 return l(第 19 行)。
2026-10-09 21:34:17 +08:00
TermiNexus 5a10a2ea10 fix(nll): 谓词改为按字段显式递归(AST 表达式节点不填 children,导致 new stack [2]char 被误判"提及")
根因([NLL-DBG] var=l 已证实):_set_parent_list 只设父指针、不填 children,故 New/Attribute/Call 等表达式节点
的 children 基本都是 None。原实现把"children==None"当作"提及/拿不到子树",于是 k 里第一条
`l = new stack [2]char` 就把整条判据否掉(var=h confined=0)。

现在 _NllMentions / _NllExprUnsafe 均按字段显式递归:Name/Constant/Attribute/Subscript/Call/New/UnaryOp/
BinOp/IfExp/Tuple|List|Set;认不出的种类保守(提及=1 / 不安全=1)。_NllStmts 的"认不出的语句"
分支改为按子节点判(无子节点的 pass 之类放行)。
2026-10-09 21:23:51 +08:00
TermiNexus e82e2ac768 chore(dbg): 加 NLL 判定链临时诊断([NLL-DBG]),定位后删除 2026-10-09 21:05:31 +08:00
TermiNexus 337be883d3 fix(nll): Attribute 节点显式只递归 .value,避免"拿不到子树=算提及"把 stdio.printf(...) 误判成不安全
现场:EscapeTest 的 h = new heap S() 永不降级(g(l,h) 照旧报逃逸)。根因是 _NllMentions 对 Attribute
落进保守分支(children 不含 value 时返回"提及")→ _NllExprUnsafe 把 stdio.printf("%s", h.q) 判为不安全
→ 整个 NLL 判据返回 0。属性名是静态字符串,变量只可能在基对象里,显式递归 .value 即可。
2026-10-09 20:57:31 +08:00
TermiNexus d0ffb094c2 feat(lifetime): §13 NLL——默认生命期的 new heap 证明帧内封闭时降级为帧对象
StructGenMixin: 新增 NllFrameConfined 及遍历器(_NllStmts/_NllExprUnsafe/_NllMentions/_NllInSet/_NllAdd/_NllCalleeEscMask)+
has_param_escapes(区分"有记录=权威不逃逸"与"无记录=判不定");别名集只增不减故单遍即可,if/while/for 全走、
不进嵌套 def/class;认不出的语句"不提及别名才放行";取址/实参位逃逸/return/长命目标一律按外泄拒绝。

CallGenCtor._new_is_nll_demotable:只降级"堆且未显式指定生命期"的 new;AssignGenMixin.propagate_life
判据成立则 life=FRAME(于是调用点同命裁剪放过 g(l,h)),_slife_note_assign 接 target_name、判据成立按托管堆
对象登记 → 复用 slife 的作用域出口释放(手动 free/del 也会被既有检查挡住)。

三条函数体翻译入口记录 NllCurFunc(谓词要遍历当前函数体),与 traceback 推帧同一处。

注:谓词按 §13.2 保守优先,判不定一律维持 HEAP;本提交未编译验证。
2026-10-09 20:49:21 +08:00
TermiNexus 79299ec47d feat(lifetime): 声明式指令契约落 .vpi(esce=/escn=),跨模块调用点也能最终裁决
写侧 Converter:读 compute_escape_summary 落在 box[3]/box[4] 的契约掩码,随函数头行尾写 esce=<强制逃逸位> / escn=<强制不逃逸位>;
只有指令(既无 esc/ret 也无 wf)时也补一个 "# esc=0 ret=0" 注释段——读侧是"见 # 才解析"。

读侧 VpiReader:_cache_vpi_esc_clears_text 同一次行扫描里顺带收 esce=/escn= 进 _g_vpi_esd_*(与 escg 同键 sha1[.Cls].func);
新增 _lookup_vpi_escape_dir(.., noescape);CallGenBase.lookup_escape_dir_via_vpi 包装。

调用点:模块级函数 / 方法 / 构造器三条路径在本文件指令表为空(= 跨模块)时改查对方 .vpi,
仍放在所有推断/回退之后最后套用:esc_mask = (esc_mask | esce) & ~escn。
2026-10-09 20:40:30 +08:00
TermiNexus 2bd37e80e3 feat(lifetime): 方法/构造器调用点也接 @life(escape/noescape) 最终裁决
StructGenMixin: 新增 _DirForceOf / _DirForceMethod(查 "Class.method" 并沿 MRO 回退)与
get_param_escape_force_method / get_param_noescape_force_method;GenApi 加同名委托。

CallGenCall: 构造器分支(esc_mask_ci)与方法分支(esc_mask_mc)在跨模块回退、常量特化之后
最后套用一次 esc_mask = (esc_mask | escape_force) & ~noescape_force,与模块级函数那条口径一致。
2026-10-09 20:34:34 +08:00
TermiNexus fc7c5d5560 feat(lifetime): @life(escape/noescape) 升为调用点最终裁决(契约不可被推断推翻)
StructGenMixin: 新增 EscDirTable(3 槽/项:[名哈希, 强制逃逸位, 强制不逃逸位]),
_RegisterFuncEscapeSummary 把 compute_escape_summary 落在 box[3]/box[4] 的指令掩码单独落表
(与 EscTable 的"推断结果"分开);新增 _DirLookup / get_param_escape_force / get_param_noescape_force。
compute_escape_summary 的指令输出缓冲由 g+68 改为 box[3](同一内存,语义显式化)。

CallGenMixin: 在常量特化、同命裁剪、跨模块回退**全部做完之后**,最后套用一次指令:
esc_mask = (esc_mask | escape_force) & ~noescape_force。这样 escape(...) 不会被"死分支/同命"折掉,
noescape(...) 也不会被任何上游路径重新解释成"逃逸"。
2026-10-09 20:30:03 +08:00
TermiNexus 8881dea2e2 fix(diag): 泄漏点采集改为"确认报错后才跑",并保存/恢复扫描期状态
原先在调用点的位循环里就调 escape_leak_line:该函数会重扫被调者函数体并改 EscConstEnv/EscIndCount,
而循环里可能紧接着发现"该实参其实不是帧生命期"从而不报错 —— 等于白白跑一遍扫描、还留下脏状态。
现在改成把 (被调者, 形参位) 传给检查方,过了 LIFE_FRAME 判定才采集;escape_leak_line 内部
把 EscConstEnv/EscIndCount/EscLeakWant 先存后还。
2026-10-09 19:50:31 +08:00
TermiNexus 2353dca8ec feat(diag): 逃逸类 LifetimeError 追加 File lifetime leak 帧,标出泄漏点
StructGenMixin: 新增 escape_leak_line——错误路径重扫被调者函数体,取指定形参的**首个逃逸点**行号;
_NoteLeak 在三处置位来源采集(字段/全局写、return 形参、传递闭包调用),新增 EscLeakWant/EscLeakLine。

Diag: DiagEntry 增加 LeakFileName/LeakLine + set_leak + emit_leak_frame(标签 File lifetime leak),
打印顺序 = 调用链 → 错误点 → 泄漏点 → 类别: 消息。

TypeGenErr.fatal_error / GenApi.fail_if_stack_addr_escape 增加 leak_file/leak_line 透传;
CallGenMixin 调用点按失败实参位取行号。**仅同模块**:跨模块 EscFuncTable 无 AST → 返回 0,不打该帧。
2026-10-09 19:32:54 +08:00
TermiNexus c98730f717 refactor(diag): 精简 LifetimeError 报错词,去掉冗长从句
逃逸主报文由三段长从句压缩为「frame-local + 悬垂 + 修法」两句:
  stack address escapes: `&x` is frame-local; using it at {where} hands out a dangling pointer
  once this frame returns. Copy the value, or allocate on the heap (`new heap hlife ...`).

同步缩短 where 短语(return value / escaping callee|method parameter / constructor parameter stored
into a field / longer-lived field-element / module-level global)

其余冗长报文一并精简:willFree 失效、slife 手动 free/del、async slife、stack+hlife 矛盾、三处容量上限、
@life 标签实参缺失

测试只断言关键字 LifetimeError,不受文案影响(Test/*.py 已确认)。
2026-10-09 19:22:04 +08:00
TermiNexus 007943142a fix(diag): DiagEntry.File 改名 FileName,避开与 fileio.File 的非限定名相撞
现场:TPC 编译 Diag.vp 时 read_source_line 的 fileio.File(...) 报 llvmlite
"Type of #1 arg mismatch: i8** != %"1f18c0c34662cbfb.File"*"
(1f18c0c34662cbfb = fileio 的模块 sha1,即 self 形参被解析成 i8**,实参本是正确的 fileio.File*)。

Diag.vp 是唯一一个"类字段名 == 同模块 import 的类名"的模块(全仓仅此处有 File: 字段),
而其余 24 个调用 fileio.File(...) 的模块(含 BuildPipeline.vp 的 6 个类)都无此字段且编译正常。
TPC 的 Gen.structs / SymbolTable / _mangle_func_name 均按非限定名索引,故改名规避。
2026-10-09 19:12:10 +08:00
TermiNexus 6769cf0335 refactor(diag): 编译期 traceback 抽为 Diag 模块,单条诊断携带完整调用链
- 新增 Diag.DiagEntry 与 new_entry/begin/set_current/add_frame/emit/free_entry,方向由 DIAG_DIR_* 决定

- TypeGenErr.fatal_error 改走 DiagEntry:填调用链 + 当前帧,emit(UP) 后 free + finish

- 顶层函数体/嵌套函数体/方法体三处翻译入口各 push/pop 一帧(def 位置),报错可回溯所在函数
2026-10-09 18:45:06 +08:00
TermiNexus c791bd5e46 feat(new): new 支持标量/数组与前导 [N]T;类名解析不出时回落基本类型路径
- CallGenCtor._translate_new_primitive:认 Array 元素类型,返回值退化为首元素指针,零初始化按整体类型写
2026-10-09 18:45:01 +08:00
TermiNexus fc797da542 feat(lifetime): @life(escape/noescape) 多形参、硬逃逸位同命精化、归纳变量区间与跨模块常量特化
- GenApi:escape/noescape 标签支持多实参;新增 compute_escape_directives 与若干委托

- StructGenMixin:硬逃逸位(box[2])、常量与归纳变量区间折叠、_PackEscapeClear/compute_escape_clears

- Call 系:调用点同命裁剪(_FrameArgsEscapeClears)+ 跨模块 escg= 查询;写侧 Converter、读侧 VpiReader

- AssignGenMixin:裸 T()(无 new、无 __new__)判为帧生命期
2026-10-09 18:44:51 +08:00
TermiNexus 14a8dcb98b feat(gen): new 支持基本类型 + 跨过程常量特化重算逃逸摘要
- CallGenCtor._translate_new_primitive:
ew [stack|heap|memgr(x)] T(T 为标量)取存储 + 零初始化 → T*;stack 走入口块 alloca、heap 走 malloc、memgr 复用既有通道;标量带实参 fail-fast,不引入第二套标量构造语义

- StructGenMixin:新增 EscFuncTable / EscRefineScratch / EscConstEnv 与常量求值器 _EvalConstInt / _EvalConstCond;_ScanEscapes 的 if 分支按形参常量折叠只扫被走到的分支(死分支里的写不记为逃逸)

- 调用点(普通函数 + 方法,方法沿 MRO 回退)在逃逸检查前用常量实参重算摘要;跨模块拿不到函数体仍保守返回原掩码

- 折叠只会减少逃逸位,不引入新的误报
2026-10-09 14:55:28 +08:00
TermiNexus ab30b264fc feat(gen): P3 覆盖注解形态的属性/下标目标
- translate_annassign 原先对非 Name 目标直接 return 0,obj.f: T = v / rr[i]: T = v 整条语句被静默丢弃(既不报错也不写出)

- 新增 _translate_lvalue_annassign:解析注解(先特化泛型)→ get_attribute_ptr / get_subscript_ptr 求左值指针 → 走同一处 P3 长命左值检查 → RHS 以注解为靶型求值 → coerce_to_type 后写出;const / volatile / f-string 逃逸与普通 Assign 同口径

- 所有失败路径 fail-fast,不再有静默丢语句
2026-10-09 14:55:10 +08:00
TermiNexus d926987d44 chore(dbg): 清除 P4 转移分支的逐点探针
崩点根因已定位(slife 登记表跨文件悬垂),debugger 探针全部移除。
2026-10-09 13:04:40 +08:00
TermiNexus eb0c3dfd31 fix(gen): slife 登记表改为跨文件存活,并修正区间弹回
- _g_slife_owned 原先从 trans.Pool(每文件池)分配,Phase2 每文件 reset_to(mark2) 后表体即悬垂,第二个文件起每次登记都写坏活跃池数据(现场 access-violation on 0x0,崩点随分配漂移、ASan 无法给栈)

- _SlifeRestore 只还了 count、未还 top,top 随函数数量单调增长;上一文件/函数的陈旧条目留在扫描区间内,因每文件池地址复用被误判为新变量,_SlifeShadow 返回悬垂影子槽,_SlifeNoteStore 直接写坏内存

- 新增 _g_slife_save/_g_slife_depth:进入时保存 count/top 两个边界,退出时一起弹回

- _SlifeDropOwnedUnlessSame 补 Pointee==0 守卫(与 _SlifeEmitGuardedFree 同口径)
2026-10-09 13:04:26 +08:00
TermiNexus 1a3ef521b3 chore(dbg): P4 转移分支逐点探针(owned/begin/ensured/drop.begin/dropped)
崩点已后移到 CaseOwnershipTransfer 的 q = p(P4 别名转移),逐点定位该分支内具体崩溃位置。TEMP。
2026-10-09 12:43:59 +08:00
TermiNexus aeb396831a chore(dbg): 清除 CallGenMixin 残留的 call.enter 探针 2026-10-09 12:43:28 +08:00
TermiNexus 6758ecbf69 chore(dbg): 清除最后一处 TEMP 探针(AssignGenMixin slife.note) 2026-10-09 12:03:43 +08:00
TermiNexus 5f92abb7bc fix(gen): 补回 CallGenMixin 的 _FreedMark(修行尾时被 checkout 回滚)
TPC 报 MemberLookupFailFast: 属性 Freed 在 VarEntry 中找不到 —— 该文件的行尾修复用 git checkout 恢复过,把未提交的 _FreedMark 改动一并回滚了,残留 ent_wf.Freed = 1。
2026-10-09 12:03:23 +08:00
TermiNexus dd24dc670f fix(gen): P2 失效状态改存独立槽表,不再给 VarEntry 加字段
现象:任何工程编译都在函数形参注册刚结束处 access-violation(pc=0,逐字节可复现)。定位:逐层探针把崩点压到 FuncGenDef 的 pre_scan 段,而该段对 Check(body 仅 if+return)应为空操作,唯一变量是 VarEntry 这个热结构被加了 Freed 字段。修法:撤销 VarEntry.Freed,改用 VarGenMixin 的独立失效槽表 _g_freed_slots(键 = 变量槽 alloca,整次翻译内唯一,故全局累积、无需按函数 reset/restore);_FreedMark/_FreedCheck/_FreedClearSlot 三个 API 替换字段读写,P2 语义不变。同时清除全部 TEMP 探针。
2026-10-09 11:51:11 +08:00
TermiNexus 95b076581a chore(dbg): pre-scan 与 _SlifeReset 之间补 p1d 探针(TEMP)
p1c 之后仍崩,最后一段只剩 pre-scan 与 _SlifeReset;p1d 把两者分开。
2026-10-09 11:30:33 +08:00
TermiNexus 054dcf0a0b chore(dbg): 形参注册与 pre-scan 之间补 p1c 探针(TEMP)
add_var 三次全过(esz=56),崩点在其后。p1c 夹出 L700-763(状态保存)与 L765-775(pre-scan)两段。
2026-10-09 11:24:29 +08:00
TermiNexus 07ff906338 chore(dbg): add_var 逐行探针 a1..a8(TEMP)
崩点落在形参注册段,而这段里唯一的结构性改动是 VarEntry 加了字段。逐行埋点定位到具体哪一步(esz/cnt 也会打出来,可用于核对 sizeof 是否异常)。
2026-10-09 11:12:31 +08:00
TermiNexus a2e1c651fc fix(gen): 变量表 Freed 不再逐个字段写,统一依赖 init_vars 的整块清零
崩点已定位在普通函数形参注册段(auto_addr 之后、_SlifeReset 之前)。add_var 里对刚加过字段的 VarEntry 末端逐个写 entry.Freed 疑似越界/踩偏移;init_vars 本就 memset 整块,去掉冗余写入。
2026-10-09 11:01:08 +08:00
TermiNexus 9b97470c09 chore(dbg): 函数翻译内段探针 p1/p2/p3
夹住 auto_addr → 形参注册/pre-scan → slife reset → 语句翻译 四段;TEMP,定位后移除。
2026-10-09 10:44:56 +08:00
TermiNexus 20e3f3694f chore(dbg): 补语句级与调用级探针(stmt.kind / call.enter fn)
崩点已收窄到 Check 函数体内的翻译,这两个探针把范围压到具体语句与具体被调者。均为 TEMP,定位后移除。
2026-10-09 10:39:24 +08:00
TermiNexus 400cc6e17b chore(dbg): 再加两个探针(函数名 / slife 赋值 rhs kind)
TR.fn: <name> 定位到具体函数;slife.note rhs_kind=<k> 定位到 _slife_note_assign 的哪条 RHS 路径。均为 TEMP,定位后移除。
2026-10-09 10:19:29 +08:00
TermiNexus be2521fe46 chore(dbg): 临时阶段日志定位 Phase A 崩溃点(定位后移除)
四个探针:PhaseA translating <path> / vpi.gen <rel_path> / TR.enter sha1 / TR.prelude.done。用于把崩溃点夹在 解析→vpi生成→模块前言→函数体翻译 四段之间。
2026-10-09 10:08:43 +08:00
TermiNexus 8b63ae4ca2 fix(gen): 模块前言的声明池逐级判空,避免 PermPool 未就绪时空指针崩溃
现象:TransPyV.exe 编译任何工程都 AddressSanitizer access-violation on address 0x0(pc=0)。根因:_emit_module_prelude 跑在每个模块翻译的开头,此时 CompilerFactory.factory 或其 PermPool 可能尚未就绪,直接 factory.PermPool.alloc(...) 解引用空指针(PermPool 是 factory 的首字段,偏移 0,与 ASan 报的地址 0x0 吻合)。改为 factory / PermPool 逐级判空,不可用时回退模块池。
2026-10-09 09:45:36 +08:00
TermiNexus b71ed43f10 feat(gen): P5 slife 登记表容量提到 256,超限 fail-fast
分配与上限判定共用 _SLIFE_MAX = 256(原 128 且满时静默停止登记)。_SlifeEnsureOwned 超限改为 fatal_error(LifetimeError):静默返回 None 会让该对象永不登记、永不释放(纯泄漏),与编译器自动管理的承诺矛盾。
2026-10-09 08:46:24 +08:00
TermiNexus efb4a54428 fix(gen): P4 自别名重复赋值不得释放共持对象
q = p; q = p 这类自别名重复赋值在覆盖释放时会把两个槽共持的对象提前释放(q 与 p 同时悬垂)。新增 _SlifeDropOwnedUnlessSame:在 IR 里比较 target 与源槽的当前值,相同则跳过整个覆盖释放(含析构调度),不同才走常规保护式释放。P4 转移改用它。
2026-10-09 08:45:09 +08:00
TermiNexus 3209a7a633 feat(gen): P4 别名赋值改为所有权转移(影子标志从源槽移到目标槽)
q = p 时若源槽确实拥有 slife 对象,则把所有权影子标志转移:先按覆盖释放处理目标槽原有对象,再置 q=1 / p=0。于是 q = p; p = None 不再释放对象(q 不悬垂),出口只释放 q 恰好一次。自赋值 p = p 与源不拥有(普通/外来指针)保持原状。
2026-10-09 08:43:48 +08:00
TermiNexus 301a9d3328 feat(gen): P3 左值比右值长命时禁止写入本帧栈地址
新增 AssignGenMixin.life_of_lvalue:Name 目标取变量槽本身的生命期(局部 FRAME / 全局 STATIC),Attribute/Subscript 目标递归其基对象的 life_of_expr(h.p 的生命期 = h 指向的存储)。在 propagate_life 的非 Name 目标出口比较:仅当左值明确长命(LIFE_HEAP/LIFE_STATIC)且右值为 LIFE_FRAME 时报 LifetimeError(复用 fail_if_life_escapes)。h.p = &local 被拒;local_struct.f = &local2 与 p = &local 放行(不误报)。已知缺口:AnnAssign 的属性/下标目标不走该出口。
2026-10-08 22:47:28 +08:00
TermiNexus 2b5ff215c7 fix(gen): 跨模块裸名 CExport/State 函数不再被误清,关键字实参不解析时 fail-fast
现象:stdlib.free(p = p) 生成 llvm.call @free()(零实参),mlir-opt 报 incorrect number of operands (0) for callee。根因:跨模块检查要求函数名前 16 字符等于模块 sha1,而 CExport/t.State 型函数(stdlib.free 等)本来就以裸名定义与声明,于是 found_func 被清成 None → 关键字实参路径整体跳过 → 位置实参循环一个也不翻译。修两处:1) 该检查对 is_cexport_func / _is_state_func_from_vpi 命中的函数放行;2) 带关键字实参却解析不出签名时 fail-fast,不再静默丢弃实参。
2026-10-08 22:38:28 +08:00
TermiNexus 8b2008031d fix(gen): 模块前言 C 库声明名改落 PermPool,避免被回绕的模块池覆盖
现象:@malloc 的声明名被渲染成乱码,mlir-opt 报 llvm.call @malloc does not reference a symbol in the current scope。根因:create_declare 用 _str_dup(pool, name) 把名字拷进传入的池,而前言用的是模块池(大模块上会被复用/回绕),早期分配的声明名被后续分配覆盖;类型是 mlirlite 内部 interned 的,所以只有名字坏、参数类型正常。修法:声明与参数名改落 CompilerFactory.PermPool(never reset)。
2026-10-08 22:30:07 +08:00
TermiNexus 8fb0e38f4c fix(gen): 泛型类构造只在真有 __init__ 时才调 __before_init__/__init__
无方法的泛型类(如 NewTest 的 Box[T])没有 __init__,ClassGenVTable 也就不会生成 __before_init__,但 _translate_generic_ctor_call 无条件发射这两个调用,导致链接期 undefined symbol(1cf2df6387df0bbf.Box[int].__before_init__ / .__init__)。改为与非泛型 _translate_struct_ctor 同判据:有 __init__ 才走 __before_init__ + __init__,否则按字段顺序逐字段 store。
2026-10-08 22:16:23 +08:00