fix(gen/assign): 模块级与 nonlocal 分支补写赋值注解上下文通道

模块级 AnnAssign 的 mod_glob 分支与 global 语句分支、以及 nonlocal 分支此前均未写入 trans.CurAssignAnnot/CurAssignNode,导致模块级/闭包内 s: Stack[int] = Stack() 在泛型裸名构造处 fail-fast、x: int = c.Deref(p) 退化为 i8 单字节读取

按主分支既有先例(AssignGenMixin set 在 RHS 翻译前 / clear 在返回前)在三处分支补齐同款 set/clear,并覆盖全部分支的提前 return 与末尾出口,避免通道残留污染后续 c.Deref 读取宽度与泛型构造类型实参推断

影响面: 全仓 模块级/global/nonlocal 的 X: T = c.Deref(...) 与 X: Generic[...] = Ctor(...) 均 0 处,属纯增量
This commit is contained in:
TermiNexus committed 2026-09-15 21:52:42 +08:00
1 parent 5a59432733
commit 617d4ea911
1 file changed
+57
+57
View File
@@ -1487,6 +1487,14 @@ class AssignGenMixin:
aa_glob_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
aa_glob_ty = mod_alloca.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支(普通局部变量路径)同一口径——必须在翻译 RHS 之前写入
# CurAssignAnnot/CurAssignNode。四处(主分支 / 本 global 分支 /
# 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步,否则
# `x: int = c.Deref(p)` 的读取宽度与 `s: Stack[int] = Stack()`
# 的类型实参推断都会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, aa_glob_ty)
@@ -1515,6 +1523,10 @@ class AssignGenMixin:
builder, stg_ty_aa, rhs_val)
if stg_obj_val_aa is not None:
llvmlite.build_store(builder, stg_obj_val_aa, stg_ref_aa)
# 清除赋值上下文(防残留):与主分支清空时机同款——
# 提前 return 前必须清,否则泄漏到后续语句
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
target_ty: llvmlite.LLVMType | t.CPtr = None
if mod_alloca.Ty is not None:
@@ -1522,6 +1534,12 @@ class AssignGenMixin:
if target_ty is not None:
rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans)
llvmlite.build_store(builder, rhs_val, mod_alloca)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 本 global 分支 / 下方 mod_glob 分支 / 下方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续 while 条件/比较等处的 c.Deref 被误判读取宽度
# (i8* 按 i32* 读 4 字节),也会污染裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
# nonlocal 变量:通过闭包 env 写入
@@ -1537,6 +1555,14 @@ class AssignGenMixin:
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
if nl_ptr is not None and nl_ptr.Ty is not None:
nl_target_ty = nl_ptr.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支、下方 mod_glob 分支同一口径——必须在
# 翻译 RHS 之前写入 CurAssignAnnot/CurAssignNode。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步,否则
# `nonlocal x: int = c.Deref(p)` 的读取宽度与
# `nonlocal s: Stack[int] = Stack()` 的类型实参推断都会拿不到注解。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, nl_target_ty)
@@ -1547,10 +1573,20 @@ class AssignGenMixin:
VLogger.error(
f"nonlocal 变量类型信息丢失,无法写入: {nm.id}",
"HandlesAnnAssign")
# 清除赋值上下文(防残留):与主分支清空时机同款——
# 提前 return 前必须清,否则泄漏到后续语句
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
rhs_coerced: llvmlite.Value | t.CPtr = self.Gen.ExprGenBase.coerce_to_type(
builder, rhs_val, nl_target_ty, node)
llvmlite.build_store(builder, rhs_coerced, nl_ptr)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# global / mod_glob / 本 nonlocal 分支)必须同步清除,否则残留注解会让
# 后续 while 条件/比较等处的 c.Deref 被误判读取宽度(i8* 按 i32* 读 4
# 字节),也会污染裸名泛型构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
# 模块级全局变量(由 handle_module_level_var 注册到模块作用域)
@@ -1572,6 +1608,13 @@ class AssignGenMixin:
mg_val_ty: llvmlite.LLVMType | t.CPtr = None
if mod_glob.Ty is not None:
mg_val_ty = mod_glob.Ty.Pointee
# 供 c.Deref(i8*) 推断读取宽度 / 裸名泛型构造从注解推断类型实参:
# 与主分支、上方 global 分支同一口径——翻译 RHS 前写入
# CurAssignAnnot/CurAssignNode。模块级 `s: Stack[int] = Stack()`
# 走的就是本分支,四处(主分支 / global / 本 mod_glob 分支 /
# 上方 nonlocal 分支)必须同步。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
rhs_val_mg: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
builder, pool, mod, aa.value,
None, 0, trans, mg_val_ty)
@@ -1600,6 +1643,10 @@ class AssignGenMixin:
builder, stg_ty_mg, rhs_val_mg)
if stg_obj_val_mg is not None:
llvmlite.build_store(builder, stg_obj_val_mg, stg_ref_mg)
# 清除赋值上下文(防残留):与主分支清空时机同款,
# 提前 return 前必须清,否则泄漏到后续语句
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
target_ty_mg: llvmlite.LLVMType | t.CPtr = None
if mod_glob.Ty is not None:
@@ -1607,6 +1654,12 @@ class AssignGenMixin:
if target_ty_mg is not None:
rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node)
llvmlite.build_store(builder, rhs_val_mg, mod_glob)
# 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 /
# 上方 global 分支 / 本 mod_glob 分支 / 上方 nonlocal 分支)必须同步清除,
# 否则残留注解会让后续语句的 c.Deref 被误判读取宽度,并污染裸名泛型
# 构造的类型实参推断
trans.CurAssignAnnot = None
trans.CurAssignNode = None
return 0
# 单参 t.CArray[elem_ty] + 列表字面量 → 按静态数组处理(count 从字面量长度推断)
@@ -1757,6 +1810,9 @@ class AssignGenMixin:
# 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → 读 i32
# CurAssignNode 记录 RHS 节点:translate_c_deref 据此判定 Deref
# 是否为赋值直接 RHS(对齐 TPC _infer_deref_target_type)。
# 注意:本主分支(普通局部变量)、上方 global 分支、mod_glob 分支、
# nonlocal 分支四处的 set/clear 必须同步(模块级 AnnAssign 走
# mod_glob;闭包内 AnnAssign 走 nonlocal)。
trans.CurAssignAnnot = aa.annotation
trans.CurAssignNode = aa.value
# P1-A:局部变量 alloca 的 pointee 类型作为 target_ty(同全局分支),
@@ -1878,6 +1934,7 @@ class AssignGenMixin:
# 清除赋值上下文(防残留):CurAssignAnnot/CurAssignNode 只在该
# AnnAssign 的 RHS 翻译期间有效,否则后续 while 条件/比较等处的
# c.Deref 会被残留注解误判读取宽度(i8* 被 bitcast i32* 读 4 字节)。
# 四处(本主分支 / global / mod_glob / nonlocal)必须同步清除。
trans.CurAssignAnnot = None
trans.CurAssignNode = None