fix(tpv): 泛型注解预特化下沉到 AnnAssign Handle 路径,修 .get() 误分派崩溃
translate_annassign 解析注解前未先注册泛型特化,`list[ast.AST | t.CPtr] | t.CPtr` 会退化为裸 list*,随后 body_list.get(i) 被分派到默认 list[int].get(返回 i32)→ inttoptr 截断指针 → 读垃圾 AST 节点, StmtGenMixin._translate_stmt_impl 崩溃(寄存器指纹与既有潜伏崩溃一致, IR 实证见 Debugger/ViperOS3.md 第十九节)。 抽出 _prespecialize_generic_annotation 供 pre_scan_allocas 与 translate_annassign 共用(该补救原先只存在于预扫描路径),使两处解析一致。 同批(P1-A):赋值/返回路径把目标类型作为 target_ty 传入 translate_value, 使 `x: uint64 = 12345` 与 `u64(12345)` 物化出同一条 i64 常量、省去 coerce 的 zext。站点:translate_annassign(global/nonlocal/模块级/局部四处)、 translate_assign(单 Name 目标)、translate_return。target_ty 仅被 Constant 节点消费,不影响调用/二元/属性路径。
This commit is contained in:
1 parent
d3690f9143
commit
4ce55e3ea3
2 files changed
+124
-40
No files matched your search
@@ -1227,33 +1227,11 @@ class AssignGenMixin:
|
||||
# 泛型特化补救(在解析之前无条件执行,_specialize_generic_class 幂等):
|
||||
# 注解可能是 list[X](Subscript)或 list[X] | t.CPtr(含 CPtr 的 BinOp)。
|
||||
# 若泛型类尚未特化,resolve_annotation_type 会把 list[X] 退化为不透明
|
||||
# 基类 list*(丢掉元素类型)——PreScan 预建的 alloca 因此变成 list*,
|
||||
# 之后 body_list.get(i) 会分派到默认 list[int] 特化(返回 i32)→
|
||||
# inttoptr i32→AST* 截断指针 → StmtGenMixin._translate_stmt_impl
|
||||
# 虚调用读 vtable 崩溃。故先提取 Subscript 一侧主动特化再解析,保证
|
||||
# PreScan 与 Handle 两处解析结果一致。
|
||||
spec_annot: ast.Subscript | t.CPtr = None
|
||||
if annot.kind() == ast.ASTKind.Subscript:
|
||||
spec_annot = (ast.Subscript | t.CPtr)(annot)
|
||||
elif annot.kind() == ast.ASTKind.BinOp:
|
||||
bop_ps: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
|
||||
if bop_ps.op == ast.OpKind.BitOr:
|
||||
if bop_ps.left is not None and bop_ps.left.kind() == ast.ASTKind.Subscript:
|
||||
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.left)
|
||||
elif bop_ps.right is not None and bop_ps.right.kind() == ast.ASTKind.Subscript:
|
||||
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.right)
|
||||
if spec_annot is not None:
|
||||
sv_ps: ast.AST | t.CPtr = spec_annot.value
|
||||
if sv_ps is not None and sv_ps.kind() == ast.ASTKind.Name:
|
||||
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sv_ps)
|
||||
if sub_nm.id is not None:
|
||||
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
|
||||
if template_cd is not None:
|
||||
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(
|
||||
pool, spec_annot.slice, trans._from_imports)
|
||||
if type_args_ps is not None and len(type_args_ps) > 0:
|
||||
self.Gen.ClassGenGeneric._specialize_generic_class(
|
||||
trans, sub_nm.id, type_args_ps)
|
||||
# 基类 list*(丢掉元素类型)——预建的 alloca 因此变成 list*,之后
|
||||
# body_list.get(i) 会分派到默认 list[int] 特化(返回 i32)→ inttoptr
|
||||
# i32→AST* 截断指针 → StmtGenMixin._translate_stmt_impl 读垃圾节点崩溃。
|
||||
# 与 Handle 路径共用 _prespecialize_generic_annotation,保证两处解析一致。
|
||||
self._prespecialize_generic_annotation(trans, pool, annot)
|
||||
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||||
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None:
|
||||
@@ -1299,6 +1277,64 @@ class AssignGenMixin:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
# ============================================================
|
||||
# _prespecialize_generic_annotation - 注解为泛型 Subscript(含 | t.CPtr 联合)
|
||||
# 时先注册特化,保证随后的 resolve_annotation_type 得到「带类型实参」的特化
|
||||
# 结构体(如 {sha1}.list[ast.ASTptr]),而不是退化为不透明基类(裸 list*)。
|
||||
#
|
||||
# 背景(崩溃根因,IR 实证):`body_list: list[ast.AST | t.CPtr] | t.CPtr =
|
||||
# mc.children` 若在 resolve 时该特化尚未注册,注解会退化成裸 list* →
|
||||
# 之后 body_list.get(i) 被分派到默认 list[int].get(返回 i32)→
|
||||
# inttoptr i32→AST* 截断指针 → 读垃圾 AST 节点,
|
||||
# StmtGenMixin._translate_stmt_impl 崩溃。gen1 IR 实证(MatchGenMixin 模块):
|
||||
# %"body_list.1" = alloca %"….list"* ← 裸 list(丢类型实参)
|
||||
# %cast_arg_0.9 = bitcast %"….list"* … to %"….list[int]"*
|
||||
# %call = call i32 @…list[int].get(…) ; 返回 i32
|
||||
# %int2ptr_stmt = inttoptr i32 %call to %"….AST"* ← 截断
|
||||
# PreScan 路径(pre_scan_allocas)此前已有此补救,但 Handle 路径
|
||||
# (translate_annassign)缺失 ⇒ 在任何「未复用预扫描 alloca」的场景
|
||||
# (如循环体内换作用域重新声明同名变量)又会建出裸 list* alloca。
|
||||
# 故抽成公共 helper,两条路径共用,保证两处解析结果一致。
|
||||
#
|
||||
# _specialize_generic_class 幂等,重复调用安全。
|
||||
# 返回 1=执行过特化尝试;0=注解形态不适用(非泛型 Subscript)。
|
||||
# ============================================================
|
||||
def _prespecialize_generic_annotation(self, trans: HT.Translator | t.CPtr,
|
||||
pool: memhub.MemBuddy | t.CPtr,
|
||||
annot: ast.AST | t.CPtr) -> int:
|
||||
"""泛型注解预特化(见上方注释),返回 1=已尝试 / 0=不适用"""
|
||||
if annot is None or trans is None:
|
||||
return 0
|
||||
# 取注解中的泛型 Subscript 一侧:Subscript 本身,或 A | t.CPtr 的 Subscript 分支
|
||||
spec_annot: ast.Subscript | t.CPtr = None
|
||||
if annot.kind() == ast.ASTKind.Subscript:
|
||||
spec_annot = (ast.Subscript | t.CPtr)(annot)
|
||||
elif annot.kind() == ast.ASTKind.BinOp:
|
||||
bop_ps: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot)
|
||||
if bop_ps.op == ast.OpKind.BitOr:
|
||||
if bop_ps.left is not None and bop_ps.left.kind() == ast.ASTKind.Subscript:
|
||||
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.left)
|
||||
elif bop_ps.right is not None and bop_ps.right.kind() == ast.ASTKind.Subscript:
|
||||
spec_annot = (ast.Subscript | t.CPtr)(bop_ps.right)
|
||||
if spec_annot is None:
|
||||
return 0
|
||||
sv_ps: ast.AST | t.CPtr = spec_annot.value
|
||||
if sv_ps is None or sv_ps.kind() != ast.ASTKind.Name:
|
||||
return 0
|
||||
sub_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sv_ps)
|
||||
if sub_nm.id is None:
|
||||
return 0
|
||||
template_cd: ast.ClassDef | t.CPtr = self.Gen.ClassGenGeneric._find_generic_template(sub_nm.id)
|
||||
if template_cd is None:
|
||||
return 0
|
||||
type_args_ps: list[str] | t.CPtr = self.Gen.CallGenCtor._extract_type_args_from_slice(
|
||||
pool, spec_annot.slice, trans._from_imports)
|
||||
if type_args_ps is None or len(type_args_ps) == 0:
|
||||
return 0
|
||||
self.Gen.ClassGenGeneric._specialize_generic_class(
|
||||
trans, sub_nm.id, type_args_ps)
|
||||
return 1
|
||||
|
||||
# ============================================================
|
||||
# translate_annassign - 翻译 AnnAssign 语句(迁移自
|
||||
# HandlesAnnAssign.AnnAssignHandle.Handle)
|
||||
@@ -1349,6 +1385,12 @@ class AssignGenMixin:
|
||||
var_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
annot: ast.AST | t.CPtr = aa.annotation
|
||||
if annot is not None:
|
||||
# 与 PreScan 同源:先注册泛型特化,避免 list[X](含 | t.CPtr 联合)
|
||||
# 在 resolve 时退化为裸 list* → 之后 .get() 误分派到默认
|
||||
# list[int](返回 i32)→ inttoptr 截断指针 → 读垃圾 AST 节点崩溃。
|
||||
# 此处是崩溃根因路径:循环体内重新声明同名变量时不会复用预扫描
|
||||
# alloca,必须在本路径同样先特化(详见 _prespecialize_generic_annotation)。
|
||||
self._prespecialize_generic_annotation(trans, pool, annot)
|
||||
resolved: llvmlite.LLVMType | t.CPtr = self.Gen.TypeGenResolve.resolve_annotation_type(
|
||||
pool, annot, trans._imported_modules, trans._from_imports, trans, 0)
|
||||
if resolved is not None:
|
||||
@@ -1377,9 +1419,15 @@ class AssignGenMixin:
|
||||
mod_alloca: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_module_var(
|
||||
trans.SymTab, nm.id)
|
||||
if mod_alloca is not None and aa.value is not None:
|
||||
# P1-A(上下文位宽传播):把目标变量的类型作为 target_ty 传入,
|
||||
# 使整数字面量按目标位宽直接物化(如 uint64 目标 → i64 const),
|
||||
# 与显式 u64(...) 强转一致,且省去后续 coerce 生成的 zext。
|
||||
aa_glob_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if mod_alloca.Ty is not None:
|
||||
aa_glob_ty = mod_alloca.Ty.Pointee
|
||||
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, aa.value,
|
||||
None, 0, trans)
|
||||
None, 0, trans, aa_glob_ty)
|
||||
if rhs_val is not None:
|
||||
# 对象类全局(有静态 storage):对象构造/赋值 → 绑定 storage + 复制对象
|
||||
stg_ref_aa: llvmlite.Value | t.CPtr = None
|
||||
@@ -1417,18 +1465,21 @@ class AssignGenMixin:
|
||||
# nonlocal 变量:通过闭包 env 写入
|
||||
if GenApi.is_nonlocal_name(trans, nm.id) != 0:
|
||||
if aa.value is not None:
|
||||
# P1-A:先把 nonlocal 槽位指针取出(原先在 RHS 翻译之后),以便把
|
||||
# 槽位类型作为 target_ty 传入;槽位/类型缺失时直接返回,不再发射
|
||||
# 无用的 RHS 死值。
|
||||
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
|
||||
trans, nm.id)
|
||||
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
|
||||
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
|
||||
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
|
||||
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, aa.value,
|
||||
None, 0, trans)
|
||||
None, 0, trans, nl_target_ty)
|
||||
if rhs_val is not None:
|
||||
nl_ptr: llvmlite.Value | t.CPtr = self.Gen.StmtGenDecl.get_nonlocal_var_ptr(
|
||||
trans, nm.id)
|
||||
if nl_ptr is not None:
|
||||
# 从 nl_ptr 的类型推断 nonlocal 变量实际类型,
|
||||
# 不再强制 i32(会截断指针/结构体/i64 导致闭包数据损坏)
|
||||
nl_target_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if nl_ptr.Ty is not None:
|
||||
nl_target_ty = nl_ptr.Ty.Pointee
|
||||
if nl_target_ty is None:
|
||||
# fast-fail: nl_ptr 类型信息丢失,无法安全 store
|
||||
VLogger.error(
|
||||
@@ -1455,9 +1506,13 @@ class AssignGenMixin:
|
||||
self._init_global_array_from_list(
|
||||
builder, pool, mod, mod_glob, aa.value, trans)
|
||||
else:
|
||||
# P1-A:模块级全局目标类型作为 target_ty(同上方 global 分支)
|
||||
mg_val_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if mod_glob.Ty is not None:
|
||||
mg_val_ty = mod_glob.Ty.Pointee
|
||||
rhs_val_mg: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, aa.value,
|
||||
None, 0, trans)
|
||||
None, 0, trans, mg_val_ty)
|
||||
if rhs_val_mg is not None:
|
||||
# 对象类全局(有静态 storage):对象构造/赋值 → 绑定 storage + 复制对象
|
||||
stg_ref_mg: llvmlite.Value | t.CPtr = None
|
||||
@@ -1604,9 +1659,14 @@ class AssignGenMixin:
|
||||
# 是否为赋值直接 RHS(对齐 TPC _infer_deref_target_type)。
|
||||
trans.CurAssignAnnot = aa.annotation
|
||||
trans.CurAssignNode = aa.value
|
||||
# P1-A:局部变量 alloca 的 pointee 类型作为 target_ty(同全局分支),
|
||||
# 使 `x: uint64 = 12345` 直接物化 i64 常量,与 `x: uint64 = u64(12345)` 一致。
|
||||
lv_target_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if alloca.Ty is not None:
|
||||
lv_target_ty = alloca.Ty.Pointee
|
||||
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, aa.value,
|
||||
None, 0, trans)
|
||||
None, 0, trans, lv_target_ty)
|
||||
if rhs_val is not None:
|
||||
# 注解退化修复(对齐 TPC 从构造结果推断类型):
|
||||
# 无实参泛型模板类注解(如 vi: vector.Vector | CPtr,模板
|
||||
@@ -1719,8 +1779,25 @@ class AssignGenMixin:
|
||||
# (防残留误判 c.Deref 宽度)
|
||||
trans.CurAssignAnnot = None
|
||||
trans.CurAssignNode = None
|
||||
# P1-A(上下文位宽传播):单 Name 目标且其存储类型已知时,把存储类型作为
|
||||
# target_ty 传入,使 `x = 12345`(x: uint64)与 `x = u64(12345)` 一致
|
||||
# (直接 i64 常量,省去 coerce 生成的 zext)。多目标/非 Name 目标无法给出
|
||||
# 唯一目标类型(且 RHS 只翻译一次供全部目标共用)⇒ 维持原行为(None)。
|
||||
asg_target_ty: llvmlite.LLVMType | t.CPtr = None
|
||||
if len(targets) == 1:
|
||||
asg_t0: ast.AST | t.CPtr = targets.get(0)
|
||||
if asg_t0 is not None and asg_t0.kind() == ast.ASTKind.Name:
|
||||
asg_nm0: ast.Name | t.CPtr = (ast.Name | t.CPtr)(asg_t0)
|
||||
if asg_nm0.id is not None:
|
||||
asg_slot: llvmlite.Value | t.CPtr = self.Gen.VarGen.lookup_var(
|
||||
trans.SymTab, asg_nm0.id)
|
||||
if asg_slot is None:
|
||||
asg_slot = self.Gen.VarGen.lookup_module_var(
|
||||
trans.SymTab, asg_nm0.id)
|
||||
if asg_slot is not None and asg_slot.Ty is not None:
|
||||
asg_target_ty = asg_slot.Ty.Pointee
|
||||
rhs_val: llvmlite.Value | t.CPtr = self.Gen.ExprGen.translate_value(
|
||||
builder, pool, mod, asgn.value, None, 0, trans)
|
||||
builder, pool, mod, asgn.value, None, 0, trans, asg_target_ty)
|
||||
if rhs_val is None:
|
||||
# Phase A 依赖未就绪(_deps_pending=1):跨模块函数/方法因 text.ll
|
||||
# 未生成返回类型退化为 void/None(CallGenMixin L4493
|
||||
|
||||
Reference in new issue
Block a user