diff --git a/App/lib/StubGen/VpiReader.vp b/App/lib/StubGen/VpiReader.vp index eb3736f..e25b47c 100644 --- a/App/lib/StubGen/VpiReader.vp +++ b/App/lib/StubGen/VpiReader.vp @@ -479,6 +479,26 @@ def _resolve_single_vpi_type_str(pool: memhub.MemBuddy | t.CPtr, return llvmlite.Int32(pool) if string.strcmp(seg, "bool") == 0: return llvmlite.Int1(pool) + # LLVM 类型串:vpi 缓存对无返回注解方法默认存 "i32" + # (见 _cache_vpi_return_annots_text;push 等无注解方法返回 i32)。 + # 之前缺分支导致解析返回 None → 泛型模板方法(Vector[int].push)调用 + # 走 fail-fast"方法返回类型无法精确获取",实际是约定不一致。 + if string.strcmp(seg, "i32") == 0: + return llvmlite.Int32(pool) + if string.strcmp(seg, "i64") == 0: + return llvmlite.Int64(pool) + if string.strcmp(seg, "i16") == 0: + return llvmlite.Int16(pool) + if string.strcmp(seg, "i8") == 0: + return i8_ty + if string.strcmp(seg, "i1") == 0: + return llvmlite.Int1(pool) + if string.strcmp(seg, "f32") == 0: + return llvmlite.Float(pool) + if string.strcmp(seg, "f64") == 0: + return llvmlite.Double(pool) + if string.strcmp(seg, "void") == 0: + return llvmlite.Void(pool) # t.XXX / c.XXX 内置类型映射 if string.strncmp(seg, "t.", 2) == 0 or string.strncmp(seg, "c.", 2) == 0: attr_nm: str = seg + 2 @@ -525,6 +545,54 @@ def _resolve_vpi_ret_annot_str(pool: memhub.MemBuddy | t.CPtr, if ret_annot is None or pool is None: return None + # tuple[T1, T2, ...] → 匿名结构体 {T1, T2, ...}(多返回值打包, + # 与 TypeGenMixin._resolve_generic_subscript_ty 的 tuple 分支一致)。 + # b2a_hqx -> tuple[str, t.CUnsignedShort] 之前按普通段解析失败 → 调用侧 + # 返回类型退化 i8* → tuple 解包失效(hqx 段崩溃根因)。 + if string.strncmp(ret_annot, "tuple[", 6) == 0: + inner_len_t: t.CSizeT = 0 + while ret_annot[6 + inner_len_t] != ']' and ret_annot[6 + inner_len_t] != '\0': + inner_len_t += 1 + if ret_annot[6 + inner_len_t] != ']' or inner_len_t == 0: + return None + tup_head_t: llvmlite.ParamNode | t.CPtr = None + tup_tail_t: llvmlite.ParamNode | t.CPtr = None + tup_count_t: int = 0 + p_t: t.CSizeT = 0 + while p_t < inner_len_t: + # 跳过前导空格/逗号 + while p_t < inner_len_t and (ret_annot[6 + p_t] == ' ' or ret_annot[6 + p_t] == ','): + p_t += 1 + seg_s_t: t.CSizeT = p_t + while p_t < inner_len_t and ret_annot[6 + p_t] != ',': + p_t += 1 + seg_e_t: t.CSizeT = p_t + while seg_e_t > seg_s_t and ret_annot[6 + seg_e_t - 1] == ' ': + seg_e_t -= 1 + if seg_e_t > seg_s_t: + seg_len_t: t.CSizeT = seg_e_t - seg_s_t + seg_buf_t: bytes = stdlib.malloc(seg_len_t + 1) + if seg_buf_t is None: + return None + string.strncpy(seg_buf_t, ret_annot + 6 + seg_s_t, seg_len_t) + seg_buf_t[seg_len_t] = '\0' + seg_ty_t: llvmlite.LLVMType | t.CPtr = _resolve_single_vpi_type_str(pool, seg_buf_t) + stdlib.free(seg_buf_t) + if seg_ty_t is None: + return None + tup_node_t: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, seg_ty_t) + if tup_node_t is None: + return None + if tup_head_t is None: + tup_head_t = tup_node_t + else: + llvmlite.paramnode_set_next(tup_tail_t, tup_node_t) + tup_tail_t = tup_node_t + tup_count_t += 1 + if tup_head_t is None: + return None + return llvmlite.Struct(pool, tup_head_t, tup_count_t, None) + has_cptr: int = 0 has_cvoid: int = 0 first_ty: llvmlite.LLVMType | t.CPtr = None @@ -836,6 +904,34 @@ def _ParseVpiIntoModuleImpl(pool: memhub.MemBuddy | t.CPtr, imported_modules = stub_imported_modules if stub_from_imports is not None: from_imports = stub_from_imports + # 把源模块自己加入 stub 注解上下文:stub 注解(如 __sha512 的 transform + # 参数 t.CArray[t.CUInt8T, SHA512_BLOCK_LEN])引用源模块自身的 CDefine, + # 但 stub 的 import 只含被 import 的模块(t/c/string),不含自己。 + # 不加则 _eval_const_int_expr 跨模块 CDefine 查找遍历不到源模块 + # (imported_modules 无 __sha512)→ t.CArray count 报"必须是整数常量"。 + # 示例:RelPath "hashlib/__sha512.vp" → 模块名 "hashlib.__sha512"。 + if src_sha1 is not None: + import lib.core.StubMerger as StubMerger + stub_rel_src: str = StubMerger.LookupSha1RelPath(src_sha1) + if stub_rel_src is not None: + sr_len_src: t.CSizeT = string.strlen(stub_rel_src) + if sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".vp", 3) == 0: + sr_len_src -= 3 + elif sr_len_src > 3 and string.strncmp(stub_rel_src + sr_len_src - 3, ".py", 3) == 0: + sr_len_src -= 3 + if sr_len_src > 0: + src_mod_buf: str = stdlib.malloc(sr_len_src + 1) + if src_mod_buf is not None: + sri_src: t.CSizeT + for sri_src in range(sr_len_src): + ch_src: t.CChar = stub_rel_src[sri_src] + if ch_src == '/' or ch_src == '\\': + src_mod_buf[sri_src] = '.' + else: + src_mod_buf[sri_src] = ch_src + src_mod_buf[sr_len_src] = '\0' + imported_modules = _stub_add_imported(pool, imported_modules, src_mod_buf) + stdlib.free(src_mod_buf) i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) @@ -1019,9 +1115,12 @@ def _inject_function_declare(pool: memhub.MemBuddy | t.CPtr, # (未定义字段),LLVM 不允许 opaque struct 作为函数参数 # (llc: invalid type for function argument)。Windows ABI # 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。 + # CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。 match resolved_ty: case llvmlite.LLVMType.Struct(_, _, _): param_ty = llvmlite.Int64(pool) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) case _: param_ty = resolved_ty llvmlite.add_param(pool, func, param_ty, pname) @@ -1134,9 +1233,12 @@ def _inject_method_declare(pool: memhub.MemBuddy | t.CPtr, # (未定义字段),LLVM 不允许 opaque struct 作为函数参数 # (llc: invalid type for function argument)。Windows ABI # 小结构体(≤8 字节,如 COORD)按整数寄存器传,映射 i64 兼容。 + # CArray 参数 → 元素指针 ty*(C 数组衰减,与编译侧一致)。 match resolved_ty: case llvmlite.LLVMType.Struct(_, _, _): param_ty = llvmlite.Int64(pool) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) case _: param_ty = resolved_ty llvmlite.add_param(pool, func, param_ty, pname) diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index 71018c6..c131fef 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -49,6 +49,15 @@ import lib.core.Gen.Generator as GenGenerator # 全局 mbuddy 指针 _mbuddy: memhub.MemBuddy | t.CPtr +# ASan 模式(--asan):对 .ll 先 opt -passes=asan 插桩,链接加 -fsanitize=address +_asan_mode: t.CInt = 0 + + +def set_asan_mode(flag: int) -> None: + """设置 ASan 模式(1=启用,0=禁用)""" + global _asan_mode + _asan_mode = flag + # 源代码缓冲区大小(1MB) SRC_BUF_SIZE: t.CDefine = 1048576 # 2^20 = 1MB @@ -367,12 +376,36 @@ def compile_ll_to_obj(ir_path: str, output_dir: str, module_name: str, cc_cmd: s if obj_path is None: return 1 + # ASan 模式(_asan_mode=1):先 opt -passes=asan 插桩再 llc。 + # -asan-globals=false 关闭全局插桩:TPV 对全局变量(如各模块 _g_gen) + # 用直接地址寻址,ASan 全局 redzone 重定位后引用错位会读到垃圾指针。 + compile_input: str = ir_path + if _asan_mode != 0: + asan_path: str = build_sliced_path(output_dir, module_name, "asan.ll") + if asan_path is None: + return 1 + opt_cmd_len: t.CSizeT = string.strlen(asan_path) + string.strlen(ir_path) + 96 + opt_cmd: bytes = _mbuddy.alloc(opt_cmd_len) + if opt_cmd is None: + return 1 + viperlib.snprintf(opt_cmd, opt_cmd_len, + "opt -passes=asan -asan-globals=false -o %s %s", + asan_path, ir_path) + opt_ret: int = stdlib.system(opt_cmd) + if opt_ret != 0: + fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb is not None: + viperlib.snprintf(fb, 1024, "opt ASan 插桩失败 (module=%s)", module_name) + VLogger.error(fb, "ASAN") + return 1 + compile_input = asan_path + # 构造命令: llc -filetype=obj -o {obj_path} ir_path - cmd_len: t.CSizeT = string.strlen(cc_cmd) + string.strlen(cc_flags) + string.strlen(obj_path) + string.strlen(ir_path) + 64 + cmd_len: t.CSizeT = string.strlen(cc_cmd) + string.strlen(cc_flags) + string.strlen(obj_path) + string.strlen(compile_input) + 64 cmd: bytes = _mbuddy.alloc(cmd_len) if cmd is None: return 1 - viperlib.snprintf(cmd, cmd_len, "%s %s -o %s %s", cc_cmd, cc_flags, obj_path, ir_path) + viperlib.snprintf(cmd, cmd_len, "%s %s -o %s %s", cc_cmd, cc_flags, obj_path, compile_input) # 用 stdlib.system() 直接在控制台运行,输出直接显示 # 避免 subprocess 管道捕获丢失输出 @@ -519,18 +552,26 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f # 构造命令: clang++ {obj_path} extra_objs -o {output_dir}/{linker_output} linker_flags # 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖 - cmd_len: t.CSizeT = string.strlen(linker_cmd) + string.strlen(obj_path) + string.strlen(linker_flags) + string.strlen(output_dir) + string.strlen(linker_output) + extra_len + 128 + # ASan 模式:追加 -fsanitize=address 链接 libasan(配合 opt 插桩) + use_flags: str = linker_flags + if _asan_mode != 0: + fl_len: t.CSizeT = string.strlen(linker_flags) + 32 + use_flags = _mbuddy.alloc(fl_len) + if use_flags is None: + return 1 + viperlib.snprintf(use_flags, fl_len, "%s -fsanitize=address", linker_flags) + cmd_len: t.CSizeT = string.strlen(linker_cmd) + string.strlen(obj_path) + string.strlen(use_flags) + string.strlen(output_dir) + string.strlen(linker_output) + extra_len + 128 cmd: bytes = _mbuddy.alloc(cmd_len) if cmd is None: return 1 if extra_len > 0: viperlib.snprintf(cmd, cmd_len, "%s %s %s -o %s/%s %s", linker_cmd, obj_path, extra_objs, - output_dir, linker_output, linker_flags) + output_dir, linker_output, use_flags) else: viperlib.snprintf(cmd, cmd_len, "%s %s -o %s/%s %s", linker_cmd, obj_path, output_dir, linker_output, - linker_flags) + use_flags) result: subprocess.CompletedProcess | t.CPtr = subprocess.run(cmd, True, True) if result is None: @@ -630,17 +671,25 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE) # 构造命令: clang++ obj_paths extra_objs -o linker_output linker_flags - cmd_len: t.CSizeT = string.strlen(linker_cmd) + obj_paths_len + string.strlen(linker_flags) + string.strlen(linker_output) + extra_len + 128 + # ASan 模式:追加 -fsanitize=address 链接 libasan(配合 opt 插桩) + use_flags: str = linker_flags + if _asan_mode != 0: + fl_len: t.CSizeT = string.strlen(linker_flags) + 32 + use_flags = _mbuddy.alloc(fl_len) + if use_flags is None: + return 1 + viperlib.snprintf(use_flags, fl_len, "%s -fsanitize=address", linker_flags) + cmd_len: t.CSizeT = string.strlen(linker_cmd) + obj_paths_len + string.strlen(use_flags) + string.strlen(linker_output) + extra_len + 128 cmd: bytes = _mbuddy.alloc(cmd_len) if cmd is None: return 1 if extra_len > 0: viperlib.snprintf(cmd, cmd_len, "%s %s %s -o %s %s", linker_cmd, obj_paths, extra_objs, - linker_output, linker_flags) + linker_output, use_flags) else: viperlib.snprintf(cmd, cmd_len, "%s %s -o %s %s", - linker_cmd, obj_paths, linker_output, linker_flags) + linker_cmd, obj_paths, linker_output, use_flags) # 用 subprocess.run 捕获 clang++ 的 stdout/stderr 输出 # 之前用 stdlib.system() 无法捕获具体错误(undefined reference 等), diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index ea58e1e..d3018e0 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -720,6 +720,30 @@ class CallGenMixin: if HandlesExpr.is_ptr_type(ptr_val.Ty) != 0: pointee: llvmlite.LLVMType | t.CPtr = ptr_val.Ty.Pointee if pointee is not None: + # i8*(void*)时按赋值目标类型推断读取宽度(对齐 TPC + # HandlesSpecialCall._infer_deref_target_type): + # v: int = c.Deref(p) → bitcast i32* 后 load 4 字节。 + # 背景:DictTest d3[key5]=500 读出 -12——固定按 i8 读 1 字节 + + # sext 截断(0xF4 → 0xFFFFFFF4);Test 1 的小值(10)恰好正确 + # 掩盖了缺陷。 + match pointee: + case llvmlite.LLVMType.Int(pointee_width): + if pointee_width == 8 and trans is not None and trans.CurAssignAnnot is not None: + dref_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( + trans.Pool, trans.CurAssignAnnot, + trans._imported_modules, trans._from_imports, trans) + if dref_ty is not None: + dref_bits: int = HandlesExpr.get_llvm_type_bits(dref_ty) + dref_fbits: int = HandlesExpr.get_llvm_float_bits(dref_ty) + # 仅整数/浮点目标(struct 值目标不推断,保持 i8 读) + if (dref_bits != 0 or dref_fbits != 0) and dref_bits != 8: + dref_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, dref_ty) + dref_ptr: llvmlite.Value | t.CPtr = llvmlite.build_bitcast( + builder, ptr_val, dref_ptr_ty) + if dref_ptr is not None: + return llvmlite.build_load(builder, dref_ty, dref_ptr) + case _: + pass return llvmlite.build_load(builder, pointee, ptr_val) i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) return llvmlite.build_load(builder, i8_ty, ptr_val) @@ -3706,6 +3730,20 @@ class CallGenMixin: if vpi_ret_ty is not None: call_ret_ty = vpi_ret_ty else: + # 定位日志:返回类型解析失败前打印关键状态, + # 便于区分是"特化未发生"还是"vpi 键不匹配"。 + fb_mcret: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_mcret is not None: + viperlib.snprintf( + fb_mcret, 1024, + "[MC-RET-FAIL] class=%s m=%s sha1=%s entry_name=%s lookup=%s gen_done=%d", + class_name, + method_name if method_name is not None else "(null)", + cls_sha1_mc if cls_sha1_mc is not None else "(null)", + mc_entry.Name if mc_entry is not None and mc_entry.Name is not None else "(null)", + lookup_name if lookup_name is not None else "(null)", + gen_done) + VLogger.info(fb_mcret, "MCRET") call_ret_ty = self._fail_method_ret_ty(method_name, cl) # 翻译调用参数 @@ -3843,6 +3881,23 @@ class CallGenMixin: if pty is not None: arg_val = HandlesExpr.coerce_to_type( builder, arg_val, pty, arg_node) + else: + # found_func=None(跨模块 stub 未注入):数组实参按 C 语义 + # 衰减为元素指针(对齐 TPC _decay_array_to_ptr)。否则 + # _ensure_method_declare 用 [N x ty]* 实参类型建 declare, + # 与定义侧元素指针(如 md5.final 的 i8*)类型不匹配。 + if arg_val.Ty is not None and HandlesExpr.is_ptr_type(arg_val.Ty) != 0: + arr_pointee_ma: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee + if arr_pointee_ma is not None: + match arr_pointee_ma: + case llvmlite.LLVMType.Array(arr_et_ma, _): + decayed_ma: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_pointee_ma, arr_et_ma, arg_val, + llvmlite.const_int32(pool, 0)) + if decayed_ma is not None: + arg_val = decayed_ma + case _: + pass llvmlite.value_set_next(arg_val, None) llvmlite.value_set_next(tail, arg_val) tail = arg_val @@ -3945,8 +4000,9 @@ class CallGenMixin: # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, total_count) - # 检查变长函数 + C ABI 提升 - is_variadic_3441: int = HandlesExprCall._check_and_promote_variadic_call(pool, builder, mod, call_name, head, total_count) + # 检查变长函数 + C ABI 提升(cargs 是实参 AST,self_offset=1 跳过 self) + is_variadic_3441: int = HandlesExprCall._check_and_promote_variadic_call( + pool, builder, mod, call_name, head, total_count, cargs, 1, trans) return llvmlite.build_call(builder, call_name, head, total_count, call_ret_ty, is_variadic_3441) # ============================================================ @@ -4116,6 +4172,21 @@ class CallGenMixin: if pty_sp is not None: arg_val_sp = HandlesExpr.coerce_to_type( builder, arg_val_sp, pty_sp, arg_node_sp) + else: + # found_func=None:数组实参按 C 语义衰减为元素指针 + # (与 _translate_method_call 一致,避免 declare 类型不匹配) + if arg_val_sp.Ty is not None and HandlesExpr.is_ptr_type(arg_val_sp.Ty) != 0: + arr_pointee_sp: llvmlite.LLVMType | t.CPtr = arg_val_sp.Ty.Pointee + if arr_pointee_sp is not None: + match arr_pointee_sp: + case llvmlite.LLVMType.Array(arr_et_sp, _): + decayed_sp: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_pointee_sp, arr_et_sp, arg_val_sp, + llvmlite.const_int32(pool, 0)) + if decayed_sp is not None: + arg_val_sp = decayed_sp + case _: + pass llvmlite.value_set_next(arg_val_sp, None) llvmlite.value_set_next(tail, arg_val_sp) tail = arg_val_sp @@ -4139,7 +4210,7 @@ class CallGenMixin: if found_func is None: self._ensure_method_declare(pool, mod, call_name, ret_ty_sp, head, arg_count_sp) is_variadic_sp: int = HandlesExprCall._check_and_promote_variadic_call( - pool, builder, mod, call_name, head, arg_count_sp) + pool, builder, mod, call_name, head, arg_count_sp, cargs_sp, 1, trans) return llvmlite.build_call(builder, call_name, head, arg_count_sp, ret_ty_sp, is_variadic_sp) # ============================================================ @@ -5109,6 +5180,43 @@ class CallGenMixin: fi_gp = trans._from_imports bare_ty = HandlesType.resolve_annotation_type( pool, gp_ast, imp_gp, fi_gp, trans) + # 泛型类型参数双参数形式: T(val, t.CPtr) → Ptr(T) + # (与 t.CInt(val, t.CPtr) → Ptr(i32) 对齐;vector.vp get() 的 + # typed_ptr = T(offset, t.CPtr) 依赖此路径,否则双参数不进 + # can==1 单参数分支 → 跌落到"裸名调用未定义"误报) + if bare_ty is not None and can == 2: + bare_arg2_node: ast.AST | t.CPtr = cargs.get(1) + bare_arg2_is_cptr: int = 0 + if bare_arg2_node is not None: + # 形式 1: t.CPtr(Attribute 形式) + if bare_arg2_node.kind() == ast.ASTKind.Attribute: + bare_arg2_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(bare_arg2_node) + if bare_arg2_at.attr is not None and string.strcmp(bare_arg2_at.attr, "CPtr") == 0: + if bare_arg2_at.value is not None and bare_arg2_at.value.kind() == ast.ASTKind.Name: + bare_arg2_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(bare_arg2_at.value) + if bare_arg2_nm.id is not None and string.strcmp(bare_arg2_nm.id, "t") == 0: + bare_arg2_is_cptr = 1 + # 形式 2: 裸名 CPtr(from t import CPtr 后直接使用) + elif bare_arg2_node.kind() == ast.ASTKind.Name: + bare_arg2_bn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(bare_arg2_node) + if bare_arg2_bn.id is not None and string.strcmp(bare_arg2_bn.id, "CPtr") == 0: + bare_arg2_is_cptr = 1 + if bare_arg2_is_cptr != 0: + # CVoid* → i8*(LLVM 中 void* 表示为 i8*) + match bare_ty: + case llvmlite.LLVMType.Void(): + bare_ty = llvmlite.Int8(pool) + case _: + pass + bare_ty = llvmlite.Ptr(pool, bare_ty) + bare_arg_node: ast.AST | t.CPtr = cargs.get(0) + if bare_arg_node is not None: + bare_arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, bare_arg_node, None, 0, trans) + if bare_arg_val is not None: + return self._translate_t_type_cast( + pool, builder, bare_arg_val, bare_ty, + bare_fn_name, bare_arg_node, trans) if bare_ty is not None and can == 1: bare_arg_node: ast.AST | t.CPtr = cargs.get(0) if bare_arg_node is not None: @@ -5830,8 +5938,21 @@ class CallGenMixin: # (TPC 参考: _HandleGenericClassNewLlvm;GenericTest Test4 Box(42)/Test5 # Pair(10,20)/Test10 Counter(0) 依赖此路径。显式类型实参 list[int](mb) # 走 translate_call 上方的 Subscript 分支,此处处理裸名 + 推断。) + # 支持模块限定调用(vector.Vector(...)):func_name 是 attr("Vector"), + # 需确认模块已导入(模板查找兜底——普通函数如 stdlib.free 的 attr 找 + # 不到模板 → 回落普通调用)。 if struct_ty is None and gen_ctor_entry is None and trans is not None: + is_gen_bare: int = 0 if cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + is_gen_bare = 1 + elif cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute: + at_gb: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) + if at_gb.value is not None and at_gb.value.kind() == ast.ASTKind.Name: + am_gb: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_gb.value) + if am_gb.id is not None: + if HandlesImports.is_module_imported(trans._imported_modules, am_gb.id) != 0: + is_gen_bare = 1 + if is_gen_bare != 0: template_cd_bare: ast.ClassDef | t.CPtr = HandlesClassDef._find_generic_template(func_name) if template_cd_bare is not None and template_cd_bare.type_params is not None: tp_bare: list[str] | t.CPtr = template_cd_bare.type_params @@ -5839,17 +5960,30 @@ class CallGenMixin: if tp_count_bare > 0: inferred_args_bare: list[str] | t.CPtr = list[str](pool, 8) if inferred_args_bare is not None: - tpi_bare: t.CSizeT = 0 - for tpi_bare in range(tp_count_bare): - ta_bare: str = "int" - if tpi_bare < can: - ta_bare = self._infer_generic_ctor_type_arg(pool, cargs.get(tpi_bare)) - inferred_args_bare.append(ta_bare) - spec_name_bare: str = HandlesClassDef._specialize_generic_class( - trans, func_name, inferred_args_bare) - if spec_name_bare is not None: - return self._translate_generic_ctor_call( - pool, builder, mod, spec_name_bare, cl, trans) + # 对齐 TPC _HandleGenericClassNewLlvm:先收集全部实参 + # 推断类型,过滤纯 int(排除 pool 等非 T 参数), + # hint_types 优先填充(Vector(pool,8,CFloat64T(0.0)) + # → Vector[double];Vector(pool,10,CInt(0)) → Vector[int]) + hint_bare: list[str] | t.CPtr = list[str](pool, 8) + if hint_bare is not None: + hi_bare: t.CSizeT + for hi_bare in range(can): + hty_bare: str = self._infer_generic_ctor_type_arg(pool, cargs.get(hi_bare)) + if string.strcmp(hty_bare, "int") != 0: + hint_bare.append(hty_bare) + tpi_bare: t.CSizeT = 0 + for tpi_bare in range(tp_count_bare): + ta_bare: str = "int" + if tpi_bare < hint_bare.__len__(): + ta_bare = hint_bare.get(tpi_bare) + elif tpi_bare < can: + ta_bare = self._infer_generic_ctor_type_arg(pool, cargs.get(tpi_bare)) + inferred_args_bare.append(ta_bare) + spec_name_bare: str = HandlesClassDef._specialize_generic_class( + trans, func_name, inferred_args_bare) + if spec_name_bare is not None: + return self._translate_generic_ctor_call( + pool, builder, mod, spec_name_bare, cl, trans) # REnum 变体构造检测: ClassName.VariantName(args...) # 如 LLVMType.Int(1) → 构造 LLVMType 的 Int 变体 @@ -6022,6 +6156,20 @@ class CallGenMixin: # 根据外部函数签名推断期望类型,提升字面量(如 i32→i64) # 避免 declare(i64) 与 call(i32) 类型不匹配 if found_func is None and arg_val.Ty is not None: + # 数组实参按 C 语义衰减为元素指针(对齐 TPC + # _decay_array_to_ptr),与 _translate_method_call 一致。 + if HandlesExpr.is_ptr_type(arg_val.Ty) != 0: + arr_pointee_fc: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee + if arr_pointee_fc is not None: + match arr_pointee_fc: + case llvmlite.LLVMType.Array(arr_et_fc, _): + decayed_fc: llvmlite.Value | t.CPtr = llvmlite.build_gep_array( + builder, arr_pointee_fc, arr_et_fc, arg_val, + llvmlite.const_int32(pool, 0)) + if decayed_fc is not None: + arg_val = decayed_fc + case _: + pass ext_param_ty: llvmlite.LLVMType | t.CPtr = self._get_external_func_param_ty( pool, func_name, ai) if ext_param_ty is not None: @@ -6385,7 +6533,9 @@ class CallGenMixin: # 跨模块调用:创建 declare 声明,避免 llc 报 undefined value self._ensure_method_declare(pool, mod, call_name, call_ret_ty, head, can) # 检查被调用函数是否为变长函数(*args),若是则对变长参数进行 C ABI 提升 - is_variadic_call: int = HandlesExprCall._check_and_promote_variadic_call(pool, builder, mod, call_name, head, can) + # (cargs 为本函数参数 AST 链表,self_offset=0 普通函数调用) + is_variadic_call: int = HandlesExprCall._check_and_promote_variadic_call( + pool, builder, mod, call_name, head, can, cargs, 0, trans) return llvmlite.build_call(builder, call_name, head, can, call_ret_ty, is_variadic_call) # ============================================================ @@ -7343,11 +7493,18 @@ class CallGenMixin: mod: llvmlite.LLVMModule | t.CPtr, call_name: str, head: llvmlite.Value | t.CPtr, - can: t.CSizeT) -> int: + can: t.CSizeT, + arg_nodes: list[ast.AST | t.CPtr] | t.CPtr = None, + self_offset: int = 0, + trans: HT.Translator | t.CPtr = None) -> int: """检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升 返回: is_variadic (1=变长调用, 0=普通调用) 副作用: 修改 head 链表中超过固定参数数量的节点(C ABI 提升) + + arg_nodes: 实参 AST 链表(不含 self),用于 i8/i16 提升的无符号性判断 + self_offset: Value 链中 self 占用的偏移(方法调用=1,普通函数=0) + trans: 供 _node_type_unsigned 查询变量注解 """ if mod is None or call_name is None or head is None: return 0 @@ -7378,13 +7535,28 @@ class CallGenMixin: # 对变长参数进行 C ABI 提升 i32_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) double_ty_promote: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool) + v_idx: int = fixed_count while cur is not None: next_node: llvmlite.Value | t.CPtr = cur.Next promoted: llvmlite.Value | t.CPtr = cur # C ABI integer promotion: i8/i16 → i32 arg_bits: int = HandlesExpr.get_llvm_type_bits(cur.Ty) if arg_bits != 0 and arg_bits < 32: - promoted = HandlesExpr.coerce_to_type(builder, cur, i32_ty_promote, None) + # 无符号整型(t.CUInt8T/CUInt16T 等)→ zext;有符号/未知 → sext。 + # 修复: printf("%02x", buf[i]) 中 buf[i] 是 CUInt8T,sext 得 + # 0xFFFFFFF3 打印 fffffff3(哈希摘要输出垃圾),应 zext 得 0xF3。 + is_u_va: int = 0 + if arg_nodes is not None and trans is not None: + ast_idx_va: int = v_idx - self_offset + if ast_idx_va >= 0 and ast_idx_va < arg_nodes.__len__(): + an_va: ast.AST | t.CPtr = arg_nodes.get(ast_idx_va) + if an_va is not None: + if HandlesExpr._node_type_unsigned(pool, an_va, trans) != 0: + is_u_va = 1 + if is_u_va != 0: + promoted = llvmlite.build_zext(builder, cur, i32_ty_promote) + else: + promoted = HandlesExpr.coerce_to_type(builder, cur, i32_ty_promote, None) if promoted is not None: promoted.Next = next_node if prev is not None: @@ -7399,6 +7571,7 @@ class CallGenMixin: prev.Next = promoted prev = promoted cur = next_node + v_idx += 1 return 1 diff --git a/App/lib/core/Gen/ClassGenMixin.vp b/App/lib/core/Gen/ClassGenMixin.vp index e5c00eb..709d724 100644 --- a/App/lib/core/Gen/ClassGenMixin.vp +++ b/App/lib/core/Gen/ClassGenMixin.vp @@ -107,6 +107,12 @@ class ClassGenMixin: GenericClassNames: list[str] | t.CPtr GenericClassNodes: list[ast.ClassDef | t.CPtr] | t.CPtr GenericClassSha1s: list[str] | t.CPtr + # 泛型模板来源文件路径(与上面三表同序)。特化翻译模板方法体时 + # node.lineno 是模板文件行号,若 CurrentFile 仍是调用方文件, + # fatal_error 会把模板行号套到调用方文件上显示错位(如 vector.vp + # 的 T(...) 错报成 vectortest.vp 的 return 0)。此表用于特化期间 + # 临时切换 CurrentFile 到模板文件,翻译完恢复。 + GenericClassFiles: list[str] | t.CPtr # --- vtable 方法收集表(原 _g_vt_full_names/_g_vt_method_names/ # _g_vt_parent_sha1s/_g_vt_method_count) --- @@ -1716,6 +1722,7 @@ class ClassGenMixin: self.GenericClassNames = list[str](pool, 8) self.GenericClassNodes = list[ast.ClassDef | t.CPtr](pool, 8) self.GenericClassSha1s = list[str](pool, 8) + self.GenericClassFiles = list[str](pool, 8) # 用局部变量接收 list,触发类型推断(避免字段 __len__ 失败) names_local: list[str] | t.CPtr = self.GenericClassNames cn: t.CSizeT = names_local.__len__() @@ -1727,6 +1734,8 @@ class ClassGenMixin: self.GenericClassNames.append(cd.name) self.GenericClassNodes.append(cd) self.GenericClassSha1s.append(module_sha1) + # 同时保存模板来源文件路径(错误定位用,见字段注释) + self.GenericClassFiles.append(HandlesType.get_current_file()) # 诊断日志:确认泛型模板注册 fb_rg: t.CChar | t.CPtr = VLogger.fmt_buf() if fb_rg is not None: @@ -1765,6 +1774,25 @@ class ClassGenMixin: return sha1_val return None + def _find_generic_template_file(self, class_name: str) -> str: + """查找泛型类模板的来源文件路径,未找到返回 None + + 与 _find_generic_template_sha1 同表同序;供特化翻译模板方法体时 + 临时切换 CurrentFile(错误定位:模板 node.lineno 需配模板文件路径)。 + """ + if self.GenericClassNames is None or class_name is None: + return None + names_local: list[str] | t.CPtr = self.GenericClassNames + files_local: list[str] | t.CPtr = self.GenericClassFiles + cn: t.CSizeT = names_local.__len__() + i: t.CSizeT + for i in range(cn): + nm: str = names_local.get(i) + if nm is not None and string.strcmp(nm, class_name) == 0: + f_val: str = files_local.get(i) + return f_val + return None + def _mangle_generic_class_name(self, pool: memhub.MemBuddy | t.CPtr, class_name: str, type_args: list[str] | t.CPtr) -> str: @@ -2124,7 +2152,18 @@ class ClassGenMixin: if template_sha1_pre is not None: trans.ModuleSha1 = template_sha1_pre self._merge_template_imports(trans, template_sha1_pre) + # 错误定位上下文:补译的是模板方法体(spec_cd_pre 克隆自模板, + # node.lineno 是模板文件行号)。切换 CurrentFile 到模板文件并压入 + # trace 帧,使 fatal_error 正确显示模板文件位置(翻译完恢复)。 + saved_cur_pre: str = HandlesType.get_current_file() + tpl_file_pre: str = self._find_generic_template_file(class_name) + if tpl_file_pre is not None: + HandlesType.set_current_file(tpl_file_pre) + HandlesType.push_trace(tpl_file_pre, template_cd.lineno) self._translate_oop_methods(trans, spec_cd_pre, pre_existing.Ty, spec_name, 0) + if tpl_file_pre is not None: + HandlesType.pop_trace() + HandlesType.set_current_file(saved_cur_pre) trans.ModuleSha1 = saved_ms_pre trans._imported_modules = saved_imp_pre trans.GenericTypeParamNames = None @@ -2208,7 +2247,18 @@ class ClassGenMixin: if template_sha1 is not None: trans.ModuleSha1 = template_sha1 self._merge_template_imports(trans, template_sha1) + # 错误定位上下文:spec_cd 克隆自模板,方法体的 node.lineno 是模板文件 + # 行号,而 CurrentFile 仍是调用方文件 → 错误显示错位。切换 CurrentFile + # 到模板文件并压入 trace 帧(翻译完恢复),使 fatal_error 正确溯源。 + saved_cur_full: str = HandlesType.get_current_file() + tpl_file_full: str = self._find_generic_template_file(class_name) + if tpl_file_full is not None: + HandlesType.set_current_file(tpl_file_full) + HandlesType.push_trace(tpl_file_full, template_cd.lineno) self.translate_class_def(trans, spec_cd) + if tpl_file_full is not None: + HandlesType.pop_trace() + HandlesType.set_current_file(saved_cur_full) # 恢复原始 ModuleSha1 与 imports trans.ModuleSha1 = saved_module_sha1 trans._imported_modules = saved_imported @@ -2262,6 +2312,14 @@ class ClassGenMixin: pool: memhub.MemBuddy | t.CPtr = trans.Pool class_name: str = cd.name + # 诊断:翻译类入口(定位 G3 1b 挂起发生在哪个类) + fb_cls: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_cls is not None: + viperlib.snprintf(fb_cls, 1024, "[CLASS] 翻译类: %s (declare_only=%d, sha1=%s)", + class_name, trans._declare_only, + trans.ModuleSha1 if trans.ModuleSha1 is not None else "(null)") + VLogger.info(fb_cls, "CLASS") + # 命名空间隔离:标记为当前文件可见(必须在 existing 检查之前, # 因为 Phase B 时结构体已注册会提前 return,否则永远无法标记本地类) HandlesStruct.add_visible_struct(pool, class_name) @@ -2308,6 +2366,13 @@ class ClassGenMixin: # self 参数类型打印),部分实现下 pool 块被复用导致 Name 悬空崩溃。 # 保险起见:深拷贝 Name 到 stdlib,确保跨文件永久存活。 if trans._declare_only == 0: + # 深拷贝整个 existing.Ty 到 stdlib:1a pool Ty 跨 1b reset 悬空, + # _ensure_field_types_in_module 遍历悬空字段 ParamNode 链表 + # 读悬空挂起(G3 LinkedNode.prepend 挂起根因)。DeepCopyType + # 递归深拷贝字段链表/子类型/Name 到 stdlib(深度上限+链守卫)。 + ty_deep: llvmlite.LLVMType | t.CPtr = llvmlite.DeepCopyType(pool, existing.Ty) + if ty_deep is not None: + existing.Ty = ty_deep old_name: t.CChar | t.CPtr = llvmlite.get_struct_name(existing.Ty) if old_name is not None: on_len: t.CSizeT = string.strlen(old_name) @@ -2325,6 +2390,11 @@ class ClassGenMixin: if trans._declare_only == 1: self._translate_oop_methods(trans, cd, existing.Ty, class_name, 1) else: + # 诊断:existing 命中后全量翻译方法(定位 G3 1b 挂起) + fb_ex: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_ex is not None: + viperlib.snprintf(fb_ex, 1024, "[CLASS] %s existing 命中, 全量翻译方法体", class_name) + VLogger.info(fb_ex, "CLASS") self._translate_oop_methods(trans, cd, existing.Ty, class_name) # SHA1 已在 register_struct 时设置,无需再补 return 0 @@ -3222,6 +3292,12 @@ class ClassGenMixin: if trans is None or cd is None or struct_ty is None or class_name is None: return 0 + # 诊断:OOP 方法翻译入口(定位 G3 1b 挂起) + fb_oop: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_oop is not None: + viperlib.snprintf(fb_oop, 1024, "[OOP] %s mark_only=%d", class_name, mark_only) + VLogger.info(fb_oop, "OOP") + children: list[ast.AST | t.CPtr] | t.CPtr = cd.children if children is None: return 0 @@ -3378,6 +3454,13 @@ class ClassGenMixin: method_name: str = fd.name + # 诊断:方法翻译入口(定位 G3 1b 挂起在哪个方法) + fb_mtd: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_mtd is not None: + viperlib.snprintf(fb_mtd, 1024, "[METHOD] %s.%s", class_name, + method_name if method_name is not None else "(null)") + VLogger.info(fb_mtd, "OOP") + # 提取装饰器元数据(@property/@staticmethod/@classmethod/@x.setter/@x.deleter) func_meta: int = self._extract_func_meta(fd.decorator_list) # property setter/deleter 函数名带 $set/$del 后缀,避免与 getter 冲突 @@ -3503,9 +3586,12 @@ class ClassGenMixin: # 裸类形参 → Ptr(struct):按"对象即指针"引用传参。 # 若按 struct 值传参,self.Head = node 会取局部拷贝地址 # (append 返回后悬垂,G5 Test4 head.Value 读到垃圾)。 + # CArray 形参 → 元素指针 ty*(数组衰减,C 语义,与函数形参一致)。 match resolved: case llvmlite.LLVMType.Struct(_, _, _): param_ty = llvmlite.Ptr(pool, resolved) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) case _: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) @@ -3573,9 +3659,12 @@ class ClassGenMixin: pool, arg2.annotation, imported_modules, from_imports, trans) if resolved2 is not None: # 与 add_param 保持一致:裸类形参 → Ptr(struct)(对象即指针) + # CArray 形参 → 元素指针 ty*(数组衰减,与 add_param 一致) match resolved2: case llvmlite.LLVMType.Struct(_, _, _): param_ty2 = llvmlite.Ptr(pool, resolved2) + case llvmlite.LLVMType.Array(aelem_ty2, _): + param_ty2 = llvmlite.Ptr(pool, aelem_ty2) case _: param_ty2 = resolved2 alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, param_ty2) @@ -3621,6 +3710,12 @@ class ClassGenMixin: for bi2 in range(bn2): stmt2: ast.AST | t.CPtr = body.get(bi2) if stmt2 is not None: + # 诊断:语句级日志(定位 G3 1b LinkedNode.prepend 挂起在第几条语句) + fb_stmt: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_stmt is not None: + viperlib.snprintf(fb_stmt, 1024, "[STMT] %s.%s stmt[%d] kind=%d ln=%d", + class_name, method_name, bi2, stmt2.kind(), stmt2.lineno) + VLogger.info(fb_stmt, "OOP") HandlesBody.translate_stmt(trans, stmt2) # 如果返回类型为 void,添加 ret void;否则添加隐式 ret 0 @@ -3688,6 +3783,12 @@ class ClassGenMixin: if trans is None or struct_ty is None or class_name is None: return 0 + # 诊断:__before_init__ 生成入口(定位 G3 1b 挂起) + fb_bi: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_bi is not None: + viperlib.snprintf(fb_bi, 1024, "[BI] %s", class_name) + VLogger.info(fb_bi, "OOP") + import lib.core.Handles.HandlesFunctions as HandlesFunctions import lib.core.Handles.HandlesVar as HandlesVar import lib.core.Handles.HandlesExpr as HandlesExpr diff --git a/App/lib/core/Gen/ExprGenMixin.vp b/App/lib/core/Gen/ExprGenMixin.vp index 04e2291..34603d6 100644 --- a/App/lib/core/Gen/ExprGenMixin.vp +++ b/App/lib/core/Gen/ExprGenMixin.vp @@ -1624,6 +1624,19 @@ class ExprGenMixin: if self._node_type_unsigned(pool, uop.operand, trans) != 0: return 1 return 0 + if k == ast.ASTKind.Subscript: + # 数组/指针下标: md5_buf[i](t.CArray[t.CUInt8T, N])或 buf[i] + # (形参 t.CUInt8T | t.CPtr)元素是无符号整型 → printf("%02x", ...) zext。 + sub_su: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + if sub_su.value is not None and sub_su.value.kind() == ast.ASTKind.Name: + snm_su: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub_su.value) + if snm_su.id is not None and trans is not None: + ve_su: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + trans.SymTab, snm_su.id) + if ve_su is not None and ve_su.AnnotClassName is not None: + if self._annot_str_unsigned(ve_su.AnnotClassName) != 0: + return 1 + return 0 return 0 def _type_name_unsigned(self, type_name: str) -> int: @@ -1654,6 +1667,26 @@ class ExprGenMixin: if string.strcmp(type_name, "ULONG") == 0: return 1 return 0 + def _annot_str_unsigned(self, annot: str) -> int: + """检查类型注解字符串是否引用无符号整型(子串扫描) + + 用于复合注解(t.CArray[t.CUInt8T, 16] / t.CUInt8T | t.CPtr), + _type_name_unsigned 的精确匹配对这类串不适用。 + """ + if annot is None: + return 0 + if string.strstr(annot, "CUInt8T") is not None: return 1 + if string.strstr(annot, "CUInt16T") is not None: return 1 + if string.strstr(annot, "CUInt32T") is not None: return 1 + if string.strstr(annot, "CUInt64T") is not None: return 1 + if string.strstr(annot, "CUnsigned") is not None: return 1 + if string.strstr(annot, "BYTE") is not None: return 1 + if string.strstr(annot, "UCHAR") is not None: return 1 + if string.strstr(annot, "USHORT") is not None: return 1 + if string.strstr(annot, "UINT") is not None: return 1 + if string.strstr(annot, "ULONG") is not None: return 1 + return 0 + def translate_compare(self, builder: llvmlite.IRBuilder | t.CPtr, pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -2201,6 +2234,14 @@ class ExprGenMixin: # 精确匹配失败:尝试 star import 回退 mod_name_raw = HandlesImports.lookup_from_import(from_imports, name, 1) + # 当前模块名(去前导点),用于构建子模块全名 base_mod.X(如 "hashlib" + "." + + # "__md5")。此前 base_mod 未赋值(未初始化变量)→ 子模块递归 strlen(NULL) + # 崩溃(ASan access-violation,Attribute count 新路径触发 _lookup_cross_module_cdefine)。 + base_mod: str = mod_name_raw + if base_mod is not None: + while base_mod[0] == '.': + base_mod = base_mod + 1 + # 2. from_imports 查找失败:直接回退到 imported_modules 遍历(步骤 9.6) # 注意:star fallback 可能返回错误模块(如 stdint),常量实际不在其中, # 步骤 9.6 会在该模块查找失败后继续遍历 imported_modules @@ -2514,6 +2555,8 @@ class ExprGenMixin: # 例如 ast.__init__.vp 中 from .base import CONST_INT, ... # 需要递归查找 ast.base 子模块中的 CONST_INT: t.CDefine = 1 if result_found == 0: + # base_mod 已在函数开头计算(mod_name_raw 去前导点), + # 供子模块递归构建 "base_mod.X" 全名。 name_len_rc: t.CSizeT = string.strlen(name) rc_pos: t.CSizeT = 0 while rc_pos < total_len and result_found == 0: @@ -4005,6 +4048,15 @@ class ExprGenMixin: VLogger.error(diag_buf_an, "ATTR") return None + # 诊断:属性访问入口(定位 G3 LinkedNode.prepend 挂起) + fb_tattr: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_tattr is not None: + viperlib.snprintf(fb_tattr, 1024, "[TATTR] attr=%s obj_kind=%d ln=%d", + at.attr if at.attr is not None else "?", + at.value.kind() if at.value is not None else -1, + at.lineno if at.lineno is not None else 0) + VLogger.info(fb_tattr, "ATTR") + # 魔法属性 __doc__:查询 docstring 登记表(预注册阶段从 AST 提取), # 命中则当前模块内联生成字符串常量返回;未命中(无 docstring 的 # 函数/类/方法)返回 null 指针(i8*)。支持本模块与跨模块符号: @@ -4647,6 +4699,14 @@ class ExprGenMixin: at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(attr_node) if at is None or at.attr is None or at.value is None: return None + # 诊断:属性链类名解析入口(定位 G3 prepend 挂起) + fb_raf: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_raf is not None: + viperlib.snprintf(fb_raf, 1024, "[RAF] attr=%s obj_kind=%d ln=%d", + at.attr if at.attr is not None else "?", + at.value.kind() if at.value is not None else -1, + at.lineno if at.lineno is not None else 0) + VLogger.info(fb_raf, "ATTR") # 递归解析基座的注解类名(Name → 变量注解;Attribute → 字段注解) parent_class: str = None if at.value.kind() == ast.ASTKind.Name: @@ -4686,6 +4746,15 @@ class ExprGenMixin: if at is None or at.value is None or at.attr is None: return None + # 诊断:链式属性指针获取入口(定位 G3 LinkedNode.prepend 挂起) + fb_gap: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_gap is not None: + viperlib.snprintf(fb_gap, 1024, "[GAP] attr=%s obj_kind=%d ln=%d", + at.attr if at.attr is not None else "?", + at.value.kind() if at.value is not None else -1, + at.lineno if at.lineno is not None else 0) + VLogger.info(fb_gap, "ATTR") + # 对于 Name 类型的 obj,直接查找 alloca(不 load 结构体); # nonlocal 变量(提升函数内)从 env 读共享堆值,避免 lookup_var # 跨函数引用外层 alloca(SSA 编号跨函数错乱崩溃) @@ -4780,6 +4849,13 @@ class ExprGenMixin: HandlesStruct.ensure_struct_def_in_module(pool, mod, struct_ty) field_info: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct.lookup_field( struct_ty, at.attr) + # 诊断:lookup_field 结果(定位 G3 LinkedNode.prepend 挂起) + fb_lf2: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_lf2 is not None: + viperlib.snprintf(fb_lf2, 1024, "[GAP-LF] attr=%s -> %s", + at.attr if at.attr is not None else "?", + "found" if field_info is not None else "None") + VLogger.info(fb_lf2, "ATTR") # 按需过滤: struct_ty 为 i8(简化联合类型)时,通过 AnnotClassName 按类名查找 # 传递 SHA1 以区分跨模块同名类 # base 注解类名提取:Name → 变量注解类名;Attribute → 属性链递归字段注解类名 @@ -4794,13 +4870,32 @@ class ExprGenMixin: if var_entry is not None: base_class = var_entry.AnnotClassName elif at.value.kind() == ast.ASTKind.Attribute: + # 诊断:回退路径(定位 G3 prepend 挂起) + fb_fb: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_fb is not None: + viperlib.snprintf(fb_fb, 1024, "[GAP-FB] attr=%s 走 _resolve_attr_field_class", + at.attr if at.attr is not None else "?") + VLogger.info(fb_fb, "ATTR") base_class = self._resolve_attr_field_class(pool, trans, at.value) + fb_fb2: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_fb2 is not None: + viperlib.snprintf(fb_fb2, 1024, "[GAP-FB2] attr=%s -> base_class=%s", + at.attr if at.attr is not None else "?", + base_class if base_class is not None else "None") + VLogger.info(fb_fb2, "ATTR") if base_class is not None: cur_sha1: str = None if trans is not None: cur_sha1 = trans.ModuleSha1 field_info = HandlesStruct.lookup_field_by_class( base_class, at.attr, cur_sha1) + fb_fb3: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_fb3 is not None: + viperlib.snprintf(fb_fb3, 1024, "[GAP-FB3] lookup_by_class(%s.%s) -> %s", + base_class if base_class is not None else "?", + at.attr if at.attr is not None else "?", + "found" if field_info is not None else "None") + VLogger.info(fb_fb3, "ATTR") # 按需过滤成功:bitcast obj_ptr 到 AnnotClassName 对应的结构体类型 # 原始 struct_ty 可能是 i8(X|t.CPtr 简化为 Ptr(i8)), # 需用实际结构体类型做 GEP,否则 GEP i8 失败 @@ -4820,6 +4915,13 @@ class ExprGenMixin: HandlesStruct.ensure_struct_def_in_module( pool, mod, annot_se.Ty) if field_info is not None: + # 诊断:GEP 前(定位 G3 prepend 挂起在 ensure 还是 GEP) + fb_gep: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_gep is not None: + viperlib.snprintf(fb_gep, 1024, "[GAP-GEP] attr=%s idx=%d", + at.attr if at.attr is not None else "?", + field_info.Index) + VLogger.info(fb_gep, "ATTR") # 联合体:bitcast obj_ptr 到 field_ty*(字段指针用于 store) if HandlesStruct.is_union_by_type(struct_ty) == 1: field_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, field_info.Ty) diff --git a/App/lib/core/Gen/FuncGenMixin.vp b/App/lib/core/Gen/FuncGenMixin.vp index 306f8bc..ec08d7d 100644 --- a/App/lib/core/Gen/FuncGenMixin.vp +++ b/App/lib/core/Gen/FuncGenMixin.vp @@ -447,9 +447,12 @@ class FuncGenMixin: pool, arg.annotation, imported_modules, from_imports, trans) if resolved is not None: # 裸类形参 → Ptr(struct)(对象即指针,与方法形参一致) + # CArray 形参 → 元素指针 ty*(C 数组衰减,与 add_param 一致) match resolved: case llvmlite.LLVMType.Struct(_, _, _): param_ty = llvmlite.Ptr(pool, resolved) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) case _: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) @@ -588,9 +591,12 @@ class FuncGenMixin: pool, arg_e.annotation, imported_modules, from_imports, trans) if resolved_e is not None: # 裸类形参 → Ptr(struct)(对象即指针,与方法形参一致) + # CArray 形参 → 元素指针 ty*(C 数组衰减) match resolved_e: case llvmlite.LLVMType.Struct(_, _, _): param_ty_e = llvmlite.Ptr(pool, resolved_e) + case llvmlite.LLVMType.Array(aelem_ty_e, _): + param_ty_e = llvmlite.Ptr(pool, aelem_ty_e) case _: param_ty_e = resolved_e pname_e: t.CChar | t.CPtr = pool.alloc(32) @@ -675,9 +681,14 @@ class FuncGenMixin: pool, arg.annotation, imported_modules, from_imports, trans) if resolved is not None: # 裸类形参 → Ptr(struct)(对象即指针,与方法形参一致) + # CArray 形参 → 元素指针 ty*(C 数组衰减:final(out: t.CArray[..., N]) + # 的 out 指向调用方数组首元素;传 Ptr(数组) 会使 out[i] 的 + # GEP 以整个数组为步长错位,摘要垃圾根因) match resolved: case llvmlite.LLVMType.Struct(_, _, _): param_ty = llvmlite.Ptr(pool, resolved) + case llvmlite.LLVMType.Array(aelem_ty, _): + param_ty = llvmlite.Ptr(pool, aelem_ty) case _: param_ty = resolved pname: t.CChar | t.CPtr = pool.alloc(32) @@ -739,10 +750,14 @@ class FuncGenMixin: resolved2: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( pool, arg2.annotation, imported_modules, from_imports, trans) if resolved2 is not None: - # 与 add_param 保持一致:裸类形参 → Ptr(struct)(对象即指针) + # 与 add_param 保持一致:裸类形参 → Ptr(struct)(对象即指针); + # CArray 形参 → 元素指针 ty*(数组衰减,alloca 类型须与形参一致, + # 否则 store ty* 到 [N x ty]* 容器类型不匹配) match resolved2: case llvmlite.LLVMType.Struct(_, _, _): param_ty2 = llvmlite.Ptr(pool, resolved2) + case llvmlite.LLVMType.Array(aelem_ty2, _): + param_ty2 = llvmlite.Ptr(pool, aelem_ty2) case _: param_ty2 = resolved2 alloca: llvmlite.Value | t.CPtr = llvmlite.build_alloca(func_builder, param_ty2) @@ -957,6 +972,8 @@ class FuncGenMixin: match resolved_n: case llvmlite.LLVMType.Struct(_, _, _): pty_n = llvmlite.Ptr(pool, resolved_n) + case llvmlite.LLVMType.Array(aelem_ty_n, _): + pty_n = llvmlite.Ptr(pool, aelem_ty_n) case _: pty_n = resolved_n if param_count < 64 and param_tys is not None: @@ -1597,6 +1614,8 @@ class FuncGenMixin: match resolved: case llvmlite.LLVMType.Struct(_, _, _): p_ty = llvmlite.Ptr(pool, resolved) + case llvmlite.LLVMType.Array(aelem_ty_l, _): + p_ty = llvmlite.Ptr(pool, aelem_ty_l) case _: p_ty = resolved ty_slot_p: str | t.CPtr = (t.CVoid | t.CPtr)( diff --git a/App/lib/core/Gen/StmtGenMixin.vp b/App/lib/core/Gen/StmtGenMixin.vp index e0c2278..ea039ad 100644 --- a/App/lib/core/Gen/StmtGenMixin.vp +++ b/App/lib/core/Gen/StmtGenMixin.vp @@ -83,6 +83,12 @@ class StmtGenMixin: return 0 k: int = node.kind() + # 诊断:全局语句级日志(定位 G3 LinkedNode.prepend if 内部挂起) + fb_sg: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_sg is not None: + viperlib.snprintf(fb_sg, 1024, "[STMT2] kind=%d ln=%d", k, node.lineno) + VLogger.info(fb_sg, "OOP") + if k == ast.ASTKind.Expr: return self.translate_expr_stmt(trans, node) elif k == ast.ASTKind.Assign: diff --git a/App/lib/core/Gen/StructGenMixin.vp b/App/lib/core/Gen/StructGenMixin.vp index 8f142cb..33b1c2b 100644 --- a/App/lib/core/Gen/StructGenMixin.vp +++ b/App/lib/core/Gen/StructGenMixin.vp @@ -192,12 +192,12 @@ class StructGenMixin: return None string.memset(entry.Fields, 0, field_size * FIELD_MAX) - # 复制类名(1b 阶段用 stdlib) + # 复制类名(统一 stdlib:1a 阶段注册的 entry 需跨 1b reset 存活。 + # 此前 1a 用 pool,1b 阶段 find_struct_by_module 的 strcmp 读悬空 + # Name 越界读 → G3 LinkedNode.prepend 挂起根因。stdlib 分配不随 + # pool reset 回收,多遍去重(Ty 比较)避免重复分配。) name_len: t.CSizeT = string.strlen(name) - if self.StdlibAlloc != 0: - name_buf: t.CChar | t.CPtr = stdlib.malloc(name_len + 1) - else: - name_buf: t.CChar | t.CPtr = pool.alloc(name_len + 1) + name_buf: t.CChar | t.CPtr = stdlib.malloc(name_len + 1) if name_buf is not None: string.strcpy(name_buf, name) entry.Name = name_buf @@ -206,12 +206,10 @@ class StructGenMixin: entry.FieldCount = 0 # 注册时直接设置 SHA1(避免 set_struct_sha1 跨模块同名时找错 entry) + # SHA1 同样统一 stdlib(跨 1a/1b 存活,防 strcmp 悬空) if sha1 is not None: sha1_len: t.CSizeT = string.strlen(sha1) - if self.StdlibAlloc != 0: - sha1_buf: t.CChar | t.CPtr = stdlib.malloc(sha1_len + 1) - else: - sha1_buf: t.CChar | t.CPtr = pool.alloc(sha1_len + 1) + sha1_buf: t.CChar | t.CPtr = stdlib.malloc(sha1_len + 1) if sha1_buf is not None: string.strcpy(sha1_buf, sha1) entry.ModuleSha1 = sha1_buf @@ -227,9 +225,9 @@ class StructGenMixin: entry: HandlesStruct.StructEntry | t.CPtr = self.find_struct(class_name) if entry is None: return 0 - # 复制 SHA1 字符串(避免悬空指针) + # 复制 SHA1 字符串(统一 stdlib,跨 1a/1b 存活防 strcmp 悬空) sha1_len: t.CSizeT = string.strlen(sha1) - sha1_buf: t.CChar | t.CPtr = pool.alloc(sha1_len + 1) + sha1_buf: t.CChar | t.CPtr = stdlib.malloc(sha1_len + 1) if sha1_buf is None: return 0 string.strcpy(sha1_buf, sha1) @@ -271,12 +269,11 @@ class StructGenMixin: if fe is None: return -1 - # 复制字段名(1b 阶段用 stdlib,避免 reset 回收后 fe.Name 悬空) + # 复制字段名(统一 stdlib:1a 注册的字段名跨 1b reset 存活, + # 否则 lookup_field 的 strcmp(fe.Name, ...) 读悬空越界挂起, + # 与 register_struct 的 Name 悬空同根因) name_len: t.CSizeT = string.strlen(field_name) - if self.StdlibAlloc != 0: - name_buf: t.CChar | t.CPtr = stdlib.malloc(name_len + 1) - else: - name_buf: t.CChar | t.CPtr = pool.alloc(name_len + 1) + name_buf: t.CChar | t.CPtr = stdlib.malloc(name_len + 1) if name_buf is not None: string.strcpy(name_buf, field_name) fe.Name = name_buf @@ -419,6 +416,10 @@ class StructGenMixin: if name is None or self.StructTable is None: return None # 第一遍:精确匹配 + # StructCount 合理性守卫(内存损坏导致巨大值 → 索引循环假挂起) + if self.StructCount < 0 or self.StructCount > 200000: + VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct)", "STRUCT") + stdlib.exit(1) for i in range(self.StructCount): entry: HandlesStruct.StructEntry | t.CPtr = self._get_struct_entry(i) if entry is not None and entry.Name is not None: @@ -444,6 +445,10 @@ class StructGenMixin: """ if name is None or self.StructTable is None: return None + # StructCount 合理性守卫(内存损坏导致巨大值 → 索引循环假挂起) + if self.StructCount < 0 or self.StructCount > 200000: + VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_module)", "STRUCT") + stdlib.exit(1) if sha1 is not None: for i in range(self.StructCount): entry: HandlesStruct.StructEntry | t.CPtr = self._get_struct_entry(i) @@ -468,6 +473,10 @@ class StructGenMixin: """ if struct_ty is None or self.StructTable is None: return None + # StructCount 合理性守卫(内存损坏导致巨大值 → 索引循环假挂起) + if self.StructCount < 0 or self.StructCount > 200000: + VLogger.error(f"StructCount 异常: {self.StructCount} (find_struct_by_type)", "STRUCT") + stdlib.exit(1) st_val: t.CUInt64T = t.CUInt64T(struct_ty) for i in range(self.StructCount): entry: HandlesStruct.StructEntry | t.CPtr = self._get_struct_entry(i) @@ -1055,6 +1064,13 @@ class StructGenMixin: """ if ty is None or mod is None or pool is None: return + # 诊断:ensure_struct_def_in_module 入口(定位 G3 prepend 挂起) + fb_esd: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd is not None: + esd_name: str = llvmlite.get_struct_name(ty) + viperlib.snprintf(fb_esd, 1024, "[ESD] ty=%s", + esd_name if esd_name is not None else "(non-struct)") + VLogger.info(fb_esd, "ATTR") # 指针类型:确保 pointee 至少有 opaque 声明 match ty: case llvmlite.LLVMType.Ptr(pointee): @@ -1068,11 +1084,32 @@ class StructGenMixin: pass # 获取结构体名称(非 Struct 类型返回 None) sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty) + # 诊断:get_struct_name 结果(限长拷贝防 %s 读悬空挂起) + fb_esd2: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd2 is not None: + if sname is not None: + sn_cp2: t.CChar | t.CPtr = pool.alloc(64) + if sn_cp2 is not None: + string.strncpy(sn_cp2, sname, 63) + sn_cp2[63] = '\0' + viperlib.snprintf(fb_esd2, 1024, "[ESD2] sname=%s", sn_cp2) + else: + viperlib.snprintf(fb_esd2, 1024, "[ESD2] sname=(null)") + VLogger.info(fb_esd2, "ATTR") if sname is None: return # 检查是否为跨模块引用(名称包含 '.') + fb_esd3: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd3 is not None: + viperlib.snprintf(fb_esd3, 1024, "[ESD3] 开始 strchr(sname, '.')") + VLogger.info(fb_esd3, "ATTR") if string.strchr(sname, 46) is None: return # 非跨模块引用,无需处理 + # 诊断:strchr 完成,后续步骤(定位 G3 prepend 挂起) + fb_esd4: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd4 is not None: + viperlib.snprintf(fb_esd4, 1024, "[ESD4] strchr 完成, StructCount=%d", self.StructCount) + VLogger.info(fb_esd4, "ATTR") # 提取类名(第一个 '.' 之后的部分) # 必须用 strchr 而非 strrchr:泛型特化名如 "list[ast.AST|t.CPtr]" # 内部含 '.'(ast.AST),strrchr 会错误地把类名解析为 "AST|t.CPtr]" @@ -1082,6 +1119,11 @@ class StructGenMixin: class_name: str = dot_pos + 1 if class_name is None or class_name[0] == '\0': return + # 诊断:strchr 后步骤(定位 G3 prepend 挂起) + fb_esd5: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd5 is not None: + viperlib.snprintf(fb_esd5, 1024, "[ESD5] class_name=%s", class_name) + VLogger.info(fb_esd5, "ATTR") # 提取 SHA1(sname 到 dot_pos 之间,用 strlen 避免指针减法) total_len: t.CSizeT = string.strlen(sname) class_name_len: t.CSizeT = string.strlen(class_name) @@ -1095,8 +1137,17 @@ class StructGenMixin: sha1_cp = 31 string.strncpy(sha1_buf, sname, sha1_cp) sha1_buf[sha1_cp] = '\0' + fb_esd6: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd6 is not None: + viperlib.snprintf(fb_esd6, 1024, "[ESD6] sha1=%s len=%d", sha1_buf, sha1_len) + VLogger.info(fb_esd6, "ATTR") # 按类名 + SHA1 查找结构体定义 entry: HandlesStruct.StructEntry | t.CPtr = self.find_struct_by_module(class_name, sha1_buf) + fb_esd7: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd7 is not None: + viperlib.snprintf(fb_esd7, 1024, "[ESD7] find_struct_by_module -> %s", + "found" if entry is not None else "None") + VLogger.info(fb_esd7, "ATTR") if entry is None: # 回退到按类名查找(可能找到不同模块的同名类,但总比没有好) entry = self.find_struct(class_name) @@ -1104,11 +1155,28 @@ class StructGenMixin: return if entry.Ty is None: return + # 诊断:进入 module_add_named_type / _ensure_field_types(定位 G3 prepend 挂起) + fb_esd8: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd8 is not None: + viperlib.snprintf(fb_esd8, 1024, "[ESD8] 进入 module_add_named_type") + VLogger.info(fb_esd8, "ATTR") # 将完整定义添加到当前模块(会替换已存在的 opaque 声明) llvmlite.module_add_named_type(mod, pool, entry.Ty) + fb_esd9: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd9 is not None: + viperlib.snprintf(fb_esd9, 1024, "[ESD9] module_add_named_type 完成") + VLogger.info(fb_esd9, "ATTR") # 递归扫描字段类型,确保值类型子结构体(如 WIN32_FIND_DATAA 中的 FILETIME) # 也有完整定义。指针类型字段只需 opaque 声明(已由 module_ensure_opaque_for_type 处理)。 + fb_esd10: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd10 is not None: + viperlib.snprintf(fb_esd10, 1024, "[ESD10] 进入 _ensure_field_types_in_module") + VLogger.info(fb_esd10, "ATTR") self._ensure_field_types_in_module(pool, mod, entry.Ty) + fb_esd11: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_esd11 is not None: + viperlib.snprintf(fb_esd11, 1024, "[ESD11] ensure_struct_def_in_module 完成") + VLogger.info(fb_esd11, "ATTR") def _ensure_field_types_in_module(self, pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, @@ -1122,7 +1190,16 @@ class StructGenMixin: # 遍历字段链表 if fields is not None: fld: llvmlite.ParamNode | t.CPtr = fields + cnt_ef: t.CInt = 0 while fld is not None: + # 步数守卫:字段链表成环(内存损坏)导致无限遍历挂起 + # (G3 LinkedNode.prepend 挂起候选),超限立即报错终止 + cnt_ef += 1 + if cnt_ef > 100000: + VLogger.error( + "字段链表疑似成环(_ensure_field_types_in_module),强制终止", + "STRUCT") + stdlib.exit(1) if fld.Ty is not None: # 值类型子结构体(如 FILETIME)需要完整定义 match fld.Ty: diff --git a/App/lib/core/Gen/TypeGenMixin.vp b/App/lib/core/Gen/TypeGenMixin.vp index 952ed08..902f5e6 100644 --- a/App/lib/core/Gen/TypeGenMixin.vp +++ b/App/lib/core/Gen/TypeGenMixin.vp @@ -522,11 +522,51 @@ class TypeGenMixin: if HandlesType.is_cdefine_found() != 0: self.ConstEvalOk = 1 return x_val + # 定位日志:跨模块 CDefine 查找失败(t.CArray count 场景) + fb_cdf: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_cdf is not None: + viperlib.snprintf(fb_cdf, 1024, + "[CDEF-MISS] name=%s fi=%s im=%s", + nm.id, + from_imports if from_imports is not None else "(null)", + imported_modules if imported_modules is not None else "(null)") + VLogger.error(fb_cdf, "CDEF") self.ConstEvalOk = 0 return 0 self.ConstEvalOk = 1 return val + # Attribute: 模块限定常量名,如 hashlib.MD5_DIGEST_LEN + # (t.CArray[t.CUInt8T, hashlib.MD5_DIGEST_LEN] 的 count 是 Attribute, + # 复用跨模块 CDefine 查找:*:模块 → 源文件提取;re-export 常量由 + # _lookup_cross_module_cdefine 的 from-import 递归子模块兜底) + if k == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) + if at.attr is None: + self.ConstEvalOk = 0 + return 0 + if at.value is not None and at.value.kind() == ast.ASTKind.Name: + at_mod: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value) + if at_mod.id is not None: + if (from_imports is not None or imported_modules is not None) \ + and pool is not None: + mod_len: t.CSizeT = string.strlen(at_mod.id) + fi_buf: str = pool.alloc(mod_len + 3) + if fi_buf is not None: + fi_buf[0] = '*' + fi_buf[1] = ':' + for mi in range(mod_len): + fi_buf[2 + mi] = at_mod.id[mi] + fi_buf[2 + mod_len] = '\0' + import lib.core.Handles.HandlesExpr as HandlesExpr + av: int = HandlesExpr._lookup_cross_module_cdefine( + pool, at.attr, fi_buf, imported_modules) + if HandlesType.is_cdefine_found() != 0: + self.ConstEvalOk = 1 + return av + self.ConstEvalOk = 0 + return 0 + # BinOp: 算术/位运算 if k == ast.ASTKind.BinOp: bop: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(node) @@ -1490,10 +1530,48 @@ class TypeGenMixin: """解析泛型类类型注解(特化或查找已注册结构体),失败返回 None""" if pool is None or sub is None or gen_cls_name is None: return None - # tuple[...] 类型注解:TransPyV 暂不支持 tuple 返回类型,返回 None 让上层默认处理 - # 避免走到 list[str](pool, 4) 构造路径崩溃(tuple 未定义为泛型类) + # tuple[T1, T2, ...] → 匿名结构体 {T1, T2, ...}(多返回值打包, + # 对齐 TPC LLVMTypeMapper._resolve_subscript 的 '{ ty1, ty2 }')。 + # 用于函数 `-> tuple[str, t.CUnsignedShort]`(如 binascii.b2a_hqx) + # 与调用端 `a, b = f()` 的打包/解包。 if string.strcmp(gen_cls_name, "tuple") == 0: - return None + tp_elts: list[ast.AST | t.CPtr] | t.CPtr = None + tp_count: t.CSizeT = 0 + if sub.slice.kind() == ast.ASTKind.Tuple: + tp_tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub.slice) + if tp_tup.elts is not None: + tp_elts = tp_tup.elts + tp_count = tp_elts.__len__() + else: + tp_single: list[ast.AST | t.CPtr] | t.CPtr = list[ast.AST | t.CPtr](pool, 1) + if tp_single is not None: + tp_single.append(sub.slice) + tp_elts = tp_single + tp_count = 1 + if tp_count == 0 or tp_elts is None: + return None + tup_head: llvmlite.ParamNode | t.CPtr = None + tup_tail: llvmlite.ParamNode | t.CPtr = None + tui: t.CSizeT + for tui in range(tp_count): + tup_elt: ast.AST | t.CPtr = tp_elts.get(tui) + if tup_elt is None: + continue + tup_ety: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type( + pool, tup_elt, imported_modules, from_imports, trans) + if tup_ety is None: + return None + tup_node: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, tup_ety) + if tup_node is None: + return None + if tup_head is None: + tup_head = tup_node + else: + llvmlite.paramnode_set_next(tup_tail, tup_node) + tup_tail = tup_node + if tup_head is None: + return None + return llvmlite.Struct(pool, tup_head, tp_count, None) # 先收集类型实参名,再分配缓冲区拼接 mangled name(elem_size=8:str 是指针) gen_arg_names: list[str] | t.CPtr = list[str](pool, 8) if sub.slice.kind() == ast.ASTKind.Tuple: @@ -1608,11 +1686,32 @@ class TypeGenMixin: if gp_tpn is not None and string.strcmp(nm.id, gp_tpn) == 0: gp_ta: str = self.GenericTypeArgs.get(gp_i) if gp_ta is not None: - # 自映射防护(泛型循环套用):实参与参数名相同(T→T) - # 时 _parse_type_string 会重新解析回同一个 Name → 无条件 - # 自递归无退出条件 → 编译卡死。直接返回 None 让上层回落。 + # TSUB-DBG: 打印泛型替换(定位 G3 泛型循环) + fb_tsub: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_tsub is not None: + viperlib.snprintf(fb_tsub, 1024, "[TSUB] %s -> %s", gp_tpn, + gp_ta if gp_ta is not None else "(null)") + VLogger.info(fb_tsub, "TSUB") + # 自映射/污染实参防护(泛型循环套用): + # 1) 实参与参数名完全一致(T→T) + # 2) 实参串包含任一类型参数名(如 "T | t.CPtr"、" T "、 + # "list[T]")——_parse_type_string 解析后仍会产出 + # Name(参数名) → 递归再次进入本分支 → 无条件自递归 + # 无退出条件 → 编译卡死。该递归不经 _specialize_generic_class + # 的深度守卫(GenericSpecDepth),必须在此拦截。 + # 直接返回 None 让上层回落(fail-fast:宁可类型退化 + # 也不静默卡死)。 if string.strcmp(gp_ta, gp_tpn) == 0: return None + polluted: int = 0 + gp_j: t.CSizeT + for gp_j in range(gp_n): + gp_pn: str = self.GenericTpNames.get(gp_j) + if gp_pn is not None and string.strstr(gp_ta, gp_pn) is not None: + polluted = 1 + break + if polluted != 0: + return None # 用具体类型名递归解析(支持 BinOp/Attribute 类型实参) gp_sub_node: ast.AST | t.CPtr = self._parse_type_string(pool, gp_ta, nm.ctx) if gp_sub_node is not None: @@ -1642,10 +1741,24 @@ class TypeGenMixin: if trans is not None and trans.ModuleSha1 is not None: own_entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module(nm.id, trans.ModuleSha1) if own_entry is not None and own_entry.Ty is not None: + # 有 __new__ 的类(堆对象)注解 → Ptr(struct)(引用语义,对齐 TPC)。 + # 否则无 __new__ 的类(栈对象,如 Point)保持 struct 值语义: + # 构造返回栈 alloca 指针,赋给 struct 值变量时 coerce load 拷贝。 + # 背景:d: dict = dict(bd) 的 dict 是堆对象(__new__ 分配), + # 裸名注解按值解析会让 sub: dict = sub_p 把 i8* 当 struct 解引用 + # 崩溃(DictTest Test 13)。 + if own_entry.HasNew != 0: + return llvmlite.Ptr(pool, own_entry.Ty) return own_entry.Ty if HandlesStruct.is_struct_visible(nm.id) != 0: struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(nm.id) if struct_ty is not None: + nm_entry_s: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty) + nm_has_new_s: int = 0 + if nm_entry_s is not None: + nm_has_new_s = nm_entry_s.HasNew + if nm_has_new_s != 0: + return llvmlite.Ptr(pool, struct_ty) return struct_ty # CTypedef 动态查表:查动态 CTypedef 表,递归解析类型表达式 # 用户自定义 CTypedef(如 MyHandle: t.CTypedef = VOIDPTR)在此解析 @@ -1779,6 +1892,21 @@ class TypeGenMixin: if string.strcmp(gtn_nmx, other_nm_x.id) == 0: keep_other_ty = 1 break + # 内建标量类型名(int/str/bytes/bool/float/double)是合法 + # 标量,不是损坏节点解析出的垃圾:int | t.CPtr 应解析为 + # i32*(int 指针),若按"Name 未注册 struct 即丢弃"踢掉 + # int → 联合退化为 i8* → int_ptr[0]=val 写 1 字节截断、 + # 读回符号扩展(DictTest key5=500 读出 -12 根因)。 + if keep_other_ty == 0 and other_node is not None and other_node.kind() == ast.ASTKind.Name: + other_nm_b: ast.Name | t.CPtr = (ast.Name | t.CPtr)(other_node) + if other_nm_b.id is not None: + if string.strcmp(other_nm_b.id, "int") == 0 or \ + string.strcmp(other_nm_b.id, "str") == 0 or \ + string.strcmp(other_nm_b.id, "bytes") == 0 or \ + string.strcmp(other_nm_b.id, "bool") == 0 or \ + string.strcmp(other_nm_b.id, "float") == 0 or \ + string.strcmp(other_nm_b.id, "double") == 0: + keep_other_ty = 1 if keep_other_ty == 0: other_ty = None if other_ty is None: diff --git a/App/lib/core/Handles/HandlesAnnAssign.vp b/App/lib/core/Handles/HandlesAnnAssign.vp index 86c7d8f..f73ff44 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.vp +++ b/App/lib/core/Handles/HandlesAnnAssign.vp @@ -559,10 +559,45 @@ class AnnAssignHandle(HandlesBase.Mixin): _init_global_array_from_list( builder, pool, mod, alloca, aa.value, self.Trans) else: + # 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → 读 i32 + self.Trans.CurAssignAnnot = aa.annotation rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, aa.value, None, 0, self.Trans) if rhs_val is not None: + # 注解退化修复(对齐 TPC 从构造结果推断类型): + # 无实参泛型模板类注解(如 vi: vector.Vector | CPtr,模板 + # 未注册 struct → resolve 退化)会让 alloca 类型退化为 + # Ptr(i8),但 rhs 构造返回精确结构体指针(如 Ptr(Vector[int] + # 结构))。此时按退化 alloca store 会丢失特化类型,后续 + # vi.push 方法调用按 Ptr(i8) 查不到类名 → AnnotClassName + # 回退到模板名(无实参)→ vpi 查不到返回类型 fail-fast。 + # 用 rhs 精确类型重建 alloca 并更新符号表注册,使方法调用 + # 按精确特化类名解析。旧退化 alloca 行残留为 entry block + # 死代码(文本 IR 即时生成,无法删除,无害)。 + precise_rhs_ty: llvmlite.LLVMType | t.CPtr = None + if rhs_val.Ty is not None: + match rhs_val.Ty: + case llvmlite.LLVMType.Ptr(inner_struct): + match inner_struct: + case llvmlite.LLVMType.Struct(_, _, _): + precise_rhs_ty = rhs_val.Ty + case _: + pass + case _: + pass + if precise_rhs_ty is not None and alloca.Ty is not None: + alloca_pt: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee + if alloca_pt is not None and \ + t.CUInt64T(alloca_pt) != t.CUInt64T(precise_rhs_ty): + new_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry( + builder, precise_rhs_ty) + if new_alloca is not None: + v_entry_rb: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( + self.Trans.SymTab, nm.id) + if v_entry_rb is not None: + v_entry_rb.Alloca = new_alloca + alloca = new_alloca # 类型转换:将 rhs_val 转换为 alloca 的 pointee 类型 # 修复:整数字面量是 i32,但变量可能是 i8/i16/i64,需 trunc/sext target_ty: llvmlite.LLVMType | t.CPtr = None diff --git a/App/lib/core/Handles/HandlesAssign.vp b/App/lib/core/Handles/HandlesAssign.vp index 11d8e61..24adf85 100644 --- a/App/lib/core/Handles/HandlesAssign.vp +++ b/App/lib/core/Handles/HandlesAssign.vp @@ -60,6 +60,8 @@ class AssignHandle(HandlesBase.Mixin): mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module # 翻译 RHS 值 + # 无注解赋值无目标类型可推断,清空 CurAssignAnnot(防残留误判 c.Deref 宽度) + self.Trans.CurAssignAnnot = None rhs_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, asgn.value, None, 0, self.Trans) if rhs_val is None: @@ -138,6 +140,51 @@ class AssignHandle(HandlesBase.Mixin): tk: int = target.kind() + # Tuple 赋值: a, b = f() 多返回值解包 + # rhs_val 是匿名 struct 值(函数返回 tuple[T1,T2] 打包)→ alloca + + # store + GEP 逐字段 load → store 到各 Name 目标。 + if tk == ast.ASTKind.Tuple: + tgt_tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(target) + tgt_elts: list[ast.AST | t.CPtr] | t.CPtr = tgt_tup.elts + if tgt_elts is not None and rhs_val is not None and rhs_val.Ty is not None: + match rhs_val.Ty: + case llvmlite.LLVMType.Struct(_, _, _): + tgt_n: t.CSizeT = tgt_elts.__len__() + tmp_s: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, rhs_val.Ty) + if tmp_s is not None: + llvmlite.build_store(builder, rhs_val, tmp_s) + fchain: llvmlite.ParamNode | t.CPtr = llvmlite.get_struct_fields(rhs_val.Ty) + fcur: llvmlite.ParamNode | t.CPtr = fchain + fti: t.CSizeT + for fti in range(tgt_n): + tgt_elt: ast.AST | t.CPtr = tgt_elts.get(fti) + if tgt_elt is None or fcur is None: + break + fty: llvmlite.LLVMType | t.CPtr = llvmlite.paramnode_get_ty(fcur) + if fty is not None and tgt_elt.kind() == ast.ASTKind.Name: + tgt_nm2: ast.Name | t.CPtr = (ast.Name | t.CPtr)(tgt_elt) + if tgt_nm2.id is not None: + fp2: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, rhs_val.Ty, fty, tmp_s, fti) + if fp2 is not None: + fv2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, fty, fp2) + tgt_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( + self.Trans.SymTab, tgt_nm2.id) + if tgt_alloca is None: + tgt_alloca = HandlesVar.get_or_create_sym( + self.Trans.SymTab, pool, builder, tgt_nm2.id, fty) + if tgt_alloca is not None: + sval: llvmlite.Value | t.CPtr = fv2 + if tgt_alloca.Ty is not None: + sp: llvmlite.LLVMType | t.CPtr = tgt_alloca.Ty.Pointee + if sp is not None: + sval = HandlesExpr.coerce_to_type(builder, fv2, sp, node) + llvmlite.build_store(builder, sval, tgt_alloca) + fcur = llvmlite.paramnode_get_next(fcur) + case _: + pass + continue + # Subscript 赋值: arr[i] = val / ptr[i] = val / list[i] = val if tk == ast.ASTKind.Subscript: # 检查是否是 list[T] 类型的 Subscript(泛型类不注册 struct) diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index f7498f8..5a6a37d 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -1489,12 +1489,16 @@ def _check_and_promote_variadic_call(pool: memhub.MemBuddy | t.CPtr, mod: llvmlite.LLVMModule | t.CPtr, call_name: str, head: llvmlite.Value | t.CPtr, - can: t.CSizeT) -> int: + can: t.CSizeT, + arg_nodes: list[ast.AST | t.CPtr] | t.CPtr = None, + self_offset: int = 0, + trans: HT.Translator | t.CPtr = None) -> int: """检查被调用函数是否为变长函数,若是则对变长参数进行 C ABI 提升(委托到 gen._check_and_promote_variadic_call)""" import lib.core.Gen.Generator as Generator g: Generator.Generator | t.CPtr = (Generator.Generator | t.CPtr)(t.CVoid(_g_gen, t.CPtr)) if g is not None: - return g._check_and_promote_variadic_call(pool, builder, mod, call_name, head, can) + return g._check_and_promote_variadic_call(pool, builder, mod, call_name, head, can, + arg_nodes, self_offset, trans) return 0 diff --git a/App/lib/core/Handles/HandlesFor.vp b/App/lib/core/Handles/HandlesFor.vp index b54d08f..b23f42a 100644 --- a/App/lib/core/Handles/HandlesFor.vp +++ b/App/lib/core/Handles/HandlesFor.vp @@ -171,6 +171,14 @@ class ForHandle(HandlesBase.Mixin): ve_lst: HandlesVar.VarEntry | t.CPtr = HandlesVar.lookup_var_entry( trans.SymTab, nm_lst.id) if ve_lst is not None and ve_lst.AnnotClassName is not None: + # dict 容器变量:按 __count__/__keys__ 布局迭代(__iter__ 协议内联) + if string.strstr(ve_lst.AnnotClassName, "dict") is not None: + lst_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, trans.Module, iter_node, + trans._funcs, trans._func_count, trans) + if lst_val is not None and lst_val.Ty is not None \ + and HandlesExpr.is_ptr_type(lst_val.Ty) != 0: + return self._handle_dict_iter(for_node, var_name, lst_val) if string.strstr(ve_lst.AnnotClassName, "list") is not None: lst_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, trans.Module, iter_node, @@ -818,6 +826,115 @@ class ForHandle(HandlesBase.Mixin): llvmlite.position_at_end(builder, end_bb) return new_vars + def _handle_dict_iter(self, for_node: ast.For | t.CPtr, + var_name: str, + dict_val: llvmlite.Value | t.CPtr) -> int: + """dict 容器迭代: for key in d: 按 __count__/__keys__ 布局迭代 + + _dict.vp 布局(8 字节槽):__keys__@0, __values__@1, __count__@2, + __capacity__@3, __mbuddy__@4, __iter_index__@5。 + __iter__ 协议(__iter__ 重置 iter_index + __next__ 取 key)内联为 + 循环:iter_idx < __count__ → key = __keys__[iter_idx]。 + """ + trans: HT.Translator | t.CPtr = self.Trans + pool: memhub.MemBuddy | t.CPtr = trans.Pool + builder: llvmlite.IRBuilder | t.CPtr = trans._cur_builder + func: llvmlite.Function | t.CPtr = trans._cur_func + + i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool) + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + + # 读取 __count__(槽 2,字节偏移 16) + cnt_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i64_ty, dict_val, llvmlite.const_int64(pool, 2)) + if cnt_ptr is None: + HandlesType.fatal_error(for_node, "dict 迭代: 无法定位 __count__") + count_val: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, cnt_ptr) + + # 循环变量 alloca(元素类型 i8*,与 dict.__next__ 返回 str 一致) + var_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var( + trans.SymTab, var_name) + new_vars: int = 0 + if var_alloca is None: + var_alloca = HandlesVar._alloca_at_entry(builder, i8_ptr_ty) + if HandlesVar.define_var( + trans.SymTab, var_name, var_alloca) == 0: + new_vars = 1 + + # 隐式 index 变量 (i64,与 __count__ 类型对齐) + idx_alloca: llvmlite.Value | t.CPtr = HandlesVar._alloca_at_entry(builder, i64_ty) + llvmlite.build_store(builder, llvmlite.const_int64(pool, 0), idx_alloca) + + # 基本块: cond / body / incr / end + cnt: int = trans._label_counter + trans._label_counter = cnt + 1 + + name_buf: t.CChar | t.CPtr = pool.alloc(32) + viperlib.snprintf(name_buf, 32, "dict.cond.%d", cnt) + cond_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + + viperlib.snprintf(name_buf, 32, "dict.body.%d", cnt) + body_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + + viperlib.snprintf(name_buf, 32, "dict.incr.%d", cnt) + incr_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + + viperlib.snprintf(name_buf, 32, "dict.end.%d", cnt) + end_bb: llvmlite.BasicBlock | t.CPtr = llvmlite.create_block(pool, func, name_buf) + + llvmlite.build_br(builder, cond_bb) + + # cond 块: load index, icmp slt idx, count + llvmlite.position_at_end(builder, cond_bb) + cur_idx: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i64_ty, idx_alloca) + in_bounds: llvmlite.Value | t.CPtr = llvmlite.build_icmp( + builder, llvmlite.ICMP_SLT, cur_idx, count_val) + llvmlite.build_cond_br(builder, in_bounds, body_bb, end_bb) + + # body 块: __keys__ + idx*8 → load key (i8*) → store to var + llvmlite.position_at_end(builder, body_bb) + kpp: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i64_ty, dict_val, llvmlite.const_int64(pool, 0)) + keys_ptr: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, kpp) + eight_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 8) + key_offset: llvmlite.Value | t.CPtr = llvmlite.build_mul(builder, cur_idx, eight_val) + key_addr: llvmlite.Value | t.CPtr = llvmlite.build_gep( + builder, i8_ty, keys_ptr, key_offset) + cur_key: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, key_addr) + llvmlite.build_store(builder, cur_key, var_alloca) + + # 保存旧循环上下文,设置 break/continue 目标 + old_break: llvmlite.BasicBlock | t.CPtr = trans._break_bb + old_continue: llvmlite.BasicBlock | t.CPtr = trans._continue_bb + trans._break_bb = end_bb + trans._continue_bb = incr_bb + + body: list[ast.AST | t.CPtr] | t.CPtr = for_node.children + if body is not None: + body_count: t.CSizeT = body.__len__() + for bi in range(body_count): + stmt: ast.AST | t.CPtr = body.get(bi) + if stmt is not None: + HandlesBody.translate_stmt(trans, stmt) + + trans._break_bb = old_break + trans._continue_bb = old_continue + + if llvmlite.builder_cur_block_is_terminated(builder) == 0: + llvmlite.build_br(builder, incr_bb) + + # incr 块: index++ + llvmlite.position_at_end(builder, incr_bb) + one_val: llvmlite.Value | t.CPtr = llvmlite.const_int64(pool, 1) + next_idx: llvmlite.Value | t.CPtr = llvmlite.build_add(builder, cur_idx, one_val) + llvmlite.build_store(builder, next_idx, idx_alloca) + llvmlite.build_br(builder, cond_bb) + + # 定位到 end 块 + llvmlite.position_at_end(builder, end_bb) + return new_vars + # ============================================================ # 多变量迭代(enumerate / zip) # diff --git a/App/lib/core/Handles/HandlesReturn.vp b/App/lib/core/Handles/HandlesReturn.vp index 3131014..6b6dfea 100644 --- a/App/lib/core/Handles/HandlesReturn.vp +++ b/App/lib/core/Handles/HandlesReturn.vp @@ -59,6 +59,64 @@ class ReturnHandle(HandlesBase.Mixin): val: llvmlite.Value | t.CPtr = None if rt.value is not None: + # return a, b → 打包匿名 struct 值(多返回值,函数返回类型 tuple[T1,T2]) + if rt.value.kind() == ast.ASTKind.Tuple: + rtp: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(rt.value) + rtp_elts: list[ast.AST | t.CPtr] | t.CPtr = rtp.elts + if rtp_elts is not None: + rt_n: t.CSizeT = rtp_elts.__len__() + if rt_n > 0 and rt_n <= 16: + rt_vals: t.CSizeT | t.CPtr = pool.alloc(8 * 16) + if rt_vals is not None: + rt_val_ok: int = 1 + rvi: t.CSizeT + for rvi in range(rt_n): + ev_i: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, rtp_elts.get(rvi), + self.Trans._funcs, self.Trans._func_count, self.Trans) + if ev_i is not None: + rt_vals[rvi] = t.CSizeT(ev_i) + else: + rt_val_ok = 0 + break + if rt_val_ok != 0: + # 构建匿名 struct 类型(字段类型 = 元素值类型) + rt_head: llvmlite.ParamNode | t.CPtr = None + rt_tail: llvmlite.ParamNode | t.CPtr = None + rvi2: t.CSizeT + for rvi2 in range(rt_n): + rv_i: llvmlite.Value | t.CPtr = (llvmlite.Value | t.CPtr)( + t.CVoid(rt_vals[rvi2], t.CPtr)) + if rv_i is None or rv_i.Ty is None: + rt_val_ok = 0 + break + rn: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, rv_i.Ty) + if rn is None: + rt_val_ok = 0 + break + if rt_head is None: + rt_head = rn + else: + llvmlite.paramnode_set_next(rt_tail, rn) + rt_tail = rn + if rt_val_ok != 0 and rt_head is not None: + rt_struct_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Struct( + pool, rt_head, rt_n, None) + if rt_struct_ty is not None: + rt_tmp: llvmlite.Value | t.CPtr = llvmlite.build_alloca(builder, rt_struct_ty) + if rt_tmp is not None: + rvi3: t.CSizeT + for rvi3 in range(rt_n): + rv3: llvmlite.Value | t.CPtr = (llvmlite.Value | t.CPtr)( + t.CVoid(rt_vals[rvi3], t.CPtr)) + rfp: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( + builder, rt_struct_ty, rv3.Ty, rt_tmp, rvi3) + if rfp is None: + rt_val_ok = 0 + break + llvmlite.build_store(builder, rv3, rfp) + if rt_val_ok != 0: + val = llvmlite.build_load(builder, rt_struct_ty, rt_tmp) # return self: 返回 self 指针本身(Ptr(StructTy)),而非结构体值 # translate_value 会 load 得到结构体值,但 return self 需要指针 # 注意: lookup_var 返回的是存储 self 的 alloca (Ptr(Ptr(StructTy))) diff --git a/App/lib/core/Handles/HandlesTranslator.vp b/App/lib/core/Handles/HandlesTranslator.vp index a78a03f..d79c698 100644 --- a/App/lib/core/Handles/HandlesTranslator.vp +++ b/App/lib/core/Handles/HandlesTranslator.vp @@ -23,6 +23,7 @@ import lib.core.Handles.HandlesMatch as HandlesMatch import lib.core.Handles.HandlesExpr as HandlesExpr import lib.core.Handles.HandlesFunctions as HandlesFunctions import lib.core.Handles.HandlesType as HandlesType +import lib.core.Handles.HandlesStruct as HandlesStruct import lib.Projectrans.Config as Config # ============================================================ @@ -87,6 +88,12 @@ class Translator: _cur_func: llvmlite.Function | t.CPtr _cur_builder: llvmlite.IRBuilder | t.CPtr + # 当前赋值语句的注解节点(AnnAssign 翻译 rhs 前设置,Assign 翻译前清空)。 + # 供 c.Deref(i8*) 推断读取宽度:v: int = c.Deref(p) → bitcast i32* load, + # 对齐 TPC HandlesSpecialCall._infer_deref_target_type(DictTest 500→-12 根因: + # i8* Deref 固定读 1 字节 + sext 截断)。 + CurAssignAnnot: ast.AST | t.CPtr + # 循环控制流目标(break/continue) _break_bb: llvmlite.BasicBlock | t.CPtr _continue_bb: llvmlite.BasicBlock | t.CPtr @@ -392,6 +399,14 @@ class Translator: cur_imp_lineno = child.lineno self.ImportsH.HandleImportFromModule(child) self.ImportsH.HandleImportFromNames(child) + # 对齐 TPC _inject_auto_imports:检测源文件对 list/dict 容器的使用 + # (裸名注解 d: dict / 构造 dict(...) / 下标 list[T]),自动把容器 + # 类标记为可见结构体。背景:_list.vp/_dict.vp 定义内建容器类且已由 + # Phase1 注册,但源文件不显式 import 它们(TPC 在翻译前自动注入 + # import _list/_dict);TPV 用可见性机制等效替代——否则裸名 + # `d: dict` 注解 resolve_annotation_type 时 is_struct_visible=0 + # 解析失败(DictTest 的 "变量类型注解解析失败")。 + self._inject_auto_container_visibility(pool, tree) # 扫描当前模块的 CTypedef 声明(NAME: t.CTypedef = Type), # 注册到动态 CTypedef 表,供 resolve_annotation_type 查询 HandlesType.ScanCTypedefDeclarations(pool, tree) @@ -425,6 +440,98 @@ class Translator: HandlesFunctions.forward_declare_functions(self, tree) added: int = HandlesMain.translate_children(self, tree) + # ============================================================ + # 容器模块自动可见性注入(对齐 TPC _inject_auto_imports) + # + # TPC 在翻译前自动注入 `import _list / import _dict (+json)` 到源文件, + # 使 list/dict 内建容器类在符号表中可见。TPV 用可见性机制等效替代: + # _list.vp/_dict.vp 已由 Phase1 注册(可达集合含它们),这里只需在 + # 当前文件翻译时把容器类名标记为可见结构体,裸名注解/构造即可解析。 + # + # 检测模式(与 TPC Phase2Translator._inject_auto_imports 一致): + # - AnnAssign 注解裸名 Name(list/dict):d: dict + # - 构造调用 func 裸名 Name(list/dict):dict(bd) + # - Subscript 的 value 裸名 Name(list/dict):list[int] + # ============================================================ + def _inject_auto_container_visibility(self, pool: memhub.MemBuddy | t.CPtr, + tree: ast.AST | t.CPtr): + """检测 list/dict 容器使用,注入容器类可见性""" + if tree is None or tree.children is None: + return + # 用栈式缓冲区传递检测结果(跨函数参数用 4 字节 int 指针) + need_list_box: t.CPtr = stdlib.malloc(4) + need_dict_box: t.CPtr = stdlib.malloc(4) + if need_list_box is None or need_dict_box is None: + if need_list_box is not None: + stdlib.free(need_list_box) + if need_dict_box is not None: + stdlib.free(need_dict_box) + return + need_list_box[0] = 0 + need_dict_box[0] = 0 + self._scan_container_usage(tree, need_list_box, need_dict_box) + if need_dict_box[0] != 0: + # dict 类可见 + _dict 模块导入(构造/方法调用的 SHA1 解析) + HandlesStruct.add_visible_struct(pool, "dict") + if self._imported_modules is None or \ + HandlesImports.is_module_imported(self._imported_modules, "_dict") == 0: + self._imported_modules = HandlesImports.add_imported_module( + pool, self._imported_modules, "_dict") + if need_list_box[0] != 0: + HandlesStruct.add_visible_struct(pool, "list") + if self._imported_modules is None or \ + HandlesImports.is_module_imported(self._imported_modules, "_list") == 0: + self._imported_modules = HandlesImports.add_imported_module( + pool, self._imported_modules, "_list") + stdlib.free(need_list_box) + stdlib.free(need_dict_box) + + def _scan_container_usage(self, node: ast.AST | t.CPtr, + need_list_box: t.CPtr, + need_dict_box: t.CPtr): + """递归扫描 AST 语句结构,检测 list/dict 容器使用""" + if node is None: + return + k: int = node.kind() + if k == ast.ASTKind.AnnAssign: + aa_s: ast.AnnAssign | t.CPtr = (ast.AnnAssign | t.CPtr)(node) + if aa_s.annotation is not None: + self._check_container_type_node(aa_s.annotation, need_list_box, need_dict_box) + elif k == ast.ASTKind.Call: + cl_s: ast.Call | t.CPtr = (ast.Call | t.CPtr)(node) + if cl_s.func is not None: + self._check_container_type_node(cl_s.func, need_list_box, need_dict_box) + elif k == ast.ASTKind.Subscript: + sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + if sub_s.value is not None: + self._check_container_type_node(sub_s.value, need_list_box, need_dict_box) + # 递归语句块子节点 + if node.children is not None: + ch_s: list[ast.AST | t.CPtr] | t.CPtr = node.children + n_s: t.CSizeT = ch_s.__len__() + for ci_s in range(n_s): + self._scan_container_usage(ch_s.get(ci_s), need_list_box, need_dict_box) + + def _check_container_type_node(self, node: ast.AST | t.CPtr, + need_list_box: t.CPtr, + need_dict_box: t.CPtr): + """检测类型位置节点是否为 list/dict(支持 Name 与 Subscript 外层)""" + if node is None: + return + k: int = node.kind() + if k == ast.ASTKind.Name: + nm_s: ast.Name | t.CPtr = (ast.Name | t.CPtr)(node) + if nm_s.id is not None: + if string.strcmp(nm_s.id, "dict") == 0: + need_dict_box[0] = 1 + elif string.strcmp(nm_s.id, "list") == 0: + need_list_box[0] = 1 + elif k == ast.ASTKind.Subscript: + sub_s: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) + if sub_s.value is not None: + self._check_container_type_node(sub_s.value, need_list_box, need_dict_box) + + # ============================================================ # IR 输出 # ============================================================ diff --git a/App/lib/core/Phase1.vp b/App/lib/core/Phase1.vp index 650b827..51394d8 100644 --- a/App/lib/core/Phase1.vp +++ b/App/lib/core/Phase1.vp @@ -1215,6 +1215,13 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, stdlib.exit(1) ret: int = tr.translate(tree) + # 诊断:translate 正常返回(若挂起在 translate 内部,此行不会打印, + # 用于区分"translate 内部挂起"与"后续 dump 挂起") + if ret == 0: + fb_done: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_done is not None: + viperlib.snprintf(fb_done, 1024, "翻译完成(translate返回): %s", file_path) + VLogger.info(fb_done, "Phase1") if ret != 0: fb: t.CChar | t.CPtr = VLogger.fmt_buf() if fb is not None: diff --git a/App/main.vp b/App/main.vp index 9536f02..8267b3b 100644 --- a/App/main.vp +++ b/App/main.vp @@ -161,6 +161,8 @@ def main() -> int: argparse.STORE_TRUE, "删除 includes.binary 预编译缓存并重新编译所有 includes") parser.add_argument("--clear-cache", None, argparse.BOOL, 0, None, False, argparse.STORE_TRUE, "清除 .transpyc_cache 全局缓存") + parser.add_argument("--asan", None, argparse.BOOL, 0, None, False, + argparse.STORE_TRUE, "ASan 模式:.ll 先 opt -passes=asan 插桩,链接加 -fsanitize=address") # 解析命令行参数 args: argparse.ParsedArgs | t.CPtr = parser.parse_args(sys._argc, sys._argv) @@ -234,6 +236,24 @@ def main() -> int: stdio.printf("\n参数解析完成。\n") + # ASan 模式:设置 BuildPipeline 全局标志(compile_ll_to_obj 先 opt 插桩,链接加 -fsanitize=address) + if args.get_bool("asan"): + BuildPipeline.set_asan_mode(1) + if log is not None: + log.info("ASan 模式已启用(opt -passes=asan + -fsanitize=address)", "asan") + # ASan 模式强制重建 includes.binary:缓存 obj 未做 asan 插桩, + # 与非 asan obj 混链会导致 ASan 运行时崩溃 + mf_inc_bin_asan: str = Config.get_includes_binary_dir() + if mf_inc_bin_asan is not None: + n_asan: int = Utils.CleanDir(mf_inc_bin_asan) + if log is not None: + fb_asan: t.CChar | t.CPtr = VLogger.fmt_buf() + if fb_asan is not None: + viperlib.snprintf(fb_asan, 1024, "ASan 模式清理 includes.binary: %d 个文件(将重建 asan obj)", n_asan) + log.info(fb_asan, "asan") + else: + BuildPipeline.set_asan_mode(0) + # 加载 project.vpj 配置 proj_loaded: int = 0 proj_path: str = project