diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 5be5d97..b4da4ff 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -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 diff --git a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp index 6aea00f..474e34d 100644 --- a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp @@ -550,9 +550,16 @@ class StmtGenCifMixin: else: val = self_ptr if val is None: + # P1-A(上下文位宽传播):把当前函数的返回类型作为 target_ty 传入, + # 使 `return 12345`(-> uint64)直接物化 i64 常量,与 `return u64(12345)` + # 一致,并省去下方返回类型对齐生成的 zext。 + ret_val_ty: llvmlite.LLVMType | t.CPtr = None + cur_func_rv: llvmlite.Function | t.CPtr = trans._cur_func + if cur_func_rv is not None: + ret_val_ty = llvmlite.function_get_ret_ty(cur_func_rv) val = self.Gen.ExprGen.translate_value( builder, pool, mod, rt.value, - trans._funcs, trans._func_count, trans) + trans._funcs, trans._func_count, trans, ret_val_ty) # val 为 None 时(裸 return):检查当前函数返回类型 # void 函数(如 __init__/__before_init__)生成 ret void,