diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index b315aea..e439cca 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -91,6 +91,8 @@ def TranslateFileGetTrans(mb: memhub.MemBuddy | t.CPtr, file_path: str, tr._declare_only = declare_only # 设置当前文件名(供报错使用) HandlesType.set_current_file(file_path) + # 清空调用栈(每个文件翻译开始前重置) + HandlesType.clear_trace() # 模块切换:清空 CDefine 常量表,确保每个模块的 CDefine 常量正确隔离 HandlesType.clear_cdefine_constants() # 命名空间隔离:includes 文件宽松模式(全部可见),用户文件严格模式(仅本地+import) diff --git a/App/lib/core/Handles/HandlesAnnAssign.vp b/App/lib/core/Handles/HandlesAnnAssign.vp index 3610471..63b6142 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.vp +++ b/App/lib/core/Handles/HandlesAnnAssign.vp @@ -14,6 +14,7 @@ import lib.core.Handles.HandlesNonlocal as HandlesNonlocal import lib.core.Handles.HandlesClassDef as HandlesClassDef import lib.core.Handles.HandlesExprCall as HandlesExprCall import lib.core.Handles.HandlesStruct as HandlesStruct +import lib.core.VLogger as VLogger # ============================================================ @@ -310,7 +311,7 @@ class AnnAssignHandle(HandlesBase.Mixin): builder: llvmlite.IRBuilder | t.CPtr = self.Trans._cur_builder # 从 annotation 推断类型(修复:原硬编码 i32 导致 str 等类型错误) - var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + var_ty: llvmlite.LLVMType | t.CPtr = None annot: ast.AST | t.CPtr = aa.annotation if annot is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( @@ -334,6 +335,18 @@ class AnnAssignHandle(HandlesBase.Mixin): pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) if resolved is not None: var_ty = resolved + else: + # fast-fail: 注解类型解析失败,不再静默回退 i32(会导致变量类型错误) + VLogger.error( + f"变量类型注解解析失败,变量名: {nm.id}", + "HandlesAnnAssign") + var_ty = llvmlite.Int32(pool) + else: + # fast-fail: AnnAssign 必须有 annotation + VLogger.error( + f"AnnAssign 缺少类型注解,变量名: {nm.id}", + "HandlesAnnAssign") + var_ty = llvmlite.Int32(pool) # 确保跨模块结构体的完整定义在当前模块中可用(供 alloca 分配空间) HandlesStruct.ensure_struct_def_in_module(pool, self.Trans.Module, var_ty) @@ -388,13 +401,25 @@ class AnnAssignHandle(HandlesBase.Mixin): mod: llvmlite.LLVMModule | t.CPtr = self.Trans.Module # 确定类型(从 annotation 推断) - var_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + var_ty: llvmlite.LLVMType | t.CPtr = None annot: ast.AST | t.CPtr = aa.annotation if annot is not None: resolved: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) if resolved is not None: var_ty = resolved + else: + # fast-fail: 注解类型解析失败,不再静默回退 i32 + VLogger.error( + f"变量类型注解解析失败,变量名: {nm.id}", + "HandlesAnnAssign") + var_ty = llvmlite.Int32(pool) + else: + # fast-fail: AnnAssign 必须有 annotation + VLogger.error( + f"AnnAssign 缺少类型注解,变量名: {nm.id}", + "HandlesAnnAssign") + var_ty = llvmlite.Int32(pool) # global 变量:写入模块作用域中的全局变量 if HT.is_global_name(self.Trans, nm.id) != 0: @@ -423,9 +448,19 @@ class AnnAssignHandle(HandlesBase.Mixin): nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr( self.Trans, nm.id) if nl_ptr is not None: - i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + # 从 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( + f"nonlocal 变量类型信息丢失,无法写入: {nm.id}", + "HandlesAnnAssign") + return 0 rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type( - builder, rhs_val, i32_ty) + builder, rhs_val, nl_target_ty) llvmlite.build_store(builder, rhs_coerced, nl_ptr) return 0 diff --git a/App/lib/core/Handles/HandlesAssign.vp b/App/lib/core/Handles/HandlesAssign.vp index 765ce8b..9649c48 100644 --- a/App/lib/core/Handles/HandlesAssign.vp +++ b/App/lib/core/Handles/HandlesAssign.vp @@ -345,9 +345,19 @@ class AssignHandle(HandlesBase.Mixin): nl_ptr: llvmlite.Value | t.CPtr = HandlesNonlocal.get_nonlocal_var_ptr( self.Trans, nm.id) if nl_ptr is not None: - i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + # 从 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( + f"nonlocal 变量类型信息丢失,无法写入: {nm.id}", + "HandlesAssign") + continue rhs_coerced: llvmlite.Value | t.CPtr = HandlesExpr.coerce_to_type( - builder, rhs_val, i32_ty) + builder, rhs_val, nl_target_ty) llvmlite.build_store(builder, rhs_coerced, nl_ptr) continue diff --git a/App/lib/core/Handles/HandlesClassDef.vp b/App/lib/core/Handles/HandlesClassDef.vp index 802ac11..f0482e4 100644 --- a/App/lib/core/Handles/HandlesClassDef.vp +++ b/App/lib/core/Handles/HandlesClassDef.vp @@ -985,6 +985,12 @@ def _translate_union_def(trans: HT.Translator | t.CPtr, field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) if field_ty is None: + # fast-fail: 字段类型解析失败,用 fatal_error 输出 Python 风格 traceback + type_name: str = HandlesType.annotation_to_str(aa.annotation) + HandlesType.fatal_error( + aa, + f"'{type_name}' is not defined", + "NameError") field_ty = llvmlite.Int32(pool) # 计算字段大小,更新 max_size @@ -1132,7 +1138,8 @@ def _get_annotation_field_size(annotation: ast.AST | t.CPtr) -> int: if string.strcmp(type_name, "bytes") == 0: return 8 if string.strcmp(type_name, "bool") == 0: - return 4 + # bool = t.CBool = i8(1 字节) + return 1 # 已注册的结构体类型 struct_ty: llvmlite.LLVMType | t.CPtr = HandlesStruct.get_struct_type(type_name) if struct_ty is not None: @@ -2209,6 +2216,14 @@ def translate_class_def(trans: HT.Translator | t.CPtr, existing: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_module( class_name, trans.ModuleSha1) if existing is not None: + # BUG 5 方案1修改点2:existing 时字段完整性检查 + # 结构体已注册,但需验证字段信息是否完整(防止 i32 回退残留或注册不完整) + if existing.FieldCount == 0 and trans._declare_only == 0: + # Phase B 时字段数为 0 说明注册不完整(Phase A-pre 可能漏注册) + HandlesType.fatal_error( + node, + f"结构体已注册但字段数为0,类: {class_name},可能注册不完整", + "InternalCompilerError") # 结构体已注册(Phase A-pre 多遍扫描或 Phase B 重新翻译),但仍需在当前模块中翻译方法 if existing.Ty is not None: # Phase B: 将已存在的命名结构体注册到当前模块(输出类型定义行) @@ -2364,7 +2379,28 @@ def translate_class_def(trans: HT.Translator | t.CPtr, field_ty: llvmlite.LLVMType | t.CPtr = HandlesType.resolve_annotation_type( pool, aa.annotation, trans._imported_modules, trans._from_imports, trans) if field_ty is None: - field_ty = llvmlite.Int32(pool) + # BUG 5 方案1修改点1:消除 i32 静默回退 + # Phase A-pre(declare_only=1):依赖的类可能尚未注册,延迟到下一遍 + # Phase B(declare_only=0):真正的类型解析错误,fast-fail 终止该类翻译 + # 不再用 i32 回退(会导致结构体布局错误 + GEP 索引错乱) + # 例外:泛型 Subscript(如 list[AST|t.CPtr])未特化时回退为 Ptr(i8), + # 因为泛型特化可能延迟到构造调用时。AnnotClassName 会携带 mangled name + # 供 translate_attribute 做后续 bitcast。 + is_generic_sub: int = 0 + if aa.annotation is not None and aa.annotation.kind() == ast.ASTKind.Subscript: + is_generic_sub = 1 + if is_generic_sub != 0: + # 泛型未特化:回退为 Ptr(i8),依赖 AnnotClassName 做后续类型恢复 + field_ty = llvmlite.Ptr(pool, llvmlite.Int8(pool)) + else: + type_name2: str = HandlesType.annotation_to_str(aa.annotation) + HandlesType.fatal_error( + aa, + f"'{type_name2}' is not defined", + "NameError") + if trans._declare_only == 1: + return 0 + return 0 # 存储到数组(指针转 CSizeT) if field_count < 48: @@ -2377,9 +2413,10 @@ def translate_class_def(trans: HT.Translator | t.CPtr, else: field_defaults_buf[field_count] = 0 # 提取原始类型注解的类名(联合类型简化为 Ptr(i8) 时回退查找结构体) + # BUG 1 方案3:传 pool 使泛型 Subscript 返回 mangled name(如 "list[ast.AST|t.CPtr]") if aa.annotation is not None: annot_cn: str = HandlesType.extract_class_name_from_annotation( - aa.annotation, trans._imported_modules) + aa.annotation, trans._imported_modules, pool) if annot_cn is not None: diag_cn_val: t.CSizeT = t.CSizeT(annot_cn) field_annot_buf[field_count] = diag_cn_val diff --git a/App/lib/core/Handles/HandlesExpr.vp b/App/lib/core/Handles/HandlesExpr.vp index 4ab24d8..6eedd36 100644 --- a/App/lib/core/Handles/HandlesExpr.vp +++ b/App/lib/core/Handles/HandlesExpr.vp @@ -129,11 +129,9 @@ def get_llvm_float_bits(ty: llvmlite.LLVMType | t.CPtr) -> int: # ============================================================ -# 检查类型是否为 Ptr(独立函数,避免嵌套 match 的编译器 BUG) +# is_ptr_type - 检查类型是否为 Ptr # -# 嵌套 match 的 REnum 检测在宿主编译器中有 BUG:当 match 嵌套在 -# 另一个 match 的 case 块中时,内层 match 走非 REnum 路径, -# 导致 case 列表为空。提取为独立函数可规避此问题。 +# 返回 1=是, 0=否。作为类型检查工具函数被广泛使用(50+ 处调用)。 # ============================================================ def is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int: """检查 ty 是否是 Ptr 类型,返回 1=是, 0=否""" @@ -178,7 +176,9 @@ def get_ptr_level(ty: llvmlite.LLVMType | t.CPtr) -> int: # ============================================================ -# 检查类型是否为 Array(独立函数,避免嵌套 match 的编译器 BUG) +# is_array_type - 检查类型是否为 Array +# +# 返回 1=是, 0=否。类型检查工具函数。 # ============================================================ def is_array_type(ty: llvmlite.LLVMType | t.CPtr) -> int: """检查 ty 是否是 Array 类型,返回 1=是, 0=否""" @@ -224,8 +224,6 @@ def _get_custom_struct_cls_nm(pool: memhub.MemBuddy | t.CPtr, # 用于支持 X|t.CPtr 类型变量的属性访问: # - 值类型变量 (cnt: Counter): alloca 类型是 Ptr(Struct) → 不解引用 # - 指针类型变量 (r: Vec2|t.CPtr): alloca 类型是 Ptr(Ptr(Struct)) → load 解引用 -# -# 用 is_ptr_type 避免嵌套 match 的编译器 BUG # ============================================================ def _deref_if_ptr_ptr(builder: llvmlite.IRBuilder | t.CPtr, obj_ptr: llvmlite.Value | t.CPtr) -> llvmlite.Value | t.CPtr: @@ -303,7 +301,6 @@ def coerce_to_type(builder: llvmlite.IRBuilder | t.CPtr, # 指针 → 非指针值: build_load 解引用 # 适用于: 指针 → 整数 (如 i8* → i8), 指针 → 结构体值 # 当构造器返回 Ptr(Struct) 但目标变量是 Struct 值类型时,需要 load - # 注意:使用独立函数 is_ptr_type 检查,避免嵌套 match 的编译器 BUG if is_ptr_type(val.Ty) != 0 and is_ptr_type(target_ty) == 0: return llvmlite.build_load(builder, target_ty, val) # 指针 → 指针: bitcast (如 i8* → i8** 当目标是全局变量存储指针) @@ -2147,7 +2144,7 @@ def translate_subscript(builder: llvmlite.IRBuilder | t.CPtr, pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee if pointee is not None: # 不调用 pointee.kind() 避免跨模块引用 LLVMType.kind 符号 - # 单层 match(避免嵌套 match 的编译器 bug) + # 用 match 检查 pointee 类型 match pointee: case llvmlite.LLVMType.Array(elem_ty, count): # 数组遍历: getelementptr [N x elem_ty], ... , i32 0, i32 %idx @@ -2536,6 +2533,21 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, return None at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(node) if at is None or at.value is None or at.attr is None: + # fast-fail: AST 节点字段缺失属解析器 bug,应报告具体缺失字段 + diag_buf_an: bytes = stdlib.malloc(256) + if diag_buf_an is not None: + missing_an: str = "?" + if at is not None: + if at.value is None: + missing_an = "value" + elif at.attr is None: + missing_an = "attr" + viperlib.snprintf(diag_buf_an, 256, + "属性访问失败: AST 节点字段缺失 (%s sha1=%s:%d)", + missing_an, + trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?", + at.lineno if at is not None and at.lineno is not None else 0) + VLogger.error(diag_buf_an, "ATTR") return None # 枚举成员访问: EnumName.MemberName → 常量值 @@ -2556,6 +2568,10 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, viperlib.snprintf(name_buf, 32, "%lld", member.Value) return llvmlite.ConstInt(pool, base_ty, member.Value, name_buf) return llvmlite.const_int32(pool, member.Value) + # fast-fail: 枚举成员未找到 + VLogger.error( + f"枚举成员未找到: {enum_nm.id}.{at.attr}", + "ATTR") return None # 嵌套枚举成员访问: Module.EnumName.MemberName → 常量值 @@ -2577,6 +2593,10 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, viperlib.snprintf(name_buf_e, 32, "%lld", member_e.Value) return llvmlite.ConstInt(pool, base_ty_e, member_e.Value, name_buf_e) return llvmlite.const_int32(pool, member_e.Value) + # fast-fail: 嵌套枚举成员未找到 + VLogger.error( + f"嵌套枚举成员未找到: {inner_at_e.attr}.{at.attr}", + "ATTR") return None # 模块属性访问 (读路径): sys._mbuddy → 加载全局变量 @_mbuddy @@ -2868,6 +2888,19 @@ def translate_attribute(builder: llvmlite.IRBuilder | t.CPtr, VLogger.error(fb_attr, "ATTR") return None case _: + # fast-fail: 对非指针类型做属性访问(如 i32.x), + # 静默返回 None 会导致下游 "store to None" 崩溃,丢失错误现场 + diag_buf_ap: bytes = stdlib.malloc(256) + if diag_buf_ap is not None: + attr_nm_ap: str = "?" + if at is not None and at.attr is not None: + attr_nm_ap = at.attr + viperlib.snprintf(diag_buf_ap, 256, + "属性访问失败: .%s (base=非指针类型,无法做属性访问 sha1=%s:%d)", + attr_nm_ap, + trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?", + at.lineno if at is not None and at.lineno is not None else 0) + VLogger.error(diag_buf_ap, "ATTR") return None # ============================================================ @@ -2900,7 +2933,7 @@ def get_subscript_ptr(builder: llvmlite.IRBuilder | t.CPtr, if is_ptr_type(alloca.Ty) != 0: pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee if pointee is not None: - # 单层 match(避免嵌套 match 的编译器 bug) + # 用 match 检查 pointee 类型 match pointee: case llvmlite.LLVMType.Array(elem_ty, count): # 数组遍历 @@ -3101,4 +3134,17 @@ def get_attribute_ptr(builder: llvmlite.IRBuilder | t.CPtr, VLogger.error(fb_attr_g, "ATTR") return None case _: + # fast-fail: 对非指针类型做属性写入(如 i32.x = ...), + # 静默返回 None 会导致下游 "store to None" 崩溃 + diag_buf_gp: bytes = stdlib.malloc(256) + if diag_buf_gp is not None: + attr_nm_gp: str = "?" + if at is not None and at.attr is not None: + attr_nm_gp = at.attr + viperlib.snprintf(diag_buf_gp, 256, + "属性写入失败: .%s (base=非指针类型,无法做属性写入 sha1=%s:%d)", + attr_nm_gp, + trans.ModuleSha1 if trans is not None and trans.ModuleSha1 is not None else "?", + at.lineno if at is not None and at.lineno is not None else 0) + VLogger.error(diag_buf_gp, "ATTR") return None diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index 1d352ec..1b4046f 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -18,89 +18,33 @@ import lib.core.Handles.HandlesType as HandlesType import lib.core.Handles.HandlesStruct as HandlesStruct import lib.core.Handles.HandlesImports as HandlesImports import lib.core.Handles.HandlesClassDef as HandlesClassDef +import lib.core.StubMerger as StubMerger # ============================================================ -# 全局模块 SHA1 映射(module_name → SHA1) +# 模块 SHA1 查询(委托给 StubMerger 全局存储器) # # 翻译时用于跨模块函数调用名混淆: # string.strchr(...) → call @{sha1_of_string}.strchr(...) # -# 通过 set_module_sha1_map 设置,_lookup_module_sha1 查询 +# 数据来源是 StubMerger.PopulateSha1MapStore/AppendToSha1MapStore +# 填充的全局存储器,不再维护独立的 SHA1 映射表副本。 # ============================================================ -_g_sha1_arr: bytes = None -_g_mod_arr: bytes = None -_g_sha1_count: int = 0 - -def set_module_sha1_map(sha1_arr: bytes, mod_arr: bytes, count: int) -> int: - """设置全局模块 SHA1 映射""" - global _g_sha1_arr - global _g_mod_arr - global _g_sha1_count - _g_sha1_arr = sha1_arr - _g_mod_arr = mod_arr - _g_sha1_count = count - return 0 def _lookup_module_sha1(mod_name: str) -> str: - """查找模块名对应的 SHA1,找不到返回 None""" - if mod_name is None: - return None - if _g_sha1_arr is None or _g_mod_arr is None: - return None - if _g_sha1_count <= 0: - return None - for i in range(_g_sha1_count): - idx: t.CSizeT = t.CSizeT(i) * 64 - if string.strcmp(_g_mod_arr + idx, mod_name) == 0: - sidx: t.CSizeT = t.CSizeT(i) * 17 - return _g_sha1_arr + sidx - return None + """查找模块名对应的 SHA1,找不到返回 None + + 委托给 StubMerger.LookupSha1ByModName(精确匹配 + 包名回退)。 + """ + return StubMerger.LookupSha1ByModName(mod_name) def _lookup_module_sha1_suffix(mod_suffix: str) -> str: """后缀匹配查找模块 SHA1,如 mod_suffix="__types" 匹配 "llvmlite.__types" 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 + 委托给 StubMerger.LookupSha1ByModNameSuffix(精确 + 后缀 + 反向后缀)。 """ - if mod_suffix is None: - return None - if _g_sha1_arr is None or _g_mod_arr is None: - return None - if _g_sha1_count <= 0: - return None - suffix_len: t.CSizeT = string.strlen(mod_suffix) - if suffix_len == 0: - return None - for i in range(_g_sha1_count): - idx: t.CSizeT = t.CSizeT(i) * 64 - mod_name: str = _g_mod_arr + idx - mod_len: t.CSizeT = string.strlen(mod_name) - if mod_len == 0: - continue - # 精确匹配 - if mod_len == suffix_len: - if string.strcmp(mod_name, mod_suffix) == 0: - sidx: t.CSizeT = t.CSizeT(i) * 17 - return _g_sha1_arr + sidx - # 后缀匹配:mod_name 以 "." + mod_suffix 结尾 - if mod_len > suffix_len: - if mod_name[mod_len - suffix_len - 1] == '.': - cmp_start: str = mod_name + mod_len - suffix_len - if string.strcmp(cmp_start, mod_suffix) == 0: - sidx2: t.CSizeT = t.CSizeT(i) * 17 - return _g_sha1_arr + sidx2 - # 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾 - # 用于 from_imports 存完整路径(如 lib.core.Handles.HandlesTranslator) - # 而 mod_arr 只存文件名(如 HandlesTranslator)的情况: - # import X as Y 的别名 Y 经 from_imports 查到完整路径, - # 需反向匹配 mod_arr 中的文件名才能找到 SHA1 - if suffix_len > mod_len: - if mod_suffix[suffix_len - mod_len - 1] == '.': - cmp_start_rev: str = mod_suffix + suffix_len - mod_len - if string.strcmp(cmp_start_rev, mod_name) == 0: - sidx_rev: t.CSizeT = t.CSizeT(i) * 17 - return _g_sha1_arr + sidx_rev - return None + return StubMerger.LookupSha1ByModNameSuffix(mod_suffix) # ============================================================ # 全局 CExport 函数表(module_sha1 + func_name) @@ -534,6 +478,10 @@ def _infer_external_func_ret_ty(pool: memhub.MemBuddy | t.CPtr, # 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配 # x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致 # 与 _infer_method_ret_ty 的默认策略保持一致 + # WARNING: 未登记的外部函数返回类型默认推断为 i32,若实际返回指针/i64 会被截断 + VLogger.warning( + f"外部函数返回类型未登记,默认推断为 i32(可能截断指针/i64): {func_name}", + "HandlesExprCall") return llvmlite.Int32(pool) # ============================================================ @@ -2146,19 +2094,37 @@ def _apply_struct_defaults(pool: memhub.MemBuddy | t.CPtr, # 用类型指针定位 entry,规避跨模块同名 find_struct 找错 entry: HandlesStruct.StructEntry | t.CPtr = HandlesStruct.find_struct_by_type(struct_ty) if entry is None: + # fast-fail: 结构体类型未注册,静默 return 会导致字段未初始化 + VLogger.error( + f"结构体类型未注册,无法应用默认值: {class_name}", + "HandlesExprCall") return for fi in range(entry.FieldCount): fe: HandlesStruct.FieldEntry | t.CPtr = HandlesStruct._get_field_entry( entry, fi) - if fe is None or fe.DefaultVal is None: + if fe is None: + # fast-fail: 字段索引越界(entry.FieldCount 与实际不一致) + VLogger.error( + f"字段索引越界,无法获取字段 entry: {class_name}", + "HandlesExprCall") continue + if fe.DefaultVal is None: + continue # 合理跳过:无默认值的字段 default_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, fe.DefaultVal, None, 0, trans) if default_val is None: + # fast-fail: 默认值翻译失败(如引用了未定义符号) + VLogger.error( + f"字段默认值翻译失败: {class_name}", + "HandlesExprCall") continue field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( builder, struct_ty, fe.Ty, tmp, fe.Index) if field_ptr is None: + # fast-fail: GEP 失败(struct_ty 与 fe.Ty 不匹配) + VLogger.error( + f"字段 GEP 失败,类型不匹配: {class_name}", + "HandlesExprCall") continue default_val = HandlesExpr.coerce_to_type(builder, default_val, fe.Ty) llvmlite.build_store(builder, default_val, field_ptr) @@ -2295,6 +2261,10 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, arg, None, 0, trans) if arg_val is None: + # fast-fail: 参数翻译失败 + VLogger.error( + f"构造函数参数翻译失败: {class_name}", + "HandlesExprCall") continue fe: HandlesStruct.FieldEntry | t.CPtr = None if ctor_entry is not None: @@ -2302,10 +2272,18 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, else: fe = HandlesStruct.get_field_by_index(class_name, ai) if fe is None: + # fast-fail: 字段索引越界(用户传了多余参数) + VLogger.error( + f"构造函数参数过多,字段索引越界: {class_name}", + "HandlesExprCall") continue field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( builder, struct_ty, fe.Ty, storage_ptr, fe.Index) if field_ptr is None: + # fast-fail: GEP 失败(struct_ty 与 fe.Ty 不匹配) + VLogger.error( + f"构造函数字段 GEP 失败: {class_name}", + "HandlesExprCall") continue arg_val = HandlesExpr.coerce_to_type(builder, arg_val, fe.Ty) llvmlite.build_store(builder, arg_val, field_ptr) @@ -2328,6 +2306,10 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( builder, pool, mod, arg, None, 0, trans) if arg_val is None: + # fast-fail: 参数翻译失败 + VLogger.error( + f"构造函数参数翻译失败: {class_name}", + "HandlesExprCall") continue # 获取字段信息(用 ctor_entry 规避跨模块同名 find_struct 找错) fe: HandlesStruct.FieldEntry | t.CPtr = None @@ -2336,11 +2318,19 @@ def _translate_struct_ctor(pool: memhub.MemBuddy | t.CPtr, else: fe = HandlesStruct.get_field_by_index(class_name, ai) if fe is None: + # fast-fail: 字段索引越界(用户传了多余参数) + VLogger.error( + f"构造函数参数过多,字段索引越界: {class_name}", + "HandlesExprCall") continue # GEP 到字段 ai field_ptr: llvmlite.Value | t.CPtr = llvmlite.build_gep_struct( builder, struct_ty, fe.Ty, tmp, fe.Index) if field_ptr is None: + # fast-fail: GEP 失败(struct_ty 与 fe.Ty 不匹配) + VLogger.error( + f"构造函数字段 GEP 失败: {class_name}", + "HandlesExprCall") continue # 类型转换并 store arg_val = HandlesExpr.coerce_to_type(builder, arg_val, fe.Ty) @@ -2975,6 +2965,10 @@ def _infer_method_ret_ty(pool: memhub.MemBuddy | t.CPtr, # 避免使用 i64 导致 stub 声明(declare i64)与实际定义(define i32)类型不匹配 # x86-64 ABI 中 i32 返回值在 rax 低 32 位,与 define i32 完全一致 # 与 _infer_external_func_ret_ty 的默认策略保持一致 + # WARNING: 未登记的方法返回类型默认推断为 i32,若实际返回指针/i64 会被截断 + VLogger.warning( + f"方法返回类型未登记,默认推断为 i32(可能截断指针/i64): {method_name}", + "HandlesExprCall") return llvmlite.Int32(pool) @@ -4071,6 +4065,35 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, i32_ty_builtin: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) return _translate_t_type_cast(pool, builder, cast_arg_int, i32_ty_builtin) return None + elif string.strcmp(func_name, "bool") == 0: + # bool(x) → i1(严格 != 0 归一化) + # 整数: icmp ne x, 0 → i1 + # 浮点: fcmp one x, 0.0 → i1 + # 指针: icmp ne x, null → i1 + cast_arg_bool: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, cargs.get(0), funcs_ptr, func_count, trans) + if cast_arg_bool is not None: + arg_bits: int = HandlesExpr.get_llvm_type_bits(cast_arg_bool.Ty) + arg_fbits: int = HandlesExpr.get_llvm_float_bits(cast_arg_bool.Ty) + # 整数: icmp ne x, 0 + if arg_bits != 0: + zero_int: llvmlite.Value | t.CPtr = llvmlite.const_int(pool, arg_bits, 0) + if zero_int is not None: + return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cast_arg_bool, zero_int) + return None + # 浮点: fcmp one x, 0.0 + if arg_fbits != 0: + zero_ty: llvmlite.LLVMType | t.CPtr = cast_arg_bool.Ty + zero_flt: llvmlite.Value | t.CPtr = llvmlite.ConstFloat(pool, zero_ty, 0.0) + if zero_flt is not None: + return llvmlite.build_fcmp(builder, llvmlite.FCMP_ONE, cast_arg_bool, zero_flt) + return None + # 指针: icmp ne x, null + ptr_null: llvmlite.Value | t.CPtr = llvmlite.ConstNull(pool, cast_arg_bool.Ty, "null") + if ptr_null is not None: + return llvmlite.build_icmp(builder, llvmlite.ICMP_NE, cast_arg_bool, ptr_null) + return None + return None # 检测 t.XXX 类型转换: t.CUInt64T(ptr), t.CPtr(val), t.CInt(val) 等 # 当 func 是 t.XXX 形式且 XXX 是已知类型名时,当作类型转换处理(而非函数调用) @@ -4184,6 +4207,10 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, sz_builtin = 8 elif string.strcmp(sz_cls_name, "CSizeT") == 0: sz_builtin = 8 + elif string.strcmp(sz_cls_name, "CBool") == 0: + sz_builtin = 1 + elif string.strcmp(sz_cls_name, "bool") == 0: + sz_builtin = 1 if sz_builtin >= 0: # 注意: 必须用 const_int64 而非 ConstInt(...,"0") # ConstInt 的 name 参数是 IR 文本表示,ValuePrint 输出时使用 Name 而非 IntVal diff --git a/App/lib/core/Handles/HandlesStruct.vp b/App/lib/core/Handles/HandlesStruct.vp index 34ccbb8..6611138 100644 --- a/App/lib/core/Handles/HandlesStruct.vp +++ b/App/lib/core/Handles/HandlesStruct.vp @@ -604,48 +604,18 @@ def struct_has_defaults(class_name: str) -> int: # # 如果 value 是 Ptr(Struct(...)),返回 Struct 类型 # ============================================================ -def _is_struct_type(ty: llvmlite.LLVMType | t.CPtr) -> int: - """检查 ty 是否是 Struct 类型(独立函数,规避嵌套 match 编译器 BUG)""" - if ty is None: - return 0 - match ty: - case llvmlite.LLVMType.Struct(fields, fcount, name): - return 1 - case _: - return 0 - - -def _is_ptr_type(ty: llvmlite.LLVMType | t.CPtr) -> int: - """检查 ty 是否是 Ptr 类型(独立函数,规避嵌套 match 编译器 BUG)""" - if ty is None: - return 0 - match ty: - case llvmlite.LLVMType.Ptr(pointee): - return 1 - case _: - return 0 - - -def _get_ptr_pointee(ty: llvmlite.LLVMType | t.CPtr) -> llvmlite.LLVMType | t.CPtr: - """获取 Ptr 类型的 pointee(独立函数,规避嵌套 match 编译器 BUG)""" - if ty is None: - return None - match ty: - case llvmlite.LLVMType.Ptr(pointee): - return pointee - case _: - return None - - def get_struct_type_from_value(val: llvmlite.Value | t.CPtr) -> llvmlite.LLVMType | t.CPtr: """从 Value 的类型推断结构体类型""" if val is None or val.Ty is None: return None + # 嵌套 match:先匹配 Ptr,再在 case 块内匹配 pointee 是否为 Struct match val.Ty: case llvmlite.LLVMType.Ptr(pointee): - if _is_struct_type(pointee) != 0: - return pointee - return None + match pointee: + case llvmlite.LLVMType.Struct(fields, fcount, name): + return pointee + case _: + return None case _: return None @@ -1051,15 +1021,16 @@ def ensure_struct_def_in_module(pool: memhub.MemBuddy | t.CPtr, if ty is None or mod is None or pool is None: return # 指针类型:确保 pointee 至少有 opaque 声明 - # 用 _is_ptr_type 检查(独立函数,规避嵌套 match BUG) - if _is_ptr_type(ty) != 0: - llvmlite.module_ensure_opaque_for_type(mod, pool, ty) - # 如果 pointee 是 Struct,递归复制完整定义 - # (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错) - pointee: llvmlite.LLVMType | t.CPtr = _get_ptr_pointee(ty) - if pointee is not None: - ensure_struct_def_in_module(pool, mod, pointee) - return + match ty: + case llvmlite.LLVMType.Ptr(pointee): + llvmlite.module_ensure_opaque_for_type(mod, pool, ty) + # 如果 pointee 是 Struct,递归复制完整定义 + # (GEP 访问字段需要 sized 类型,仅 opaque 会导致 llc 报错) + if pointee is not None: + ensure_struct_def_in_module(pool, mod, pointee) + return + case _: + pass # 获取结构体名称(非 Struct 类型返回 None) sname: t.CChar | t.CPtr = llvmlite.get_struct_name(ty) if sname is None: @@ -1113,7 +1084,7 @@ def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr, """递归扫描类型树,确保值类型子结构体有完整定义""" if ty is None or mod is None or pool is None: return - # 用 match 遍历类型(独立函数,规避嵌套 match BUG) + # 嵌套 match 遍历类型树 match ty: case llvmlite.LLVMType.Struct(fields, fcount, sname): # 遍历字段链表 @@ -1122,8 +1093,11 @@ def _ensure_field_types_in_module(pool: memhub.MemBuddy | t.CPtr, while fld is not None: if fld.Ty is not None: # 值类型子结构体(如 FILETIME)需要完整定义 - if _is_struct_type(fld.Ty) != 0: - ensure_struct_def_in_module(pool, mod, fld.Ty) + match fld.Ty: + case llvmlite.LLVMType.Struct(f2, fc2, n2): + ensure_struct_def_in_module(pool, mod, fld.Ty) + case _: + pass _ensure_field_types_in_module(pool, mod, fld.Ty) fld = fld.Next case llvmlite.LLVMType.Ptr(pointee): diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index 4960821..3b85855 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -28,6 +28,92 @@ import lib.core.Handles.HandlesStruct as HandlesStruct _g_current_file: str = None +# ============================================================ +# 仿 Python Traceback 调用栈 +# +# 在编译过程中通过 push_trace/pop_trace 维护翻译上下文栈, +# fatal_error 输出时从底到顶展示所有帧,形成多帧 traceback。 +# ============================================================ +TRACE_MAX: t.CDefine = 32 + + +@t.NoVTable +class TraceFrame: + file_name: str # 文件名 + lineno: int # 行号 + col_start: int # 列起始(1-based,0=不指定) + col_end: int # 列结束(1-based,0=不指定) + + +_g_trace_frames: TraceFrame | t.CPtr = None +_g_trace_count: int = 0 + + +def _ensure_trace_init() -> int: + """确保调用栈已初始化(懒加载,用 malloc 不依赖 pool)""" + global _g_trace_frames + if _g_trace_frames is not None: + return 1 + entry_size: t.CSizeT = TraceFrame.__sizeof__() + _g_trace_frames = stdlib.malloc(entry_size * TRACE_MAX) + if _g_trace_frames is None: + return 0 + string.memset(_g_trace_frames, 0, entry_size * TRACE_MAX) + return 1 + + +def _get_trace_frame(i: int) -> TraceFrame | t.CPtr: + """获取第 i 个 TraceFrame 槽位""" + if _g_trace_frames is None or i < 0 or i >= TRACE_MAX: + return None + entry_size: t.CSizeT = TraceFrame.__sizeof__() + addr: t.CUInt64T = t.CUInt64T(_g_trace_frames) + i * entry_size + return (TraceFrame | t.CPtr)(t.CVoid(addr, t.CPtr)) + + +def push_trace(file_name: str, lineno: int, col_start: int = 0, col_end: int = 0): + """压入一个翻译上下文帧(进入新的翻译作用域时调用)""" + if _ensure_trace_init() == 0: + return + if _g_trace_count >= TRACE_MAX: + return + frame: TraceFrame | t.CPtr = _get_trace_frame(_g_trace_count) + if frame is None: + return + frame.file_name = file_name + frame.lineno = lineno + frame.col_start = col_start + frame.col_end = col_end + _g_trace_count += 1 + + +def pop_trace(): + """弹出栈顶帧(退出翻译作用域时调用)""" + if _g_trace_count <= 0: + return + _g_trace_count -= 1 + frame: TraceFrame | t.CPtr = _get_trace_frame(_g_trace_count) + if frame is not None: + frame.file_name = None + frame.lineno = 0 + frame.col_start = 0 + frame.col_end = 0 + + +def clear_trace(): + """清空调用栈(每个文件翻译开始前调用)""" + global _g_trace_count + i: int + for i in range(_g_trace_count): + frame: TraceFrame | t.CPtr = _get_trace_frame(i) + if frame is not None: + frame.file_name = None + frame.lineno = 0 + frame.col_start = 0 + frame.col_end = 0 + _g_trace_count = 0 + + # ============================================================ # 全局 temp_dir(pyi 文件所在目录,由 BuildPipeline/Phase1 设置) # @@ -444,39 +530,80 @@ def _read_source_line(file_path: str, lineno: int) -> str: # ============================================================ -# fatal_error - 以 Python 风格报错并退出(带源代码行) +# fatal_error - 以 Python 风格报错并退出(支持多帧、错误类型、列范围) # # 格式: # Traceback (most recent call last): -# File "path", line N -# source code line -# SyntaxError: msg +# File "path1", line N1 +# source code line 1 +# File "path2", line N2 +# source code line 2 +# ^^^^ ← 列范围标记(可选) +# TypeError: msg +# +# 参数: +# node: AST 节点(提供行号),可为 None +# msg: 错误消息 +# err_type: 错误类型字符串(默认 "SyntaxError") +# col_start: 列起始(1-based,0=不标记列范围) +# col_end: 列结束(1-based,0=不标记列范围) # ============================================================ -def fatal_error(node: ast.AST | t.CPtr, msg: str): - """以 Python 风格报错并退出(带源代码行)""" +def fatal_error(node: ast.AST | t.CPtr, msg: str, + err_type: str = "SyntaxError", + col_start: int = 0, col_end: int = 0): + """以 Python 风格报错并退出(支持多帧、错误类型、列范围) + + 向后兼容:err_type/col_start/col_end 有默认值,旧调用 fatal_error(node, msg) 仍可用。 + """ + stdio.printf("Traceback (most recent call last):\n") + # 输出调用栈帧(从底到顶) + i: int + for i in range(_g_trace_count): + frame: TraceFrame | t.CPtr = _get_trace_frame(i) + if frame is not None and frame.file_name is not None: + stdio.printf(' File "%s", line %d\n', frame.file_name, frame.lineno) + if frame.lineno > 0: + src: str = _read_source_line(frame.file_name, frame.lineno) + if src is not None: + stdio.printf(" %s\n", src) + stdlib.free(src) + # 输出当前帧(node 对应的位置) lineno: int = 0 if node is not None: lineno = node.lineno file_name: str = _g_current_file if file_name is None: file_name = "" - stdio.printf("Traceback (most recent call last):\n") stdio.printf(' File "%s", line %d\n', file_name, lineno) if lineno > 0 and string.strcmp(file_name, "") != 0: src_line: str = _read_source_line(file_name, lineno) if src_line is not None: stdio.printf(" %s\n", src_line) + # 列范围标记:在源代码行下方用 ^ 标记出错范围 + if col_start > 0 and col_end >= col_start: + stdio.printf(" ") + j: int + for j in range(col_end): + if j >= col_start - 1 and j < col_end: + stdio.printf("^") + else: + stdio.printf(" ") + stdio.printf("\n") stdlib.free(src_line) - stdio.printf("SyntaxError: %s\n", msg) + # 输出错误类型 + 消息 + if err_type is None or err_type[0] == 0: + err_type = "SyntaxError" + stdio.printf("%s: %s\n", err_type, msg) sys.exit(1) # ============================================================ -# fatal_type_error - 兼容别名(指向 fatal_error) +# fatal_type_error - 类型错误别名(err_type="TypeError") # ============================================================ -def fatal_type_error(node: ast.AST | t.CPtr, msg: str): - """以 Python 风格报错并退出(fatal_error 的别名)""" - fatal_error(node, msg) +def fatal_type_error(node: ast.AST | t.CPtr, msg: str, + col_start: int = 0, col_end: int = 0): + """以 Python 风格报错并退出(TypeError 版本)""" + fatal_error(node, msg, "TypeError", col_start, col_end) # ============================================================ # 将 t.CXxx 类型名映射到 LLVMType(纯函数) @@ -551,7 +678,8 @@ def map_t_type(pool: memhub.MemBuddy | t.CPtr, return llvmlite.Int64(pool) # bool elif string.strcmp(type_name, "CBool") == 0: - return llvmlite.Int8(pool) + # bool = t.CBool = i1(LLVM 1 位整数,语义与 C _Bool 一致) + return llvmlite.Int1(pool) # ptrdiff_t elif string.strcmp(type_name, "CPtrDiffT") == 0: return llvmlite.Int64(pool) @@ -777,9 +905,104 @@ def _is_t_attr(node: ast.AST | t.CPtr, # Attribute 节点 (memhub.MemBuddy) → "MemBuddy" # BinOp 联合类型 (MemBuddy | t.CPtr) → 非 CPtr 一边的类名 # ============================================================ + + +# ============================================================ +# annotation_to_str - 将类型注解 AST 转换为可读字符串(用于错误消息) +# +# 与 extract_class_name_from_annotation 不同,此函数不跳过任何类型名, +# 纯粹用于错误消息显示。支持递归处理嵌套类型。 +# +# 支持的节点: +# Name → "bool", "int", "MemBuddy" +# Attribute → "t.CBool", "memhub.MemBuddy" +# Subscript → "list[...]", "t.CArray[...]" +# Constant (字符串注解) → 字符串值 +# BinOp (A | B) → "A | B" +# ============================================================ +def annotation_to_str(annot: ast.AST | t.CPtr) -> str: + """将类型注解 AST 转换为可读字符串,返回 None=无法提取 + + 注意:返回的字符串可能由 malloc 分配,调用方无需释放(错误路径用)。 + """ + if annot is None: + return "" + k: int = annot.kind() + + # Constant 节点: 字符串注解 + if k == ast.ASTKind.Constant: + cn: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(annot) + if cn.const_kind == ast.CONST_STR and cn.str_val is not None: + return cn.str_val + return "" + + # Name 节点: bool, int, MemBuddy + if k == ast.ASTKind.Name: + nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(annot) + if nm.id is not None: + return nm.id + return "" + + # Attribute 节点: t.CBool, memhub.MemBuddy + if k == ast.ASTKind.Attribute: + at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(annot) + if at.attr is None: + return "" + if at.value is not None: + prefix: str = annotation_to_str(at.value) + if prefix is not None: + buf: t.CChar | t.CPtr = stdlib.malloc( + string.strlen(prefix) + string.strlen(at.attr) + 2) + if buf is not None: + viperlib.snprintf(buf, + string.strlen(prefix) + string.strlen(at.attr) + 2, + "%s.%s", prefix, at.attr) + return buf + return at.attr + + # Subscript 节点: list[...], t.CArray[...] + if k == ast.ASTKind.Subscript: + sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot) + base: str = annotation_to_str(sub.value) + if base is not None: + buf2: t.CChar | t.CPtr = stdlib.malloc(string.strlen(base) + 5) + if buf2 is not None: + viperlib.snprintf(buf2, string.strlen(base) + 5, + "%s[...]", base) + return buf2 + return "" + + # BinOp 节点: A | B (联合类型) + if k == ast.ASTKind.BinOp: + bo: ast.BinOp | t.CPtr = (ast.BinOp | t.CPtr)(annot) + left_s: str = annotation_to_str(bo.left) + right_s: str = annotation_to_str(bo.right) + if left_s is not None and right_s is not None: + buf3: t.CChar | t.CPtr = stdlib.malloc( + string.strlen(left_s) + string.strlen(right_s) + 5) + if buf3 is not None: + viperlib.snprintf(buf3, + string.strlen(left_s) + string.strlen(right_s) + 5, + "%s | %s", left_s, right_s) + return buf3 + return "" + + return "" + + +# ============================================================ +# extract_class_name_from_annotation +# ============================================================ def extract_class_name_from_annotation(node: ast.AST | t.CPtr, - imported_modules: str) -> str: - """从类型注解 AST 中提取类名,返回 None=无法提取""" + imported_modules: str, + pool: memhub.MemBuddy | t.CPtr = None) -> str: + """从类型注解 AST 中提取类名,返回 None=无法提取 + + pool 可用时,对泛型 Subscript(如 list[AST | t.CPtr])返回 mangled name + (如 "list[ast.AST|t.CPtr]"),与 resolve_annotation_type 的 mangled name 格式一致, + 使 find_struct 能找到特化的泛型结构体。 + pool 为 None 时,对 Subscript 只返回裸类名(如 "list"),保持向后兼容。 + """ if node is None: return None k: int = node.kind() @@ -796,6 +1019,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr, return None if string.strcmp(cn.str_val, "bytes") == 0: return None + if string.strcmp(cn.str_val, "bool") == 0: + return None return cn.str_val return None @@ -811,6 +1036,8 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr, return None if string.strcmp(nm.id, "bytes") == 0: return None + if string.strcmp(nm.id, "bool") == 0: + return None return nm.id # Attribute 节点: memhub.MemBuddy → "memhub.MemBuddy"(模块限定名) @@ -841,23 +1068,76 @@ def extract_class_name_from_annotation(node: ast.AST | t.CPtr, right_is_cptr: int = _is_t_attr(bop.right, "CPtr", imported_modules) # 提取非 CPtr 一边的类名 if left_is_cptr != 0 and right_is_cptr == 0: - return extract_class_name_from_annotation(bop.right, imported_modules) + return extract_class_name_from_annotation(bop.right, imported_modules, pool) if right_is_cptr != 0 and left_is_cptr == 0: - return extract_class_name_from_annotation(bop.left, imported_modules) + return extract_class_name_from_annotation(bop.left, imported_modules, pool) # 无 CPtr 标记:尝试两边 if left_is_cptr == 0 and right_is_cptr == 0: - left_name: str = extract_class_name_from_annotation(bop.left, imported_modules) + left_name: str = extract_class_name_from_annotation(bop.left, imported_modules, pool) if left_name is not None: return left_name - return extract_class_name_from_annotation(bop.right, imported_modules) + return extract_class_name_from_annotation(bop.right, imported_modules, pool) return None - # Subscript 节点: list[AST | t.CPtr] → "list" + # BUG 1 方案3:Subscript 节点拼接 mangled name + # list[AST | t.CPtr] → "list[ast.AST|t.CPtr]"(pool 可用时) + # 与 resolve_annotation_type 的 mangled name 格式一致,使 find_struct 能找到特化结构体 if k == ast.ASTKind.Subscript: sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(node) - if sub.value is not None: - return extract_class_name_from_annotation(sub.value, imported_modules) - return None + if sub.value is None: + return None + base_name: str = extract_class_name_from_annotation(sub.value, imported_modules, pool) + if base_name is None: + return None + # pool 不可用时,只返回裸类名(向后兼容) + if pool is None or sub.slice is None: + return base_name + # pool 可用:拼接 mangled name "base_name[slice_name]" + # slice 是 Tuple 时用逗号分隔多个参数,否则单个参数 + slice_name: str = None + if sub.slice.kind() == ast.ASTKind.Tuple: + # Tuple slice: dict[str, int] → "str,int" + sub_tup: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub.slice) + if sub_tup.elts is not None: + tup_elts_sn: list[ast.AST | t.CPtr] | t.CPtr = sub_tup.elts + tn_sn: t.CSizeT = tup_elts_sn.__len__() + # 计算总长度 + tup_total: t.CSizeT = 1 + ti_sn: t.CSizeT + for ti_sn in range(tn_sn): + if ti_sn > 0: + tup_total += 1 + elt_node: ast.AST | t.CPtr = tup_elts_sn.get(ti_sn) + elt_name: str = _get_type_name_from_annotation(pool, elt_node) + if elt_name is not None: + tup_total += string.strlen(elt_name) + tup_buf: str = pool.alloc(tup_total) + if tup_buf is not None: + tup_buf[0] = '\0' + for ti_sn in range(tn_sn): + if ti_sn > 0: + string.strcat(tup_buf, ",") + elt_node2: ast.AST | t.CPtr = tup_elts_sn.get(ti_sn) + elt_name2: str = _get_type_name_from_annotation(pool, elt_node2) + if elt_name2 is not None: + string.strcat(tup_buf, elt_name2) + slice_name = tup_buf + else: + # 非 Tuple slice: list[AST | t.CPtr] → "ast.AST|t.CPtr" + slice_name = _get_type_name_from_annotation(pool, sub.slice) + if slice_name is None: + return base_name + # 拼接 "base_name[slice_name]" + mangled_total: t.CSizeT = string.strlen(base_name) + 1 + string.strlen(slice_name) + 1 + 1 + mangled_buf: str = pool.alloc(mangled_total) + if mangled_buf is None: + return base_name + mangled_buf[0] = '\0' + string.strcat(mangled_buf, base_name) + string.strcat(mangled_buf, "[") + string.strcat(mangled_buf, slice_name) + string.strcat(mangled_buf, "]") + return mangled_buf return None @@ -925,6 +1205,9 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, return llvmlite.Int32(pool) elif string.strcmp(nm.id, "bytes") == 0: return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + elif string.strcmp(nm.id, "bool") == 0: + # bool = t.CBool = i1(与 map_t_type 中 CBool 映射一致) + return llvmlite.Int1(pool) # class 名称: 查找已注册的 StructType,返回 StructType 本身(不包装 Ptr) # 这样 build_alloca 会分配结构体大小的空间,alloca 返回 struct_ty* # 命名空间隔离:严格模式下仅解析可见的结构体 diff --git a/App/lib/core/Phase1.vp b/App/lib/core/Phase1.vp index 11b432e..f22878e 100644 --- a/App/lib/core/Phase1.vp +++ b/App/lib/core/Phase1.vp @@ -541,14 +541,8 @@ def RunPhase1(mb: memhub.MemBuddy | t.CPtr, includes_dir: str, temp_dir: str, VLogger.warning("扫描结果无有效 SHA1,跳过 Phase1(项目可能无 includes 依赖)", "Phase1") stdlib.free(sha1_set) return 0 - # 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) - td_len_p1map: t.CSizeT = string.strlen(temp_dir) - p1_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) - p1_mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) - p1_inc_count: int = 0 - if p1_sha1_arr is not None and p1_mod_arr is not None: - p1_inc_count = StubMerger._BuildIncludesSha1Map(temp_dir, td_len_p1map, p1_sha1_arr, p1_mod_arr) - HandlesExprCall.set_module_sha1_map(p1_sha1_arr, p1_mod_arr, p1_inc_count) + # 模块 SHA1 映射已由 PopulateSha1MapStore 填充到 StubMerger 全局存储器, + # HandlesExprCall._lookup_module_sha1 直接查全局存储器,不再需要独立副本。 # 初始化 AST 表(只需一次) ast._init_tables(mb) diff --git a/App/lib/core/Phase2.vp b/App/lib/core/Phase2.vp index 262aec6..1ed8271 100644 --- a/App/lib/core/Phase2.vp +++ b/App/lib/core/Phase2.vp @@ -264,49 +264,9 @@ def RunMultiFileProject(mb: memhub.MemBuddy | t.CPtr, VLogger.info(fb, "project") stdlib.free(map_path_am) - # 构建模块 SHA1 映射(供跨模块函数调用名混淆使用) - td_len_pb: t.CSizeT = string.strlen(temp_dir) - pb_sha1_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 17) - pb_mod_arr: bytes = stdlib.malloc(StubMerger.MAX_INCLUDES * 64) - pb_inc_count: int = 0 - if pb_sha1_arr is not None and pb_mod_arr is not None: - pb_inc_count = StubMerger._BuildIncludesSha1Map(temp_dir, td_len_pb, pb_sha1_arr, pb_mod_arr) - # 追加用户源文件的 (模块名, SHA1) 到全局映射 - # 使跨模块方法调用能通过 from_imports + _lookup_module_sha1 找到正确的 SHA1 - if pb_sha1_arr is not None and pb_mod_arr is not None: - src_dir_len_us: t.CSizeT = string.strlen(source_dir) - for i in range(file_count): - ea_us: t.CUInt64T = t.CUInt64T(entries) + i * entry_size - ent_us: SrcFileEntry | t.CPtr = (SrcFileEntry | t.CPtr)(t.CVoid(ea_us, t.CPtr)) - if ent_us is None or ent_us.Path is None or ent_us.Sha1 is None: - continue - if pb_inc_count >= StubMerger.MAX_INCLUDES: - break - # 计算相对路径并转换为点分模块名(保留完整包路径) - path_str: str = ent_us.Path - path_len_us: t.CSizeT = string.strlen(path_str) - rel_path_us: str = path_str - if path_len_us > src_dir_len_us + 1: - if string.strncmp(path_str, source_dir, src_dir_len_us) == 0: - rel_path_us = path_str + src_dir_len_us + 1 - # 使用 _PathToModuleName 转换为点分模块名(处理 __init__.vp → 包名) - mod_name_us: str = StubMerger._PathToModuleName(rel_path_us) - if mod_name_us is None: - continue - # 写入模块名 - mod_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 64 - mn_len_us: t.CSizeT = string.strlen(mod_name_us) - if mn_len_us >= 64: - string.strncpy(pb_mod_arr + mod_idx, mod_name_us, 63) - pb_mod_arr[mod_idx + 63] = '\0' - else: - string.strcpy(pb_mod_arr + mod_idx, mod_name_us) - stdlib.free(mod_name_us) - # 写入 SHA1 - sha1_idx: t.CSizeT = t.CSizeT(pb_inc_count) * 17 - string.strcpy(pb_sha1_arr + sha1_idx, ent_us.Sha1) - pb_inc_count += 1 - HandlesExprCall.set_module_sha1_map(pb_sha1_arr, pb_mod_arr, pb_inc_count) + # 模块 SHA1 映射已由 PopulateSha1MapStore + AppendToSha1MapStore 统一填充到 + # StubMerger 全局存储器,HandlesExprCall._lookup_module_sha1 直接查全局存储器, + # 不再需要构建独立的 SHA1 映射表副本。 # === 2. Phase A: 为每个文件生成 stub + text === diff --git a/App/lib/core/StubMerger.vp b/App/lib/core/StubMerger.vp index 02ced4c..5e3d170 100644 --- a/App/lib/core/StubMerger.vp +++ b/App/lib/core/StubMerger.vp @@ -503,6 +503,96 @@ def GetSha1StoreCount() -> int: return _sha1_store_count +# ============================================================ +# LookupSha1ByModName - 从模块名查找 SHA1(直接查全局存储器) +# +# 精确匹配失败时,尝试 "{mod_name}.__init__" 回退, +# 支持 import ast(包)→ ast.__init__ 的 SHA1 查找。 +# +# Returns: +# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 +# ============================================================ +def LookupSha1ByModName(mod_name: str) -> str: + """从模块名查找 SHA1(精确匹配 + 包名回退)""" + if mod_name is None: + return None + if _sha1_store_arr is None or _sha1_store_mod is None: + return None + if _sha1_store_count <= 0: + return None + # 第一遍:精确匹配 + i_em: int + for i_em in range(_sha1_store_count): + idx_em: t.CSizeT = t.CSizeT(i_em) * 64 + if string.strcmp(_sha1_store_mod + idx_em, mod_name) == 0: + sidx_em: t.CSizeT = t.CSizeT(i_em) * 17 + return _sha1_store_arr + sidx_em + # 第二遍:包名回退("ast" → "ast.__init__") + nm_len_em: t.CSizeT = string.strlen(mod_name) + init_buf_em: bytes = stdlib.malloc(nm_len_em + 12) + if init_buf_em is not None: + viperlib.snprintf(init_buf_em, nm_len_em + 12, "%s.__init__", mod_name) + i_im: int + for i_im in range(_sha1_store_count): + idx2_im: t.CSizeT = t.CSizeT(i_im) * 64 + if string.strcmp(_sha1_store_mod + idx2_im, init_buf_em) == 0: + sidx2_im: t.CSizeT = t.CSizeT(i_im) * 17 + stdlib.free(init_buf_em) + return _sha1_store_arr + sidx2_im + stdlib.free(init_buf_em) + return None + + +# ============================================================ +# LookupSha1ByModNameSuffix - 后缀匹配查找模块 SHA1 +# +# 用于 from .X import Y 的相对导入:impf.module 是 ".X"(带前导点), +# 去掉前导点后为 "X",SHA1 映射表中键是 "pkg.X",需要后缀匹配。 +# 包含三种匹配方式:精确、后缀、反向后缀。 +# +# Returns: +# SHA1 字符串指针(指向全局存储器内部,无需释放),None 未找到 +# ============================================================ +def LookupSha1ByModNameSuffix(mod_suffix: str) -> str: + """后缀匹配查找模块 SHA1""" + if mod_suffix is None: + return None + if _sha1_store_arr is None or _sha1_store_mod is None: + return None + if _sha1_store_count <= 0: + return None + suffix_len_sf: t.CSizeT = string.strlen(mod_suffix) + if suffix_len_sf == 0: + return None + i_sf: int + for i_sf in range(_sha1_store_count): + idx_sf: t.CSizeT = t.CSizeT(i_sf) * 64 + mod_name_sf: str = _sha1_store_mod + idx_sf + mod_len_sf: t.CSizeT = string.strlen(mod_name_sf) + if mod_len_sf == 0: + continue + # 精确匹配 + if mod_len_sf == suffix_len_sf: + if string.strcmp(mod_name_sf, mod_suffix) == 0: + sidx_sf: t.CSizeT = t.CSizeT(i_sf) * 17 + return _sha1_store_arr + sidx_sf + # 后缀匹配:mod_name 以 "." + mod_suffix 结尾 + if mod_len_sf > suffix_len_sf: + if mod_name_sf[mod_len_sf - suffix_len_sf - 1] == '.': + cmp_start_sf: str = mod_name_sf + mod_len_sf - suffix_len_sf + if string.strcmp(cmp_start_sf, mod_suffix) == 0: + sidx2_sf: t.CSizeT = t.CSizeT(i_sf) * 17 + return _sha1_store_arr + sidx2_sf + # 反向后缀匹配:mod_suffix 以 "." + mod_name 结尾 + if suffix_len_sf > mod_len_sf: + if mod_suffix[suffix_len_sf - mod_len_sf - 1] == '.': + cmp_start_rev_sf: str = mod_suffix + suffix_len_sf - mod_len_sf + if string.strcmp(cmp_start_rev_sf, mod_name_sf) == 0: + sidx_rev_sf: t.CSizeT = t.CSizeT(i_sf) * 17 + return _sha1_store_arr + sidx_rev_sf + return None + + # ============================================================ # PopulateIncludesDeps - 将 includes 文件的依赖存入内存 # @@ -1382,13 +1472,9 @@ def _BuildReachableSha1Set(mb: memhub.MemBuddy | t.CPtr, source_dir: str, app_deps: App 源文件的直接依赖模块名(空格分隔),如果非空则跳过自己的扫描步骤 """ - global _g_reachable_incomplete if source_dir is None or temp_dir is None or reachable_set is None: return -1 - # 重置不完整标志 - _g_reachable_incomplete = 0 - SRC_BUF_SIZE_R: t.CSizeT = 1048576 # 1. 构建 SHA1→module_name 映射 diff --git a/Test/App/nested_match_test.vp b/Test/App/nested_match_test.vp new file mode 100644 index 0000000..5bf3450 --- /dev/null +++ b/Test/App/nested_match_test.vp @@ -0,0 +1,268 @@ +import stdio +import t, c +import memhub +import stdlib +import llvmlite +import string + + +# ============================================================ +# nested_match_test - 嵌套 match REnum 检测测试 +# +# 验证 BUG 2 修复:当 match 嵌套在另一个 match 的 case 块中时, +# 内层 match 能正确识别 REnum 变体并生成正确的 case 列表。 +# +# 修复前:内层 match 走非 REnum 路径,case 列表为空。 +# 修复后:内层 match 能正确识别 REnum 变体。 +# ============================================================ + +# 内存池(测试内部使用) +_mbuddy: memhub.MemBuddy | t.CPtr + +# 测试用内存大小(64KB,足够构造 LLVMType 值) +_NMATCH_POOL_SIZE: t.CDefine = 65536 + + +def _init_mbuddy() -> int: + """初始化内存池,返回 0=成功, 1=失败""" + arena: bytes = stdlib.malloc(_NMATCH_POOL_SIZE) + if arena is None: + stdio.printf("FAIL: malloc for arena failed\n") + return 1 + mb: memhub.MemBuddy | t.CPtr = memhub.MemBuddy(arena, _NMATCH_POOL_SIZE) + if mb is None: + stdio.printf("FAIL: MemBuddy init failed\n") + return 1 + _mbuddy = mb + return 0 + + +def test_single_match() -> int: + """Test 1: 单层 match(基线测试)""" + stdio.printf("--- Test 1: 单层 match LLVMType ---\n") + + ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy) + if ty is None: + stdio.printf("FAIL: Int32 returned None\n") + return 1 + + match ty: + case llvmlite.LLVMType.Int(bits): + stdio.printf("Int(%d) OK\n", bits) + if bits == 32: + stdio.printf("bits==32 OK\n") + return 0 + stdio.printf("bits==%d FAIL\n", bits) + return 1 + case _: + stdio.printf("single match default FAIL\n") + return 1 + + +def test_nested_match_other() -> int: + """Test 2: 嵌套 match(case 块内 match 另一个 REnum 值) + + BUG 2 核心测试:在 match 的 case 块内嵌套 match 另一个 REnum 值。 + """ + stdio.printf("--- Test 2: 嵌套 match(case 块内 match 另一值)---\n") + + ty_outer: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy) + ty_inner: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(_mbuddy) + + match ty_outer: + case llvmlite.LLVMType.Int(bits): + # 在 Int 的 case 块内嵌套 match 另一个 REnum 值 + match ty_inner: + case llvmlite.LLVMType.Int(inner_bits): + stdio.printf("outer Int(%d), inner Int(%d) OK\n", bits, inner_bits) + if bits == 32 and inner_bits == 8: + stdio.printf("values OK\n") + return 0 + stdio.printf("values FAIL\n") + return 1 + case _: + stdio.printf("inner match default FAIL\n") + return 1 + case _: + stdio.printf("outer match default FAIL\n") + return 1 + + +def test_nested_match_pointee() -> int: + """Test 3: 嵌套 match 同一值的子结构(Ptr 的 pointee) + + 模拟 HandlesStruct.vp 中 get_struct_type_from_value 的嵌套 match 模式。 + """ + stdio.printf("--- Test 3: 嵌套 match pointee ---\n") + + # 构造 Ptr(Int32) 类型 + inner_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy) + ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(_mbuddy, inner_ty) + + match ptr_ty: + case llvmlite.LLVMType.Ptr(pointee): + # 在 Ptr 的 case 块内嵌套 match pointee + match pointee: + case llvmlite.LLVMType.Int(bits): + stdio.printf("Ptr(Int(%d)) OK\n", bits) + if bits == 32: + stdio.printf("pointee bits==32 OK\n") + return 0 + stdio.printf("pointee bits==%d FAIL\n", bits) + return 1 + case _: + stdio.printf("pointee match default FAIL\n") + return 1 + case _: + stdio.printf("outer Ptr match FAIL\n") + return 1 + + +def test_three_level_nested() -> int: + """Test 4: 3 层嵌套 match + + 模拟 HandlesStruct.vp 中 _ensure_field_types_in_module 的嵌套模式: + 外层 match → case 块内 match pointee → 再嵌套 match 另一值。 + """ + stdio.printf("--- Test 4: 3 层嵌套 match ---\n") + + # 构造 Ptr(Ptr(Int32)) 类型 + innermost: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy) + mid_ptr: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(_mbuddy, innermost) + outer_ptr: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(_mbuddy, mid_ptr) + + # 另一个值用于第 3 层 match + extra_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int16(_mbuddy) + + match outer_ptr: + case llvmlite.LLVMType.Ptr(p1): + # 第 2 层 match p1 + match p1: + case llvmlite.LLVMType.Ptr(p2): + # 第 3 层 match p2 + match p2: + case llvmlite.LLVMType.Int(bits): + stdio.printf("Ptr(Ptr(Int(%d))) OK\n", bits) + case _: + stdio.printf("level 3 default FAIL\n") + return 1 + # 第 3 层 match 另一个值(验证 case 块内多次 match) + match extra_ty: + case llvmlite.LLVMType.Int(ebits): + stdio.printf("extra Int(%d) in nested OK\n", ebits) + if ebits == 16: + stdio.printf("3-level values OK\n") + return 0 + stdio.printf("3-level values FAIL\n") + return 1 + case _: + stdio.printf("extra match FAIL\n") + return 1 + case _: + stdio.printf("level 2 default FAIL\n") + return 1 + case _: + stdio.printf("level 1 default FAIL\n") + return 1 + + +def test_nested_match_in_if() -> int: + """Test 5: 在 if 块内嵌套 match + + 另一种常见的嵌套场景:if 条件块内使用 match。 + """ + stdio.printf("--- Test 5: if 块内嵌套 match ---\n") + + ty: llvmlite.LLVMType | t.CPtr = llvmlite.Float(_mbuddy) + + if ty is not None: + match ty: + case llvmlite.LLVMType.Float(bits): + # 在 Float 的 case 块内再 match + ty2: llvmlite.LLVMType | t.CPtr = llvmlite.Double(_mbuddy) + match ty2: + case llvmlite.LLVMType.Float(bits2): + stdio.printf("Float(%d) + Double(%d) OK\n", bits, bits2) + if bits == 32 and bits2 == 64: + stdio.printf("float values OK\n") + return 0 + stdio.printf("float values FAIL\n") + return 1 + case _: + stdio.printf("inner float match FAIL\n") + return 1 + case _: + stdio.printf("outer float match FAIL\n") + return 1 + stdio.printf("ty is None FAIL\n") + return 1 + + +def test_nested_match_mismatch() -> int: + """Test 6: 嵌套 match 不同变体(验证 default 分支) + + 确保嵌套 match 的 default 分支(case _)能正确触发。 + """ + stdio.printf("--- Test 6: 嵌套 match 不同变体(default 分支)---\n") + + # 外层是 Int,内层是 Float - 验证内层 match 不会误匹配 Int + ty_outer: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(_mbuddy) + ty_inner: llvmlite.LLVMType | t.CPtr = llvmlite.Float(_mbuddy) + + match ty_outer: + case llvmlite.LLVMType.Int(bits): + match ty_inner: + case llvmlite.LLVMType.Int(wrong_bits): + stdio.printf("FAIL: Float matched as Int(%d)\n", wrong_bits) + return 1 + case llvmlite.LLVMType.Float(fbits): + stdio.printf("outer Int(%d), inner Float(%d) OK\n", bits, fbits) + if bits == 32 and fbits == 32: + stdio.printf("mismatch values OK\n") + return 0 + stdio.printf("mismatch values FAIL\n") + return 1 + case _: + stdio.printf("inner default FAIL (should be Float)\n") + return 1 + case _: + stdio.printf("outer default FAIL\n") + return 1 + + +def nested_match_test() -> int: + stdio.printf("=== nested_match_test: 嵌套 match REnum 检测测试 ===\n\n") + + # 初始化内存池 + if _init_mbuddy() != 0: + stdio.printf("FAIL: 内存池初始化失败\n") + return 1 + + r: int = 0 + + stdio.fflush(None) + if test_single_match() != 0: + r = 1 + stdio.fflush(None) + if test_nested_match_other() != 0: + r = 1 + stdio.fflush(None) + if test_nested_match_pointee() != 0: + r = 1 + stdio.fflush(None) + if test_three_level_nested() != 0: + r = 1 + stdio.fflush(None) + if test_nested_match_in_if() != 0: + r = 1 + stdio.fflush(None) + if test_nested_match_mismatch() != 0: + r = 1 + stdio.fflush(None) + + if r == 0: + stdio.printf("\n=== nested_match_test: ALL PASS ===\n") + else: + stdio.printf("\n=== nested_match_test: SOME FAIL ===\n") + + return r diff --git a/Test/App/test_main.vp b/Test/App/test_main.vp index 9608995..20995bf 100644 --- a/Test/App/test_main.vp +++ b/Test/App/test_main.vp @@ -32,6 +32,7 @@ from namespace_test import namespace_test from testcheck_test import testcheck_test from opovl_test import opovl_test from circ_test import circ_test +from nested_match_test import nested_match_test # from generic_test import generic_test # 屏蔽泛型模块(GenericTest 运行时崩溃,待修复) @@ -101,6 +102,8 @@ def main() -> int: stdio.fflush(None) r = circ_test() stdio.fflush(None) + r = nested_match_test() + stdio.fflush(None) # 屏蔽泛型模块(GenericTest 运行时崩溃,待修复) # stdio.printf("[TM] before generic_test\n")