升级了仿 Python 错误堆栈提示,修复了 bool 不存在的问题,将 bool 改为 i1 而非 i8,实现了 bool 的归一化

This commit is contained in:
TermiNexus committed 2026-07-31 04:12:12 +08:00
1 parent bfb55d68f4
commit 9dcf34599d
13 files changed
+938 -213

No files matched your search

+2
View File
@@ -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)
+39 -4
View File
@@ -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
+12 -2
View File
@@ -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
+40 -3
View File
@@ -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
+56 -10
View File
@@ -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
+95 -68
View File
@@ -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
+22 -48
View File
@@ -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):
+306 -23
View File
@@ -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 = "<unknown>"
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, "<unknown>") != 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 "<none>"
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 "<constant>"
# 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 "<name>"
# 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 "<attribute>"
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 "<subscript>"
# 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 "<binop>"
return "<unknown>"
# ============================================================
# 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*
# 命名空间隔离:严格模式下仅解析可见的结构体
+2 -8
View File
@@ -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)
+3 -43
View File
@@ -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 ===
+90 -4
View File
@@ -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 映射
+268
View File
@@ -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
+3
View File
@@ -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")