From 15f77da9d3cafd9b1b7b01fc768a347a0de09168 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 12 Oct 2026 15:06:48 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=94=B9=E5=90=8D=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=20TransPyV=20->=20ViperC=EF=BC=88=E6=BA=90=E7=A0=81/=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=86=85=E7=9A=84=E7=BA=AF=E6=9B=BF=E6=8D=A2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Tools/AuditViperParseEntries.py | 6 +- Tools/ScanAstFieldMiss.py | 6 +- lib/Projectrans/Config.py | 4 +- lib/Projectrans/DeclarationGenerator.py | 2684 +++--- lib/Projectrans/Phase2Translator.py | 10096 +++++++++++----------- lib/StubGen/Converter.py | 2264 ++--- lib/core/ConstEvaluator.py | 1368 +-- lib/core/Handles/HandlesAnnAssign.py | 2414 +++--- lib/core/Handles/HandlesAugAssign.py | 598 +- lib/core/Handles/HandlesBase.py | 2980 +++---- lib/core/Handles/HandlesClassDef.py | 4066 ++++----- lib/core/Handles/HandlesExprAttr.py | 4734 +++++----- lib/core/Handles/HandlesExprBuiltin.py | 2534 +++--- lib/core/Handles/HandlesExprFormat.py | 558 +- lib/core/Handles/HandlesExprOps.py | 1168 +-- lib/core/Handles/HandlesExprUtils.py | 884 +- lib/core/Handles/HandlesFunctions.py | 4582 +++++----- lib/core/Handles/HandlesImports.py | 8314 +++++++++--------- lib/core/Handles/HandlesTypeMerge.py | 2284 ++--- lib/core/LLVMCG/BaseGen.py | 4986 +++++------ lib/core/LLVMCG/ExprGen.py | 4 +- lib/core/LLVMCG/FuncGen.py | 2516 +++--- lib/core/LLVMCG/MemoryOps.py | 14 +- lib/core/LLVMTypeMapper.py | 944 +- lib/core/SymbolExtractor.py | 638 +- lib/core/Translator/ConstEval.py | 636 +- lib/core/Translator/LlvmGenerator.py | 1544 ++-- lib/core/TypeAnnotationResolver.py | 2634 +++--- lib/vast/Parser.py | 14 +- 29 files changed, 32737 insertions(+), 32737 deletions(-) diff --git a/Tools/AuditViperParseEntries.py b/Tools/AuditViperParseEntries.py index 89a1236..1c39fc1 100644 --- a/Tools/AuditViperParseEntries.py +++ b/Tools/AuditViperParseEntries.py @@ -20,9 +20,9 @@ SUSPECT 其余 ⇒ **需人工判定**;`--strict` 下 exit 1(门禁失败) 覆盖范围:仓库内全部 `*.py`(含根目录脚本、`MCP/**`、`TransPyC/Tools/**`), - 跳过 `.git`/`__pycache__`/`output`/`temp`/`.tpv_build`/`dlls` 与解析器自身 + 跳过 `.git`/`__pycache__`/`output`/`temp`/`__vpcbuild__`/`dlls` 与解析器自身 (`lib/vast`、`lib/vast_c` 是前端**实现**,不是前端**消费者**)。 - TPV 侧源码(`TransPyV/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。 + VPC 侧源码(`ViperC/**` 的 `.vp`)跑自己的前端,不在本脚本范围内。 用法(**只读,不写任何文件**) python TransPyC/Tools/AuditViperParseEntries.py # 打印审计表 @@ -35,7 +35,7 @@ import sys ScriptDir = os.path.dirname(os.path.abspath(__file__)) RepoRoot = os.path.dirname(os.path.dirname(ScriptDir)) -SkipDirs = {'__pycache__', '.git', '.tpv_build', 'dlls', 'output', 'temp', 'node_modules'} +SkipDirs = {'__pycache__', '.git', '__vpcbuild__', 'dlls', 'output', 'temp', 'node_modules'} # 前端实现(vast 解析器本体;它自己就是"唯一许可入口"的实现层) SkipRelPrefixes = ('TransPyC/lib/vast/', 'TransPyC/lib/vast_c/') diff --git a/Tools/ScanAstFieldMiss.py b/Tools/ScanAstFieldMiss.py index 849d68b..d84ee7d 100644 --- a/Tools/ScanAstFieldMiss.py +++ b/Tools/ScanAstFieldMiss.py @@ -18,7 +18,7 @@ 5) 类成员表按族隔离: 基类先在**本族**找, 再找**支撑族**(''), 最后全局唯一名; 同一基类名跨族重复时报 AMBIG(宁可 UNRESOLVED 也不猜)。 6) `t.REnum`(tagged union)派生类: 变体字段合并进父类成员集 - (TPV 允许在父类指针上访问变体字段, 不合并会产生大量误报)。 + (VPC 允许在父类指针上访问变体字段, 不合并会产生大量误报)。 前端 = **TPC 自研 vast**(`lib/vast`,与 StubGen/Converter.py 用的 `import vast as ast` 是同一源文件; 不可用 CPython `ast`, 否则含 `list[X*]` 等 vast 专有写法的文件会整体 @@ -44,7 +44,7 @@ SyntaxError 被跳过)。vast 与 CPython ast 的差异及本脚本的适配: python TransPyC/Tools/ScanAstFieldMiss.py --selftest [--includes DIR] [--out FILE] 默认值: - --app-root /TransPyV/App + --app-root /ViperC/App --includes /includes --out /Debugger/ScanAstFieldMiss.txt @@ -81,7 +81,7 @@ ScriptDir = os.path.dirname(os.path.abspath(__file__)) TpcRoot = os.path.dirname(ScriptDir) RepoRoot = os.path.dirname(TpcRoot) -DefaultAppRoot = os.path.join(RepoRoot, 'TransPyV', 'App') +DefaultAppRoot = os.path.join(RepoRoot, 'ViperC', 'App') DefaultIncludesDir = os.path.join(RepoRoot, 'includes') DefaultOutFile = os.path.join(RepoRoot, 'Debugger', 'ScanAstFieldMiss.txt') diff --git a/lib/Projectrans/Config.py b/lib/Projectrans/Config.py index d0fd48f..45c89de 100644 --- a/lib/Projectrans/Config.py +++ b/lib/Projectrans/Config.py @@ -95,7 +95,7 @@ def resolve_paths(config: dict[str, Any], project_file: str) -> dict[str, Any]: resolved_sources.append(resolve(src)) result['sources'] = [s for s in resolved_sources if os.path.isfile(s)] - # 直接使用 output_dir / temp_dir(与新版本 TPV 一致,不再支持 build_dir) + # 直接使用 output_dir / temp_dir(与新版本 VPC 一致,不再支持 build_dir) result['temp_dir'] = resolve(config.get('temp_dir', './temp')) result['output_dir'] = resolve(config.get('output_dir', './output')) @@ -150,7 +150,7 @@ def main() -> None: parser.add_argument('--asan', action='store_true', help='ASan 模式:对全部 .ll 做 opt -passes=asan 插桩,并显式链接 clang64 的 ASan 运行库(mingw64 的 clang 没有 x86_64-w64-windows-gnu 目标的 ASan runtime,不能用 -fsanitize=address)') parser.add_argument('--dwarf', action='store_true', help='函数级 DWARF 调试元数据:每模块一份 DICompileUnit/DIFile,每个 define 一份 DISubprogram 并挂 !dbg,函数入口第一条指令挂 DILocation。使 ASan/llvm-symbolizer 能把崩溃地址反查为 "<文件>.vp:函数起始行"(默认关闭;--asan 时自动开启)。开启时强制重译 App 模块(不复用不含 !llvm.dbg.cu 的旧 .ll/.obj),.ll/.obj 体积与编译时间略增') parser.add_argument('--traceback', action='store_true', help='Ctrl+C 调用栈打印(仅 Windows/PE):链接完成后用 llvm-dwarfdump 把最终 exe 的 .debug_line/.debug_info 导出为 ".stackmap" 地址→源行旁文件(小端定长格式),供 exe 内运行时在 Ctrl+C/Ctrl+Break 时把 CRT 到当前的返回地址逐帧翻译为 "<文件>.vp:行/函数"。会自动开启 DWARF(同 --asan,否则行表为空);找不到 llvm-dwarfdump / 调用失败 / 解析不到任何行记录 / 目标不是 Windows PE 时直接报错终止编译') - parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 TransPyV.exe)') + parser.add_argument('--nowarn-register', action='store_true', help='抑制"外部函数返回类型未登记"类 WARN 输出(透传给自举编译的 viperc.exe)') parser.add_argument('--hotspot-sites', nargs='?', const='', default=None, help='调用点热点统计:为每个匹配白名单的直接 call 点插入计数器,运行时 Ctrl+C 各模块按真实次数降序写入 (默认为 hotspot_sites.txt);同时为每个函数定义加计时器,按自身耗时(self)降序写入同目录 hotspot_time.txt,行为 `<函数名> <进入次数>`(单位 tick,x86_64 上为序列化 lfence; rdtsc);self 已扣除被插桩子调用的 inc,但不含未插桩被调方(间接调用、strcmp/memcpy 等无定义外部函数),它们计入调用者的 self') parser.add_argument('--hotspot-callees', default=None, help='--hotspot-sites 的白名单被调函数基名(逗号分隔),默认内存/字符串族 strcmp,strncmp,strchr,strlen,strstr,memcpy,memset,memmove,strcpy,strcat') parser.add_argument('--hotspot-sites-all', action='store_true', help='--hotspot-sites 对全部直接 call 点插桩(忽略白名单;体积/开销大,用于深度排查)') diff --git a/lib/Projectrans/DeclarationGenerator.py b/lib/Projectrans/DeclarationGenerator.py index 590c947..decacf3 100644 --- a/lib/Projectrans/DeclarationGenerator.py +++ b/lib/Projectrans/DeclarationGenerator.py @@ -1,14 +1,14 @@ -from __future__ import annotations - -import vast as ast -import copy -import re -from typing import List - -import llvmlite.ir as ir - -from lib.includes import t -from lib.includes.t import CTypeRegistry +from __future__ import annotations + +import vast as ast +import copy +import re +from typing import List + +import llvmlite.ir as ir + +from lib.includes import t +from lib.includes.t import CTypeRegistry from lib.core.SymbolUtils import ( IsListAnnotation, AnnotationContainsName, IsCPtrSubscriptNode, IsCPtrNode, IsRawPtrSubscriptNode, IsPtrSubscriptNode, ResolveGenericTypeArgStr, @@ -20,748 +20,748 @@ _RE_T_TYPE_PTR: re.Pattern[str] = re.compile(r'%"[a-f0-9]+\.T"\*') _RE_T_TYPE: re.Pattern[str] = re.compile(r'%"[a-f0-9]+\.T"') -class DeclarationGenerator: - """从 .pyi AST 生成纯 LLVM IR 声明(不使用字符串操作)""" - - # 匹配 LLVM 数组类型:[N x elem_type] - _ARRAY_TYPE_RE: re.Pattern = re.compile(r'^\[(\d+)\s+x\s+(.+)\]$') - - @staticmethod - def _decay_array_to_ptr(type_str: str) -> str: - """C 语言中数组参数退化为指针:[N x elem_type] → elem_type*""" - m = DeclarationGenerator._ARRAY_TYPE_RE.match(type_str) - if m: - return f'{m.group(2)}*' - return type_str - - def __init__(self, struct_names: set[str] | None = None, enum_names: set[str] | None = None, module_sha1: str | None = None, target_triple: str | None = None, target_datalayout: str | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, typedef_map: dict[str, ast.AST] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None: - self.ir: ir.Module = ir - self.module: ir.Module | None = None - self.builder: ir.IRBuilder | None = None - self._DefineConstants: dict[str, int | str] = {} - self.struct_names: set[str] = struct_names or set() - self.enum_names: set[str] = enum_names or set() - self.module_sha1: str | None = module_sha1 - self.struct_sha1_map: dict[str, str] = struct_sha1_map or {} - self.exception_names: set[str] = exception_names or set() - self.target_triple: str = target_triple or "x86_64-none-elf" - self.target_datalayout: str = target_datalayout or "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - self.typedef_map: dict[str, ast.AST] = typedef_map or {} - self._pyi_tree: ast.Module | None = None - self._t_alias_map: dict[str, type] = {} - # 泛型模板类名(PEP 695 `class Box[T]`):声明端特化判定用,与定义端 - # HandlesClassDef._generic_class_templates 同源(ClassDef 的 type_params - # 标记),generate() 里从本模块 pyi 与 class_def_map 收集 - self._generic_class_names: set[str] = set() - # t/c 导入名表(from t import CInt as myint → 'myint' → ('t','CInt')), - # 与定义端 Trans._t_c_imported_names 同口径,供泛型实参串解析使用 - self._t_c_imported_names: dict[str, tuple[str, str]] = {} - # 跨模块类定义映射:{class_name: ClassDef_node},供 _get_inherited_members - # 查找其他模块定义的父类(如 exprs.py 的 Name(AST) 查找 base.py 的 AST) - self._cross_module_class_defs: dict[str, ast.ClassDef] = class_def_map or {} - t.configure_platform(self.target_triple) - - def generate(self, pyi_content: str, src_path: str) -> str: - # 传 src_path:解析失败时报错/回退诊断才带得上来源(否则 vast 记为 , - # 既无法定位文件,也无法知道是哪份 .pyi 出问题) - tree: ast.Module = ast.parse(pyi_content, src_path) - - self._DefineConstants: dict[str, int | str] = {} - self._pyi_tree = tree - # 泛型模板类名(PEP 695 `class Box[T]`):class_def_map 覆盖所有已生成 - # .vpi 的模块(含本模块),本模块 pyi 直接解析的 ClassDef 优先, - # 与定义端 HandlesClassDef 注册 _generic_class_templates 的判据一致 - # (都只看 ClassDef.type_params 是否非空,不看类名白名单)。 - self._generic_class_names = { - node.name for node in ast.iter_child_nodes(tree) - if isinstance(node, ast.ClassDef) and getattr(node, 'type_params', None)} - for _gc_name, _gc_node in self._cross_module_class_defs.items(): - if getattr(_gc_node, 'type_params', None): - self._generic_class_names.add(_gc_name) - global_typedef_map: dict[str, ast.AST] = self.typedef_map - self.typedef_map: dict[str, ast.AST] = {} - if global_typedef_map: - self.typedef_map.update(global_typedef_map) - - # 构建 t/c 类型别名映射(from t import CInt as myint) - self._t_alias_map: dict[str, type] = {} - self._t_c_imported_names: dict[str, tuple[str, str]] = {} - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.ImportFrom): - if node.module in ('t', 'c'): - for alias in node.names: - name: str = alias.name - asname: str = alias.asname or name - self._t_c_imported_names[asname] = (node.module, name) - if node.module == 't': - t_cls: type | None = getattr(t, name, None) - if isinstance(t_cls, type) and issubclass(t_cls, t.CType): - self._t_alias_map[asname] = t_cls - - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): - var_name: str = node.target.id - if node.value and isinstance(node.value, ast.Constant): - self._DefineConstants[var_name] = node.value.value - elif node.value and isinstance(node.value, ast.Name): - self._DefineConstants[var_name] = node.value.id - is_typedef: bool = False - if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': - is_typedef = True - elif isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': - is_typedef = True - elif isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': - is_typedef = True - if is_typedef: - if node.value: - self.typedef_map[var_name] = node.value - elif isinstance(node.annotation, ast.BinOp) and node.annotation.right: - self.typedef_map[var_name] = node.annotation.right - - lines: list[str] = [] - lines.append('; ModuleID = "transpyc_decl"') - lines.append(f'target triple = "{self.target_triple}"') - lines.append(f'target datalayout = "{self.target_datalayout}"') - lines.append('') - - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.FunctionDef): - if hasattr(node, 'type_params') and node.type_params: - continue - decl: str | None = self._generate_func_decl(node) - if decl: - lines.append(decl) - elif isinstance(node, ast.AnnAssign): - decl: str | None = self._generate_global_decl(node) - if decl: - lines.append(decl) - elif isinstance(node, ast.Assign): - decl: str | None = self._generate_global_assign_decl(node) - if decl: - lines.append(decl) - elif isinstance(node, ast.ClassDef): - # PEP 695 泛型类不再完全跳过:子类(如 Value(GSListNode[Value]))的 - # 继承字段展平需要从泛型基类的 stub 中读取字段(如 GSListNode.Next)。 - # _generate_class_decl 内部会跳过泛型类的方法声明(T 参数 → opaque struct)。 - decls: list[str] = self._generate_class_decl(node) - lines.extend(decls) - - # 后处理:扫描引用但未定义的结构体类型,自动添加 opaque 声明 - # 这解决了泛型类(如 list[T])在 stub 中被跳过导致的 "undefined type" 错误 - opaque_decls: list[str] = self._collect_undefined_type_decls(lines) - if opaque_decls: - # 在 header 之后插入 opaque 声明(ModuleID, triple, datalayout, 空行 = 4 行) - insert_idx: int = 4 - for i, od in enumerate(opaque_decls): - lines.insert(insert_idx + i, od) - - return '\n'.join(lines) - - def _collect_undefined_type_decls(self, lines: list[str]) -> list[str]: - """扫描所有引用的 %"sha1.TypeName" 类型,返回未定义类型的 opaque 声明列表""" - # 收集已定义的类型名 - defined_types: set[str] = set() - type_def_re: re.Pattern = re.compile(r'^(%".+?")\s*=\s*type\s') - for line in lines: - m = type_def_re.match(line.strip()) - if m: - defined_types.add(m.group(1)) - - # 扫描所有引用的类型名 - referenced_types: set[str] = set() - type_ref_re: re.Pattern = re.compile(r'%"[a-f0-9]+\.[^"]+"') - for line in lines: - for m in type_ref_re.finditer(line): - ref: str = m.group(0) - if ref not in defined_types: - referenced_types.add(ref) - - # 生成 opaque 声明(排序确保确定性) - result: list[str] = [] - for type_name in sorted(referenced_types): - result.append(f'{type_name} = type opaque') - return result - - def _generate_func_decl(self, node: ast.FunctionDef) -> str | None: - """生成函数声明""" - func_name: str = node.name - is_export: bool = self._is_export_func(node) - if self.module_sha1 and not is_export: - func_name = f"{self.module_sha1}.{func_name}" - MultiRetTypes: list[ast.AST] = [] - if node.decorator_list: - for decorator in node.decorator_list: - # fail-fast:`@c.CReturn` 装饰器已移除,任何地方出现都必须报错, - # 不得静默忽略或改写;多返回值请改用 `-> tuple[ty1, ty2, ...]`。 - if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): - if decorator.func.attr == 'CReturn': - raise SyntaxError( - f"函数 '{node.name}':`@c.CReturn` 已移除:请改用返回注解 " - f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" - ) - if node.returns: - if isinstance(node.returns, ast.Subscript) and isinstance(node.returns.value, ast.Name) and node.returns.value.id == 'tuple': - slice_node: ast.AST = node.returns.slice - if isinstance(slice_node, ast.Tuple): - for elt in slice_node.elts: - MultiRetTypes.append(elt) - else: - MultiRetTypes.append(slice_node) - ret_type: str - if MultiRetTypes: - elem_types: list[str] = [self._get_type_str(rt, embedded=True) for rt in MultiRetTypes] - ret_type = '{ ' + ', '.join(elem_types) + ' }' - else: - ret_type = self._get_type_str(node.returns) - if not ret_type or ret_type == 'void': - ret_type = 'void' - elif ret_type == 'i8*': - if node.returns is None: - ret_type = 'void' - - params: list[str] = [] - for arg in node.args.args: - arg_type: str - if arg.annotation: - arg_type = self._get_type_str(arg.annotation) - else: - arg_type = 'i8*' - # C 语言中数组参数退化为指针:[N x elem_type] → elem_type* - arg_type = self._decay_array_to_ptr(arg_type) - if arg_type and arg_type != 'void': - params.append(arg_type) - - param_str: str = ', '.join(params) if params else '' - if node.args.vararg: - param_str = param_str + ', ...' if param_str else '...' - if func_name[0].isdigit(): - return f'declare {ret_type} @"{func_name}"({param_str})' - return f'declare {ret_type} @{func_name}({param_str})' - - def _is_export_func(self, node: ast.FunctionDef) -> bool: - """检查函数是否标记为 CExport""" - if not node.returns: - return False - return self._check_annotation_for_export(node.returns) - - def _check_annotation_for_export(self, annotation: ast.AST | None) -> bool: - """递归检查类型注解中是否包含 CExport 或 t.State""" - return bool(annotation) and (AnnotationContainsName(annotation, 'CExport') or AnnotationContainsName(annotation, 'State')) - - def _generate_global_decl(self, node: ast.AnnAssign) -> str | None: - """生成全局变量声明""" - if not isinstance(node.target, ast.Name): - return None - var_name: str = node.target.id - # CDefine 是编译期常量,不是变量:stub 里**不得**生成 external global。 - # `define X: T = V` 会展开为注解 `T | t.CDefine`,故判据必须能识别**联合形态**—— - # 原判据只认裸 `t.CDefine`(注解为单个 Attribute),联合注解会漏判 → - # stub 出现 `@X = external global i32` → 其他模块引用它时变成未定义符号(链接失败), - # 且会遮住跨模块 CDefine 常量回退(同名全局优先于常量)。与 _check_annotation_for_export - # 一样用递归判据。 - if AnnotationContainsName(node.annotation, 'CDefine'): - return None - if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': - return None - if isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': - return None - if isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': - return None - if isinstance(node.annotation, ast.List): - for elt in node.annotation.elts: - if isinstance(elt, ast.Attribute) and hasattr(elt, 'attr') and elt.attr in ('CTypedef', 'Callable'): - return None - if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable': - return None - VarType: str = self._get_type_str(node.annotation) - if VarType.startswith('[0 x ') and node.value: - elem_type: str = VarType[5:-1] - actual_count: int = 0 - if isinstance(node.value, ast.List): - actual_count = len(node.value.elts) - elif isinstance(node.value, ast.Constant) and isinstance(node.value.value, str): - # 字符串字面量:与 HandlesBase.ParseArrayCount(:731-732) / HandlesAssign - # 全局路径同口径(len(str)+1,含结尾 NUL)。此前只修 List 值 → CArray - # 字符串全局的 stub 声明停留在 [0 x i8],与定义端 [N x i8] 长度不一致 - # (跨模块 type mismatch / llc 拒绝)。 - actual_count = len(node.value.value) + 1 - if actual_count > 0: - VarType = f'[{actual_count} x {elem_type}]' - return f'@{var_name} = external global {VarType}' - - def _generate_global_assign_decl(self, node: ast.Assign) -> str | None: - """从无类型标注赋值推断并生成全局变量声明""" - if not node.targets or not isinstance(node.targets[0], ast.Name): - return None - var_name: str = node.targets[0].id - VarType: str | None = self._infer_type(node.value) - if VarType: - return f'@{var_name} = external global {VarType}' - return None - - def _resolve_base_kind(self, base_name: str) -> str | None: - """解析基类名称的类型种类(支持别名) - - Returns: - 'enum' | 'union' | 'renum' | 'struct' | 'ctype_marker' | None - """ - # 1) 查 t 别名映射(from t import CEnum as en) - if base_name in self._t_alias_map: - t_cls: type = self._t_alias_map[base_name] - if issubclass(t_cls, (t.CEnum, t.REnum)): - return 'renum' if issubclass(t_cls, t.REnum) else 'enum' - if issubclass(t_cls, t.CUnion): - return 'union' - if issubclass(t_cls, t.CStruct): - return 'struct' - # CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记, - # 不应被编译为真实结构体,也不应被生成 stub 声明。 - if issubclass(t_cls, t.CType): - return 'ctype_marker' - - # 2) 直接查 t 模块属性 - t_cls: type | None = getattr(t, base_name, None) - if isinstance(t_cls, type) and issubclass(t_cls, t.CType): - if issubclass(t_cls, (t.CEnum, t.REnum)): - return 'renum' if issubclass(t_cls, t.REnum) else 'enum' - if issubclass(t_cls, t.CUnion): - return 'union' - if issubclass(t_cls, t.CStruct): - return 'struct' - # CType 及其子类是"类型强转"标记,不编译为结构体。 - return 'ctype_marker' - - return None - - def _is_marker_base(self, base_name: str) -> bool: - """检查是否为标记基类(Object/CVTable/Exception/CEnum/CUnion/CStruct/REnum 及其别名)""" - if base_name in ('Object', 'CVTable', 'Exception', 'CEnum', 'Enum', 'CStruct', 'CUnion', 'REnum'): - return True - if self._resolve_base_kind(base_name) is not None: - return True - return False - - def _generate_class_decl(self, node: ast.ClassDef) -> List[str]: - """生成结构体/类声明""" - decls: list[str] = [] - class_name: str = node.name - is_enum: bool = False - is_renum: bool = False - is_union: bool = False - is_exception: bool = False - if node.bases: - for base in node.bases: - base_name: str | None = None - if isinstance(base, ast.Attribute) and hasattr(base, 'attr'): - base_name = base.attr - elif isinstance(base, ast.Name) and hasattr(base, 'id'): - base_name = base.id - if base_name: - if base_name in self.exception_names or base_name == 'Exception': - is_exception = True - break - kind: str | None = self._resolve_base_kind(base_name) - if kind == 'enum': - is_enum = True - break - elif kind == 'union': - is_union = True - elif kind == 'renum': - is_renum = True - elif kind == 'ctype_marker': - # CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是 - # "类型强转"标记,不应被编译为真实结构体,也不生成 stub 声明。 - return decls - if is_exception: - return decls - if is_enum: - enum_values: dict[str, int] = {} - next_value: int = 0 - # 枚举存储宽度 = 最宽成员类型(与定义端 HandlesClassDef / 跨模块读取端 - # 同一规则源 EnumClassIntWidth;此处无 SymbolTable,走 t 类名 / builtins / - # 库 typedef 的表)。声明端的全局类型必须与定义端运行时常量同宽(ABI)。 - enum_width: int = EnumClassIntWidth(node.body) - for item in node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - var_name: str = item.target.id - if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - enum_values[var_name] = item.value.value - else: - enum_values[var_name] = next_value - next_value += 1 - elif isinstance(item, ast.Assign) and len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): - var_name: str = item.targets[0].id - if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - enum_values[var_name] = item.value.value - next_value = item.value.value + 1 - else: - enum_values[var_name] = next_value - next_value += 1 - for var_name, value in enum_values.items(): - decls.append(f'@__config_{class_name}_{var_name} = external global i{enum_width}') - return decls - if is_renum: - # REnum 布局: { i32 __tag, } - # 对齐 _EmitREnumLlvm (HandlesClassDef.py) 的 Phase 2 逻辑 - # Bug 修复:按每个位置的 max 字段尺寸构建布局,而非选总尺寸最大的变体。 - # 不同变体在同一位置可能有不同尺寸的字段(如 Concrete 位置3是 i32, - # 但 Var 位置3是 MetaType*),用最大变体的布局会导致指针截断。 - variant_fields_list: list[list[str]] = [] - for item in node.body: - if isinstance(item, ast.ClassDef): - variant_fields: list[str] = [] - for nested_item in item.body: - if isinstance(nested_item, ast.AnnAssign) and isinstance(nested_item.target, ast.Name): - ft: str = self._get_type_str(nested_item.annotation, embedded=True) - if ft: - variant_fields.append(ft) - variant_fields_list.append(variant_fields) - elif isinstance(item, ast.Assign): - # 显式值变体(无 payload) - variant_fields_list.append([]) - # 计算每个位置的最大尺寸 - max_field_count: int = 0 - for vf in variant_fields_list: - if len(vf) > max_field_count: - max_field_count = len(vf) - renum_member_types: list[str] = ['i32'] # __tag - for pos in range(max_field_count): - pos_max_size: int = 0 - for vf in variant_fields_list: - if pos < len(vf): - sz: int = self._llvm_type_size_str(vf[pos]) - if sz > pos_max_size: - pos_max_size = sz - # <=4 用 i32,>4 用 i64(8字节,可容纳指针) - if pos_max_size <= 4: - renum_member_types.append('i32') - else: - renum_member_types.append('i64') - if len(renum_member_types) < 2: - # 无类变体或所有变体无字段时,使用 i32 作为占位(对齐 _EmitREnumLlvm 默认行为) - renum_member_types = ['i32', 'i32'] - struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' - struct_decl: str = f'{struct_type_name} = type {{ {", ".join(renum_member_types)} }}' - decls.append(struct_decl) - return decls - member_types: list[str] = [] - has_bitfield: bool = False - total_bits: int = 0 - has_methods: bool = any(isinstance(item, ast.FunctionDef) for item in node.body) - is_cvtable: bool = False - is_cpython_object: bool = False - is_novtable: bool = False - if hasattr(node, 'decorator_list') and node.decorator_list: - for decorator in node.decorator_list: - if isinstance(decorator, ast.Attribute): - if getattr(decorator.value, 'id', None) == 't': - if decorator.attr == 'CVTable': - is_cvtable = True - elif decorator.attr == 'Object': - is_cpython_object = True - elif decorator.attr == 'NoVTable': - is_novtable = True - elif isinstance(decorator, ast.Name): - if decorator.id == 'CVTable': - is_cvtable = True - elif decorator.id == 'Object': - is_cpython_object = True - elif decorator.id == 'NoVTable': - is_novtable = True - if has_methods and not is_cpython_object: - is_cpython_object = True - has_parent_class: bool = False - parent_is_novtable: bool = False - for base in node.bases: - base_name: str | None = None - if isinstance(base, ast.Attribute): - base_name = base.attr - elif isinstance(base, ast.Name): - base_name = base.id - elif isinstance(base, ast.Subscript): - if isinstance(base.value, ast.Attribute): - base_name = base.value.attr - elif isinstance(base.value, ast.Name): - base_name = base.value.id - if base_name and not self._is_marker_base(base_name): - has_parent_class = True - # 检查父类是否为 @t.NoVTable(非多态继承) - _p_node: ast.ClassDef | None = None - for n in ast.iter_child_nodes(self._pyi_tree): - if isinstance(n, ast.ClassDef) and n.name == base_name: - _p_node = n - break - if _p_node is None: - _p_node = self._cross_module_class_defs.get(base_name) - if _p_node is not None and hasattr(_p_node, 'decorator_list') and _p_node.decorator_list: - for dec in _p_node.decorator_list: - if isinstance(dec, ast.Attribute) and getattr(dec.value, 'id', None) == 't' and dec.attr == 'NoVTable': - parent_is_novtable = True - elif isinstance(dec, ast.Name) and dec.id == 'NoVTable': - parent_is_novtable = True - break - # 仅当非 NoVTable 且父类非 NoVTable 时才因继承启用 CVTable - # (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入) - if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable: - is_cvtable = True - # 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable: - # 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的 - # `_has_any_func_cvtable` 分支逐条对齐(类级 NoVTable / 无真实父类)。 - # 必须在此同步:定义模块按 IsCVTable 在 struct 首部放 vptr,若 stub 不同步, - # 跨模块使用该类的 struct 布局会比定义模块少一个字段 ⇒ 字段整体错位。 - if not is_cvtable and ClassHasFuncLevelCVTable(node): - if is_novtable or not has_parent_class: - is_cvtable = True - base_has_vtable: bool = False - for base in node.bases: - base_name: str | None = None - if isinstance(base, ast.Attribute): - base_name = base.attr - elif isinstance(base, ast.Name): - base_name = base.id - elif isinstance(base, ast.Subscript): - if isinstance(base.value, ast.Attribute): - base_name = base.value.attr - elif isinstance(base.value, ast.Name): - base_name = base.value.id - if base_name and not self._is_marker_base(base_name): - # 先在当前 .pyi 中查找父类,找不到则查跨模块类定义映射 - _base_node: ast.ClassDef | None = None - for n in ast.iter_child_nodes(self._pyi_tree): - if isinstance(n, ast.ClassDef) and n.name == base_name: - _base_node = n - break - if _base_node is None: - _base_node = self._cross_module_class_defs.get(base_name) - if _base_node is not None and hasattr(_base_node, 'decorator_list') and _base_node.decorator_list: - for dec in _base_node.decorator_list: - if isinstance(dec, ast.Attribute) and getattr(dec.value, 'id', None) == 't' and dec.attr == 'CVTable': - base_has_vtable = True - elif isinstance(dec, ast.Name) and dec.id == 'CVTable': - base_has_vtable = True - if base_has_vtable: - break - # NoVTable 类不添加 vtable 指针(即使有方法和父类) - if has_methods and is_cvtable and not base_has_vtable and not is_novtable: - member_types.append('i8*') - # 多继承字段展平:按继承源去重(2026-08-18)——菱形继承不再重复展平 - # 祖先字段(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase, - # 而它们都继承 ClassGenBase → ClassGenBase 字段被展平 6 次,struct 膨胀 - # 且 this_adjust/字段 GEP 错位)。每类只展平一次,只取自有字段。 - # 与 HandlesClassDef._FlattenInherited 保持同一算法。 - _seen_cls: set[str] = set() - for base in node.bases: - base_name: str | None = None - type_args: list[str] = [] - if isinstance(base, ast.Attribute): - base_name = base.attr - elif isinstance(base, ast.Name): - base_name = base.id - elif isinstance(base, ast.Subscript): - if isinstance(base.value, ast.Attribute): - base_name = base.value.attr - elif isinstance(base.value, ast.Name): - base_name = base.value.id - slice_node: ast.AST = base.slice - if isinstance(slice_node, ast.Name): - type_args.append(slice_node.id) - elif isinstance(slice_node, ast.Tuple): - for elt in slice_node.elts: - if isinstance(elt, ast.Name): - type_args.append(elt.id) - if base_name and not self._is_marker_base(base_name): - self._FlattenInheritedDecl(base_name, _seen_cls, member_types, - type_args or None) - # 自身字段收集(复用 _CollectOwnMemberTypes,与 _FlattenInheritedDecl 一致) - _own_types: list[str] - _own_types, has_bitfield, total_bits = self._CollectOwnMemberTypes(node) - member_types.extend(_own_types) - if has_bitfield: - if total_bits <= 8: - member_types.insert(0, 'i8') - elif total_bits <= 16: - member_types.insert(0, 'i16') - elif total_bits <= 32: - member_types.insert(0, 'i32') - else: - member_types.insert(0, 'i64') - struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' - struct_decl: str = f'{struct_type_name} = type {{ {", ".join(member_types)} }}' - decls.append(struct_decl) - # 嵌套类 / 嵌套 struct:定义端以扁平名 `{外类}_{内类}` 发射(见 HandlesClassDef. - # _EmitNestedClassDefs),此处同步产出同名完整类型声明 —— 否则跨模块引用 - # `外类.内类` 只会得到一个 opaque 前向声明(指针可用、字段偏移全失效)。 - # deepcopy 后改名再递归:本函数所有名字都派生自 node.name,改名即全链路一致; - # 判断/枚举/联合等分支在递归时会各自按内层类自身的基类重新分派。 - for _nested_item in getattr(node, 'body', None) or []: - if not isinstance(_nested_item, ast.ClassDef): - continue - _nested_inner: str | None = getattr(_nested_item, 'name', None) - if not _nested_inner: - continue - _nested_node: ast.ClassDef = copy.deepcopy(_nested_item) - _nested_node.name = f'{class_name}_{_nested_inner}' - decls.extend(self._generate_class_decl(_nested_node)) - if is_cpython_object or is_cvtable: - new_func_name: str = f'{class_name}.__before_init__' - if self.module_sha1: - new_func_name = f"{self.module_sha1}.{new_func_name}" - new_func_decl: str - if new_func_name[0].isdigit(): - new_func_decl = f'declare void @"{new_func_name}"({struct_type_name}*)' - else: - new_func_decl = f'declare void @{new_func_name}({struct_type_name}*)' - decls.append(new_func_decl) - # PEP 695 泛型类跳过方法声明生成:方法参数类型 T 会被解析为 opaque struct, - # LLVM 报 "invalid type for function argument"。字段(AnnAssign)仍正常生成, - # 确保子类(如 Value(GSListNode[Value]))能从 stub 中读取继承字段(如 Next)。 - IsGenericClass: bool = hasattr(node, 'type_params') and bool(node.type_params) - if not IsGenericClass: - for item in node.body: - if isinstance(item, ast.FunctionDef): - if hasattr(item, 'type_params') and item.type_params: - continue - method_name: str = f'{class_name}.{item.name}' - if self.module_sha1: - method_name = f"{self.module_sha1}.{method_name}" - ret_type: str = self._get_type_str(item.returns) if item.returns else 'void' - if not ret_type: - ret_type = 'void' - params: list[str] = [] - for arg_idx, arg in enumerate(item.args.args): - arg_type: str - if arg.annotation: - arg_type = self._get_type_str(arg.annotation) - elif arg_idx == 0 and arg.arg == 'self': - arg_type = f'{struct_type_name}*' - else: - arg_type = 'i8*' - # C 语言中数组参数退化为指针:[N x elem_type] → elem_type* - arg_type = self._decay_array_to_ptr(arg_type) - params.append(arg_type) - param_str: str = ', '.join(params) if params else '' - if method_name[0].isdigit(): - decls.append(f'declare {ret_type} @"{method_name}"({param_str})') - else: - decls.append(f'declare {ret_type} @{method_name}({param_str})') - # 为继承但未覆写的方法生成包装声明 - # 这些声明让跨模块调用能正确解析子类包装函数的签名, - # 避免 stub 缺失导致默认 i32 返回类型 → 64 位指针截断 - decls.extend(self._generate_inherited_method_decls(node, class_name, struct_type_name)) - return decls - - def _generate_inherited_method_decls(self, node: ast.ClassDef, child_class_name: str, child_struct_type: str) -> list[str]: - """为继承但未覆写的方法生成包装声明 - - 在 Phase 1 stub 中声明子类继承方法的签名(与父类相同,但 self 参数为子类类型)。 - 这样测试模块翻译时能从 temp stub 中获取正确的返回类型, - 避免 Phase 2 includes 翻译滞后导致跨模块调用使用默认 i32 返回类型。 - """ - decls: list[str] = [] - # 收集子类自身定义的方法名(这些不需要生成包装,子类有自己的实现) - seen_method_names: set[str] = set() - for item in node.body: - if isinstance(item, ast.FunctionDef): - seen_method_names.add(item.name) - - # 沿继承链收集父类方法,近祖先优先 - for base in node.bases: - base_name: str | None = None - if isinstance(base, ast.Attribute): - base_name = base.attr - elif isinstance(base, ast.Name): - base_name = base.id - elif isinstance(base, ast.Subscript): - if isinstance(base.value, ast.Attribute): - base_name = base.value.attr - elif isinstance(base.value, ast.Name): - base_name = base.value.id - if not base_name or self._is_marker_base(base_name): - continue - self._CollectInheritedWrapperDecls( - base_name, child_class_name, child_struct_type, - seen_method_names, decls) - return decls - - def _CollectInheritedWrapperDecls( - self, parent_name: str, child_class_name: str, child_struct_type: str, - seen_method_names: set[str], decls: list[str]) -> None: - """递归收集父类及其祖先的方法,为未覆写的方法生成包装声明""" - # 查找父类节点:先在当前 .pyi 中查找,找不到则查跨模块类定义映射 - parent_node: ast.ClassDef | None = None - for n in ast.iter_child_nodes(self._pyi_tree): - if isinstance(n, ast.ClassDef) and n.name == parent_name: - parent_node = n - break - if parent_node is None: - parent_node = self._cross_module_class_defs.get(parent_name) - if parent_node is None: - return - - # 为父类的每个方法生成包装声明 - for item in parent_node.body: - if isinstance(item, ast.FunctionDef): - if hasattr(item, 'type_params') and item.type_params: - continue - method_name: str = item.name - # __before_init__ 只在自身类生成,不生成继承包装 - if method_name == '__before_init__': - continue - # 跳过子类已覆写的方法,以及已处理的祖先方法 - if method_name in seen_method_names: - continue - seen_method_names.add(method_name) - - # 构建包装方法名:{module_sha1}.{child_class}.{method_name} - wrapper_name: str = f'{child_class_name}.{method_name}' - if self.module_sha1: - wrapper_name = f"{self.module_sha1}.{wrapper_name}" - - # 解析返回类型(与父类方法相同) - ret_type: str = self._get_type_str(item.returns) if item.returns else 'void' - if not ret_type: - ret_type = 'void' - elif ret_type == 'i8*': - if item.returns is None: - ret_type = 'void' - - # 构建参数列表:第一个参数(self)替换为子类类型,其余与父类相同 - params: list[str] = [] - for arg_idx, arg in enumerate(item.args.args): - arg_type: str - if arg_idx == 0 and arg.arg == 'self': - arg_type = f'{child_struct_type}*' - else: - if arg.annotation: - arg_type = self._get_type_str(arg.annotation) - else: - arg_type = 'i8*' - arg_type = self._decay_array_to_ptr(arg_type) - params.append(arg_type) - - param_str: str = ', '.join(params) if params else '' - if wrapper_name[0].isdigit(): - decls.append(f'declare {ret_type} @"{wrapper_name}"({param_str})') - else: - decls.append(f'declare {ret_type} @{wrapper_name}({param_str})') - - # 递归处理祖先类 - for base in parent_node.bases: - base_name: str | None = None - if isinstance(base, ast.Attribute): - base_name = base.attr - elif isinstance(base, ast.Name): - base_name = base.id - elif isinstance(base, ast.Subscript): - if isinstance(base.value, ast.Attribute): - base_name = base.value.attr - elif isinstance(base.value, ast.Name): - base_name = base.value.id - if base_name and not self._is_marker_base(base_name): - self._CollectInheritedWrapperDecls( - base_name, child_class_name, child_struct_type, - seen_method_names, decls) - +class DeclarationGenerator: + """从 .pyi AST 生成纯 LLVM IR 声明(不使用字符串操作)""" + + # 匹配 LLVM 数组类型:[N x elem_type] + _ARRAY_TYPE_RE: re.Pattern = re.compile(r'^\[(\d+)\s+x\s+(.+)\]$') + + @staticmethod + def _decay_array_to_ptr(type_str: str) -> str: + """C 语言中数组参数退化为指针:[N x elem_type] → elem_type*""" + m = DeclarationGenerator._ARRAY_TYPE_RE.match(type_str) + if m: + return f'{m.group(2)}*' + return type_str + + def __init__(self, struct_names: set[str] | None = None, enum_names: set[str] | None = None, module_sha1: str | None = None, target_triple: str | None = None, target_datalayout: str | None = None, struct_sha1_map: dict[str, str] | None = None, exception_names: set[str] | None = None, typedef_map: dict[str, ast.AST] | None = None, class_def_map: dict[str, ast.ClassDef] | None = None) -> None: + self.ir: ir.Module = ir + self.module: ir.Module | None = None + self.builder: ir.IRBuilder | None = None + self._DefineConstants: dict[str, int | str] = {} + self.struct_names: set[str] = struct_names or set() + self.enum_names: set[str] = enum_names or set() + self.module_sha1: str | None = module_sha1 + self.struct_sha1_map: dict[str, str] = struct_sha1_map or {} + self.exception_names: set[str] = exception_names or set() + self.target_triple: str = target_triple or "x86_64-none-elf" + self.target_datalayout: str = target_datalayout or "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + self.typedef_map: dict[str, ast.AST] = typedef_map or {} + self._pyi_tree: ast.Module | None = None + self._t_alias_map: dict[str, type] = {} + # 泛型模板类名(PEP 695 `class Box[T]`):声明端特化判定用,与定义端 + # HandlesClassDef._generic_class_templates 同源(ClassDef 的 type_params + # 标记),generate() 里从本模块 pyi 与 class_def_map 收集 + self._generic_class_names: set[str] = set() + # t/c 导入名表(from t import CInt as myint → 'myint' → ('t','CInt')), + # 与定义端 Trans._t_c_imported_names 同口径,供泛型实参串解析使用 + self._t_c_imported_names: dict[str, tuple[str, str]] = {} + # 跨模块类定义映射:{class_name: ClassDef_node},供 _get_inherited_members + # 查找其他模块定义的父类(如 exprs.py 的 Name(AST) 查找 base.py 的 AST) + self._cross_module_class_defs: dict[str, ast.ClassDef] = class_def_map or {} + t.configure_platform(self.target_triple) + + def generate(self, pyi_content: str, src_path: str) -> str: + # 传 src_path:解析失败时报错/回退诊断才带得上来源(否则 vast 记为 , + # 既无法定位文件,也无法知道是哪份 .pyi 出问题) + tree: ast.Module = ast.parse(pyi_content, src_path) + + self._DefineConstants: dict[str, int | str] = {} + self._pyi_tree = tree + # 泛型模板类名(PEP 695 `class Box[T]`):class_def_map 覆盖所有已生成 + # .vpi 的模块(含本模块),本模块 pyi 直接解析的 ClassDef 优先, + # 与定义端 HandlesClassDef 注册 _generic_class_templates 的判据一致 + # (都只看 ClassDef.type_params 是否非空,不看类名白名单)。 + self._generic_class_names = { + node.name for node in ast.iter_child_nodes(tree) + if isinstance(node, ast.ClassDef) and getattr(node, 'type_params', None)} + for _gc_name, _gc_node in self._cross_module_class_defs.items(): + if getattr(_gc_node, 'type_params', None): + self._generic_class_names.add(_gc_name) + global_typedef_map: dict[str, ast.AST] = self.typedef_map + self.typedef_map: dict[str, ast.AST] = {} + if global_typedef_map: + self.typedef_map.update(global_typedef_map) + + # 构建 t/c 类型别名映射(from t import CInt as myint) + self._t_alias_map: dict[str, type] = {} + self._t_c_imported_names: dict[str, tuple[str, str]] = {} + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.ImportFrom): + if node.module in ('t', 'c'): + for alias in node.names: + name: str = alias.name + asname: str = alias.asname or name + self._t_c_imported_names[asname] = (node.module, name) + if node.module == 't': + t_cls: type | None = getattr(t, name, None) + if isinstance(t_cls, type) and issubclass(t_cls, t.CType): + self._t_alias_map[asname] = t_cls + + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + var_name: str = node.target.id + if node.value and isinstance(node.value, ast.Constant): + self._DefineConstants[var_name] = node.value.value + elif node.value and isinstance(node.value, ast.Name): + self._DefineConstants[var_name] = node.value.id + is_typedef: bool = False + if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': + is_typedef = True + elif isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': + is_typedef = True + elif isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': + is_typedef = True + if is_typedef: + if node.value: + self.typedef_map[var_name] = node.value + elif isinstance(node.annotation, ast.BinOp) and node.annotation.right: + self.typedef_map[var_name] = node.annotation.right + + lines: list[str] = [] + lines.append('; ModuleID = "transpyc_decl"') + lines.append(f'target triple = "{self.target_triple}"') + lines.append(f'target datalayout = "{self.target_datalayout}"') + lines.append('') + + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.FunctionDef): + if hasattr(node, 'type_params') and node.type_params: + continue + decl: str | None = self._generate_func_decl(node) + if decl: + lines.append(decl) + elif isinstance(node, ast.AnnAssign): + decl: str | None = self._generate_global_decl(node) + if decl: + lines.append(decl) + elif isinstance(node, ast.Assign): + decl: str | None = self._generate_global_assign_decl(node) + if decl: + lines.append(decl) + elif isinstance(node, ast.ClassDef): + # PEP 695 泛型类不再完全跳过:子类(如 Value(GSListNode[Value]))的 + # 继承字段展平需要从泛型基类的 stub 中读取字段(如 GSListNode.Next)。 + # _generate_class_decl 内部会跳过泛型类的方法声明(T 参数 → opaque struct)。 + decls: list[str] = self._generate_class_decl(node) + lines.extend(decls) + + # 后处理:扫描引用但未定义的结构体类型,自动添加 opaque 声明 + # 这解决了泛型类(如 list[T])在 stub 中被跳过导致的 "undefined type" 错误 + opaque_decls: list[str] = self._collect_undefined_type_decls(lines) + if opaque_decls: + # 在 header 之后插入 opaque 声明(ModuleID, triple, datalayout, 空行 = 4 行) + insert_idx: int = 4 + for i, od in enumerate(opaque_decls): + lines.insert(insert_idx + i, od) + + return '\n'.join(lines) + + def _collect_undefined_type_decls(self, lines: list[str]) -> list[str]: + """扫描所有引用的 %"sha1.TypeName" 类型,返回未定义类型的 opaque 声明列表""" + # 收集已定义的类型名 + defined_types: set[str] = set() + type_def_re: re.Pattern = re.compile(r'^(%".+?")\s*=\s*type\s') + for line in lines: + m = type_def_re.match(line.strip()) + if m: + defined_types.add(m.group(1)) + + # 扫描所有引用的类型名 + referenced_types: set[str] = set() + type_ref_re: re.Pattern = re.compile(r'%"[a-f0-9]+\.[^"]+"') + for line in lines: + for m in type_ref_re.finditer(line): + ref: str = m.group(0) + if ref not in defined_types: + referenced_types.add(ref) + + # 生成 opaque 声明(排序确保确定性) + result: list[str] = [] + for type_name in sorted(referenced_types): + result.append(f'{type_name} = type opaque') + return result + + def _generate_func_decl(self, node: ast.FunctionDef) -> str | None: + """生成函数声明""" + func_name: str = node.name + is_export: bool = self._is_export_func(node) + if self.module_sha1 and not is_export: + func_name = f"{self.module_sha1}.{func_name}" + MultiRetTypes: list[ast.AST] = [] + if node.decorator_list: + for decorator in node.decorator_list: + # fail-fast:`@c.CReturn` 装饰器已移除,任何地方出现都必须报错, + # 不得静默忽略或改写;多返回值请改用 `-> tuple[ty1, ty2, ...]`。 + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): + if decorator.func.attr == 'CReturn': + raise SyntaxError( + f"函数 '{node.name}':`@c.CReturn` 已移除:请改用返回注解 " + f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" + ) + if node.returns: + if isinstance(node.returns, ast.Subscript) and isinstance(node.returns.value, ast.Name) and node.returns.value.id == 'tuple': + slice_node: ast.AST = node.returns.slice + if isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + MultiRetTypes.append(elt) + else: + MultiRetTypes.append(slice_node) + ret_type: str + if MultiRetTypes: + elem_types: list[str] = [self._get_type_str(rt, embedded=True) for rt in MultiRetTypes] + ret_type = '{ ' + ', '.join(elem_types) + ' }' + else: + ret_type = self._get_type_str(node.returns) + if not ret_type or ret_type == 'void': + ret_type = 'void' + elif ret_type == 'i8*': + if node.returns is None: + ret_type = 'void' + + params: list[str] = [] + for arg in node.args.args: + arg_type: str + if arg.annotation: + arg_type = self._get_type_str(arg.annotation) + else: + arg_type = 'i8*' + # C 语言中数组参数退化为指针:[N x elem_type] → elem_type* + arg_type = self._decay_array_to_ptr(arg_type) + if arg_type and arg_type != 'void': + params.append(arg_type) + + param_str: str = ', '.join(params) if params else '' + if node.args.vararg: + param_str = param_str + ', ...' if param_str else '...' + if func_name[0].isdigit(): + return f'declare {ret_type} @"{func_name}"({param_str})' + return f'declare {ret_type} @{func_name}({param_str})' + + def _is_export_func(self, node: ast.FunctionDef) -> bool: + """检查函数是否标记为 CExport""" + if not node.returns: + return False + return self._check_annotation_for_export(node.returns) + + def _check_annotation_for_export(self, annotation: ast.AST | None) -> bool: + """递归检查类型注解中是否包含 CExport 或 t.State""" + return bool(annotation) and (AnnotationContainsName(annotation, 'CExport') or AnnotationContainsName(annotation, 'State')) + + def _generate_global_decl(self, node: ast.AnnAssign) -> str | None: + """生成全局变量声明""" + if not isinstance(node.target, ast.Name): + return None + var_name: str = node.target.id + # CDefine 是编译期常量,不是变量:stub 里**不得**生成 external global。 + # `define X: T = V` 会展开为注解 `T | t.CDefine`,故判据必须能识别**联合形态**—— + # 原判据只认裸 `t.CDefine`(注解为单个 Attribute),联合注解会漏判 → + # stub 出现 `@X = external global i32` → 其他模块引用它时变成未定义符号(链接失败), + # 且会遮住跨模块 CDefine 常量回退(同名全局优先于常量)。与 _check_annotation_for_export + # 一样用递归判据。 + if AnnotationContainsName(node.annotation, 'CDefine'): + return None + if isinstance(node.annotation, ast.Attribute) and hasattr(node.annotation, 'attr') and node.annotation.attr == 'CTypedef': + return None + if isinstance(node.annotation, ast.Name) and node.annotation.id == 'CTypedef': + return None + if isinstance(node.annotation, ast.BinOp) and isinstance(node.annotation.left, ast.Attribute) and hasattr(node.annotation.left, 'attr') and node.annotation.left.attr == 'CTypedef': + return None + if isinstance(node.annotation, ast.List): + for elt in node.annotation.elts: + if isinstance(elt, ast.Attribute) and hasattr(elt, 'attr') and elt.attr in ('CTypedef', 'Callable'): + return None + if isinstance(elt, ast.Subscript) and isinstance(elt.value, ast.Attribute) and isinstance(elt.value.value, ast.Name) and elt.value.value.id == 't' and elt.value.attr == 'Callable': + return None + VarType: str = self._get_type_str(node.annotation) + if VarType.startswith('[0 x ') and node.value: + elem_type: str = VarType[5:-1] + actual_count: int = 0 + if isinstance(node.value, ast.List): + actual_count = len(node.value.elts) + elif isinstance(node.value, ast.Constant) and isinstance(node.value.value, str): + # 字符串字面量:与 HandlesBase.ParseArrayCount(:731-732) / HandlesAssign + # 全局路径同口径(len(str)+1,含结尾 NUL)。此前只修 List 值 → CArray + # 字符串全局的 stub 声明停留在 [0 x i8],与定义端 [N x i8] 长度不一致 + # (跨模块 type mismatch / llc 拒绝)。 + actual_count = len(node.value.value) + 1 + if actual_count > 0: + VarType = f'[{actual_count} x {elem_type}]' + return f'@{var_name} = external global {VarType}' + + def _generate_global_assign_decl(self, node: ast.Assign) -> str | None: + """从无类型标注赋值推断并生成全局变量声明""" + if not node.targets or not isinstance(node.targets[0], ast.Name): + return None + var_name: str = node.targets[0].id + VarType: str | None = self._infer_type(node.value) + if VarType: + return f'@{var_name} = external global {VarType}' + return None + + def _resolve_base_kind(self, base_name: str) -> str | None: + """解析基类名称的类型种类(支持别名) + + Returns: + 'enum' | 'union' | 'renum' | 'struct' | 'ctype_marker' | None + """ + # 1) 查 t 别名映射(from t import CEnum as en) + if base_name in self._t_alias_map: + t_cls: type = self._t_alias_map[base_name] + if issubclass(t_cls, (t.CEnum, t.REnum)): + return 'renum' if issubclass(t_cls, t.REnum) else 'enum' + if issubclass(t_cls, t.CUnion): + return 'union' + if issubclass(t_cls, t.CStruct): + return 'struct' + # CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记, + # 不应被编译为真实结构体,也不应被生成 stub 声明。 + if issubclass(t_cls, t.CType): + return 'ctype_marker' + + # 2) 直接查 t 模块属性 + t_cls: type | None = getattr(t, base_name, None) + if isinstance(t_cls, type) and issubclass(t_cls, t.CType): + if issubclass(t_cls, (t.CEnum, t.REnum)): + return 'renum' if issubclass(t_cls, t.REnum) else 'enum' + if issubclass(t_cls, t.CUnion): + return 'union' + if issubclass(t_cls, t.CStruct): + return 'struct' + # CType 及其子类是"类型强转"标记,不编译为结构体。 + return 'ctype_marker' + + return None + + def _is_marker_base(self, base_name: str) -> bool: + """检查是否为标记基类(Object/CVTable/Exception/CEnum/CUnion/CStruct/REnum 及其别名)""" + if base_name in ('Object', 'CVTable', 'Exception', 'CEnum', 'Enum', 'CStruct', 'CUnion', 'REnum'): + return True + if self._resolve_base_kind(base_name) is not None: + return True + return False + + def _generate_class_decl(self, node: ast.ClassDef) -> List[str]: + """生成结构体/类声明""" + decls: list[str] = [] + class_name: str = node.name + is_enum: bool = False + is_renum: bool = False + is_union: bool = False + is_exception: bool = False + if node.bases: + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute) and hasattr(base, 'attr'): + base_name = base.attr + elif isinstance(base, ast.Name) and hasattr(base, 'id'): + base_name = base.id + if base_name: + if base_name in self.exception_names or base_name == 'Exception': + is_exception = True + break + kind: str | None = self._resolve_base_kind(base_name) + if kind == 'enum': + is_enum = True + break + elif kind == 'union': + is_union = True + elif kind == 'renum': + is_renum = True + elif kind == 'ctype_marker': + # CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是 + # "类型强转"标记,不应被编译为真实结构体,也不生成 stub 声明。 + return decls + if is_exception: + return decls + if is_enum: + enum_values: dict[str, int] = {} + next_value: int = 0 + # 枚举存储宽度 = 最宽成员类型(与定义端 HandlesClassDef / 跨模块读取端 + # 同一规则源 EnumClassIntWidth;此处无 SymbolTable,走 t 类名 / builtins / + # 库 typedef 的表)。声明端的全局类型必须与定义端运行时常量同宽(ABI)。 + enum_width: int = EnumClassIntWidth(node.body) + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + var_name: str = item.target.id + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + enum_values[var_name] = item.value.value + else: + enum_values[var_name] = next_value + next_value += 1 + elif isinstance(item, ast.Assign) and len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): + var_name: str = item.targets[0].id + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + enum_values[var_name] = item.value.value + next_value = item.value.value + 1 + else: + enum_values[var_name] = next_value + next_value += 1 + for var_name, value in enum_values.items(): + decls.append(f'@__config_{class_name}_{var_name} = external global i{enum_width}') + return decls + if is_renum: + # REnum 布局: { i32 __tag, } + # 对齐 _EmitREnumLlvm (HandlesClassDef.py) 的 Phase 2 逻辑 + # Bug 修复:按每个位置的 max 字段尺寸构建布局,而非选总尺寸最大的变体。 + # 不同变体在同一位置可能有不同尺寸的字段(如 Concrete 位置3是 i32, + # 但 Var 位置3是 MetaType*),用最大变体的布局会导致指针截断。 + variant_fields_list: list[list[str]] = [] + for item in node.body: + if isinstance(item, ast.ClassDef): + variant_fields: list[str] = [] + for nested_item in item.body: + if isinstance(nested_item, ast.AnnAssign) and isinstance(nested_item.target, ast.Name): + ft: str = self._get_type_str(nested_item.annotation, embedded=True) + if ft: + variant_fields.append(ft) + variant_fields_list.append(variant_fields) + elif isinstance(item, ast.Assign): + # 显式值变体(无 payload) + variant_fields_list.append([]) + # 计算每个位置的最大尺寸 + max_field_count: int = 0 + for vf in variant_fields_list: + if len(vf) > max_field_count: + max_field_count = len(vf) + renum_member_types: list[str] = ['i32'] # __tag + for pos in range(max_field_count): + pos_max_size: int = 0 + for vf in variant_fields_list: + if pos < len(vf): + sz: int = self._llvm_type_size_str(vf[pos]) + if sz > pos_max_size: + pos_max_size = sz + # <=4 用 i32,>4 用 i64(8字节,可容纳指针) + if pos_max_size <= 4: + renum_member_types.append('i32') + else: + renum_member_types.append('i64') + if len(renum_member_types) < 2: + # 无类变体或所有变体无字段时,使用 i32 作为占位(对齐 _EmitREnumLlvm 默认行为) + renum_member_types = ['i32', 'i32'] + struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' + struct_decl: str = f'{struct_type_name} = type {{ {", ".join(renum_member_types)} }}' + decls.append(struct_decl) + return decls + member_types: list[str] = [] + has_bitfield: bool = False + total_bits: int = 0 + has_methods: bool = any(isinstance(item, ast.FunctionDef) for item in node.body) + is_cvtable: bool = False + is_cpython_object: bool = False + is_novtable: bool = False + if hasattr(node, 'decorator_list') and node.decorator_list: + for decorator in node.decorator_list: + if isinstance(decorator, ast.Attribute): + if getattr(decorator.value, 'id', None) == 't': + if decorator.attr == 'CVTable': + is_cvtable = True + elif decorator.attr == 'Object': + is_cpython_object = True + elif decorator.attr == 'NoVTable': + is_novtable = True + elif isinstance(decorator, ast.Name): + if decorator.id == 'CVTable': + is_cvtable = True + elif decorator.id == 'Object': + is_cpython_object = True + elif decorator.id == 'NoVTable': + is_novtable = True + if has_methods and not is_cpython_object: + is_cpython_object = True + has_parent_class: bool = False + parent_is_novtable: bool = False + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if base_name and not self._is_marker_base(base_name): + has_parent_class = True + # 检查父类是否为 @t.NoVTable(非多态继承) + _p_node: ast.ClassDef | None = None + for n in ast.iter_child_nodes(self._pyi_tree): + if isinstance(n, ast.ClassDef) and n.name == base_name: + _p_node = n + break + if _p_node is None: + _p_node = self._cross_module_class_defs.get(base_name) + if _p_node is not None and hasattr(_p_node, 'decorator_list') and _p_node.decorator_list: + for dec in _p_node.decorator_list: + if isinstance(dec, ast.Attribute) and getattr(dec.value, 'id', None) == 't' and dec.attr == 'NoVTable': + parent_is_novtable = True + elif isinstance(dec, ast.Name) and dec.id == 'NoVTable': + parent_is_novtable = True + break + # 仅当非 NoVTable 且父类非 NoVTable 时才因继承启用 CVTable + # (@t.NoVTable 标记的类及其子类不使用 vtable,字段展平嵌入) + if has_parent_class and not is_cvtable and not is_novtable and not parent_is_novtable: + is_cvtable = True + # 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable: + # 与 HandlesClassDef._resolve_class_flags / VPC _detect_vtable_status 的 + # `_has_any_func_cvtable` 分支逐条对齐(类级 NoVTable / 无真实父类)。 + # 必须在此同步:定义模块按 IsCVTable 在 struct 首部放 vptr,若 stub 不同步, + # 跨模块使用该类的 struct 布局会比定义模块少一个字段 ⇒ 字段整体错位。 + if not is_cvtable and ClassHasFuncLevelCVTable(node): + if is_novtable or not has_parent_class: + is_cvtable = True + base_has_vtable: bool = False + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if base_name and not self._is_marker_base(base_name): + # 先在当前 .pyi 中查找父类,找不到则查跨模块类定义映射 + _base_node: ast.ClassDef | None = None + for n in ast.iter_child_nodes(self._pyi_tree): + if isinstance(n, ast.ClassDef) and n.name == base_name: + _base_node = n + break + if _base_node is None: + _base_node = self._cross_module_class_defs.get(base_name) + if _base_node is not None and hasattr(_base_node, 'decorator_list') and _base_node.decorator_list: + for dec in _base_node.decorator_list: + if isinstance(dec, ast.Attribute) and getattr(dec.value, 'id', None) == 't' and dec.attr == 'CVTable': + base_has_vtable = True + elif isinstance(dec, ast.Name) and dec.id == 'CVTable': + base_has_vtable = True + if base_has_vtable: + break + # NoVTable 类不添加 vtable 指针(即使有方法和父类) + if has_methods and is_cvtable and not base_has_vtable and not is_novtable: + member_types.append('i8*') + # 多继承字段展平:按继承源去重(2026-08-18)——菱形继承不再重复展平 + # 祖先字段(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase, + # 而它们都继承 ClassGenBase → ClassGenBase 字段被展平 6 次,struct 膨胀 + # 且 this_adjust/字段 GEP 错位)。每类只展平一次,只取自有字段。 + # 与 HandlesClassDef._FlattenInherited 保持同一算法。 + _seen_cls: set[str] = set() + for base in node.bases: + base_name: str | None = None + type_args: list[str] = [] + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + slice_node: ast.AST = base.slice + if isinstance(slice_node, ast.Name): + type_args.append(slice_node.id) + elif isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + if isinstance(elt, ast.Name): + type_args.append(elt.id) + if base_name and not self._is_marker_base(base_name): + self._FlattenInheritedDecl(base_name, _seen_cls, member_types, + type_args or None) + # 自身字段收集(复用 _CollectOwnMemberTypes,与 _FlattenInheritedDecl 一致) + _own_types: list[str] + _own_types, has_bitfield, total_bits = self._CollectOwnMemberTypes(node) + member_types.extend(_own_types) + if has_bitfield: + if total_bits <= 8: + member_types.insert(0, 'i8') + elif total_bits <= 16: + member_types.insert(0, 'i16') + elif total_bits <= 32: + member_types.insert(0, 'i32') + else: + member_types.insert(0, 'i64') + struct_type_name: str = f'%"{self.module_sha1}.{class_name}"' if self.module_sha1 else f'%struct.{class_name}' + struct_decl: str = f'{struct_type_name} = type {{ {", ".join(member_types)} }}' + decls.append(struct_decl) + # 嵌套类 / 嵌套 struct:定义端以扁平名 `{外类}_{内类}` 发射(见 HandlesClassDef. + # _EmitNestedClassDefs),此处同步产出同名完整类型声明 —— 否则跨模块引用 + # `外类.内类` 只会得到一个 opaque 前向声明(指针可用、字段偏移全失效)。 + # deepcopy 后改名再递归:本函数所有名字都派生自 node.name,改名即全链路一致; + # 判断/枚举/联合等分支在递归时会各自按内层类自身的基类重新分派。 + for _nested_item in getattr(node, 'body', None) or []: + if not isinstance(_nested_item, ast.ClassDef): + continue + _nested_inner: str | None = getattr(_nested_item, 'name', None) + if not _nested_inner: + continue + _nested_node: ast.ClassDef = copy.deepcopy(_nested_item) + _nested_node.name = f'{class_name}_{_nested_inner}' + decls.extend(self._generate_class_decl(_nested_node)) + if is_cpython_object or is_cvtable: + new_func_name: str = f'{class_name}.__before_init__' + if self.module_sha1: + new_func_name = f"{self.module_sha1}.{new_func_name}" + new_func_decl: str + if new_func_name[0].isdigit(): + new_func_decl = f'declare void @"{new_func_name}"({struct_type_name}*)' + else: + new_func_decl = f'declare void @{new_func_name}({struct_type_name}*)' + decls.append(new_func_decl) + # PEP 695 泛型类跳过方法声明生成:方法参数类型 T 会被解析为 opaque struct, + # LLVM 报 "invalid type for function argument"。字段(AnnAssign)仍正常生成, + # 确保子类(如 Value(GSListNode[Value]))能从 stub 中读取继承字段(如 Next)。 + IsGenericClass: bool = hasattr(node, 'type_params') and bool(node.type_params) + if not IsGenericClass: + for item in node.body: + if isinstance(item, ast.FunctionDef): + if hasattr(item, 'type_params') and item.type_params: + continue + method_name: str = f'{class_name}.{item.name}' + if self.module_sha1: + method_name = f"{self.module_sha1}.{method_name}" + ret_type: str = self._get_type_str(item.returns) if item.returns else 'void' + if not ret_type: + ret_type = 'void' + params: list[str] = [] + for arg_idx, arg in enumerate(item.args.args): + arg_type: str + if arg.annotation: + arg_type = self._get_type_str(arg.annotation) + elif arg_idx == 0 and arg.arg == 'self': + arg_type = f'{struct_type_name}*' + else: + arg_type = 'i8*' + # C 语言中数组参数退化为指针:[N x elem_type] → elem_type* + arg_type = self._decay_array_to_ptr(arg_type) + params.append(arg_type) + param_str: str = ', '.join(params) if params else '' + if method_name[0].isdigit(): + decls.append(f'declare {ret_type} @"{method_name}"({param_str})') + else: + decls.append(f'declare {ret_type} @{method_name}({param_str})') + # 为继承但未覆写的方法生成包装声明 + # 这些声明让跨模块调用能正确解析子类包装函数的签名, + # 避免 stub 缺失导致默认 i32 返回类型 → 64 位指针截断 + decls.extend(self._generate_inherited_method_decls(node, class_name, struct_type_name)) + return decls + + def _generate_inherited_method_decls(self, node: ast.ClassDef, child_class_name: str, child_struct_type: str) -> list[str]: + """为继承但未覆写的方法生成包装声明 + + 在 Phase 1 stub 中声明子类继承方法的签名(与父类相同,但 self 参数为子类类型)。 + 这样测试模块翻译时能从 temp stub 中获取正确的返回类型, + 避免 Phase 2 includes 翻译滞后导致跨模块调用使用默认 i32 返回类型。 + """ + decls: list[str] = [] + # 收集子类自身定义的方法名(这些不需要生成包装,子类有自己的实现) + seen_method_names: set[str] = set() + for item in node.body: + if isinstance(item, ast.FunctionDef): + seen_method_names.add(item.name) + + # 沿继承链收集父类方法,近祖先优先 + for base in node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if not base_name or self._is_marker_base(base_name): + continue + self._CollectInheritedWrapperDecls( + base_name, child_class_name, child_struct_type, + seen_method_names, decls) + return decls + + def _CollectInheritedWrapperDecls( + self, parent_name: str, child_class_name: str, child_struct_type: str, + seen_method_names: set[str], decls: list[str]) -> None: + """递归收集父类及其祖先的方法,为未覆写的方法生成包装声明""" + # 查找父类节点:先在当前 .pyi 中查找,找不到则查跨模块类定义映射 + parent_node: ast.ClassDef | None = None + for n in ast.iter_child_nodes(self._pyi_tree): + if isinstance(n, ast.ClassDef) and n.name == parent_name: + parent_node = n + break + if parent_node is None: + parent_node = self._cross_module_class_defs.get(parent_name) + if parent_node is None: + return + + # 为父类的每个方法生成包装声明 + for item in parent_node.body: + if isinstance(item, ast.FunctionDef): + if hasattr(item, 'type_params') and item.type_params: + continue + method_name: str = item.name + # __before_init__ 只在自身类生成,不生成继承包装 + if method_name == '__before_init__': + continue + # 跳过子类已覆写的方法,以及已处理的祖先方法 + if method_name in seen_method_names: + continue + seen_method_names.add(method_name) + + # 构建包装方法名:{module_sha1}.{child_class}.{method_name} + wrapper_name: str = f'{child_class_name}.{method_name}' + if self.module_sha1: + wrapper_name = f"{self.module_sha1}.{wrapper_name}" + + # 解析返回类型(与父类方法相同) + ret_type: str = self._get_type_str(item.returns) if item.returns else 'void' + if not ret_type: + ret_type = 'void' + elif ret_type == 'i8*': + if item.returns is None: + ret_type = 'void' + + # 构建参数列表:第一个参数(self)替换为子类类型,其余与父类相同 + params: list[str] = [] + for arg_idx, arg in enumerate(item.args.args): + arg_type: str + if arg_idx == 0 and arg.arg == 'self': + arg_type = f'{child_struct_type}*' + else: + if arg.annotation: + arg_type = self._get_type_str(arg.annotation) + else: + arg_type = 'i8*' + arg_type = self._decay_array_to_ptr(arg_type) + params.append(arg_type) + + param_str: str = ', '.join(params) if params else '' + if wrapper_name[0].isdigit(): + decls.append(f'declare {ret_type} @"{wrapper_name}"({param_str})') + else: + decls.append(f'declare {ret_type} @{wrapper_name}({param_str})') + + # 递归处理祖先类 + for base in parent_node.bases: + base_name: str | None = None + if isinstance(base, ast.Attribute): + base_name = base.attr + elif isinstance(base, ast.Name): + base_name = base.id + elif isinstance(base, ast.Subscript): + if isinstance(base.value, ast.Attribute): + base_name = base.value.attr + elif isinstance(base.value, ast.Name): + base_name = base.value.id + if base_name and not self._is_marker_base(base_name): + self._CollectInheritedWrapperDecls( + base_name, child_class_name, child_struct_type, + seen_method_names, decls) + def _CollectOwnMemberTypes(self, node: ast.ClassDef) -> tuple[list[str], bool, int]: """收集类自身声明的字段类型(不含继承字段)。 @@ -1021,592 +1021,592 @@ class DeclarationGenerator: member_types.append('i8*') member_types.extend(own) - def _get_inherited_members(self, base_name: str) -> tuple[list[str], set[str]]: - member_types: list[str] = [] - seen_names: set[str] = set() - if not hasattr(self, '_pyi_tree') or self._pyi_tree is None: - return member_types, seen_names - base_node: ast.ClassDef | None = None - for node in ast.iter_child_nodes(self._pyi_tree): - if isinstance(node, ast.ClassDef) and node.name == base_name: - base_node = node - break - if base_node is None: - # 跨模块父类查找:当前 .pyi 中没有父类定义时,从跨模块类定义映射中查找 - # (如 exprs.py 的 Name(AST),AST 定义在 base.py) - base_node = self._cross_module_class_defs.get(base_name) - if base_node is None: - return member_types, seen_names - base_decls: list[str] = self._generate_class_decl(base_node) - for decl in base_decls: - if '= type {' in decl: - type_body: str = decl.split('= type {')[1].rstrip('}').strip() - if type_body: - for t in type_body.split(','): - t = t.strip() - if t: - member_types.append(t) - break - for item in base_node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - seen_names.add(item.target.id) - elif isinstance(item, ast.Assign) and len(item.targets) == 1: - if isinstance(item.targets[0], ast.Attribute) and isinstance(item.targets[0].value, ast.Name) and item.targets[0].value.id == 'self': - seen_names.add(item.targets[0].attr) - for item in base_node.body: - if isinstance(item, ast.FunctionDef) and item.name == '__init__': - for stmt in item.body: - if isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Attribute) and isinstance(stmt.target.value, ast.Name) and stmt.target.value.id == 'self': - seen_names.add(stmt.target.attr) - elif isinstance(stmt, ast.Assign) and len(stmt.targets) == 1 and isinstance(stmt.targets[0], ast.Attribute) and isinstance(stmt.targets[0].value, ast.Name) and stmt.targets[0].value.id == 'self': - seen_names.add(stmt.targets[0].attr) - return member_types, seen_names - - def _specialize_member_types(self, member_types: list[str], type_args: list[str]) -> list[str]: - """将基类成员类型中的泛型参数 T 替换为具体类型参数 - - 处理泛型基类继承(如 GSListNode[BasicBlock])时,基类 _get_inherited_members - 返回的成员类型中会包含未特化的 T 引用(形式为 %"SHA1.T"*)。 - - 替换策略:使用 i8*(不透明指针)代替 T*,而非具体类型指针。 - 原因:跨模块场景中,具体类型指针的 SHA1 前缀可能与导入模块不一致, - 导致 TransPyC 类型转换失败。i8* 作为通用指针类型,可安全赋值给 - 任何 Class | t.CPtr 注解的变量,运行时行为与 T* 相同(都是指针)。 - - Args: - member_types: 基类成员类型字符串列表 - type_args: 类型参数名列表(如 ['BasicBlock']) - - Returns: - 特化后的成员类型字符串列表 - """ - if not type_args: - return member_types - result: list[str] = [] - for mt in member_types: - new_mt: str = mt - # 将 %"SHA1.T"* 替换为 i8*(不透明指针) - new_mt = _RE_T_TYPE_PTR.sub('i8*', new_mt) - # 将 %"SHA1.T"(非指针,如 embedded struct)替换为 i8* - new_mt = _RE_T_TYPE.sub('i8*', new_mt) - result.append(new_mt) - return result - - def _llvm_type_size_str(self, type_str: str) -> int: - """估算 LLVM 类型字符串的大小(字节),用于 REnum 变体大小比较 - - 对齐 _EmitREnumLlvm 中的大小计算逻辑: - - i1=1, i8=1, i16=2, i32=4, i64=8, float=4, double=8, ptr=8 - - [N x T] = N * size(T) - - 其他结构体类型按指针大小 8 估算(保守值) - """ - s: str = type_str.strip() - if not s: - return 0 - # 指针类型 - if s.endswith('*'): - return 8 - # 数组类型 [N x T] - # 注:此处原写作 `self._ARR_TYPE_RE`(少一个 A)并用 `hasattr` 守卫 —— 该属性 - # **全仓从未定义** ⇒ 守卫恒假 ⇒ 本分支恒不执行:数组字段一律落到下方"未知类型 → 8" - # 的保守值,与 docstring 声明的"_EmitREnumLlvm 尺寸口径对齐"不符(静默降级)。 - # 现改用本类已定义的 `_ARRAY_TYPE_RE`(见文件顶部),并去掉守卫: - # 属性若再丢失应当响亮失败,而不是静默退回 8。 - m = self._ARRAY_TYPE_RE.match(s) - if m: - try: - n: int = int(m.group(1)) - return n * self._llvm_type_size_str(m.group(2)) - except ValueError: - return 8 - # 基本整数/浮点类型 - size_map: dict[str, int] = { - 'i1': 1, 'i8': 1, 'i16': 2, 'i32': 4, 'i64': 8, - 'float': 4, 'double': 8, 'half': 2, 'fp128': 16, - 'void': 0, - } - if s in size_map: - return size_map[s] - # %{...} 结构体类型 - 保守估算为指针大小 - if s.startswith('%') or s.startswith('{'): - return 8 - # 其他未知类型保守估算 - return 8 - - @staticmethod - def _ptr_level_of(type_str: str) -> int: - """统计类型字符串尾部的指针层数('i8***' → 3,'i32' → 0)""" - return len(type_str) - len(type_str.rstrip('*')) - - def _merge_cptr_subscript(self, base_type: str, cptr_sub_type: str) -> str: - """base | t.CPtr[T] → Ptr^N(base_type),N = t.CPtr[T] 段自身指针层数。 - - 与定义侧 TPV TypeGenResolve.vp:2610-2639 同口径:数 CPtr 侧已解析类型的 - 外层 Ptr 嵌套层 N(TypeGenResolve.vp:2616-2632),再给另一侧**纯外包** - N 层 Ptr(TypeGenResolve.vp:2633-2639,对 struct 基类型亦不吸收)。 - 与 LLVMTypeMapper._resolve_bitor 的 IsCPtrSubscriptNode 分支(_add_pointer_levels - 逐层追加)同口径,对同一批形态两处结果一致。 - 注:裸 | t.CPtr(非下标标记)仍走上方的 %"..."* 吸收分支,与本节无关。 - """ - return base_type + '*' * self._ptr_level_of(cptr_sub_type) - - def _ptr_subscript_type_str(self, annotation: ast.Subscript) -> str: - """指针下标(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],判据见 - SymbolUtils.IsPtrSubscriptNode)→ LLVM 类型串。 - - 与定义端同源:SymbolUtils.DesugarPtrSubscriptToUnion 把三者折叠为等价 - 联合写法 `T | t.CPtr` 后交 HandlesTypeMerge.MergeTypes,指针层数由 - _MergeAllComponentTypes 的"struct 基类型吸收一层 CPtr"决定 —— 裸类引用 - 的非 embedded 形式本身即指针('%"sha1.X"*'),再包一层会被吸收 → 结果 - 就是该指针本身;其余类型(标量 / 已是指针的 typedef / 泛型容器特化 / - 联合写法 / 嵌套指针下标)逐层外包。rawptr[...] 与 t.CPtr[...] 共用本条 - 规则(此前 t.CPtr[list[X]] 在此得 list[X]**、rawptr[list[X]] 得 - list[X]*,同一指针语义两种结果)。 - """ - inner: ast.AST = annotation.slice - inner_type: str = self._get_type_str(inner) - if self._is_bare_class_ref_annotation(inner, inner_type): - return inner_type - if inner_type in ('', 'void', 'void*'): - return 'i8*' - return f'{inner_type}*' - - def _is_bare_class_ref_annotation(self, inner: ast.AST, inner_type: str) -> bool: - """节点是否为"裸类引用"(本身即指针 → 吸收一层 t.CPtr)。 - - 与 _get_type_str 的既有约定一致:类引用的**非** embedded 形式本身就是 - 指针('%"sha1.X"*'),所以 `AST | t.CPtr` 合并后仍是该指针(定义端 - _MergeAllComponentTypes 的 struct 吸收)。联合写法(ast.BinOp)与指针 - 下标(rawptr[...] / t.CPtr[...])不是裸类引用——它们已自带指针层数, - 须逐层外包(rawptr[Node] | t.CPtr → Node**、ast.AST | t.CPtr | t.CPtr - → AST**,与定义端整体拉平后只吸收一次 CPtr 一致)。 - - **例外:纯装饰器标记联合**。`t.CExtern | X` / `t.CExport | X` / `t.State | X` - (vpi 记录的模块级全局就是这种写法,见 lib.Phase.Phase1.__init__.vpi 的 - `_mbuddy: t.CExtern | memhub.MemBuddy | t.CPtr`)里标记侧展开为**空串** - (CTypeRegistry.NameToLLVM('CExtern') == ''),该联合语义上仍是 X 这一个 - 裸类引用,故 `t.CExtern | X | t.CPtr` 必须与 `X | t.CPtr` 同判(吸收)—— - 对齐 TPV TypeGenResolve 的第 3 条语义「ActualType | DecoratorMarker → - ActualType」。漏此判据时联合被当成"已自带指针层数的左值"而多外包一层: - 声明端 `%"sha1.X"**` 与定义端 `%"sha1.X"*` 差一层,消费模块里 - `self.__mbuddy__ = _mbuddy` 之类的赋值即报"层数不同"fail-fast。 - """ - if not inner_type: - return False - if isinstance(inner, ast.BinOp): - if not isinstance(inner.op, ast.BitOr): - return False - LeftType: str = self._get_type_str(inner.left) - RightType: str = self._get_type_str(inner.right) - # 恰一侧是纯装饰器标记(展开为空/void)⇒ 类型全部来自另一侧,按另一侧判 - if LeftType in ('', 'void') and RightType not in ('', 'void'): - return self._is_bare_class_ref_annotation(inner.right, RightType) - if RightType in ('', 'void') and LeftType not in ('', 'void'): - return self._is_bare_class_ref_annotation(inner.left, LeftType) - return False - if isinstance(inner, ast.Subscript): - return not (IsRawPtrSubscriptNode(inner) or IsCPtrSubscriptNode(inner)) - return inner_type.startswith('%') and inner_type.endswith('*') - - def _get_type_str(self, annotation: ast.AST | None, embedded: bool = False) -> str: - if annotation is None: - return 'i8*' - - non_struct_types: set[str] = {'list', 'dict', 'tuple', 'set', 'array', 'CArray', 'rawarray'} - - if isinstance(annotation, ast.Name): - if annotation.id == 'None': - return 'void' - if annotation.id in ('str', 'bytes'): - return 'i8*' - llvm_type: str | None = CTypeRegistry.NameToLLVM(annotation.id) - if llvm_type is not None: - return llvm_type - resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(annotation.id) - if resolved is not None: - ctype_cls: type - ptr_level: int - ctype_cls, ptr_level = resolved - base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if ptr_level > 0: - if base == 'void': - return 'i8*' - if '*' in base: - return base - return f'{base}*' - return base - if annotation.id in self.struct_names: - sha1: str | None = self.struct_sha1_map.get(annotation.id, self.module_sha1) - sname: str = f'%"{sha1}.{annotation.id}"' if sha1 else f'%struct.{annotation.id}' - if embedded: - return sname - else: - return f'{sname}*' - if annotation.id in non_struct_types: - return 'i32' - if annotation.id in self.enum_names: - return 'i32' - if annotation.id in self.typedef_map: - resolved_str: str = self._get_type_str(self.typedef_map[annotation.id], embedded=embedded) - return resolved_str - # 检查 t 类型别名(from t import CInt as myint) - if annotation.id in self._t_alias_map: - t_cls: type = self._t_alias_map[annotation.id] - llvm: str | None = CTypeRegistry.CTypeToLLVM(t_cls) - if llvm: - return llvm - # builtins 隐式 CTypedef(int64/uint64/size_t 等:builtins.vp 声明、 - # 用户文件无需 import 即用):常规路径 miss 后若落下方 fallback 会按 - # "本模块结构体"建 %"{mod}.int64" opaque(ClassGenGeneric._clone_annotation - # 的 "i64 != %int64*" 根因)。按 builtins.vp 权威定义展开。 - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _DG_TAR - _dg_bv: str | None = _DG_TAR.builtins_typedef_value(annotation.id) - if _dg_bv: - if _dg_bv.startswith('t.'): - _dg_llvm: str | None = CTypeRegistry.NameToLLVM(_dg_bv[2:]) - if _dg_llvm: - return _dg_llvm - _dg_node: ast.AST | None = None - try: - _dg_node = ast.parse(_dg_bv, '', mode='eval').body - except Exception: - _dg_node = None - if _dg_node is not None: - _dg_ret: str = self._get_type_str(_dg_node, embedded=embedded) - if _dg_ret and _dg_ret != 'i32': - return _dg_ret - sha1: str | None = self.struct_sha1_map.get(annotation.id, self.module_sha1) - sname: str = f'%"{sha1}.{annotation.id}"' if sha1 else f'%struct.{annotation.id}' - return f'{sname}*' - elif isinstance(annotation, ast.Attribute): - attr_name: str = annotation.attr if hasattr(annotation, 'attr') else '' - # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):按 t.CPtr - # 同形的「一层指针 = i8*」参与联合合并(`T | t.CRef` → T*),保证跨模块 - # .vpi 声明端类型与定义端 _ctype_to_llvm 一致。 - if attr_name in ('CRef', 'CTempRef'): - return 'i8*' - module_name: str = '' - if hasattr(annotation, 'value'): - if isinstance(annotation.value, ast.Name): - module_name = annotation.value.id - elif isinstance(annotation.value, ast.Attribute) and hasattr(annotation.value, 'attr'): - module_name = annotation.value.attr - # 嵌套类 / 嵌套 struct:限定名 `外类.内类`(module_name 是**类名**而非模块名)。 - # 定义端以扁平名 `{外类}_{内类}` 发射(见 HandlesClassDef._EmitNestedClassDefs), - # 故此处按扁平名拼类型;命中与否由 struct_names 里是否存在该扁平名裁决 - # (不存在即维持下方既有路径,不改变任何既有类型名)。 - if module_name and module_name != 't' and module_name != 'c': - NestedName: str = f'{module_name}_{attr_name}' - if NestedName in self.struct_names: - NestedSha1: str | None = self.struct_sha1_map.get(NestedName, self.module_sha1) - NestedStr: str = f'%"{NestedSha1}.{NestedName}"' if NestedSha1 else f'%struct.{NestedName}' - return NestedStr if embedded else f'{NestedStr}*' - if (module_name == 't' and attr_name == 'State') or attr_name == 'State': - return '' - if (module_name == 't' and attr_name == 'Callable') or attr_name == 'Callable': - return 'i8*' - llvm_type: str | None = CTypeRegistry.NameToLLVM(attr_name) - if llvm_type is not None: - return llvm_type - resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(attr_name) - if resolved is not None: - ctype_cls: type - ptr_level: int - ctype_cls, ptr_level = resolved - base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if ptr_level > 0: - if base == 'void': - return 'i8*' - if '*' in base: - return base - return f'{base}*' - return base - if attr_name in self.typedef_map: - return self._get_type_str(self.typedef_map[attr_name], embedded=embedded) - if attr_name in self.enum_names: - return 'i32' - if attr_name in self.struct_names: - sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) - sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' - if embedded: - return sname - else: - return f'{sname}*' - if attr_name and attr_name[0].isupper() and attr_name not in non_struct_types: - sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) - sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' - return f'{sname}*' - if attr_name and attr_name not in non_struct_types: - sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) - sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' - return f'{sname}*' - return 'i32' - elif isinstance(annotation, ast.BinOp): - left_type: str = self._get_type_str(annotation.left, embedded=embedded) - right_type: str = self._get_type_str(annotation.right, embedded=embedded) - if left_type in ('', 'void'): - return right_type if right_type not in ('', 'void') else (left_type or right_type) - if right_type in ('', 'void'): - return left_type - # t.CPtr (i8*) 与 left 合并:对齐 Phase 2 _MergeAllComponentTypes 的 - # "first CPtr absorption" 语义:struct | t.CPtr → struct*(吸收,struct 变量默认是指针)。 - # 但对于本身已是指针的类型(如 bytes=str=i8*),bytes | t.CPtr → i8**(不吸收)。 - # 例外:LLVM 聚合数组([N x T],字符串以 '[' 开头)不可再外包指针层 → i8*。 - # 注:具名泛型特化(%"mod.list[X]"*)**不**走此回退——stub 已由 - # _collect_undefined_type_decls 为其补 opaque 声明,保留名字才能与 define 端 - # 的 %"mod.list[ast.ASTptr]"* 逐字一致(原 `'[' in left_type` 会把 - # list[X] | t.CPtr 一并退化成 i8*,与定义端 %"mod.list[X]"* 不一致)。 - if right_type == 'i8*': - if left_type[:1] == '[': - return 'i8*' - if left_type in ('', 'void'): - return 'i8*' - # 吸收仅对"裸类引用"成立(其非 embedded 形式本身即指针): - # ast.AST | t.CPtr → %"sha1.AST"*。已自带指针层数的左值 - # (rawptr[...] / t.CPtr[...] 下标 / 联合写法)须逐层外包: - # rawptr[Node] | t.CPtr → Node**、ast.AST | t.CPtr | t.CPtr → AST** - # ——与定义端 _CollectAllOperandTypes 整体拉平、只吸收一次 CPtr - # (_MergeAllComponentTypes 的 base_ptr_count>0 分支)一致。 - if self._is_bare_class_ref_annotation(annotation.left, left_type): - return left_type - if '*' in left_type: - return f'{left_type}*' - return f'{left_type}*' - # X | t.CPtr[T](T 为下标段,非裸标记):CPtr 侧贡献自身指针层数 N, - # 另一侧外包 N 层(Ptr^N(X));左右两侧写法对称(定义侧 TPV - # TypeGenResolve.vp:2604-2609 亦两侧皆判)。必须先于下方 - # "'*' in left_type → 直接返回左值":否则左值本身已是指针 - # (BYTEPTR⇒i8*)时会吞掉右段层数(BYTEPTR | t.CPtr[t.CPtr] 改前得 i8*)。 - # LLVM 聚合([N x T] / { ... })不可再外包指针层,保持既有行为(只报不改)。 - if IsCPtrSubscriptNode(annotation.right) and left_type[:1] not in ('[', '{'): - return self._merge_cptr_subscript(left_type, right_type) - if IsCPtrSubscriptNode(annotation.left) and right_type[:1] not in ('[', '{'): - return self._merge_cptr_subscript(right_type, left_type) - # t.CPtr 裸标记在左侧(t.CPtr | X):与右侧写法(X | t.CPtr,见上) - # 完全对称。缺此分支时左值 i8* 会吞掉右侧的指针层数: - # t.CPtr | rawptr[Node] 声明端曾得 i8*,而定义端为 %"sha1.Node"**(差一层)。 - # 仅对裸标记 t.CPtr 生效(left 为 t.CPtr[T] 下标走上一条分支; - # str/bytes 等其它 i8* 左值不受影响)。 - if IsCPtrNode(annotation.left) and right_type[:1] not in ('[', '{'): - if self._is_bare_class_ref_annotation(annotation.right, right_type): - return right_type - return f'{right_type}*' - if '*' in left_type: - return left_type - if '*' in right_type: - return right_type - return left_type - elif isinstance(annotation, ast.Subscript): - # 指针下标(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T] / 导入别名 X[T],以及带 - # 模块前缀书写的 X.CPtr[T])≡ 源码联合写法 `T | t.CPtr`:与定义端 - # **同一判据**(SymbolUtils.IsPtrSubscriptNode,别名经本模块的 - # _t_c_imported_names 表解析 X→CPtr)+ 同一层数规则 - # (_ptr_subscript_type_str:裸类引用吸收一层、其余逐层外包),内层 T - # 照常解析(跨模块类由 struct_sha1_map 补 SHA1 前缀)。此前 rawptr[T] - # 在此退化成 i8*,t.CPtr[T] 走的是"embedded + 追加一层"的独立分支: - # 非同一规则,且对泛型容器特化多出一层(t.CPtr[list[X]] 得 list[X]**, - # 而 rawptr[list[X]] 得 list[X]*)。 - ValueIsCPtr: bool = ( - (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CPtr') - or (isinstance(annotation.value, ast.Name) and annotation.value.id == 'CPtr') - ) - if (IsPtrSubscriptNode(annotation, TCAliasMap=self._t_c_imported_names) - or (ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)))): - return self._ptr_subscript_type_str(annotation) - # 内建裸名函数指针构造子 callable[...] / Callable[...]:与 Attribute 分支的 - # t.Callable[...] 逐点等价(同为 i8*)。缺此分支时落到下方 Name 泛型特化 / - # 兜底返回 'i32'(现场 Repl.vp:307 `ReplFragFn: callable[[rawptr], int]` - # 声明成 i32 全局 ⇒ 赋 i8* 触发「指针隐式转整数」)。 - if isinstance(annotation.value, ast.Name) and (annotation.value.id == 'callable' or annotation.value.id == 'Callable'): - return 'i8*' - base: str = self._get_type_str(annotation.value) - if base == 'i8*' and isinstance(annotation.slice, ast.Constant): - return f'[{self._get_const_int(annotation.slice)} x i8]' - if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): - return f'[{annotation.slice.value} x {base}]' - # 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] / array[elem_type, count] - # 注解 → [count x elem_type] - # 支持 t.CArray (ast.Attribute)、CArray/rawarray/array (ast.Name) 三种写法 - # (array 为本轮新增的内建软关键字,与 t.CArray 逐点等价) - ValueIsCArray: bool = ( - (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray') - or (isinstance(annotation.value, ast.Name) and annotation.value.id in ('CArray', 'rawarray', 'array')) - ) - if ValueIsCArray: - SliceNode: ast.AST = annotation.slice - if isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2: - ElemType: str = self._get_type_str(SliceNode.elts[0], embedded=True) - CountVal: int = self._get_const_int(SliceNode.elts[1]) - if CountVal > 0: - return f'[{CountVal} x {ElemType}]' - # count 为 None 或 0 → 零长度数组(外部符号) - return f'[0 x {ElemType}]' - elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)): - # 单参数 t.CArray[elem_type]:与 t.CArray[elem_type, None] 语义统一为 - # "长度自动推导数组"。此处无初始化字面量可推导 → 与 ,None 分支同用 - # [0 x elem] 占位,真实长度由 _generate_global_decl 按初始化字面量 - # (List 长度 / str 长度+1) 修正。 - # 参数位置经 _decay_array_to_ptr(:23-28) 退化为 elem*,与旧指针行为一致。 - ElemType: str = self._get_type_str(SliceNode, embedded=True) - return f'[0 x {ElemType}]' - if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple': - slice_node: ast.AST = annotation.slice - elem_types: list[str] - if isinstance(slice_node, ast.Tuple): - elem_types = [self._get_type_str(e, embedded=True) for e in slice_node.elts] - else: - elem_types = [self._get_type_str(slice_node, embedded=True)] - return '{ ' + ', '.join(elem_types) + ' }' - # 处理泛型类类型注解(如 list[str] / list[rawptr[ast.AST]] / - # 用户泛型类 Box[Node]) - # 实参串走 SymbolUtils.ResolveGenericTypeArgStr(与定义端 - # _mangle_generic_class_name 同一规则源):此前只认 Name / Name-Tuple - # 实参,Attribute(ast.AST)/ Subscript(rawptr[...])/ BinOp - # (X | t.CPtr)/ 混合 Tuple 一律退回未特化的 %"...list"*, - # 与 define 端发射的 %".list[ast.ASTptr]" 不一致 - # (Undefined method / 签名错位)。 - # 内建容器(non_struct_types)之外,**用户泛型类**同样特化:判据 - # _generic_class_names(ClassDef.type_params,与定义端 - # HandlesClassDef._generic_class_templates 同源),此前只有内建容器名 - # 会特化 → Box[Node] 字段在 stub 端是 %".Box"*、定义端是 - # %".Box[<已解析实参>]"*(Holder 字段布局两侧不一致)。 - if isinstance(annotation.value, ast.Name): - gc_name: str = annotation.value.id - if (gc_name in non_struct_types or gc_name in self._generic_class_names) \ - and gc_name != 'tuple': - gc_slice: ast.AST = annotation.slice - # 多参数泛型(dict[K, V]):'][' 分隔,与 - # _mangle_generic_class_name 一致 - gc_nodes: list[ast.AST] = ( - list(gc_slice.elts) if isinstance(gc_slice, ast.Tuple) else [gc_slice]) - gc_mangled: list[str] = [ - ResolveGenericTypeArgStr( - gc_node, - TCAliasMap=self._t_c_imported_names, - StructSha1Map=self.struct_sha1_map) - for gc_node in gc_nodes] - gc_spec: str = gc_name + '[' + ']['.join(gc_mangled) + ']' - # 特化名前缀取**泛型模板类定义模块**的 SHA1(list → includes/_list.vp、 - # Box → 定义 Box 的模块,查 struct_sha1_map 即"类名 → 定义模块"表), - # 与定义端 HandlesClassDef._specialize_generic_class 的 - # Gen.module_sha1 = template['sha1']、调用点 - # Gen._struct_sha1_map[spec_name] 同一条规则源。 - # 此前用 self.module_sha1(使用方模块):同一特化在 declare(使用方前缀) - # 与 define/call(模板模块前缀)两侧不一致 → 跨模块 undefined symbol。 - gc_sha1: str | None = self.struct_sha1_map.get(gc_name, self.module_sha1) - gc_sname: str = f'%"{gc_sha1}.{gc_spec}"' if gc_sha1 else f'%struct.{gc_spec}' - # 一律以指针引用(不区分 embedded):定义端对嵌入式泛型容器 - # (字段 / t.CArray 元素)用不透明指针(实测字段 i8*,见 - # FieldForms/FieldBox/FieldOrder),嵌入 unsized opaque - # 结构体值会让字段/元素偏移错位;非 embedded(参数/返回) - # 亦与定义端同为指针 - return f'{gc_sname}*' - return f'{base}' - elif isinstance(annotation, ast.Constant): - if annotation.value is None: - return 'void' - if isinstance(annotation.value, int): - return 'i32' - if isinstance(annotation.value, str): - type_name: str = annotation.value - if type_name in self.enum_names: - return 'i32' - if type_name in self.struct_names: - sha1: str | None = self.struct_sha1_map.get(type_name, self.module_sha1) - sname: str = f'%"{sha1}.{type_name}"' if sha1 else f'%struct.{type_name}' - if embedded: - return sname - else: - return f'{sname}*' - llvm_type: str | None = CTypeRegistry.NameToLLVM(type_name) - if llvm_type is not None: - return llvm_type - resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(type_name) - if resolved is not None: - ctype_cls: type - ptr_level: int - ctype_cls, ptr_level = resolved - base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if ptr_level > 0: - if base == 'void': - return 'i8*' - if '*' in base: - return base - return f'{base}*' - return base - sha1: str | None = self.struct_sha1_map.get(type_name, self.module_sha1) - sname: str = f'%"{sha1}.{type_name}"' if sha1 else f'%struct.{type_name}' - return f'{sname}*' - return 'i8*' - elif isinstance(annotation, ast.Call): - if isinstance(annotation.func, ast.Name) and annotation.func.id == 'callable': - return 'i8*' - if isinstance(annotation.func, ast.Attribute) and annotation.func.attr == 'Callable': - return 'i8*' - return 'i32' - - def _infer_type(self, value: ast.AST) -> str: - """从值推断类型""" - if isinstance(value, ast.Constant): - if isinstance(value.value, int): - return 'i32' - elif isinstance(value.value, float): - return 'double' - elif isinstance(value.value, str): - return 'i8*' - elif isinstance(value.value, bool): - return 'i8' - elif isinstance(value, ast.List): - return 'i8*' - elif isinstance(value, ast.Dict): - return 'i8*' - elif isinstance(value, ast.Name): - return 'i32' - elif isinstance(value, ast.BinOp): - return self._infer_type(value.left) - elif isinstance(value, ast.Call): - return 'i8*' - return 'i32' - - def _get_bitfield_width(self, annotation: ast.AST) -> int | None: - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - right_width: int | None = self._get_bitfield_width(annotation.right) - if right_width is not None: - return right_width - return self._get_bitfield_width(annotation.left) - if isinstance(annotation, ast.Call): - if isinstance(annotation.func, ast.Attribute) and annotation.func.attr == 'Bit': - if annotation.args: - arg: ast.AST = annotation.args[0] - if isinstance(arg, ast.Constant) and isinstance(arg.value, int): - return arg.value - if isinstance(annotation, ast.Subscript): - if isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'Bit': - if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): - return annotation.slice.value - return None - - def _get_const_int(self, node: ast.AST) -> int: - """获取常量整数值,支持符号常量和简单表达式""" - if isinstance(node, ast.Constant) and isinstance(node.value, int): - return node.value - if isinstance(node, ast.Name): - if node.id in self._DefineConstants: - val: int | str = self._DefineConstants[node.id] - if isinstance(val, int): - return val - if isinstance(node, ast.BinOp): - left_val: int = self._get_const_int(node.left) - right_val: int = self._get_const_int(node.right) - if left_val and right_val: - if isinstance(node.op, ast.Add): - return left_val + right_val - if isinstance(node.op, ast.Sub): - return left_val - right_val - if isinstance(node.op, ast.Mult): - return left_val * right_val - if isinstance(node.op, ast.Div): - return left_val // right_val - if isinstance(node.op, ast.FloorDiv): - return left_val // right_val - return 0 + def _get_inherited_members(self, base_name: str) -> tuple[list[str], set[str]]: + member_types: list[str] = [] + seen_names: set[str] = set() + if not hasattr(self, '_pyi_tree') or self._pyi_tree is None: + return member_types, seen_names + base_node: ast.ClassDef | None = None + for node in ast.iter_child_nodes(self._pyi_tree): + if isinstance(node, ast.ClassDef) and node.name == base_name: + base_node = node + break + if base_node is None: + # 跨模块父类查找:当前 .pyi 中没有父类定义时,从跨模块类定义映射中查找 + # (如 exprs.py 的 Name(AST),AST 定义在 base.py) + base_node = self._cross_module_class_defs.get(base_name) + if base_node is None: + return member_types, seen_names + base_decls: list[str] = self._generate_class_decl(base_node) + for decl in base_decls: + if '= type {' in decl: + type_body: str = decl.split('= type {')[1].rstrip('}').strip() + if type_body: + for t in type_body.split(','): + t = t.strip() + if t: + member_types.append(t) + break + for item in base_node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + seen_names.add(item.target.id) + elif isinstance(item, ast.Assign) and len(item.targets) == 1: + if isinstance(item.targets[0], ast.Attribute) and isinstance(item.targets[0].value, ast.Name) and item.targets[0].value.id == 'self': + seen_names.add(item.targets[0].attr) + for item in base_node.body: + if isinstance(item, ast.FunctionDef) and item.name == '__init__': + for stmt in item.body: + if isinstance(stmt, ast.AnnAssign) and isinstance(stmt.target, ast.Attribute) and isinstance(stmt.target.value, ast.Name) and stmt.target.value.id == 'self': + seen_names.add(stmt.target.attr) + elif isinstance(stmt, ast.Assign) and len(stmt.targets) == 1 and isinstance(stmt.targets[0], ast.Attribute) and isinstance(stmt.targets[0].value, ast.Name) and stmt.targets[0].value.id == 'self': + seen_names.add(stmt.targets[0].attr) + return member_types, seen_names + + def _specialize_member_types(self, member_types: list[str], type_args: list[str]) -> list[str]: + """将基类成员类型中的泛型参数 T 替换为具体类型参数 + + 处理泛型基类继承(如 GSListNode[BasicBlock])时,基类 _get_inherited_members + 返回的成员类型中会包含未特化的 T 引用(形式为 %"SHA1.T"*)。 + + 替换策略:使用 i8*(不透明指针)代替 T*,而非具体类型指针。 + 原因:跨模块场景中,具体类型指针的 SHA1 前缀可能与导入模块不一致, + 导致 TransPyC 类型转换失败。i8* 作为通用指针类型,可安全赋值给 + 任何 Class | t.CPtr 注解的变量,运行时行为与 T* 相同(都是指针)。 + + Args: + member_types: 基类成员类型字符串列表 + type_args: 类型参数名列表(如 ['BasicBlock']) + + Returns: + 特化后的成员类型字符串列表 + """ + if not type_args: + return member_types + result: list[str] = [] + for mt in member_types: + new_mt: str = mt + # 将 %"SHA1.T"* 替换为 i8*(不透明指针) + new_mt = _RE_T_TYPE_PTR.sub('i8*', new_mt) + # 将 %"SHA1.T"(非指针,如 embedded struct)替换为 i8* + new_mt = _RE_T_TYPE.sub('i8*', new_mt) + result.append(new_mt) + return result + + def _llvm_type_size_str(self, type_str: str) -> int: + """估算 LLVM 类型字符串的大小(字节),用于 REnum 变体大小比较 + + 对齐 _EmitREnumLlvm 中的大小计算逻辑: + - i1=1, i8=1, i16=2, i32=4, i64=8, float=4, double=8, ptr=8 + - [N x T] = N * size(T) + - 其他结构体类型按指针大小 8 估算(保守值) + """ + s: str = type_str.strip() + if not s: + return 0 + # 指针类型 + if s.endswith('*'): + return 8 + # 数组类型 [N x T] + # 注:此处原写作 `self._ARR_TYPE_RE`(少一个 A)并用 `hasattr` 守卫 —— 该属性 + # **全仓从未定义** ⇒ 守卫恒假 ⇒ 本分支恒不执行:数组字段一律落到下方"未知类型 → 8" + # 的保守值,与 docstring 声明的"_EmitREnumLlvm 尺寸口径对齐"不符(静默降级)。 + # 现改用本类已定义的 `_ARRAY_TYPE_RE`(见文件顶部),并去掉守卫: + # 属性若再丢失应当响亮失败,而不是静默退回 8。 + m = self._ARRAY_TYPE_RE.match(s) + if m: + try: + n: int = int(m.group(1)) + return n * self._llvm_type_size_str(m.group(2)) + except ValueError: + return 8 + # 基本整数/浮点类型 + size_map: dict[str, int] = { + 'i1': 1, 'i8': 1, 'i16': 2, 'i32': 4, 'i64': 8, + 'float': 4, 'double': 8, 'half': 2, 'fp128': 16, + 'void': 0, + } + if s in size_map: + return size_map[s] + # %{...} 结构体类型 - 保守估算为指针大小 + if s.startswith('%') or s.startswith('{'): + return 8 + # 其他未知类型保守估算 + return 8 + + @staticmethod + def _ptr_level_of(type_str: str) -> int: + """统计类型字符串尾部的指针层数('i8***' → 3,'i32' → 0)""" + return len(type_str) - len(type_str.rstrip('*')) + + def _merge_cptr_subscript(self, base_type: str, cptr_sub_type: str) -> str: + """base | t.CPtr[T] → Ptr^N(base_type),N = t.CPtr[T] 段自身指针层数。 + + 与定义侧 VPC TypeGenResolve.vp:2610-2639 同口径:数 CPtr 侧已解析类型的 + 外层 Ptr 嵌套层 N(TypeGenResolve.vp:2616-2632),再给另一侧**纯外包** + N 层 Ptr(TypeGenResolve.vp:2633-2639,对 struct 基类型亦不吸收)。 + 与 LLVMTypeMapper._resolve_bitor 的 IsCPtrSubscriptNode 分支(_add_pointer_levels + 逐层追加)同口径,对同一批形态两处结果一致。 + 注:裸 | t.CPtr(非下标标记)仍走上方的 %"..."* 吸收分支,与本节无关。 + """ + return base_type + '*' * self._ptr_level_of(cptr_sub_type) + + def _ptr_subscript_type_str(self, annotation: ast.Subscript) -> str: + """指针下标(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],判据见 + SymbolUtils.IsPtrSubscriptNode)→ LLVM 类型串。 + + 与定义端同源:SymbolUtils.DesugarPtrSubscriptToUnion 把三者折叠为等价 + 联合写法 `T | t.CPtr` 后交 HandlesTypeMerge.MergeTypes,指针层数由 + _MergeAllComponentTypes 的"struct 基类型吸收一层 CPtr"决定 —— 裸类引用 + 的非 embedded 形式本身即指针('%"sha1.X"*'),再包一层会被吸收 → 结果 + 就是该指针本身;其余类型(标量 / 已是指针的 typedef / 泛型容器特化 / + 联合写法 / 嵌套指针下标)逐层外包。rawptr[...] 与 t.CPtr[...] 共用本条 + 规则(此前 t.CPtr[list[X]] 在此得 list[X]**、rawptr[list[X]] 得 + list[X]*,同一指针语义两种结果)。 + """ + inner: ast.AST = annotation.slice + inner_type: str = self._get_type_str(inner) + if self._is_bare_class_ref_annotation(inner, inner_type): + return inner_type + if inner_type in ('', 'void', 'void*'): + return 'i8*' + return f'{inner_type}*' + + def _is_bare_class_ref_annotation(self, inner: ast.AST, inner_type: str) -> bool: + """节点是否为"裸类引用"(本身即指针 → 吸收一层 t.CPtr)。 + + 与 _get_type_str 的既有约定一致:类引用的**非** embedded 形式本身就是 + 指针('%"sha1.X"*'),所以 `AST | t.CPtr` 合并后仍是该指针(定义端 + _MergeAllComponentTypes 的 struct 吸收)。联合写法(ast.BinOp)与指针 + 下标(rawptr[...] / t.CPtr[...])不是裸类引用——它们已自带指针层数, + 须逐层外包(rawptr[Node] | t.CPtr → Node**、ast.AST | t.CPtr | t.CPtr + → AST**,与定义端整体拉平后只吸收一次 CPtr 一致)。 + + **例外:纯装饰器标记联合**。`t.CExtern | X` / `t.CExport | X` / `t.State | X` + (vpi 记录的模块级全局就是这种写法,见 lib.Phase.Phase1.__init__.vpi 的 + `_mbuddy: t.CExtern | memhub.MemBuddy | t.CPtr`)里标记侧展开为**空串** + (CTypeRegistry.NameToLLVM('CExtern') == ''),该联合语义上仍是 X 这一个 + 裸类引用,故 `t.CExtern | X | t.CPtr` 必须与 `X | t.CPtr` 同判(吸收)—— + 对齐 VPC TypeGenResolve 的第 3 条语义「ActualType | DecoratorMarker → + ActualType」。漏此判据时联合被当成"已自带指针层数的左值"而多外包一层: + 声明端 `%"sha1.X"**` 与定义端 `%"sha1.X"*` 差一层,消费模块里 + `self.__mbuddy__ = _mbuddy` 之类的赋值即报"层数不同"fail-fast。 + """ + if not inner_type: + return False + if isinstance(inner, ast.BinOp): + if not isinstance(inner.op, ast.BitOr): + return False + LeftType: str = self._get_type_str(inner.left) + RightType: str = self._get_type_str(inner.right) + # 恰一侧是纯装饰器标记(展开为空/void)⇒ 类型全部来自另一侧,按另一侧判 + if LeftType in ('', 'void') and RightType not in ('', 'void'): + return self._is_bare_class_ref_annotation(inner.right, RightType) + if RightType in ('', 'void') and LeftType not in ('', 'void'): + return self._is_bare_class_ref_annotation(inner.left, LeftType) + return False + if isinstance(inner, ast.Subscript): + return not (IsRawPtrSubscriptNode(inner) or IsCPtrSubscriptNode(inner)) + return inner_type.startswith('%') and inner_type.endswith('*') + + def _get_type_str(self, annotation: ast.AST | None, embedded: bool = False) -> str: + if annotation is None: + return 'i8*' + + non_struct_types: set[str] = {'list', 'dict', 'tuple', 'set', 'array', 'CArray', 'rawarray'} + + if isinstance(annotation, ast.Name): + if annotation.id == 'None': + return 'void' + if annotation.id in ('str', 'bytes'): + return 'i8*' + llvm_type: str | None = CTypeRegistry.NameToLLVM(annotation.id) + if llvm_type is not None: + return llvm_type + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(annotation.id) + if resolved is not None: + ctype_cls: type + ptr_level: int + ctype_cls, ptr_level = resolved + base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if ptr_level > 0: + if base == 'void': + return 'i8*' + if '*' in base: + return base + return f'{base}*' + return base + if annotation.id in self.struct_names: + sha1: str | None = self.struct_sha1_map.get(annotation.id, self.module_sha1) + sname: str = f'%"{sha1}.{annotation.id}"' if sha1 else f'%struct.{annotation.id}' + if embedded: + return sname + else: + return f'{sname}*' + if annotation.id in non_struct_types: + return 'i32' + if annotation.id in self.enum_names: + return 'i32' + if annotation.id in self.typedef_map: + resolved_str: str = self._get_type_str(self.typedef_map[annotation.id], embedded=embedded) + return resolved_str + # 检查 t 类型别名(from t import CInt as myint) + if annotation.id in self._t_alias_map: + t_cls: type = self._t_alias_map[annotation.id] + llvm: str | None = CTypeRegistry.CTypeToLLVM(t_cls) + if llvm: + return llvm + # builtins 隐式 CTypedef(int64/uint64/size_t 等:builtins.vp 声明、 + # 用户文件无需 import 即用):常规路径 miss 后若落下方 fallback 会按 + # "本模块结构体"建 %"{mod}.int64" opaque(ClassGenGeneric._clone_annotation + # 的 "i64 != %int64*" 根因)。按 builtins.vp 权威定义展开。 + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _DG_TAR + _dg_bv: str | None = _DG_TAR.builtins_typedef_value(annotation.id) + if _dg_bv: + if _dg_bv.startswith('t.'): + _dg_llvm: str | None = CTypeRegistry.NameToLLVM(_dg_bv[2:]) + if _dg_llvm: + return _dg_llvm + _dg_node: ast.AST | None = None + try: + _dg_node = ast.parse(_dg_bv, '', mode='eval').body + except Exception: + _dg_node = None + if _dg_node is not None: + _dg_ret: str = self._get_type_str(_dg_node, embedded=embedded) + if _dg_ret and _dg_ret != 'i32': + return _dg_ret + sha1: str | None = self.struct_sha1_map.get(annotation.id, self.module_sha1) + sname: str = f'%"{sha1}.{annotation.id}"' if sha1 else f'%struct.{annotation.id}' + return f'{sname}*' + elif isinstance(annotation, ast.Attribute): + attr_name: str = annotation.attr if hasattr(annotation, 'attr') else '' + # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):按 t.CPtr + # 同形的「一层指针 = i8*」参与联合合并(`T | t.CRef` → T*),保证跨模块 + # .vpi 声明端类型与定义端 _ctype_to_llvm 一致。 + if attr_name in ('CRef', 'CTempRef'): + return 'i8*' + module_name: str = '' + if hasattr(annotation, 'value'): + if isinstance(annotation.value, ast.Name): + module_name = annotation.value.id + elif isinstance(annotation.value, ast.Attribute) and hasattr(annotation.value, 'attr'): + module_name = annotation.value.attr + # 嵌套类 / 嵌套 struct:限定名 `外类.内类`(module_name 是**类名**而非模块名)。 + # 定义端以扁平名 `{外类}_{内类}` 发射(见 HandlesClassDef._EmitNestedClassDefs), + # 故此处按扁平名拼类型;命中与否由 struct_names 里是否存在该扁平名裁决 + # (不存在即维持下方既有路径,不改变任何既有类型名)。 + if module_name and module_name != 't' and module_name != 'c': + NestedName: str = f'{module_name}_{attr_name}' + if NestedName in self.struct_names: + NestedSha1: str | None = self.struct_sha1_map.get(NestedName, self.module_sha1) + NestedStr: str = f'%"{NestedSha1}.{NestedName}"' if NestedSha1 else f'%struct.{NestedName}' + return NestedStr if embedded else f'{NestedStr}*' + if (module_name == 't' and attr_name == 'State') or attr_name == 'State': + return '' + if (module_name == 't' and attr_name == 'Callable') or attr_name == 'Callable': + return 'i8*' + llvm_type: str | None = CTypeRegistry.NameToLLVM(attr_name) + if llvm_type is not None: + return llvm_type + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(attr_name) + if resolved is not None: + ctype_cls: type + ptr_level: int + ctype_cls, ptr_level = resolved + base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if ptr_level > 0: + if base == 'void': + return 'i8*' + if '*' in base: + return base + return f'{base}*' + return base + if attr_name in self.typedef_map: + return self._get_type_str(self.typedef_map[attr_name], embedded=embedded) + if attr_name in self.enum_names: + return 'i32' + if attr_name in self.struct_names: + sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) + sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' + if embedded: + return sname + else: + return f'{sname}*' + if attr_name and attr_name[0].isupper() and attr_name not in non_struct_types: + sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) + sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' + return f'{sname}*' + if attr_name and attr_name not in non_struct_types: + sha1: str | None = self.struct_sha1_map.get(attr_name, self.module_sha1) + sname: str = f'%"{sha1}.{attr_name}"' if sha1 else f'%struct.{attr_name}' + return f'{sname}*' + return 'i32' + elif isinstance(annotation, ast.BinOp): + left_type: str = self._get_type_str(annotation.left, embedded=embedded) + right_type: str = self._get_type_str(annotation.right, embedded=embedded) + if left_type in ('', 'void'): + return right_type if right_type not in ('', 'void') else (left_type or right_type) + if right_type in ('', 'void'): + return left_type + # t.CPtr (i8*) 与 left 合并:对齐 Phase 2 _MergeAllComponentTypes 的 + # "first CPtr absorption" 语义:struct | t.CPtr → struct*(吸收,struct 变量默认是指针)。 + # 但对于本身已是指针的类型(如 bytes=str=i8*),bytes | t.CPtr → i8**(不吸收)。 + # 例外:LLVM 聚合数组([N x T],字符串以 '[' 开头)不可再外包指针层 → i8*。 + # 注:具名泛型特化(%"mod.list[X]"*)**不**走此回退——stub 已由 + # _collect_undefined_type_decls 为其补 opaque 声明,保留名字才能与 define 端 + # 的 %"mod.list[ast.ASTptr]"* 逐字一致(原 `'[' in left_type` 会把 + # list[X] | t.CPtr 一并退化成 i8*,与定义端 %"mod.list[X]"* 不一致)。 + if right_type == 'i8*': + if left_type[:1] == '[': + return 'i8*' + if left_type in ('', 'void'): + return 'i8*' + # 吸收仅对"裸类引用"成立(其非 embedded 形式本身即指针): + # ast.AST | t.CPtr → %"sha1.AST"*。已自带指针层数的左值 + # (rawptr[...] / t.CPtr[...] 下标 / 联合写法)须逐层外包: + # rawptr[Node] | t.CPtr → Node**、ast.AST | t.CPtr | t.CPtr → AST** + # ——与定义端 _CollectAllOperandTypes 整体拉平、只吸收一次 CPtr + # (_MergeAllComponentTypes 的 base_ptr_count>0 分支)一致。 + if self._is_bare_class_ref_annotation(annotation.left, left_type): + return left_type + if '*' in left_type: + return f'{left_type}*' + return f'{left_type}*' + # X | t.CPtr[T](T 为下标段,非裸标记):CPtr 侧贡献自身指针层数 N, + # 另一侧外包 N 层(Ptr^N(X));左右两侧写法对称(定义侧 VPC + # TypeGenResolve.vp:2604-2609 亦两侧皆判)。必须先于下方 + # "'*' in left_type → 直接返回左值":否则左值本身已是指针 + # (BYTEPTR⇒i8*)时会吞掉右段层数(BYTEPTR | t.CPtr[t.CPtr] 改前得 i8*)。 + # LLVM 聚合([N x T] / { ... })不可再外包指针层,保持既有行为(只报不改)。 + if IsCPtrSubscriptNode(annotation.right) and left_type[:1] not in ('[', '{'): + return self._merge_cptr_subscript(left_type, right_type) + if IsCPtrSubscriptNode(annotation.left) and right_type[:1] not in ('[', '{'): + return self._merge_cptr_subscript(right_type, left_type) + # t.CPtr 裸标记在左侧(t.CPtr | X):与右侧写法(X | t.CPtr,见上) + # 完全对称。缺此分支时左值 i8* 会吞掉右侧的指针层数: + # t.CPtr | rawptr[Node] 声明端曾得 i8*,而定义端为 %"sha1.Node"**(差一层)。 + # 仅对裸标记 t.CPtr 生效(left 为 t.CPtr[T] 下标走上一条分支; + # str/bytes 等其它 i8* 左值不受影响)。 + if IsCPtrNode(annotation.left) and right_type[:1] not in ('[', '{'): + if self._is_bare_class_ref_annotation(annotation.right, right_type): + return right_type + return f'{right_type}*' + if '*' in left_type: + return left_type + if '*' in right_type: + return right_type + return left_type + elif isinstance(annotation, ast.Subscript): + # 指针下标(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T] / 导入别名 X[T],以及带 + # 模块前缀书写的 X.CPtr[T])≡ 源码联合写法 `T | t.CPtr`:与定义端 + # **同一判据**(SymbolUtils.IsPtrSubscriptNode,别名经本模块的 + # _t_c_imported_names 表解析 X→CPtr)+ 同一层数规则 + # (_ptr_subscript_type_str:裸类引用吸收一层、其余逐层外包),内层 T + # 照常解析(跨模块类由 struct_sha1_map 补 SHA1 前缀)。此前 rawptr[T] + # 在此退化成 i8*,t.CPtr[T] 走的是"embedded + 追加一层"的独立分支: + # 非同一规则,且对泛型容器特化多出一层(t.CPtr[list[X]] 得 list[X]**, + # 而 rawptr[list[X]] 得 list[X]*)。 + ValueIsCPtr: bool = ( + (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CPtr') + or (isinstance(annotation.value, ast.Name) and annotation.value.id == 'CPtr') + ) + if (IsPtrSubscriptNode(annotation, TCAliasMap=self._t_c_imported_names) + or (ValueIsCPtr and isinstance(annotation.slice, (ast.Attribute, ast.Name, ast.Subscript)))): + return self._ptr_subscript_type_str(annotation) + # 内建裸名函数指针构造子 callable[...] / Callable[...]:与 Attribute 分支的 + # t.Callable[...] 逐点等价(同为 i8*)。缺此分支时落到下方 Name 泛型特化 / + # 兜底返回 'i32'(现场 Repl.vp:307 `ReplFragFn: callable[[rawptr], int]` + # 声明成 i32 全局 ⇒ 赋 i8* 触发「指针隐式转整数」)。 + if isinstance(annotation.value, ast.Name) and (annotation.value.id == 'callable' or annotation.value.id == 'Callable'): + return 'i8*' + base: str = self._get_type_str(annotation.value) + if base == 'i8*' and isinstance(annotation.slice, ast.Constant): + return f'[{self._get_const_int(annotation.slice)} x i8]' + if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): + return f'[{annotation.slice.value} x {base}]' + # 处理 t.CArray[elem_type, count] / rawarray[elem_type, count] / array[elem_type, count] + # 注解 → [count x elem_type] + # 支持 t.CArray (ast.Attribute)、CArray/rawarray/array (ast.Name) 三种写法 + # (array 为本轮新增的内建软关键字,与 t.CArray 逐点等价) + ValueIsCArray: bool = ( + (isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'CArray') + or (isinstance(annotation.value, ast.Name) and annotation.value.id in ('CArray', 'rawarray', 'array')) + ) + if ValueIsCArray: + SliceNode: ast.AST = annotation.slice + if isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2: + ElemType: str = self._get_type_str(SliceNode.elts[0], embedded=True) + CountVal: int = self._get_const_int(SliceNode.elts[1]) + if CountVal > 0: + return f'[{CountVal} x {ElemType}]' + # count 为 None 或 0 → 零长度数组(外部符号) + return f'[0 x {ElemType}]' + elif isinstance(SliceNode, (ast.Attribute, ast.Name, ast.Subscript)): + # 单参数 t.CArray[elem_type]:与 t.CArray[elem_type, None] 语义统一为 + # "长度自动推导数组"。此处无初始化字面量可推导 → 与 ,None 分支同用 + # [0 x elem] 占位,真实长度由 _generate_global_decl 按初始化字面量 + # (List 长度 / str 长度+1) 修正。 + # 参数位置经 _decay_array_to_ptr(:23-28) 退化为 elem*,与旧指针行为一致。 + ElemType: str = self._get_type_str(SliceNode, embedded=True) + return f'[0 x {ElemType}]' + if isinstance(annotation.value, ast.Name) and annotation.value.id == 'tuple': + slice_node: ast.AST = annotation.slice + elem_types: list[str] + if isinstance(slice_node, ast.Tuple): + elem_types = [self._get_type_str(e, embedded=True) for e in slice_node.elts] + else: + elem_types = [self._get_type_str(slice_node, embedded=True)] + return '{ ' + ', '.join(elem_types) + ' }' + # 处理泛型类类型注解(如 list[str] / list[rawptr[ast.AST]] / + # 用户泛型类 Box[Node]) + # 实参串走 SymbolUtils.ResolveGenericTypeArgStr(与定义端 + # _mangle_generic_class_name 同一规则源):此前只认 Name / Name-Tuple + # 实参,Attribute(ast.AST)/ Subscript(rawptr[...])/ BinOp + # (X | t.CPtr)/ 混合 Tuple 一律退回未特化的 %"...list"*, + # 与 define 端发射的 %".list[ast.ASTptr]" 不一致 + # (Undefined method / 签名错位)。 + # 内建容器(non_struct_types)之外,**用户泛型类**同样特化:判据 + # _generic_class_names(ClassDef.type_params,与定义端 + # HandlesClassDef._generic_class_templates 同源),此前只有内建容器名 + # 会特化 → Box[Node] 字段在 stub 端是 %".Box"*、定义端是 + # %".Box[<已解析实参>]"*(Holder 字段布局两侧不一致)。 + if isinstance(annotation.value, ast.Name): + gc_name: str = annotation.value.id + if (gc_name in non_struct_types or gc_name in self._generic_class_names) \ + and gc_name != 'tuple': + gc_slice: ast.AST = annotation.slice + # 多参数泛型(dict[K, V]):'][' 分隔,与 + # _mangle_generic_class_name 一致 + gc_nodes: list[ast.AST] = ( + list(gc_slice.elts) if isinstance(gc_slice, ast.Tuple) else [gc_slice]) + gc_mangled: list[str] = [ + ResolveGenericTypeArgStr( + gc_node, + TCAliasMap=self._t_c_imported_names, + StructSha1Map=self.struct_sha1_map) + for gc_node in gc_nodes] + gc_spec: str = gc_name + '[' + ']['.join(gc_mangled) + ']' + # 特化名前缀取**泛型模板类定义模块**的 SHA1(list → includes/_list.vp、 + # Box → 定义 Box 的模块,查 struct_sha1_map 即"类名 → 定义模块"表), + # 与定义端 HandlesClassDef._specialize_generic_class 的 + # Gen.module_sha1 = template['sha1']、调用点 + # Gen._struct_sha1_map[spec_name] 同一条规则源。 + # 此前用 self.module_sha1(使用方模块):同一特化在 declare(使用方前缀) + # 与 define/call(模板模块前缀)两侧不一致 → 跨模块 undefined symbol。 + gc_sha1: str | None = self.struct_sha1_map.get(gc_name, self.module_sha1) + gc_sname: str = f'%"{gc_sha1}.{gc_spec}"' if gc_sha1 else f'%struct.{gc_spec}' + # 一律以指针引用(不区分 embedded):定义端对嵌入式泛型容器 + # (字段 / t.CArray 元素)用不透明指针(实测字段 i8*,见 + # FieldForms/FieldBox/FieldOrder),嵌入 unsized opaque + # 结构体值会让字段/元素偏移错位;非 embedded(参数/返回) + # 亦与定义端同为指针 + return f'{gc_sname}*' + return f'{base}' + elif isinstance(annotation, ast.Constant): + if annotation.value is None: + return 'void' + if isinstance(annotation.value, int): + return 'i32' + if isinstance(annotation.value, str): + type_name: str = annotation.value + if type_name in self.enum_names: + return 'i32' + if type_name in self.struct_names: + sha1: str | None = self.struct_sha1_map.get(type_name, self.module_sha1) + sname: str = f'%"{sha1}.{type_name}"' if sha1 else f'%struct.{type_name}' + if embedded: + return sname + else: + return f'{sname}*' + llvm_type: str | None = CTypeRegistry.NameToLLVM(type_name) + if llvm_type is not None: + return llvm_type + resolved: tuple[type, int] | None = CTypeRegistry.ResolveName(type_name) + if resolved is not None: + ctype_cls: type + ptr_level: int + ctype_cls, ptr_level = resolved + base: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if ptr_level > 0: + if base == 'void': + return 'i8*' + if '*' in base: + return base + return f'{base}*' + return base + sha1: str | None = self.struct_sha1_map.get(type_name, self.module_sha1) + sname: str = f'%"{sha1}.{type_name}"' if sha1 else f'%struct.{type_name}' + return f'{sname}*' + return 'i8*' + elif isinstance(annotation, ast.Call): + if isinstance(annotation.func, ast.Name) and annotation.func.id == 'callable': + return 'i8*' + if isinstance(annotation.func, ast.Attribute) and annotation.func.attr == 'Callable': + return 'i8*' + return 'i32' + + def _infer_type(self, value: ast.AST) -> str: + """从值推断类型""" + if isinstance(value, ast.Constant): + if isinstance(value.value, int): + return 'i32' + elif isinstance(value.value, float): + return 'double' + elif isinstance(value.value, str): + return 'i8*' + elif isinstance(value.value, bool): + return 'i8' + elif isinstance(value, ast.List): + return 'i8*' + elif isinstance(value, ast.Dict): + return 'i8*' + elif isinstance(value, ast.Name): + return 'i32' + elif isinstance(value, ast.BinOp): + return self._infer_type(value.left) + elif isinstance(value, ast.Call): + return 'i8*' + return 'i32' + + def _get_bitfield_width(self, annotation: ast.AST) -> int | None: + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + right_width: int | None = self._get_bitfield_width(annotation.right) + if right_width is not None: + return right_width + return self._get_bitfield_width(annotation.left) + if isinstance(annotation, ast.Call): + if isinstance(annotation.func, ast.Attribute) and annotation.func.attr == 'Bit': + if annotation.args: + arg: ast.AST = annotation.args[0] + if isinstance(arg, ast.Constant) and isinstance(arg.value, int): + return arg.value + if isinstance(annotation, ast.Subscript): + if isinstance(annotation.value, ast.Attribute) and annotation.value.attr == 'Bit': + if isinstance(annotation.slice, ast.Constant) and isinstance(annotation.slice.value, int): + return annotation.slice.value + return None + + def _get_const_int(self, node: ast.AST) -> int: + """获取常量整数值,支持符号常量和简单表达式""" + if isinstance(node, ast.Constant) and isinstance(node.value, int): + return node.value + if isinstance(node, ast.Name): + if node.id in self._DefineConstants: + val: int | str = self._DefineConstants[node.id] + if isinstance(val, int): + return val + if isinstance(node, ast.BinOp): + left_val: int = self._get_const_int(node.left) + right_val: int = self._get_const_int(node.right) + if left_val and right_val: + if isinstance(node.op, ast.Add): + return left_val + right_val + if isinstance(node.op, ast.Sub): + return left_val - right_val + if isinstance(node.op, ast.Mult): + return left_val * right_val + if isinstance(node.op, ast.Div): + return left_val // right_val + if isinstance(node.op, ast.FloorDiv): + return left_val // right_val + return 0 diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index 04d958d..702e6f1 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -1,5048 +1,5048 @@ -from __future__ import annotations - -import sys -import os -import re -import shutil -import subprocess -import vast as ast -import traceback -import json -import pickle -import time -import cProfile -from collections import OrderedDict -from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.core.Handles.HandlesBase import CTypeInfo -from lib.core.SymbolData import ModuleSymbols -from lib.includes import t -from lib.core.SymbolUtils import AnnotationContainsName, ExpandSoftKeywords, DefineDeclIntTypeFromAnnotation -from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod -from lib.Projectrans.Utils import compute_sha1, _check_annotation_for_export, find_reachable_files_from_entries, topological_sort_files, parse_python_file -from lib.Projectrans.Phase1Generator import _TryGlobalCache, _SaveToGlobalCache, _GLOBAL_CACHE_DIR -import TransPyC -from StubGen import PythonToStubConverter -from lib.core.LLVMCG.IRMetadataFix import SanitizeIRMetadata - - -# PROF 热点:nt._path_isfile 2.6 万次 16.4s(Windows 磁盘 stat ~0.6ms/次)。 -# 编译进程内文件状态稳定(obj/ll 一旦生成不删除、不被移动),用模块级缓存 -# 避免重复 stat。仅用于"存在性检查"类调用(不用于内容变化的判断)。 -_FILE_STAT_CACHE: dict[str, bool] = {} - - -# --traceback(Ctrl+C 调用栈)强制纳入编译+链接的 include 模块源文件名。 -# 该模块对外只有 Install() 入口,且无任何用户代码 import 它 —— 若不强制纳入, -# 产物里既没有 Install 符号、也没有调用它的启动期构造器,功能静默失效。 -# .vp 优先,.py 仅为向后兼容的回退名。 -_TRACEBACK_MODULE_FILES: tuple[str, ...] = ('traceback.vp', 'traceback.py') - - -def _CachedIsFile(path: str) -> bool: - r: bool | None = _FILE_STAT_CACHE.get(path) - if r is None: - r = os.path.isfile(path) - _FILE_STAT_CACHE[path] = r - return r - - -# --------------------------------------------------------------- -# 文本读缓存(性能):同一个文件在一次构建里被**多个阶段反复读** -# (prof 基线 PROF/20260930_224857:_io.open 12,740 次 / self 28.8s、 -# read 11,902 次 / self 30.4s、TextIOWrapper.__exit__ 16.6s —— 128 个模块 -# 却产生上万次读,主要是同一份 .stub.ll / .ll / deps.json 被 -# _compile_one / _recombine_ll / 依赖解析等重复打开)。 -# 键 = **路径**(不做 stat / 时间戳校验)。性能前提(用户口径):TPC 单次运行内 -# 非本进程写出的文件内容不变 —— 因此不需要"每次读都 stat 一次"来判新旧; -# 若再 stat,等于把省下的 1.2 万次 open 换成 1.2 万次 stat(仍是系统调用热点)。 -# 正确性前提:**本进程写出/改写/删除过的文件,一律在该动作后调用 -# _InvalidateTextCache**(写点见文件内全部 open(..., 'w') 与 os.remove 处)。 -# 只缓存 newline=None 的默认换行翻译读,与直接 open(...).read() 语义逐字相同。 -# 内存:按总字节预算 + LRU 淘汰;单文件超过 _TEXT_CACHE_FILE_MAX 不入缓存 -# (合并后的 .ll 可达数十 MB,避免把内存吃光)。 -# --------------------------------------------------------------- -_TEXT_CACHE_MAX_BYTES: int = 64 * 1024 * 1024 -_TEXT_CACHE_FILE_MAX: int = 32 * 1024 * 1024 -_TextReadCache: OrderedDict[str, str] = OrderedDict() -_TextReadCacheBytes: int = 0 - - -def _ReadTextCached(path: str, newline: str | None = None) -> str: - """读文本文件(按路径缓存,零 stat);newline 非 None 时退化为直接读(不进缓存)。""" - global _TextReadCacheBytes - if newline is not None: - # 逐字节保真的读(newline='' 等)语义特殊:不进缓存,避免与默认换行翻译混用 - with open(path, 'r', encoding='utf-8', newline=newline) as _f: - return _f.read() - _hit: str | None = _TextReadCache.get(path) - if _hit is not None: - _TextReadCache.move_to_end(path) - return _hit - with open(path, 'r', encoding='utf-8') as _f: - _text: str = _f.read() - if len(_text) <= _TEXT_CACHE_FILE_MAX: - _TextReadCache[path] = _text - _TextReadCacheBytes += len(_text) - while _TextReadCacheBytes > _TEXT_CACHE_MAX_BYTES and len(_TextReadCache) > 1: - _, _old = _TextReadCache.popitem(last=False) - _TextReadCacheBytes -= len(_old) - return _text - - -def _InvalidateTextCache(path: str) -> None: - """丢弃 path 的文本读缓存条目:本进程**写出/改写/删除**该文件后必须调用。 - - 这是本缓存唯一的正确性保证(读侧不再 stat 判新旧):漏掉任一写点,后续读会 - 拿到该路径的旧文本。典型必失效场景:先读 → 同一 tick 内原地改写 → 再读, - 且改写前后字节数相同(实例:_compile_one 前的 type 定义重排,行集合不变仅次序变; - _recombine_ll 的旧 sha1 前缀替换,16 位 hex 等长替换)。 - """ - global _TextReadCacheBytes - _old = _TextReadCache.pop(path, None) - if _old is not None: - _TextReadCacheBytes -= len(_old) - - -_ASAN_DLL_A = 'libclang_rt.asan_dynamic-x86_64.dll.a' - - -def _FindAsanRuntime() -> tuple[str, str] | None: - """通过 PATH 定位 clang64 的 ASan 运行库(按 LLVM 大版本取最新的那个) - - mingw64 的 clang 只为 x86_64-w64-windows-gnu 目标提供 builtins / fuzzer / - profile / orc 运行库,**没有 ASan runtime**(/lib/ - x86_64-w64-windows-gnu/ 是空目录),所以不能靠 `-fsanitize=address` 让驱动 - 自动去找 —— 那只会得到 - "could not open .../libclang_rt.asan_dynamic.dll.a: No such file or directory"。 - clang64(MSVC ABI) 那边才有 libclang_rt.asan_dynamic-x86_64.dll.a 与 - libclang_rt.asan_dynamic_runtime_thunk-x86_64.a,只能借用这两份。 - - 路径**不再写死**:遍历 PATH 的每个目录 E,按 `E` 与 `dirname(E)` 两种可能取 base, - 拼出 `/lib/clang/<大版本>/lib/windows`,命中即为链接库目录;对应的运行期 - DLL 目录取 `/bin`。 - - Returns: - (链接库目录, 运行期 DLL 目录);都找不到返回 None - """ - hits: list[tuple[str, str]] = [] - seen: set[str] = set() - for entry in os.environ.get('PATH', '').split(os.pathsep): - if not entry: - continue - for base in (entry, os.path.dirname(entry)): - if not base: - continue - root = os.path.join(base, 'lib', 'clang') - if root in seen or not os.path.isdir(root): - continue - seen.add(root) - for name in os.listdir(root): - cand = os.path.join(root, name, 'lib', 'windows') - if os.path.isfile(os.path.join(cand, _ASAN_DLL_A)): - hits.append((cand, os.path.join(base, 'bin'))) - if not hits: - return None - - def _Ver(item: tuple[str, str]) -> int: - # 目录名是 LLVM 大版本号(如 "22")——按数值比,否则 "9" 会排在 "22" 之后 - v = item[0].split(os.sep)[-3] - return int(v) if v.isdigit() else 0 - - hits.sort(key=_Ver) - return hits[-1] - - -# ---------- --traceback:地址→源行 旁文件(.stackmap)---------- -# -# 开启 --traceback 时,链接成功后用 llvm-dwarfdump 解析最终 PE 的 -# .debug_line/.debug_info,把已有的"函数级/语句级 DWARF 行标记"导出成 -# .stackmap,供 exe 内运行时处理器在 Ctrl+C/Ctrl+Break 时把 CRT 到当前 -# 的返回地址逐帧翻译回 ".vp:行/函数"。以下函数只在开关开启时被调用;未开启时 -# 不引入任何新依赖、任何新行为。 - -_PE_MAGIC: bytes = b'PE\0\0' - - -def _ReadPEImageLayout(exe_path: str) -> tuple[int, int, dict[str, tuple[int, int]]] | None: - """读取 PE 的 (ImageBase, SizeOfImage, 节表) ;非 PE / 解析失败返回 None。 - - 节表为 {节名: (节 RVA, 节 VirtualSize)}。写端需要这三样才能把 DWARF 地址严格 - 归一到"运行期 RVA"(运行期 traceback.vp 用 `帧地址 − GetModuleHandleA(NULL)`): - * 合法行 ⇔ 地址落在映像 VA 区间 [ImageBase, ImageBase + SizeOfImage); - * "运行期可能出现的 RVA 区间"的粗判据 = [.text 节 RVA, SizeOfImage)。 - (原 _ReadPEImageBase 只返回 ImageBase,不足以做上述判定,故扩展为本函数。) - """ - import struct - try: - with open(exe_path, 'rb') as f: - head: bytes = f.read(0x1000) - except Exception: - return None - if len(head) < 0x40 or head[:2] != b'MZ': - return None - e_lfanew: int = struct.unpack_from(' len(head) or head[e_lfanew:e_lfanew + 4] != _PE_MAGIC: - return None - coff_off: int = e_lfanew + 4 - n_sections: int = struct.unpack_from(' len(head): - return None - magic: int = struct.unpack_from(' len(head): - break - name: str = head[base:base + 8].split(b'\x00', 1)[0].decode('ascii', 'replace') - sections[name] = (struct.unpack_from(' list[tuple[int, str, int]]: - """从 llvm-dwarfdump 输出解析出 [(地址, 文件绝对路径, 行号)]。 - - 逐个 CU(debug_line[0x..])重建目录/文件表;行记录过滤掉 line==0 的 - end_sequence 行。文件路径优先用文件表里的绝对 name;name 为相对时按 dir_index - 指向的目录拼接(DWARF v5 取 directory_names,v4 取 include_directories)。 - - ⚠ 地址口径:**原样返回 dwarfdump 打印的地址**(本例为含 ImageBase 的 VA - 0x14000xxxx,但被链接器按 COMDAT 去重丢弃的泛型副本其地址恰为 0)。 - 本函数**不**做"整表 VA/RVA"的判定——那正是旧实现失真的根因(全局最小值一旦为 - 0,整表都不减基址)。归一化(逐条 `地址 − ImageBase`,并丢弃不能归一的条目) - 由调用方 `_WriteStackMap` 依 PE.ImageBase/SizeOfImage 完成。 - """ - rows: list[tuple[int, str, int]] = [] - dirs: dict[int, str] = {} - files: dict[int, tuple[str, int]] = {} - pending_kind: str | None = None - pending_idx: int = -1 - pending_name: str | None = None - pending_dir_index: int = 0 - in_line_section: bool = False - - def _Unquote(text: str) -> str: - text = text.strip() - if len(text) >= 2 and text[0] == '"' and text[-1] == '"': - text = text[1:-1] - return text - - def _Commit() -> None: - nonlocal pending_kind, pending_name, pending_dir_index - if pending_kind == 'file' and pending_name is not None: - files[pending_idx] = (pending_name, pending_dir_index) - pending_kind = None - pending_name = None - pending_dir_index = 0 - - def _Resolve(idx: int) -> str: - entry = files.get(idx) - if entry is None: - return '??' - name, dir_index = entry - if os.path.isabs(name): - return name - base: str = dirs.get(dir_index, '') - if base and not os.path.isabs(base): - root: str = dirs.get(0, '') - if root and os.path.isabs(root): - base = os.path.join(root, base) - return os.path.normpath(os.path.join(base, name)) if base else name - - for line in dump_text.splitlines(): - stripped: str = line.strip() - if not stripped: - continue - if 'debug_line contents' in stripped: - in_line_section = True - continue - if 'contents:' in stripped and 'debug_' in stripped: - in_line_section = False - continue - if not in_line_section: - continue - if stripped.startswith('debug_line['): - _Commit() - dirs = {} - files = {} - continue - m = _DWARF_FILENAMES_RE.match(line) - if m: - _Commit() - pending_kind = 'file' - pending_idx = int(m.group(1)) - continue - m = _DWARF_DIRNAMES_RE.match(line) - if m: - _Commit() - value: str = _Unquote(m.group(2)) - if value: - dirs[int(m.group(1))] = value - else: - pending_kind = 'dir' - pending_idx = int(m.group(1)) - continue - m = _DWARF_NAME_RE.match(line) - if m and pending_kind is not None: - name: str = _Unquote(m.group(1)) - if pending_kind == 'file': - pending_name = name - else: - dirs[pending_idx] = name - pending_kind = None - continue - m = _DWARF_DIRINDEX_RE.match(line) - if m and pending_kind == 'file': - pending_dir_index = int(m.group(1)) - continue - if _DWARF_LINE_HEADER_RE.match(line): - _Commit() - continue - m = _DWARF_LINE_ROW_RE.match(line) - if m: - _Commit() - row_line: int = int(m.group(2)) - if row_line > 0: - rows.append((int(m.group(1), 16), _Resolve(int(m.group(4))), row_line)) - return rows - - -def _ParseDwarfSubprograms(dump_text: str) -> list[tuple[int, int, str]]: - """从 llvm-dwarfdump --debug-info 输出解析 [(low_pc, high_pc, 函数名)]。 - - DW_AT_high_pc 有两种形态且 dwarfdump 都打成 `(0x…)`:`DW_FORM_addr`(绝对 - 结束地址,值 > low_pc)与常量形式(相对 low_pc 的**长度**,值通常 < low_pc)。 - 本函数以 `high_pc <= low_pc` 判定后者并按 `low_pc + 长度` 还原;缺 high_pc 时 - 退化为 [low_pc, low_pc + 1)。 - - ⚠ 地址口径同 `_ParseDwarfLineRows`:**原样返回**,不做整表 VA/RVA 判定;被 - 链接器 COMDAT 去重丢弃的泛型副本其 low_pc 恰为 0(实测 TransPyV.exe 有 756 个), - 由调用方 `_WriteStackMap` 逐条归一化并丢弃。 - """ - result: list[tuple[int, int, str]] = [] - cur_low: int | None = None - cur_high: int | None = None - cur_name: str | None = None - in_prog: bool = False - - def _Flush() -> None: - nonlocal cur_low, cur_high, cur_name, in_prog - if in_prog and cur_low is not None and cur_name: - hi: int = cur_high if cur_high is not None else cur_low + 1 - if hi <= cur_low: - hi = cur_low + cur_high # high_pc 为 offset 形式 - if hi > cur_low: - result.append((cur_low, hi, cur_name)) - cur_low = None - cur_high = None - cur_name = None - in_prog = False - - for line in dump_text.splitlines(): - if _DWARF_TAG_ANY_RE.search(line): - if _DWARF_TAG_SUBPROGRAM_RE.search(line): - _Flush() - in_prog = True - else: - _Flush() - continue - if not in_prog: - continue - m = _DWARF_LOWPC_RE.search(line) - if m: - cur_low = int(m.group(1), 16) - continue - m = _DWARF_HIGHPC_RE.search(line) - if m: - cur_high = int(m.group(1), 16) - continue - m = _DWARF_ATNAME_RE.search(line) - if m and cur_name is None: - cur_name = m.group(1) - _Flush() - return result - - -# 显式并行度覆盖:TPC_JOBS=N(N>=1)——使用者明确指定 worker 数并**自行承担** -# 内存风险(含 N=1 即显式接受串行)。设了它就不再走内存校验/预分配。 -_JOBS_ENV: str = 'TPC_JOBS' -# 已通过"预分配校验"的预算(MB):同一预算不重复校验(翻译/编译两阶段各查一次) -_MemProbeDone: set[int] = set() -# 预分配校验的分块大小(MB):分块提交并**全部持有**到校验结束(峰值 = 预算), -# 结束后立即整体释放 —— 紧接其后就创建 worker 进程,OS 直接复用这批刚释放的页。 -_MEM_PROBE_CHUNK_MB: int = 256 -# 自动并行度上限(用户 2026-10-01 定:16 worker 太多)。只约束**自动**取值, -# 显式 TPC_JOBS=N 仍按硬件/文件数上限生效。 -_MAX_WORKERS: int = 8 - - -def _MemStatus() -> tuple[int, int]: - """(可用物理内存 MB, 物理内存总量 MB);读取失败返回 (-1, -1),调用方按"未知"处理""" - try: - import ctypes - - class _MemStat(ctypes.Structure): - _fields_ = [ - ("dwLength", ctypes.c_ulong), - ("dwMemoryLoad", ctypes.c_ulong), - ("ullTotalPhys", ctypes.c_ulonglong), - ("ullAvailPhys", ctypes.c_ulonglong), - ("ullTotalPageFile", ctypes.c_ulonglong), - ("ullAvailPageFile", ctypes.c_ulonglong), - ("ullTotalVirtual", ctypes.c_ulonglong), - ("ullAvailVirtual", ctypes.c_ulonglong), - ("ullAvailExtendedVirtual", ctypes.c_ulonglong), - ] - - stat = _MemStat() - stat.dwLength = ctypes.sizeof(_MemStat) - if ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)): - return (stat.ullAvailPhys // (1024 * 1024), - stat.ullTotalPhys // (1024 * 1024)) - except Exception: - pass - return (-1, -1) - - -def _ProbeReserveMb(mb: int) -> bool: - """预分配校验:一次性提交 mb MB(分块持有后整体释放),成功返回 True。 - - 为什么要"预分配"而不是只读 ullAvailPhys:可用物理内存含大量**可回收**的 - 缓存页/其他进程随时会吃掉的余量,只按它决策会出现"这一秒够、真跑起来不够" - 的波动(编译时间在 400s / 1180s 两档之间随机跳的直接来源)。先真正把预算 - 拿到手(页级提交)再放掉,等于向 OS 确认过这笔预算确实可分配。 - """ - if mb <= 0: - return True - Bufs = [] - Left = mb - try: - while Left > 0: - Cur = min(_MEM_PROBE_CHUNK_MB, Left) - Bufs.append(bytearray(Cur * 1024 * 1024)) - Left -= Cur - return True - except MemoryError: - return False - finally: - del Bufs - - -def _PickWorkerCount(n_files: int, per_worker_mb: int = 350) -> int: - """并行 worker 数的**唯一**决策入口(CPU 上限 + 内存预分配双约束)。 - - 口径(用户 2026-10-01 定,取代旧"内存紧就降级串行"): - · **自动上限 `_MAX_WORKERS`=8**:16 worker 收益不足、内存压力翻倍(用户 2026-10-01 定); - 显式 TPC_JOBS 不受此限(只受硬件/文件数约束)。 - 1. **不降级**:内存不足**绝不**静默退回单 worker 串行 —— 旧实现 `by_mem = - max(1, avail_mb // per_worker_mb)` 在可用内存稍紧时就整体降为串行, - 同一条命令 76 文件逐个翻译 1180s、并行 ~400s,时间随机跳 2-3 倍 - (profile 实证:224857 那次主进程 0 次 _translate_file=并行,438s; - 231916/235354 两次主进程 75 次 _translate_file=串行,1048s/1168s)。 - 2. **预分配 + 强制预留**:先按 base×per_worker_mb 做一次性提交校验 - (_ProbeReserveMb),拿到就并行、拿不到就 fail-fast 报出"可用/需要" - 数字,由使用者释放内存或**显式** TPC_JOBS=N 接受其它并行度。 - 3. 显式 TPC_JOBS=N 优先且跳过校验(使用者自行承担内存风险,N=1 即显式串行)。 - - 单 worker 预估 per_worker_mb(Python 解释器 + llvmlite module + 共享符号表 - 快照);实测翻译阶段 worker 稳态 ~110MB、峰值 ~300MB,故取 350(编译阶段 - 调用方传 400)。Windows 用 GlobalMemoryStatusEx 读物理内存。 - """ - hw_max = min(os.cpu_count() or 4, n_files) - base = min(hw_max, _MAX_WORKERS) - if base <= 1: - return base - env_jobs: str | None = os.environ.get(_JOBS_ENV) - if env_jobs: - try: - want = int(env_jobs) - except ValueError: - want = base - # 显式指定只受硬件/文件数约束(不受 _MAX_WORKERS 限制),并跳过内存校验 - want = max(1, min(want, hw_max)) - _vlog().info(f" [并行] {_JOBS_ENV}={env_jobs} → workers={want}(显式指定,跳过校验)") - return want - avail_mb, total_mb = _MemStatus() - need_mb = base * per_worker_mb - if avail_mb < 0: - # 读不到内存信息:不阻塞编译(按上限并行),但必须显式告知 - _vlog().warning(f" [并行] 读不到物理内存,取 workers={base}(预算 {need_mb}MB)") - return base - if avail_mb < need_mb: - raise RuntimeError( - f"[并行翻译] 内存不足:需 {need_mb}MB({base}×{per_worker_mb}MB)," - f"可用 {avail_mb}MB/{total_mb}MB;释放内存或设 {_JOBS_ENV}=N 重跑") - if need_mb not in _MemProbeDone: - _t0: float = _time.time() - if not _ProbeReserveMb(need_mb): - raise RuntimeError( - f"[并行翻译] 预分配 {need_mb}MB 失败({base}×{per_worker_mb}MB," - f"可用 {avail_mb}MB/{total_mb}MB);释放内存或设 {_JOBS_ENV}=N 重跑") - _MemProbeDone.add(need_mb) - _vlog().info(f" [并行] 预分配 {need_mb}MB 通过({_time.time() - _t0:.2f}s)") - _vlog().info(f" [并行] workers={base}(预算 {need_mb}MB / 可用 {avail_mb}MB)") - return base - - -def _ExtractCDefineConstantsFromPyi(pyi_path: str, all_dc: dict[str, object], - symtab: object | None = None, - all_dt: dict[str, tuple[int, bool]] | None = None) -> None: - """从 .pyi 文件中提取 CDefine 常量到 all_dc。文件不存在时静默跳过。 - - 治本修复:原实现只支持 ast.Constant 和 float() 调用, - 无法处理 t.CUnsignedLong(-11)(ast.Call)和 - FOREGROUND_GREEN | FOREGROUND_INTENSITY(ast.BinOp)等复杂表达式。 - 现使用 ConstEvaluator.eval_full 统一求值,支持: - - ast.Constant: 简单常量 (0x0002) - - ast.Call: 类型构造 (t.CUnsignedLong(-11)) - - ast.BinOp: 位运算 (FOREGROUND_RED | FOREGROUND_GREEN) - - ast.UnaryOp: 一元运算 (-11) - - ast.Name: 引用其他 CDefine 常量 - """ - if not os.path.exists(pyi_path): - return - try: - # 复用 parse_python_file 的 AST 缓存(.pyi 文件被多处解析) - _, tree = parse_python_file(pyi_path) - if tree is None: - return - # 治本修复:使用 ConstEvaluator 处理复杂表达式 - # 创建 mock Gen 对象,其 _define_constants 指向 all_dc, - # 使 ConstEvaluator 能查找同文件中已提取的常量(如 FOREGROUND_RED 引用 FOREGROUND_BLUE) - from lib.core.ConstEvaluator import ConstEvaluator, EvalContext - - class _MockGen: - """轻量 mock,仅提供 _define_constants 供 ConstEvaluator 查找""" - def __init__(self) -> None: - self._define_constants: dict[str, object] = all_dc - - mock_gen: _MockGen = _MockGen() - ctx: EvalContext = EvalContext(Gen=mock_gen) - - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): - if AnnotationContainsName(node.annotation, 'CDefine') and node.value: - val: object = ConstEvaluator.eval_full(node.value, ctx) - if val is None and isinstance(node.value, ast.Call) and len(node.value.args) == 1: - # 类型构造的**裸别名**形态(如 `DWORD(-11)`,非 `t.CUnsignedLong(-11)`): - # eval_full 只认 `t.X(...)` / `from t import X`(见 ConstEvaluator - # _eval_compile_time_call),裸 typedef 别名既不在 _t_module 也不在 - # mock ctx 的 _t_type_symbols 里 → 返回 None → 该常量被整条丢弃。 - # 现场:win32file.vp 的 STD_INPUT/OUTPUT/ERROR_HANDLE 是全仓**唯一**用 - # 别名构造写的 define → 唯独这三个进不了 _all_define_constants,导致 - # 模块属性访问 `wf.STD_ERROR_HANDLE` 解析落空、退化成未定义的全局符号 - # (链接失败)。CDefine 关心的是常量值,故回退求值其唯一实参(位型一致)。 - val = ConstEvaluator.eval_full(node.value.args[0], ctx) - if val is not None and isinstance(val, (int, float, str)): - all_dc[node.target.id] = val - # 带类型注册:记录该 define 的**声明整数类型**(如 `DWORD` → (32, False)), - # 供使用点按声明类型物化(u32 用 i32 + 位型,而不是按值大小猜 i32/i64)。 - if all_dt is not None and isinstance(val, int) and not isinstance(val, bool): - decl_type: tuple[int, bool] | None = DefineDeclIntTypeFromAnnotation(symtab, node.annotation) - if decl_type is not None: - all_dt[node.target.id] = decl_type - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - - -def _CountUnresolvedTypedefs(module_symbols: object) -> int: - """该模块"有值注解却解析不出类型"的 typedef 个数(供声明阶段判断是否要下一轮)。 - - typedef 右侧可以引用**其它模块**的 typedef:例 win32base 的 - `HANDLE: t.CTypedef = VOIDPTR`(VOIDPTR 在 stdint)——本模块先 stage 时右侧 - 解析为空(original_type_kind/original_class 均为 None),要等依赖模块入表后 - 重 stage 才能补全。返回 0 表示本模块声明已完整(不再需要重跑)。 - """ - miss: int = 0 - for td in getattr(module_symbols, 'typedefs', []): - if getattr(td, 'value', None) is None and getattr(td, 'annotation', None) is None: - continue - if getattr(td, 'original_type_kind', None) is None and getattr(td, 'original_class', None) is None: - miss += 1 - return miss - - -def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp_dir: str, output_dir: str, - include_dirs: list[str], triple: str | None, datalayout: str | None, - sha1_map: dict[str, str], sig_files: dict[str, str], - stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int, - shared_sym_pickle_path: str, struct_sha1_map: dict[str, str], - prebuilt_ModuleSha1Map: dict[str, str] | None = None, - hotspot_sites_file: str | None = None, - hotspot_sites_all: bool = False, - hotspot_callees: list[str] | None = None, - traceback_enabled: bool = False, - dwarf_enabled: bool = False) -> tuple[str, str, str]: - # 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中 - # 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的 - # 符号污染后文件 → 跨模块 struct 类型前缀错位(如 7cadfe07e983fd90.CompilerFactory - # 混入 _list 前缀,Type of #1 arg mismatch)。每任务必须从 pickle 反序列化出 - # 独立副本(原实现;worker 复用省掉的 pickle.load 时间仅 ~0.5s/次,远小于 - # worker 内翻译计算,不值得以正确性为代价)。 - try: - trans = Phase2Translator(src_root, temp_dir, output_dir, - compile_cmd='llc', include_dirs=include_dirs, - target_triple=triple, target_datalayout=datalayout, - build_pyi_cache=False) - trans.sha1_map = sha1_map - trans.sig_files = sig_files - trans.stub_files = stub_files - trans.include_py_map = include_py_map - trans.slice_level = slice_level - trans.struct_sha1_map = struct_sha1_map - # 调用点热点统计与每函数计时(--hotspot-sites):worker 子进程无父进程属性, - # 须显式透传(None=未启用,零影响);生成 IR 时各模块处理器统一追加写最终 - trans.hotspot_sites_file = hotspot_sites_file - trans.hotspot_sites_all = hotspot_sites_all - trans.hotspot_callees = hotspot_callees - # Ctrl+C 调用栈(--traceback):worker 子进程无父进程属性,须显式透传 - # (False=未启用,零影响);traceback 模块据此注入 Install() 启动期构造器 - trans.traceback_enabled = traceback_enabled - # 函数级 DWARF(--traceback 时 include 模块也必须带行表,否则栈里落到 - # includes/* 的帧取不到源行):worker 子进程无父进程属性,须显式透传 - # (False=未启用,零影响),仅用于 _translate_file 的 dwarf_enabled 形参 - trans.dwarf_enabled = dwarf_enabled - - if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path): - # 使用 pickle.load(f) 流式加载,避免 pickle.loads(f.read()) 一次性 - # 将整个 pickle 文件读入内存(磁盘副本 + 反序列化对象同时占用内存)导致 MemoryError。 - # 同时提高递归限制,防止深层嵌套对象反序列化失败。 - sys.setrecursionlimit(max(sys.getrecursionlimit(), 100000)) - with open(shared_sym_pickle_path, 'rb') as f: - shared_data = pickle.load(f) - _sym_bytes: bytes | None = shared_data.get('symbol_table_bytes') - if _sym_bytes is not None: - trans._shared_symbol_table = pickle.loads(_sym_bytes) - trans._shared_source_module_sig_files = shared_data['source_module_sig_files'] - trans._shared_all_dc = shared_data['all_dc'] - trans._shared_all_decl_types = shared_data.get('all_decl_types', {}) - trans._shared_export_extern_funcs = shared_data['export_extern_funcs'] - trans.inline_func_symbols = shared_data['inline_func_symbols'] - trans.function_default_args = shared_data['function_default_args'] - trans.function_param_names = shared_data.get('function_param_names', {}) - trans._shared_generic_class_templates = shared_data.get('generic_class_templates', {}) - trans._shared_doc_meta = shared_data.get('shared_doc_meta', {}) - trans._shared_class_vtable = shared_data.get('class_vtable', set()) - trans._shared_cross_module_vtable = shared_data.get('cross_module_vtable', set()) - trans._shared_class_methods = shared_data.get('class_methods', {}) - trans._shared_cross_module_novtable = shared_data.get('cross_module_novtable', set()) - else: - trans._precollect_inline_symbols() - trans._build_shared_symbol_data() - - trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=prebuilt_ModuleSha1Map, dwarf_enabled=dwarf_enabled) - return ('ok', src_path, '') - except Exception as e: - # fail-fast(2026-08-18):include 文件编译出错立即终止整个编译进程 - # (主进程收到 BrokenProcessPool 后取消剩余任务退出)。 - sys.stderr.write( - f"[FATAL] include 翻译失败,立即终止编译: {src_path}\n" - f"{traceback.format_exc()}\n") - sys.stderr.flush() - os._exit(88) - - -def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_dir: str, - include_dirs: list[str], triple: str | None, datalayout: str | None, - sha1_map: dict[str, str], sig_files: dict[str, str], - stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int, - shared_sym_pickle_path: str, struct_sha1_map: dict[str, str], - hotspot_sites_file: str | None = None, - hotspot_sites_all: bool = False, - hotspot_callees: list[str] | None = None, - dwarf_enabled: bool = False) -> tuple[list, list]: - """批次子任务:worker 内创建一个 Phase2Translator + pickle.load 一次,顺序翻译批内全部文件。 - - batch 每项: (i, src_path, out_path, sha1, prebuilt_ModuleSha1Map) - - 防污染:SymbolTable 是编译期唯一被写的共享对象(import insert/类注册), - 批内每个文件翻译前从 symbol_table_bytes 反序列化出干净副本,前文件的 - 符号写入不会残留到后文件(否则跨模块 struct 类型前缀错位)。 - 返回 (错误列表[(src_path, err)], batch, per_file_times[(src_path, elapsed, rel)])。 - """ - try: - trans = Phase2Translator(src_root, temp_dir, output_dir, - compile_cmd='llc', include_dirs=include_dirs, - target_triple=triple, target_datalayout=datalayout, - build_pyi_cache=False) - trans.sha1_map = sha1_map - trans.sig_files = sig_files - trans.stub_files = stub_files - trans.include_py_map = include_py_map - trans.slice_level = slice_level - trans.struct_sha1_map = struct_sha1_map - # 调用点热点统计与每函数计时(--hotspot-sites):批内每文件同样透传 - trans.hotspot_sites_file = hotspot_sites_file - trans.hotspot_sites_all = hotspot_sites_all - trans.hotspot_callees = hotspot_callees - - symbol_table_bytes: bytes | None = None - if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path): - sys.setrecursionlimit(max(sys.getrecursionlimit(), 100000)) - with open(shared_sym_pickle_path, 'rb') as f: - shared_data = pickle.load(f) - symbol_table_bytes = shared_data.get('symbol_table_bytes') - if symbol_table_bytes is not None: - trans._shared_symbol_table = pickle.loads(symbol_table_bytes) - trans._shared_source_module_sig_files = shared_data['source_module_sig_files'] - trans._shared_all_dc = shared_data['all_dc'] - trans._shared_all_decl_types = shared_data.get('all_decl_types', {}) - trans._shared_export_extern_funcs = shared_data['export_extern_funcs'] - trans.inline_func_symbols = shared_data['inline_func_symbols'] - trans.function_default_args = shared_data['function_default_args'] - trans.function_param_names = shared_data.get('function_param_names', {}) - trans._shared_generic_class_templates = shared_data.get('generic_class_templates', {}) - trans._shared_doc_meta = shared_data.get('shared_doc_meta', {}) - trans._shared_class_vtable = shared_data.get('class_vtable', set()) - trans._shared_cross_module_vtable = shared_data.get('cross_module_vtable', set()) - trans._shared_class_methods = shared_data.get('class_methods', {}) - trans._shared_cross_module_novtable = shared_data.get('cross_module_novtable', set()) - else: - trans._precollect_inline_symbols() - trans._build_shared_symbol_data() - - batch_errors: list = [] - per_file_times: list[tuple[str, float, str]] = [] - for _i, src_path, out_path, _sha1, msm in batch: - _f0: float = time.time() - try: - # 每文件前重置 SymbolTable 为干净副本(防跨文件污染) - if symbol_table_bytes is not None: - trans._shared_symbol_table = pickle.loads(symbol_table_bytes) - trans._shared_symbol_table.translator = None - trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm, dwarf_enabled=dwarf_enabled) - per_file_times.append((src_path, time.time() - _f0, os.path.relpath(src_path, src_root))) - except Exception as e: - # fail-fast(2026-08-18):任意文件编译出错立即终止整个编译进程, - # 不再收集错误继续翻译批内剩余文件(避免浪费大量时间)。主进程 - # 收到 BrokenProcessPool/future 异常后取消全部剩余批次退出。 - sys.stderr.write( - f"[FATAL] 文件翻译失败,立即终止编译: {src_path}\n" - f"{traceback.format_exc()}\n") - sys.stderr.flush() - os._exit(88) - return (batch_errors, batch, per_file_times) - except Exception as e: - return ([(item[1], f'{e}\n{traceback.format_exc()}') for item in batch], batch, []) - - -# ================================================================== -# 子进程性能剖析:主进程 cProfile 只能看到进程池等待(WaitForMultipleObjects/ -# as_completed),看不到 worker 进程内部 CPU 分布(vast parse 等热点)。 -# 固定启用(不设环境变量开关),worker 每个任务导出 cProfile dump -# (worker_prof_{pid}_{seq}.prof),主进程导出 main_prof_{pid}.prof + main_time.txt。 -# 输出落在 PROF_DIR/<启动时间>/ 下的**本次编译专属目录**(2026-09-29 起)。 -# ================================================================== -import functools as _functools -import time as _time - -# 硬编码 PROF 根目录(2026-08-18 起不再用 TPC_WORKER_PROF 环境变量控制) -PROF_DIR: str = r'D:\Users\TermiNexus\Desktop\TransPyC\PROF' - -# 本次编译的 run 目录:按启动时间在 PROF_DIR 下新建子目录,**不清理旧目录** -# (2026-09-29 用户要求保留历次 profile 以便跨次对比,旧行为是每次编译删掉 -# PROF 根目录下全部 *.prof ⇒ 只能看最近一次)。 -# 子目录名经环境变量传给 worker:ProcessPoolExecutor 用 spawn 起新解释器, -# 进程内模块态不共享;环境变量随子进程继承 ⇒ 同一次编译的所有 worker 落在 -# 同一个 run 目录,不会各建各的目录。 -_PROF_RUN_ENV: str = 'TPC_PROF_RUN_DIR' -_prof_run_dir: str | None = None - - -def StartProfRun() -> str: - """每次编译开始时调用一次:创建本次编译专属的 PROF 子目录,返回其路径。 - - 目录名 = PROF 根目录下 `YYYYmmdd_HHMMSS`(编译启动时刻);同名(同一秒内 - 二次启动)补 `_N` 序号。创建失败时回落到 PROF 根目录,保证不因 profiling - 影响编译本身。成功后把目录写进环境变量,供 worker 子进程复用。 - """ - global _prof_run_dir - if _prof_run_dir: - return _prof_run_dir - _name: str = _time.strftime('%Y%m%d_%H%M%S') - _dir: str = os.path.join(PROF_DIR, _name) - _seq: int = 1 - while os.path.isdir(_dir): - _seq += 1 - _dir = os.path.join(PROF_DIR, f'{_name}_{_seq}') - try: - os.makedirs(_dir, exist_ok=True) - except OSError: - return PROF_DIR - _prof_run_dir = _dir - os.environ[_PROF_RUN_ENV] = _dir - return _dir - - -def GetProfRunDir() -> str: - """当前进程写 profile 应落的目录。 - - 主进程 = StartProfRun 选定的 run 目录;worker = 继承来的环境变量指向的目录。 - 两者都取不到(未先调 StartProfRun,如 worker 被单独拉起)时回落到 PROF 根目录, - 宁可文件落在根目录也不丢 dump。 - """ - global _prof_run_dir - if _prof_run_dir: - return _prof_run_dir - _env_dir: str = os.environ.get(_PROF_RUN_ENV, '') - if _env_dir: - _prof_run_dir = _env_dir - return _env_dir - return PROF_DIR - - -def _ProfileWorker(_fn): - @_functools.wraps(_fn) - def _wrapper(*args, **kwargs): - _prof = None - try: - # cProfile 默认不启用(环境变量 TPC_WORKER_PROF=1 开启): - # Python 只允许一个 profiling tool 同时活跃,TPC 的 cProfile 与 - # viztracer --log_multiprocess 冲突(cProfile.enable() 检测到已激活 - # profiler 抛 "Another profiling tool is already active" → worker 崩溃 - # → batch 异常 → 编译终止)。外部 profiler 活跃(sys.getprofile() - # 非 None)时即使开启也跳过让位;enable() 异常兜底吞掉保编译。 - if os.environ.get('TPC_WORKER_PROF', '0') == '1' and sys.getprofile() is None: - _prof = cProfile.Profile() - try: - _prof.enable() - except RuntimeError: - _prof = None - return _fn(*args, **kwargs) - finally: - if _prof is not None: - _prof.disable() - _seq: int = getattr(_wrapper, '_seq', 0) + 1 - _wrapper._seq = _seq - try: - _out_dir: str = GetProfRunDir() - if not os.path.isdir(_out_dir): - os.makedirs(_out_dir, exist_ok=True) - _out: str = os.path.join(_out_dir, f'worker_prof_{os.getpid()}_{_seq}.prof') - _prof.dump_stats(_out) - except Exception: - pass - - return _wrapper - - -_parallel_translate_worker = _ProfileWorker(_parallel_translate_worker) -_parallel_translate_batch = _ProfileWorker(_parallel_translate_batch) - - -# ================================================================== -# worker pyi 缓存预热:ProcessPoolExecutor(spawn) 下每个 worker 重新 -# import 模块后,HandlesImports.ImportHandle 的类级 _pyi_cache 全空, -# 每进程首次任务重新 vast.parse 全部依赖 .vpi/.pyi -# (cProfile: 4479 次 parse / 245s)。 -# 方案:主进程把 temp 下全部 .vpi 的 vast AST 树 pickle 落盘 -# (temp/_pyi_cache_v1.pkl,任务 B),worker initializer 启动时 -# pickle.load 一次填充 _pyi_cache(任务 A);load 失败回退逐文件 -# vast.parse。文件系统共享即为"共用缓存",不做跨进程共享内存(任务 C)。 -# 环境变量 TPC_WORKER_WARMUP=0 可关闭(回归对比基线)。 -# ================================================================== -_PYI_CACHE_PKL_NAME: str = '_pyi_cache_v1.pkl' -_PYI_CACHE_SIG_NAME: str = '_pyi_cache_v1.sig.json' -_PYI_CACHE_VERSION: int = 1 - - -def _ScanPyiFiles(temp_dir: str) -> list[str]: - """扫描 temp 目录下全部 .vpi/.pyi 文件,返回绝对路径列表(与 - _TryLoadClassMembersFromPyi 的 _pyi_cache 键格式一致)。""" - try: - names: list[str] = os.listdir(temp_dir) - except OSError: - return [] - paths: list[str] = [] - for fname in sorted(names): - if fname.startswith('_'): - continue - if fname.endswith('.vpi') or fname.endswith('.pyi'): - paths.append(os.path.join(temp_dir, fname)) - return paths - - -def _PyiCacheSignature(paths: list[str]) -> list[tuple[str, int, float]]: - """vpi 清单签名:(basename, size, mtime) 元组列表,用于缓存失效判断。""" - sig: list[tuple[str, int, float]] = [] - for p in paths: - try: - st = os.stat(p) - sig.append((os.path.basename(p), st.st_size, st.st_mtime)) - except OSError: - sig.append((os.path.basename(p), -1, -1.0)) - return sig - - -def _LoadPyiCachePkl(temp_dir: str) -> dict | None: - """pickle.load temp/_pyi_cache_v1.pkl,返回 {abs_path: vast树|None}。 - 文件不存在/损坏/版本不符时返回 None(调用方回退逐文件解析)。""" - pkl_path: str = os.path.join(temp_dir, _PYI_CACHE_PKL_NAME) - try: - with open(pkl_path, 'rb') as f: - data: dict = pickle.load(f) - if not isinstance(data, dict) or data.get('version') != _PYI_CACHE_VERSION: - return None - cache = data.get('cache') - if not isinstance(cache, dict): - return None - return cache - except Exception: - return None - - -def _BuildPyiCacheFile(temp_dir: str) -> str | None: - """构建/刷新 temp/_pyi_cache_v1.pkl:遍历全部 .vpi 做 vast.parse 后 - pickle.dump(含版本号与签名)。签名(basename,size,mtime)未变时跳过。 - 返回 pkl 路径;失败(无 vpi / pickle 不兼容等)返回 None。 - 注意:vast AST 可 pickle 性由 Nodes.py 侧保证;若实际 pickle.dump - 失败,此处静默回退,worker 的 initializer 将改为逐文件 vast.parse。""" - paths: list[str] = _ScanPyiFiles(temp_dir) - if not paths: - return None - sig: list[tuple[str, int, float]] = _PyiCacheSignature(paths) - pkl_path: str = os.path.join(temp_dir, _PYI_CACHE_PKL_NAME) - sig_path: str = os.path.join(temp_dir, _PYI_CACHE_SIG_NAME) - try: - if _CachedIsFile(pkl_path) and _CachedIsFile(sig_path): - stored = json.loads(_ReadTextCached(sig_path)) - # json 往返后 tuple→list,需规范化再比较 - if stored == [[n, s, m] for n, s, m in sig]: - return pkl_path - except Exception: - pass - cache: dict[str, object] = {} - # 增量复用:Phase2 过程会新增 include .vpi(签名每次变 → 全量重建 12.2s)。 - # 加载旧 pkl,按 (basename, size, mtime) 匹配未变化的文件复用 vast 树, - # 只对新增/变化的文件重新 parse。 - old_by_name: dict[str, tuple[int, float, object | None]] = {} - try: - old = _LoadPyiCachePkl(temp_dir) - if isinstance(old, dict): - old_sig = old.get('signature') or [] - old_trees = list((old.get('cache') or {}).values()) - for _i, (_name, _size, _mtime) in enumerate(old_sig): - _tree = old_trees[_i] if _i < len(old_trees) else None - old_by_name[_name] = (_size, _mtime, _tree) - except Exception: - pass - for p, (name, size, mtime) in zip(paths, sig): - old_entry = old_by_name.get(name) - if (old_entry is not None and old_entry[0] == size - and old_entry[1] == mtime and old_entry[2] is not None): - cache[p] = old_entry[2] - continue - try: - # 走 LRU 文本缓存:同一 .vpi 在增量复用/多 worker 预热路径会被反复读 - cache[p] = ast.parse(_ReadTextCached(p), p) - except Exception: - cache[p] = None - try: - with open(pkl_path, 'wb') as f: - pickle.dump({'version': _PYI_CACHE_VERSION, 'signature': sig, 'cache': cache}, f) - with open(sig_path, 'w', encoding='utf-8') as f: - json.dump(sig, f) - return pkl_path - except Exception as e: - _vlog().warning(f"Phase2Translator: pyi 缓存落盘失败({e}),worker 将回退逐文件解析") - return None - - -def _PyiCacheWorkerInitializer(temp_dir: str) -> None: - """worker 进程启动时预热 ImportHandle._pyi_cache(类级缓存)。 - - spawn 下每 worker 重新 import 模块后 _pyi_cache 为空,首次任务会 - 重新 vast.parse 全部依赖 .vpi(4479 次 parse / 245s)。此处启动时 - 一次性填充:优先 pickle.load 主进程落盘的 _pyi_cache_v1.pkl; - 失败(文件不存在/损坏/版本不符/pickle 不兼容)回退逐文件 vast.parse。 - 预热只解析 .vpi(pyi 树小);stub 索引保留 _build_stub_index 懒加载。 - TPC_WORKER_WARMUP=0 时关闭(便于回归对比)。 - """ - if os.environ.get('TPC_WORKER_WARMUP', '1') != '1': - return - if not os.path.isdir(temp_dir): - return - from lib.core.Handles.HandlesImports import ImportHandle - cache: dict | None = _LoadPyiCachePkl(temp_dir) - if cache is not None: - ImportHandle._pyi_cache = cache - ImportHandle._pyi_cache_dir = os.path.abspath(temp_dir) - return - # 回退:逐文件 vast.parse 预热(worker 单线程,无竞争) - try: - new_cache: dict[str, object] = {} - for p in _ScanPyiFiles(temp_dir): - try: - new_cache[p] = ast.parse(_ReadTextCached(p), p) - except Exception: - new_cache[p] = None - if new_cache: - ImportHandle._pyi_cache = new_cache - ImportHandle._pyi_cache_dir = os.path.abspath(temp_dir) - except Exception: - pass - - -def _count_braces_outside_strings(text: str) -> int: - """统计花括号差值,跳过 c"..." 字符串字面量内部的括号 - - 快路径(prof 基线:本函数 102,397 次调用 / self 2.0s,占 _split_ll cumtime - 3.35s 的 83%,是全流程 str.strip 之后第二大的"纯 Python 逐字符"热点): - 行内不含 `c"` 时,"跳过字符串内部括号"这一语义退化为"整行括号计数", - 直接用 C 级 str.count 一次算完。含 `c"` 的行(主要见于 c"..." 全局串常量) - 仍走下面的逐字符扫描,语义逐字不变。 - """ - if 'c"' not in text: - return text.count('{') - text.count('}') - count = 0 - i = 0 - while i < len(text): - if text[i:i+2] == 'c"' and (i == 0 or text[i-1] != '\\'): - # 跳过 c"..." 字面量 - i += 2 - while i < len(text): - if text[i] == '"' and (i == 0 or text[i-1] != '\\'): - i += 1 - break - i += 1 - continue - if text[i] == '{': - count += 1 - elif text[i] == '}': - count -= 1 - i += 1 - return count - - -# LLVM IR "无引号标识符"的精确规则(LangRef "Identifiers";实现即 -# LLLexer::ReadVarName):首字符 ∈ [-a-zA-Z$._],后续字符 ∈ [-a-zA-Z$._0-9]。 -# 首字符**不允许是数字**:`$884f3d94ea4d0123.md5_F` 会让 LLLexer::LexDollar -# 走到 ReadVarName() 返回 false ⇒ 产出 error token ⇒ 顶层解析报 -# "expected top-level entity"。故 comdat 名必须以同一规则判定是否加引号 -# (与 LLVM 自身 AsmWriter 的输出形态一致:`$"884f3d94ea4d0123.md5_F"`)。 -# 另:引号名 @"..." 不支持转义,名字含 `"`/`\` 时两种形态都非法 ⇒ 见 -# _AddWeakComdat 内的 fail-fast(不静默替换、不跳过)。 -_LLVM_UNQUOTED_NAME_RE: re.Pattern[str] = re.compile( - r'[-a-zA-Z$._][-a-zA-Z$._0-9]*\Z') - -# comdat 声明行:`$name = comdat any` / `$"name" = comdat any`(允许行首缩进) -_RE_COMDAT_DECL: re.Pattern[str] = re.compile(r'^\s*\$("?)([^"=\s]+)\1\s*=\s*comdat\b') - - -def _DedupComdatDecls(ll_content: str) -> str: - """删除重复的 `$name = comdat any` 声明(保留首次出现)。 - - 落盘的 `_dso.ll` 是「本模块 + 各依赖 .stub.ll/.text.ll 片段」拼接而成, - 每个片段各自带一份同名 comdat 声明(片段生成时也会各自补 comdat)。同名 - 声明出现两次即 llc 报 `redefinition of comdat '$…'`(现场:泛型特化 - `list[.AST*].__before_init__` 在同一文件里出现 4 次)。comdat 声明 - 在顶层可位于任意位置,保留首次出现即可,语义不变。 - """ - if '= comdat' not in ll_content: - return ll_content - seen: set[str] = set() - kept: list[str] = [] - for line in ll_content.splitlines(True): - m = _RE_COMDAT_DECL.match(line) - if m is not None: - name: str = m.group(2) - if name in seen: - continue - seen.add(name) - kept.append(line) - return ''.join(kept) - - -def _AddWeakComdat(ll_content: str) -> str: - """为 weak/linkonce 系列 函数/全局 定义补 COMDAT(COFF/PE 必需)。 - - COFF 下无 COMDAT 的 weak/linkonce 定义会被 llc 展开成强符号 - `.weak..default`(weak external 的实现副本)。同一弱定义出现在 - 多个模块时(泛型特化方法如 `e94ef9e139461915.list[rawptr[AST]]. - __before_init__`、`linkonce`/`weak` 标注的跨模块共享常量),副本同名 → - ld.lld 报 duplicate symbol。补 COMDAT(any) 后 llc 输出单一 COMDAT - 成员,链接器自动去重(llc/ld.lld 端到端已实验验证)。 - """ - if (' linkonce ' not in ll_content and ' linkonce_odr ' not in ll_content - and ' weak_odr ' not in ll_content and ' weak ' not in ll_content): - return _DedupComdatDecls(ll_content) - # 拼接而来的片段可能各自已带同名 comdat 声明 ⇒ 先去重,再做本次补全 - ll_content = _DedupComdatDecls(ll_content) - weak_links = 'linkonce|linkonce_odr|weak|weak_odr|extern_weak' - comdat_names: list[str] = [] - - # 1) 函数定义:define [dso_local] ... @name(...) { → @name(...) comdat { - def _fn_repl(m: re.Match) -> str: - comdat_names.append(m.group(3)) - head: str = m.group(1) - # comdat 是函数头关键字,必须写在 !dbg 等元数据附着**之前**:LLVM 解析器 - # 读完 `!...` 附着后就只接受 `{`,附着之后写 comdat 会报 - # "expected '{' in function body"(`[^{]*` 会跨行吞掉换行,--dwarf 给 - # 每个 define 行尾加了 !dbg 后插入点就落到了附着之后)。故插入点前移到 - # 第一个元数据附着之前。 - attach = _RE_METADATA_ATTACH.search(head) - if attach is None: - return head + ' comdat ' + m.group(4) - return head[:attach.start()] + ' comdat' + head[attach.start():] + m.group(4) - ll_content = re.sub( - r'^(define\s+(?:dso_local\s+)?(?:%s)\s+[^@]*@("?)([^"\s\(]+)\2[^{]*)(\{)' % weak_links, - _fn_repl, ll_content, flags=re.MULTILINE) - - # 2) 全局定义:@name = [dso_local] (global|constant) → 值后补 ', comdat' - # (comdat 需位于 align/section 等尾部属性之前,符合 LLVM IR 文法) - def _g_repl(m: re.Match) -> str: - line = m.group(0) - if ' comdat' in line: - return line - comdat_names.append(m.group(3)) - ins = _RE_COMDAT_TAIL_ATTRS.search(line) - if ins: - return line[:ins.start()] + ', comdat' + line[ins.start():] - return line + ', comdat' - ll_content = re.sub( - r'^(@("?)([^"=\s]+)\2\s*=\s*(?:%s)\s+(?:dso_local\s+)?(?:global|constant)[^\n]*)' % weak_links, - _g_repl, ll_content, flags=re.MULTILINE) - - if not comdat_names: - return ll_content - decls: list[str] = [] - for name in dict.fromkeys(comdat_names): - # fail-fast:LLVM 的引号标识符 @"..." **不支持转义**(LLLexer 读到 `"` 即认为名字 - # 结束),故名字里含 `"` / `\` / 换行时,加不加引号都产不出合法 IR。此处不静默 - # 跳过、也不做字符替换(那会把符号名改得与源码/其它模块不一致,链接期更难查), - # 直接指名报错。名字来自弱符号命名 `.<标识符>`,出现这些字符说明上游符号名 - # 构造有误,应在上游修正。 - if '"' in name or '\\' in name or '\n' in name or '\r' in name: - raise RuntimeError( - f'comdat 名无法表达为 LLVM 标识符(含双引号/反斜杠/换行): {name!r};' - 'LLVM 引号名 @"..." 不支持转义,产出即非法 IR(opt 解析报 ' - '"expected top-level entity")。请在上游符号名构造处修正。') - if _LLVM_UNQUOTED_NAME_RE.match(name): - decls.append(f'${name} = comdat any') - else: - decls.append(f'$"{name}" = comdat any') - comdat_block = '\n'.join(decls) + '\n' - lines = ll_content.splitlines(True) - header_end = 0 - for i, line in enumerate(lines): - stripped = line.strip() - if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: - header_end = i + 1 - continue - break - return ''.join(lines[:header_end]) + comdat_block + ''.join(lines[header_end:]) - - -class Phase2Translator: - """阶段二:使用声明接口翻译源文件""" - - INCLUDE_LIB_EXTENSIONS: tuple[str, ...] = ('.dll', '.ll', '.so', '.o', '.a', '.lib') - INCLUDE_SRC_EXTENSIONS: tuple[str, ...] = ('.c', '.cpp', '.cc', '.cxx', '.m', '.mm') - # includes 只认项目配置(project.vpj includes),禁止仓库硬编码回退。 - # INCLUDE_DIRS 保留为空以兼容旧引用;实际目录由 __init__ 写入 PROJECT_INCLUDE_DIRS。 - INCLUDE_DIRS: list[str] = [] - PROJECT_INCLUDE_DIRS: list[str] = [] - # include 源文件 sha1/存在性缓存(_build_current_ModuleSha1Map): - # 编译期间 include .vp/.py 源文件不变,避免每文件重复 isfile + open + - # compute_sha1(PROF 显示 9306 isfile + 4653 open + TextIOWrapper.read - # 35.9s 的大头来源,99 次调用 × 全量 include 重算)。 - _include_sha1_cache: dict[str, str] = {} - _include_exist_cache: dict[str, bool] = {} - - def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, static_libs: list[str] | None = None, dynamic_libs: list[str] | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False, build_pyi_cache: bool = True, asan_mode: bool = False, dwarf_enabled: bool = False, traceback_enabled: bool = False) -> None: - self.src_root = os.path.abspath(src_root) - self.temp_dir = os.path.abspath(temp_dir) - self.output_dir = os.path.abspath(output_dir) - self.compile_cmd = compile_cmd - self.compile_flags = compile_flags or ['-filetype=obj'] - self.triple = target_triple - self.datalayout = target_datalayout - if not self.triple: - for i, flag in enumerate(self.compile_flags): - if flag.startswith('-mtriple='): - self.triple = flag[len('-mtriple='):] - elif flag == '-mtriple' and i + 1 < len(self.compile_flags): - self.triple = self.compile_flags[i + 1] - self.linker_cmd = linker_cmd - self.linker_flags = linker_flags or [] - self.linker_output = linker_output - # ASan 模式:由 Config 的 --asan 参数直传(不再用 TPC_ASAN 环境变量, - # 环境变量在 worker 子进程/外部调用链下不可靠)。 - # 检测 stdlib 堆/栈/全局越界与垃圾指针(MemBuddy pool 内部由 canary 覆盖)。 - # 注意:mingw64 的 clang 不提供 x86_64-w64-windows-gnu 目标的 ASan runtime, - # 只能借用 clang64(MSVC ABI) 的那两份:PATH 末尾追加 clang64/bin(运行期 DLL), - # 链接时显式 -l: 那两份 .a/.dll.a(见 _FindAsanRuntime 的说明)。 - self.asan_mode = asan_mode - self.asan_rt_dir: str | None = None - if self.asan_mode: - _rt = _FindAsanRuntime() - if _rt is not None: - self.asan_rt_dir, _asan_bin = _rt - # clang64/bin 必须**追加**到 PATH 末尾:既要让运行期能找到 - # libclang_rt.asan_dynamic-x86_64.dll,又不能前置 —— 前置会让裸名 - # clang++.exe / llc / opt 解析到 clang64 的 libc++ 版本,与 includes/ - # 里 mingw64(libstdc++) 编的静态库链接不上。 - if _asan_bin and os.path.isdir(_asan_bin): - os.environ['PATH'] = os.environ.get('PATH', '') + os.pathsep + _asan_bin - self.slice_level = 3 - # 函数级 DWARF 调试元数据(--dwarf;--asan 时自动开启):False=未启用 - # (零影响,不构造任何 DI 元数据);True=App 源模块的 .ll 携带 - # !llvm.dbg.cu + 每函数 DISubprogram + 入口 DILocation,供 ASan / - # llvm-symbolizer 反查 ".vp:函数起始行"。 - self.dwarf_enabled: bool = dwarf_enabled - # Ctrl+C 调用栈(--traceback):False=未启用(零影响,不调用任何外部 - # 工具、不写任何旁文件);True=链接成功后额外用 llvm-dwarfdump 把最终 - # exe 的 DWARF 行表导出为 ".stackmap"(地址→源行旁文件,供 exe 内 - # 运行时在 Ctrl+C/Ctrl+Break 时逐帧翻译返回地址)。要求 --dwarf 已开启 - # (Config 侧联动,否则行表为空),且目标为 Windows PE。 - self.traceback_enabled: bool = traceback_enabled - # 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数 - # 落盘文件路径(每函数计时落同目录 hotspot_time.txt)。各模块运行时自带 - # Ctrl+C 处理器、统一以追加方式写该路径;含 main 的模块在 main 入口清空一次 - # → 每次运行一份结果。 - self.hotspot_sites_file: str | None = None - # 是否对全部直接 call 点插桩(忽略白名单);白名单被调函数基名列表(None=默认) - self.hotspot_sites_all: bool = False - self.hotspot_callees: list[str] | None = None - self.sha1_map: dict[str, str] = {} - self.sig_files: dict[str, str] = {} - self.stub_files: dict[str, str] = {} - self.include_py_map: dict[str, str] = {} - self.used_includes: set[str] = set() - self._last_ErrorStack: list | None = None - self.function_default_args: dict[str, list] = {} - self.function_param_names: dict[str, list] = {} - self.inline_func_symbols: dict[str, CTypeInfo] = {} - self.extra_link_files: list[str] = [] - self.extra_compile_files: list[str] = [] - self.extra_py_files: list[str] = [] - # project.vpj linker.static_libs:显式静态链接文件(.a/.obj), - # 加入 extra_link_files 参与最终链接(位于 -o 之后、-l 库标志之前)。 - if static_libs: - for lib in static_libs: - if lib not in self.extra_link_files: - self.extra_link_files.append(lib) - # project.vpj linker.dynamic_libs:显式动态链接文件(.dll/.so), - # 同样作为链接输入(GNU ld 从 DLL/SO 提取导入符号);运行时需 - # DLL/SO 位于 exe 同目录 / PATH / LD_LIBRARY_PATH。 - if dynamic_libs: - for lib in dynamic_libs: - if lib not in self.extra_link_files: - self.extra_link_files.append(lib) - self._include_sha1s: set[str] = set() - # 持有 ProcessPoolExecutor 引用,避免局部变量 GC 时 finalizer 阻塞 ~99s - # (join_executor_internals 在 GC 时同步等待 worker 退出队列) - self._executor_pool: ProcessPoolExecutor | None = None - self.entry_files: list[str] | None = entry_files - self.force_recompile_includes: bool = force_recompile_includes - # pyi cache 构建开关:仅主进程编译启动时构建一次(_LoadSha1Map 内), - # worker 子进程只读主进程落盘的 temp/_pyi_cache_v1.pkl(_PyiCacheWorkerInitializer), - # 不重复构建——worker 每任务重建 pkl 的 pickle.load/dump(43 次 × ~0.5s ≈ 23s - # + pickle.loads 10.4s)纯属浪费;worker 缺 includes vpi 时运行期按需 parse 回退。 - self._build_pyi_cache: bool = build_pyi_cache - self._shared_symbol_table: object | None = None - self._shared_source_module_sig_files: dict[str, str] = {} - self._shared_all_dc: dict[str, dict] = {} - self._shared_all_decl_types: dict[str, tuple[int, bool]] = {} - self._shared_export_extern_funcs: dict[str, str] = {} - self.include_dirs: list[str] = [] - self.struct_sha1_map: dict[str, str] = {} - self._shared_generic_class_templates: dict[str, dict] = {} - self._shared_doc_meta: dict[str, dict[str, str]] = {} - # 共享 vtable 状态跨模块(修复 pool.alloc(48) 虚表分派失败导致段错误) - self._shared_class_vtable: set[str] = set() - self._shared_cross_module_vtable: set[str] = set() - self._shared_class_methods: dict[str, list[str]] = {} - self._shared_cross_module_novtable: set[str] = set() - default_dirs: list[str] = [] - if include_dirs: - seen = set() - self.include_dirs = [] - for d in list(include_dirs) + default_dirs: - d = os.path.abspath(d) - if d not in seen and os.path.isdir(d): - self.include_dirs.append(d) - seen.add(d) - else: - self.include_dirs = default_dirs - # 严格:只认项目 includes;供 builtins.vp 扫描等静态路径使用 - Phase2Translator.PROJECT_INCLUDE_DIRS = list(self.include_dirs) - self._LoadSha1Map() - - def _LoadSha1Map(self) -> None: - """从 temp_dir 加载 SHA1 映射、签名文件和 stub 声明文件列表。 - temp_dir 中缺失的文件会从全局缓存补充。 - """ - if not os.path.exists(self.temp_dir): - _vlog().error(f"声明目录不存在: {self.temp_dir}") - return - - for fname in os.listdir(self.temp_dir): - fpath = os.path.join(self.temp_dir, fname) - if fname.startswith('_'): - continue - if fname.endswith('.vpi') or fname.endswith('.pyi'): - sha1 = fname[:-4] - self.sig_files[sha1] = fpath - elif fname.endswith('.stub.ll'): - sha1 = fname[:-8] - self.stub_files[sha1] = fpath - - # 从内存存储器读取 SHA1 映射(_sha1_map.txt 仅人类可读,不再作为机器数据源) - # 延迟导入避免循环依赖(Config 导入 Phase2Translator) - from lib.Projectrans.Config import get_sha1_map_store - mem_store: dict[str, str] = get_sha1_map_store() - if mem_store: - for sha1, rel in mem_store.items(): - self.sha1_map[sha1] = rel - if rel.startswith('includes/'): - module_name = os.path.splitext(os.path.basename(rel))[0] - self.include_py_map[module_name] = sha1 - else: - # 内存存储器为空(独立运行 Phase2 的回退):从 stub 文件名推断 - for sha1, stub_path in self.stub_files.items(): - self.sha1_map[sha1] = sha1 - - # 从全局缓存补充 temp_dir 中缺失的 .vpi / .stub.ll。 - # 只补充"当前编译有效"的 sha1(在 sha1_map 中):.transpyc_cache 会残留 - # 旧版源码的缓存(如旧 __builder.vp 的 9026a429f5e2d0e0.pyi/.stub.ll 与新 - # d9b3d06934cd1898 共存),不加过滤会把旧版 .vpi/.stub.ll 也加载进 - # sig_files/stub_files → 同名类(IRBuilder)按旧 sha1 前缀注册 → - # ld.lld undefined symbol: 9026a429f5e2d0e0.new_builder。 - if os.path.isdir(_GLOBAL_CACHE_DIR): - for fname in os.listdir(_GLOBAL_CACHE_DIR): - if fname.startswith('_'): - continue - if fname.endswith('.vpi') or fname.endswith('.pyi'): - sha1 = fname[:-4] - if sha1 in self.sha1_map and sha1 not in self.sig_files: - self.sig_files[sha1] = os.path.join(_GLOBAL_CACHE_DIR, fname) - elif fname.endswith('.stub.ll'): - sha1 = fname[:-8] - if sha1 in self.sha1_map and sha1 not in self.stub_files: - self.stub_files[sha1] = os.path.join(_GLOBAL_CACHE_DIR, fname) - - # 构建 pyi AST pickle 缓存(temp/_pyi_cache_v1.pkl)供 worker 预热: - # Phase1 已生成全部 .vpi,此处把 vast 树落盘,避免每个 worker 首次任务 - # 重新 vast.parse(4479 次 parse / 245s)。签名未变时零开销跳过。 - # 仅主进程构建(build_pyi_cache=True);worker 只读 pkl,不重复构建。 - if self._build_pyi_cache and os.environ.get('TPC_WORKER_WARMUP', '1') == '1': - _BuildPyiCacheFile(self.temp_dir) - - def _build_struct_sha1_map(self) -> dict[str, str]: - struct_sha1_map = {} - valid_sha1_keys = set(self.sha1_map.keys()) - # 收集 temp_dir 和全局缓存中的 .vpi/.pyi 文件 - pyi_dirs: list[str] = [self.temp_dir] - if os.path.isdir(_GLOBAL_CACHE_DIR): - pyi_dirs.append(_GLOBAL_CACHE_DIR) - _scanned_sha1: set[str] = set() - for scan_dir in pyi_dirs: - if not os.path.isdir(scan_dir): - continue - for fname in os.listdir(scan_dir): - if not (fname.endswith('.vpi') or fname.endswith('.pyi')): - continue - sha1_key = fname.replace('.vpi', '').replace('.pyi', '') - if sha1_key not in valid_sha1_keys: - continue - # 同一模块在两处目录都有文件时只解析一次(与下面的类名归属判据无关) - if sha1_key in _scanned_sha1: - continue - _scanned_sha1.add(sha1_key) - pyi_path = os.path.join(scan_dir, fname) - # 复用 parse_python_file 的 AST 缓存(.pyi 文件可能被多处解析) - _, tree = parse_python_file(pyi_path) - if tree is None: - continue - for node in ast.iter_child_nodes(tree): - # 每类只登记一次,**先扫到的为准**:判据必须是**类名**。 - # 原实现写成 `if sha1_key in struct_sha1_map` —— 用模块 SHA1 去查一张 - # 按**类名**建的表,判据恒假 ⇒ 实际是"后扫到的覆盖先扫到的", - # 同名类的归属随 os.listdir 顺序漂移(现场:includes/ast/astaux.vp 与 - # includes/ast/tokens.vp 各有一个 `class Keyword`,定义端拿到的前缀 - # 可能是**另一个模块**的 sha1 ⇒ 同一类出现两个 struct 实例、 - # `__before_init__` 声明与调用点分叉)。 - if isinstance(node, ast.ClassDef): - # 嵌套类按扁平名 `{外类}_{内类}` 登记(与定义端 - # HandlesClassDef._EmitNestedClassDefs、Phase1Generator. - # _RegisterNestedStructNames 同源)。此前用 ast.walk 会把嵌套类按 - # **短名** `Inner` 登记 —— 既占住平坦命名空间,又与定义端的 - # `外类_内类` 不同源(跨模块前缀分裂)。 - self._RegisterStructSha1Rec(node, node.name, sha1_key, struct_sha1_map) - return struct_sha1_map - - def _RegisterStructSha1Rec(self, Node, FlatName: str, Sha1Key: str, - StructSha1Map: dict) -> None: - """递归登记"类名 → 定义模块 SHA1"(嵌套类用扁平名 `{外类}_{内类}`)""" - if not FlatName: - return - if FlatName not in StructSha1Map: - StructSha1Map[FlatName] = Sha1Key - Body = getattr(Node, 'body', None) - if not Body: - return - for Item in Body: - if not isinstance(Item, ast.ClassDef): - continue - InnerName: str | None = getattr(Item, 'name', None) - if not InnerName: - continue - self._RegisterStructSha1Rec(Item, f'{FlatName}_{InnerName}', Sha1Key, StructSha1Map) - - def run(self) -> None: - """扫描源目录,翻译并生成 .ll 含代码文件(导入遍历)""" - import sys as _sys - if self.entry_files: - py_files = list(self.entry_files) - else: - main_py = os.path.join(self.src_root, 'main.vp') - if not os.path.exists(main_py): - # 回退到 main.py(向后兼容) - main_py = os.path.join(self.src_root, 'main.py') - if os.path.exists(main_py): - py_files = [main_py] - else: - py_files = [] - for root, dirs, files in os.walk(self.src_root): - dirs[:] = [d for d in dirs if d not in ('__pycache__',)] - for file in files: - if file.endswith('.vp') or file.endswith('.py'): - py_files.append(os.path.join(root, file)) - - if not py_files: - _vlog().info("未找到入口文件或源文件") - return - - reachable = find_reachable_files_from_entries(self.src_root, py_files) - py_files = list(reachable) - - py_files = topological_sort_files(py_files, self.src_root) - - _vlog().info(f"找到 {len(py_files)} 个可达源文件(已按依赖排序)") - _vlog().info("处理顺序:") - for i, f in enumerate(py_files[:10], 1): - rel = os.path.relpath(f, self.src_root) - _vlog().info(f" {i}. {rel}") - os.makedirs(self.output_dir, exist_ok=True) - - valid_sha1s = set(self.sha1_map.keys()) - old_stub_count = len(self.stub_files) - old_sig_count = len(self.sig_files) - self.stub_files = {k: v for k, v in self.stub_files.items() if k in valid_sha1s} - self.sig_files = {k: v for k, v in self.sig_files.items() if k in valid_sha1s} - if old_stub_count != len(self.stub_files) or old_sig_count != len(self.sig_files): - _vlog().info(f"过滤旧文件: stub {old_stub_count}->{len(self.stub_files)}, sig {old_sig_count}->{len(self.sig_files)}") - - active_sha1s = set() - self._precollect_inline_symbols() - self._build_shared_symbol_data() - self._precollect_vtable_state() - self.struct_sha1_map = self._build_struct_sha1_map() - - need_translate = [] - # --hotspot-sites:缓存 .ll / .stub.ll+.text.ll 可能不含热点计数/计时 IR, - # 启用时一律重译,保证每个模块都嵌入 internal 计数器、计时器与 - # @llvm.global_ctors 自注册。未启用时 hotspot_on=False,缓存与重组分支 - # 行为与改动前完全一致(零影响)。 - # --dwarf(函数级 DWARF)同理:缓存 .ll/.obj 不含 !llvm.dbg.cu,必须一并 - # 强制重译,否则开启开关后仍复用旧产物、看不到任何 .vp 行号。 - # --traceback 依赖同一份 .debug_line 行表(Config 已联动开启 --dwarf), - # 这里显式纳入,避免将来联动改动后静默复用无 DI 的旧产物。 - hotspot_on: bool = bool(self.hotspot_sites_file) or bool(self.dwarf_enabled) or bool(self.traceback_enabled) - for i, src_path in enumerate(py_files, 1): - rel = os.path.relpath(src_path, self.src_root) - # 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过) - content, tree = parse_python_file(src_path) - sha1 = compute_sha1(content) - active_sha1s.add(sha1) - out_path = os.path.join(self.output_dir, f"{sha1}.ll") - - current_ModuleSha1Map = self._build_current_ModuleSha1Map(sha1) - - if tree is not None: - try: - for node in ast.walk(tree): - if isinstance(node, ast.Import): - for alias in node.names: - self.used_includes.add(alias.name.split('.')[0]) - elif isinstance(node, ast.ImportFrom): - if node.module: - self.used_includes.add(node.module.split('.')[0]) - # 检测 assert 使用,自动添加 testcheck 到 used_includes: - # assert 译为 testcheck.check(...),该库须被发现、编译并链接。 - if isinstance(node, ast.Assert): - self.used_includes.add('testcheck') - # 检测 list/dict 容器使用,自动添加 _list/_dict/json 到 used_includes - # 以便 _scan_include_libraries 能发现并编译这些库文件 - if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name): - if node.value.id == 'dict': - self.used_includes.add('_dict') - self.used_includes.add('json') - elif node.value.id == 'list': - self.used_includes.add('_list') - if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): - if node.func.id == 'dict': - self.used_includes.add('_dict') - self.used_includes.add('json') - elif node.func.id == 'list': - self.used_includes.add('_list') - # 检测类型注解 d: dict / l: list (非泛型形式) - if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): - if node.annotation.id == 'dict': - self.used_includes.add('_dict') - self.used_includes.add('json') - elif node.annotation.id == 'list': - self.used_includes.add('_list') - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"收集源文件导入的 include 模块失败: {_e}", "Exception") - - # --hotspot-sites:上面两个缓存/重组分支在 hotspot_on 时被跳过, - # 本轮每个模块都会强制重译 .ll;此时必须让旧的 {sha1}.obj 一并失效, - # 否则 _compile_ll_files 会因 .obj 已存在而跳过重编,复用上一次未插桩 - # 的目标文件,导致部分模块热点计时/计数静默缺失、耗时被计入调用者 self。 - # 这里用 os.path.isfile 而非 _CachedIsFile:避免把"已存在"写进存在性 - # 缓存后又被本函数删除,使后续 _compile_ll_files 读到过期缓存而漏编。 - if hotspot_on: - obj_path = os.path.join(self.output_dir, f"{sha1}.obj") - if os.path.isfile(obj_path): - try: - os.remove(obj_path) - except OSError as e: - # 不中断构建,但真实错误(如权限不足)必须打印告警,不静默吞掉 - _vlog().warning( - f"Phase2Translator: 删除热点插桩模块的旧目标文件失败: {obj_path}: {e}") - - if not hotspot_on and _CachedIsFile(out_path): - deps_changed = self._check_deps_changed(sha1, current_ModuleSha1Map) - if not deps_changed: - _vlog().info(f"[{i}/{len(py_files)}] 跳过(缓存): {rel} -> {sha1}.ll") - continue - else: - if self._recombine_ll(sha1, current_ModuleSha1Map): - _vlog().info(f"[{i}/{len(py_files)}] 重组(依赖变化): {rel} -> {sha1}.ll") - continue - _vlog().info(f"[{i}/{len(py_files)}] 重译(依赖变化): {rel} -> {sha1}.ll") - - elif not hotspot_on: - stub_cache = os.path.join(self.output_dir, f"{sha1}.stub.ll") - text_cache = os.path.join(self.output_dir, f"{sha1}.text.ll") - if _CachedIsFile(stub_cache) and _CachedIsFile(text_cache): - if self._recombine_ll(sha1, current_ModuleSha1Map): - _vlog().info(f"[{i}/{len(py_files)}] 重组(缓存恢复): {rel} -> {sha1}.ll") - continue - - need_translate.append((i, src_path, out_path, sha1, current_ModuleSha1Map)) - - if need_translate: - # 提前从 include 文件源码中提取函数参数名,供主项目编译时 keyword 参数匹配使用 - self._extract_include_param_names_from_dirs() - # 提前从 App 源文件中提取函数默认参数,供跨模块调用参数检查使用 - # (避免并行编译时,被调用函数的文件尚未编译完,导致默认参数信息缺失) - self._extract_app_default_args(need_translate) - - _vlog().info(f"\n需要翻译 {len(need_translate)} 个文件") - t_start = time.time() - - n_workers = _PickWorkerCount(len(need_translate)) - - if n_workers > 1 and len(need_translate) > 1: - _vlog().info(f" 并行翻译 (workers={n_workers})") - - shared_pickle_path = os.path.join(self.temp_dir, '_shared_sym.json') - try: - # symbol_table_bytes:SymbolTable 独立 pickle 字节,供批次 worker - # 每文件前反序列化出干净副本(SymbolTable 是编译期唯一被写的共享 - # 对象,批次内复用 trans 时必须在每个文件前重置,防止前文件 import - # 写入的符号污染后文件 → 跨模块 struct 类型前缀错位)。 - symbol_table_bytes: bytes = pickle.dumps(self._shared_symbol_table) - shared_data = { - 'symbol_table_bytes': symbol_table_bytes, - 'source_module_sig_files': self._shared_source_module_sig_files, - 'all_dc': self._shared_all_dc, - 'all_decl_types': self._shared_all_decl_types, - 'export_extern_funcs': self._shared_export_extern_funcs, - 'inline_func_symbols': self.inline_func_symbols, - 'function_default_args': self.function_default_args, - 'function_param_names': self.function_param_names, - 'generic_class_templates': self._shared_generic_class_templates, - 'shared_doc_meta': self._shared_doc_meta, - 'class_vtable': self._shared_class_vtable, - 'cross_module_vtable': self._shared_cross_module_vtable, - 'class_methods': self._shared_class_methods, - 'cross_module_novtable': self._shared_cross_module_novtable, - } - with open(shared_pickle_path, 'wb') as f: - f.write(pickle.dumps(shared_data)) - _vlog().info(f" 共享符号表已序列化 ({os.path.getsize(shared_pickle_path)//1024}KB)") - except Exception as e: - _vlog().warning(f"符号表序列化失败({e}),worker将自行构建") - shared_pickle_path = '' - - errors = [] - # 复用 self._executor_pool:避免局部变量 GC 时 finalizer 阻塞 ~99s - # (join_executor_internals 在 GC 时同步等待 worker 退出队列) - if self._executor_pool is None: - # initializer 预热 ImportHandle._pyi_cache(pickle.load 落盘缓存), - # 避免每个 worker 首次任务重新 vast.parse 全部依赖 .vpi - self._executor_pool = ProcessPoolExecutor(max_workers=n_workers, - initializer=_PyiCacheWorkerInitializer, - initargs=(self.temp_dir,)) - executor = self._executor_pool - try: - # 批次子任务模型:多个文件包装为一个子任务(batch), - # 每 worker 处理一批——Phase2Translator 创建 + pickle.load 只做 - # 一次,批内文件顺序翻译(每文件前重置 SymbolTable 防污染)。 - # 相比"一文件一任务":减少进程池调度/通信/构造开销 - # (200+ 文件 → n_workers 个批次任务)。 - batches: list[list] = [] - # 按源文件大小加权分桶(LPT 贪心:最大文件优先放入当前总成本最小的桶), - # 使各批次总成本均匀。原按文件数均分(batch_size=ceil(N/n))导致大文件 - # 集中的批次拖长尾,其他 worker 提前完成空等(任务颗粒度不均匀)。 - if len(need_translate) <= n_workers: - batches = [[item] for item in need_translate] - else: - sized_items: list = [] - for item in need_translate: - try: - _size: int = os.path.getsize(item[1]) - except OSError: - _size = 0 - sized_items.append((item, _size)) - buckets: list[list] = [[] for _ in range(n_workers)] - bucket_cost: list[int] = [0] * n_workers - for item, _size in sorted(sized_items, key=lambda t: t[1], reverse=True): - target: int = min(range(n_workers), key=lambda w: bucket_cost[w]) - buckets[target].append(item) - bucket_cost[target] += _size - batches = [b for b in buckets if b] - futures = {} - for bidx, batch in enumerate(batches): - rel_first = os.path.relpath(batch[0][1], self.src_root) - future = executor.submit( - _parallel_translate_batch, - batch, - self.src_root, self.temp_dir, self.output_dir, - self.include_dirs, self.triple, self.datalayout, - self.sha1_map, self.sig_files, self.stub_files, - self.include_py_map, self.slice_level, - shared_pickle_path, self.struct_sha1_map, - hotspot_sites_file=self.hotspot_sites_file, - hotspot_sites_all=self.hotspot_sites_all, - hotspot_callees=self.hotspot_callees, - dwarf_enabled=self.dwarf_enabled, - ) - futures[future] = (bidx, rel_first, len(batch)) - - done_files = 0 - total_files = len(need_translate) - for future in as_completed(futures): - bidx, rel_first, bcount = futures[future] - try: - batch_errors, batch, per_file_times = future.result() - for src, elapsed, rel in per_file_times: - done_files += 1 - _vlog().info(f" [{done_files}/{total_files}] {rel} ({elapsed:.1f}s)") - for src, err in batch_errors: - rel = os.path.relpath(src, self.src_root) - _vlog().error(f" {rel}: {err}") - errors.append((rel, err)) - _vlog().info(f" [批次完成] {bcount} 文件, 首个 {rel_first}") - except Exception as e: - # fail-fast:worker 已 os._exit(文件编译出错)或进程池 - # 已 broken → 立即取消全部剩余批次并终止编译,不空等。 - _vlog().error(f" 批次 {bidx} 异常(worker 已终止): {e}") - for fut in futures: - if not fut.done(): - fut.cancel() - if self._executor_pool is not None: - self._executor_pool.shutdown(wait=False, cancel_futures=True) - self._executor_pool = None - raise RuntimeError( - f"批次 {bidx} worker 异常终止,编译立即停止: {e}") - except Exception: - # 异常时立即清理,正常流程不 shutdown(留给 includes 阶段复用) - if self._executor_pool is not None: - self._executor_pool.shutdown(wait=False, cancel_futures=True) - self._executor_pool = None - raise - - if errors: - _vlog().error(f"\n{len(errors)} 个文件翻译失败,立即终止编译") - for rel, err in errors: - _vlog().error(f" {rel}: {err}") - raise RuntimeError(f"{len(errors)} 个文件翻译失败,已终止编译") - else: - for idx, (i, src_path, out_path, sha1, msm) in enumerate(need_translate): - rel = os.path.relpath(src_path, self.src_root) - t0 = time.time() - try: - self._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm, dwarf_enabled=self.dwarf_enabled) - t1 = time.time() - _vlog().info(f" [{idx+1}/{len(need_translate)}] {rel} ({t1-t0:.1f}s)") - except Exception as e: - _vlog().error(f" {rel}: {e}") - traceback.print_exc() - raise RuntimeError(f"翻译 {rel} 失败,已终止编译: {e}") from e - t_end = time.time() - _vlog().info(f" 翻译总耗时: {t_end-t_start:.1f}s") - - _vlog().info(f"\n[阶段二完成] .ll 文件生成到: {self.output_dir}") - self._scan_include_libraries() - self._compile_include_py_files(active_sha1s) - self._compile_ll_files(active_sha1s) - self._compile_include_sources() - if self.linker_cmd: - self._link_obj_files(active_sha1s) - # 清理 ProcessPoolExecutor:所有翻译已完成,shutdown(wait=False) 让 worker - # 后台退出。避免 GC finalizer 在程序末尾阻塞 ~99s(join_executor_internals) - if self._executor_pool is not None: - self._executor_pool.shutdown(wait=False, cancel_futures=True) - self._executor_pool = None - - def _build_current_ModuleSha1Map(self, self_sha1: str | None) -> dict[str, str]: - """构建当前模块的依赖 SHA1 映射(与 _translate_file 中逻辑一致)""" - ModuleSha1Map = {} - for sha1_key, sig_path in self.sig_files.items(): - if sha1_key == self_sha1: - continue - rel_path = self.sha1_map.get(sha1_key, sha1_key) - if rel_path.startswith('includes/'): - inc_mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') - inc_mod_name = inc_mod_name[len('includes/'):] - inc_short_name = inc_mod_name.split('.')[-1] if '.' in inc_mod_name else inc_mod_name - actual_sha1 = sha1_key - for inc_dir in self.include_dirs: - if os.path.isdir(inc_dir): - py_path = os.path.join(inc_dir, rel_path[len('includes/'):].replace(os.sep, '/')) - py_path = os.path.splitext(py_path)[0] + '.vp' - # 存在性/内容缓存:编译期 include 源文件不变,命中跳过磁盘 I/O - exists = Phase2Translator._include_exist_cache.get(py_path) - if exists is None: - exists = _CachedIsFile(py_path) - Phase2Translator._include_exist_cache[py_path] = exists - if not exists: - # 回退到 .py(向后兼容) - py_path = os.path.splitext(py_path)[0] + '.py' - exists = Phase2Translator._include_exist_cache.get(py_path) - if exists is None: - exists = _CachedIsFile(py_path) - Phase2Translator._include_exist_cache[py_path] = exists - if exists: - py_sha1 = Phase2Translator._include_sha1_cache.get(py_path) - if py_sha1 is None: - py_sha1 = compute_sha1(_ReadTextCached(py_path)) - Phase2Translator._include_sha1_cache[py_path] = py_sha1 - actual_sha1 = py_sha1 - break - ModuleSha1Map[inc_mod_name] = actual_sha1 - ModuleSha1Map[inc_short_name] = actual_sha1 - # Also add parent package name (e.g., 'hashlib' for 'hashlib.__init__') - if inc_short_name == '__init__' and '.' in inc_mod_name: - parent_pkg = inc_mod_name.rsplit('.', 1)[0] - ModuleSha1Map[parent_pkg] = actual_sha1 - continue - module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') - ModuleSha1Map[module_name] = sha1_key - short_name = module_name.split('.')[-1] if '.' in module_name else module_name - ModuleSha1Map[short_name] = sha1_key - # 包入口文件:注册父包名(如 'os.__init__' -> 'os'),使 import os 能解析到包级符号 - if short_name == '__init__' and '.' in module_name: - parent_pkg = module_name.rsplit('.', 1)[0] - ModuleSha1Map[parent_pkg] = sha1_key - return ModuleSha1Map - - @staticmethod - def _extract_llvm_type_name(line: str) -> str | None: - """从 LLVM IR 类型定义行提取类型名(字符串操作,不使用正则) - - 支持 list[str]、dict[str, int] 等含特殊字符的类型名。 - 例如: - %"18e6990aa81cc5b6.list[str]" = type { ... } → "18e6990aa81cc5b6.list[str]" - %struct.foo = type { ... } → "struct.foo" - """ - stripped = line.strip() - if not stripped.startswith('%'): - return None - # 处理带引号的情况: %"name" = type ... - if stripped.startswith('%"'): - end_quote = stripped.find('"', 2) - if end_quote > 0: - return stripped[2:end_quote] - return None - # 处理不带引号的情况: %name = type ... - eq_pos = stripped.find('=') - if eq_pos > 1: - return stripped[1:eq_pos].strip() - return None - - @staticmethod - def _find_all_type_refs(text: str) -> set[str]: - """查找文本中所有 %"..."/%name 类型引用(字符串操作,不使用正则) - - 支持 list[str]、dict[str, int] 等含特殊字符的类型名。 - """ - refs: set[str] = set() - i: int = 0 - n: int = len(text) - while i < n: - if text[i] == '%': - if i + 1 < n and text[i + 1] == '"': - # 带引号: %"name" - end: int = text.find('"', i + 2) - if end > 0: - name: str = text[i + 2:end] - if name: - refs.add(name) - i = end + 1 - continue - else: - # 不带引号: %name (仅匹配字母、数字、下划线、点) - j: int = i + 1 - while j < n and (text[j].isalnum() or text[j] in '._'): - j += 1 - if j > i + 1: - refs.add(text[i + 1:j]) - i = j - continue - i += 1 - return refs - - @staticmethod - def _split_ll(ll_content: str) -> tuple[str, str]: - """将 .ll 内容拆分为 stub(声明)和 text(代码)两部分 - - stub: target, 注释, %type = type, declare, @xxx = external global - text: define ... { ... }, @xxx = global/constant (带初始化器) - """ - stub_lines = [] - text_lines = [] - in_define = False - brace_depth = 0 - module_sha1 = None - defined_names = set() - - for line in ll_content.splitlines(True): - # 第一遍只关心 `define ...` 行,而合并后的 .ll 里这类行占比极低。 - # `'define' not in line` 是 C 级子串扫描 ⇒ 绝大多数行连 strip 都不做 - # (prof 实测 str.strip 1.79 亿次 / str.startswith 2.37 亿次是全流程 - # 最大的 Python 侧热点,本函数两遍逐行扫描是其主要来源)。 - if 'define' not in line: - continue - stripped = line.strip() - - if stripped.startswith('define '): - dm = _RE_DEFINE_SHA1.match(stripped) - if dm and module_sha1 is None: - module_sha1 = dm.group(1) - dm2 = _RE_DEFINE_NAME.match(stripped) - if dm2: - defined_names.add(dm2.group(1)) - - in_global_def = False - global_var_name = None - global_type_parts = [] - global_brace_depth = 0 - - for line in ll_content.splitlines(True): - # 续行(函数体指令 / 全局初始化器)在合并 .ll 里占绝大多数,而这两支 - # 只需要"行内容 + 花括号计数" ⇒ 不进 strip(前导/尾随空白不含花括号, - # _count_braces_outside_strings 的结果逐字不变)。prof 实测 str.strip - # 1.79 亿次为最大 Python 侧热点,逐行 strip 是其主要来源。 - if in_global_def: - text_lines.append(line) - global_type_parts.append(line.strip()) - # 预筛:无花括号的行必然贡献 0(与计数器返回值逐字等价,见其快路径) - if '{' in line or '}' in line: - global_brace_depth += _count_braces_outside_strings(line) - if global_brace_depth <= 0: - full_type = ' '.join(global_type_parts).strip() - depth = 0 - type_end = len(full_type) - for ci, cc in enumerate(full_type): - if cc in ('{', '['): - depth += 1 - elif cc in ('}', ']'): - depth -= 1 - elif depth == 0 and cc == ' ': - type_end = ci - break - var_type = full_type[:type_end].strip() - if var_type: - stub_lines.append(f'{global_var_name} = external global {var_type}\n') - in_global_def = False - global_var_name = None - global_type_parts = [] - global_brace_depth = 0 - continue - - if in_define: - text_lines.append(line) - # 预筛:函数体指令行绝大多数不含花括号,跳过即省掉一次函数调用 - # (prof:本函数 102k 次调用;有花括号的行仍按原语义计数) - if '{' in line or '}' in line: - brace_depth += _count_braces_outside_strings(line) - if brace_depth <= 0: - in_define = False - continue - - stripped = line.strip() - if stripped.startswith('define '): - text_lines.append(line) - in_define = True - brace_depth = _count_braces_outside_strings(stripped) - decl_line = _RE_DEFINE_PREFIX.sub('declare ', stripped) - decl_line = _RE_DEFINE_LINKAGE.sub('', decl_line) - decl_line = _RE_ALWAYSINLINE.sub('', decl_line) - paren_pos = decl_line.rfind(')') - if paren_pos >= 0: - decl_line = decl_line[:paren_pos + 1] - stub_lines.append(decl_line + '\n') - continue - - if stripped == '{' and text_lines and not in_define: - prev_stripped = text_lines[-1].strip() if text_lines else '' - if prev_stripped.startswith('define ') or prev_stripped.endswith('noredzone') or prev_stripped.endswith(')'): - text_lines.append(line) - brace_depth = 1 - in_define = True - if prev_stripped.startswith('define '): - # 与上面 define 分支同一组预编译正则(模式串一字未改), - # 避免 re 模块级的"模式串哈希→缓存查找"逐行开销 - decl_line = _RE_DEFINE_PREFIX.sub('declare ', prev_stripped) - decl_line = _RE_DEFINE_LINKAGE.sub('', decl_line) - decl_line = _RE_ALWAYSINLINE.sub('', decl_line) - paren_pos = decl_line.rfind(')') - if paren_pos >= 0: - decl_line = decl_line[:paren_pos + 1] - stub_lines.append(decl_line + '\n') - continue - - if stripped.startswith('@') and ' global ' in stripped and 'external global' not in stripped: - text_lines.append(line) - # LLVM 保留全局(如 --hotspot-sites 的 @llvm.global_ctors;名字含点故 - # 最终 IR 保留引号)是 appending 定义,禁止改写为 "external global" - # 存根(否则 .ll 重组后 llc 报错) - if _RE_LLVM_INTRINSIC.match(stripped): - continue - if ' internal ' not in stripped and ' private ' not in stripped: - name_match = _RE_AT_NAME.match(stripped) - if name_match: - var_name = name_match.group(1) - global_pos = stripped.find(' global ') - after_global = stripped[global_pos + 8:] - depth = 0 - type_end = len(after_global) - for ci, cc in enumerate(after_global): - if cc in ('{', '['): - depth += 1 - elif cc in ('}', ']'): - depth -= 1 - elif depth == 0 and cc == ' ': - type_end = ci - break - var_type = after_global[:type_end].strip() - brace_depth_gv = 0 - for cc in var_type: - if cc == '{': - brace_depth_gv += 1 - elif cc == '}': - brace_depth_gv -= 1 - if brace_depth_gv > 0: - in_global_def = True - global_var_name = var_name - global_type_parts = [var_type] - global_brace_depth = brace_depth_gv - elif var_type: - stub_lines.append(f'{var_name} = external global {var_type}\n') - continue - if stripped.startswith('@') and ' constant ' in stripped: - text_lines.append(line) - continue - - if stripped.startswith('declare'): - dm = _RE_DECLARE_NAME.match(stripped) - if dm: - fname = dm.group(1) - if fname in defined_names: - stub_lines.append(line) - continue - dot_pos = fname.find('.') - if dot_pos > 0: - fname_sha1 = fname[:dot_pos] - if module_sha1 and fname_sha1 != module_sha1 and len(fname_sha1) >= 16: - continue - if _RE_SHA1_16_DOT.match(fname): - continue - stub_lines.append(line) - continue - - stub_lines.append(line) - - return ''.join(stub_lines), ''.join(text_lines) - - def _save_deps(self, sha1: str, ModuleSha1Map: dict[str, str]) -> None: - """保存依赖指纹到 .deps.json""" - deps_path = os.path.join(self.output_dir, f"{sha1}.deps.json") - deps = {} - for mod_name, dep_sha1 in ModuleSha1Map.items(): - deps[mod_name] = dep_sha1 - with open(deps_path, 'w', encoding='utf-8') as f: - json.dump(deps, f) - _InvalidateTextCache(deps_path) - - def _LoadDeps(self, sha1: str) -> dict[str, str] | None: - """加载依赖指纹""" - deps_path = os.path.join(self.output_dir, f"{sha1}.deps.json") - if _CachedIsFile(deps_path): - try: - return json.loads(_ReadTextCached(deps_path)) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"加载依赖指纹 JSON 失败: {_e}", "Exception") - return None - - def _check_deps_changed(self, sha1: str, current_ModuleSha1Map: dict[str, str]) -> bool: - """检查依赖指纹是否变化""" - saved_deps = self._LoadDeps(sha1) - if saved_deps is None: - return True - for mod_name, dep_sha1 in current_ModuleSha1Map.items(): - if saved_deps.get(mod_name) != dep_sha1: - return True - for mod_name, dep_sha1 in saved_deps.items(): - if current_ModuleSha1Map.get(mod_name) != dep_sha1: - return True - return False - - def _recombine_ll(self, sha1: str, current_ModuleSha1Map: dict[str, str]) -> bool: - """依赖变化时重新组合 .ll:更新 .stub.ll 中的 SHA1 前缀,拼接 .stub.ll + .text.ll""" - saved_deps = self._LoadDeps(sha1) - if saved_deps is None: - return False - - stub_path = os.path.join(self.output_dir, f"{sha1}.stub.ll") - text_path = os.path.join(self.output_dir, f"{sha1}.text.ll") - ll_path = os.path.join(self.output_dir, f"{sha1}.ll") - - if not _CachedIsFile(stub_path) or not _CachedIsFile(text_path): - return False - - stub_content = _ReadTextCached(stub_path) - text_content = _ReadTextCached(text_path) - - sha1_replacements = {} - for mod_name, old_sha1 in saved_deps.items(): - new_sha1 = current_ModuleSha1Map.get(mod_name) - if new_sha1 and old_sha1 != new_sha1: - sha1_replacements[old_sha1] = new_sha1 - - if sha1_replacements: - for old_sha1, new_sha1 in sha1_replacements.items(): - stub_content = stub_content.replace(old_sha1, new_sha1) - text_content = text_content.replace(old_sha1, new_sha1) - - combined = stub_content - if not combined.endswith('\n'): - combined += '\n' - combined += text_content - - with open(ll_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(combined) - with open(stub_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(stub_content) - with open(text_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(text_content) - _InvalidateTextCache(ll_path) - _InvalidateTextCache(stub_path) - _InvalidateTextCache(text_path) - - self._save_deps(sha1, current_ModuleSha1Map) - - obj_path = os.path.join(self.output_dir, f"{sha1}.obj") - if _CachedIsFile(obj_path): - os.remove(obj_path) - - return True - - def _inject_auto_imports(self, code: str, src_path: str) -> str: - """检测源码中对 list/dict 容器的使用,自动注入 import _list / import _dict。 - 当使用 dict 时,同时注入 import json (用于 dict 的 JSON 处理功能)。 - 检测到 assert 语句时自动注入 import testcheck(assert 译为 - testcheck.check(...),需该模块的跨模块符号声明)。 - - 检测模式: - - list[T] / dict[V] 作为类型注解 (ast.Subscript, value=Name(id='list'/'dict')) - - list[T](...) / dict[V](...) 作为构造函数调用 (ast.Call, func=Subscript) - - list(...) / dict(...) 作为构造函数调用 (ast.Call, func=Name) - - assert 语句 (ast.Assert) - """ - try: - # `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析: - # 否则此处 SyntaxError 会静默 return 原 code(自动注入 import _list/_dict - # 失效),与主翻译路径(Shell/TransPyC 已展开)口径不一致。 - tree = ast.parse(ExpandSoftKeywords(code), src_path) - except SyntaxError: - return code - - # 检查是否已经导入了 _list / _dict / json / testcheck - has_list_import: bool = False - has_dict_import: bool = False - has_json_import: bool = False - has_testcheck_import: bool = False - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.Import): - for alias in node.names: - if alias.name == '_list': - has_list_import = True - elif alias.name == '_dict': - has_dict_import = True - elif alias.name == 'json': - has_json_import = True - elif alias.name == 'testcheck': - has_testcheck_import = True - elif isinstance(node, ast.ImportFrom): - if node.module == '_list': - has_list_import = True - elif node.module == '_dict': - has_dict_import = True - elif node.module == 'json': - has_json_import = True - elif node.module == 'testcheck': - has_testcheck_import = True - if has_list_import and has_dict_import and has_json_import and has_testcheck_import: - break - - # 检查是否使用了 list / dict 容器,以及是否含 assert 语句 - uses_list: bool = False - uses_dict: bool = False - uses_assert: bool = False - for node in ast.walk(tree): - if isinstance(node, ast.Assert): - uses_assert = True - if isinstance(node, ast.Subscript): - if isinstance(node.value, ast.Name): - if node.value.id == 'list': - uses_list = True - elif node.value.id == 'dict': - uses_dict = True - if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): - if node.func.id == 'list': - uses_list = True - elif node.func.id == 'dict': - uses_dict = True - # 检测类型注解 d: dict / l: list (非泛型形式) - if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): - if node.annotation.id == 'dict': - uses_dict = True - elif node.annotation.id == 'list': - uses_list = True - - # 注入 import 到源码开头 - prefix: str = '' - if uses_list and not has_list_import: - prefix += 'import _list\n' - if uses_dict and not has_dict_import: - prefix += 'import _dict\n' - if uses_dict and not has_json_import: - prefix += 'import json\n' - # 含 assert 的模块等价于在文件顶部写下 import testcheck(已导入则不重复) - if uses_assert and not has_testcheck_import: - prefix += 'import testcheck\n' - if prefix: - return prefix + code - return code - - def _translate_file(self, src_path: str, out_path: str, prebuilt_ModuleSha1Map: dict[str, str] | None = None, dwarf_enabled: bool = False) -> None: - """翻译单个源文件,嵌入相关 .stub.ll 声明 - - dwarf_enabled: 函数级 DWARF 调试元数据(--dwarf),默认 False(零影响)。 - App 源模块在 --dwarf/--asan/--traceback 时传 True。include 文件默认 - 传 False(其 .obj 会落进跨项目共享的 includes.binary 预编译缓存,注入 DI - 会污染该缓存);仅 --traceback 时传 True——此时 include 的 .obj 改落 - 隔离目录(includes.binary_traceback,见 _get_precompiled_obj_path), - 不会污染共享缓存。 - """ - _t0: float = time.time() - # 每个文件翻译入口置脏:本文件/其它文件在此之间新写的 *.stub.ll 全部可见 - # (ImportHandle._build_stub_index 的零 syscall 快路径据此判断是否重扫目录; - # 顺序模式与 worker 模式的每文件粒度皆覆盖,见 MarkStubSetDirty)。 - from lib.core.Handles.HandlesImports import ImportHandle as _IH - _IH.MarkStubSetDirty() - code = _ReadTextCached(src_path) - - # SHA1 以主流程 out_path 文件名为准(= 主流程 compute_sha1(parse_python_file 内容)), - # 必须在自动导入注入之前确定。不可在此重读文件重算:主流程收集(need_translate) - # 与本次实际翻译之间若文件内容变化(如编辑器保存),重算会得到不同 sha1, - # 导致函数定义前缀 ≠ 文件名/调用方引用前缀 → 链接 undefined symbol - # (实例:696d41ca86e0a29a.BuildTopoSortedIndices 定义被生成成 5536… 前缀)。 - sha1 = os.path.splitext(os.path.basename(out_path))[0] - cur_sha1 = compute_sha1(code) - if cur_sha1 != sha1: - _vlog().warning( - f"模块内容在收集后已变化(文件名sha1={sha1}, 当前sha1={cur_sha1}): {src_path}," - f"仍按主流程 sha1 生成前缀以保证链接一致;下次构建将采用新 sha1", "SHA1") - - # 自动导入: 检测 list 容器使用,自动注入 import _list - # 注入行会让 AST 节点 lineno 整体下移 N(磁盘真实行号 = AST 行号 - N); - # 记下 N 供报错渲染还原磁盘行号(如 _RaiseUnresolvedExpr)。 - _raw_newlines: int = code.count('\n') - code = self._inject_auto_imports(code, src_path) - _line_offset: int = code.count('\n') - _raw_newlines - - if prebuilt_ModuleSha1Map is not None: - ModuleSha1Map = prebuilt_ModuleSha1Map - else: - ModuleSha1Map = self._build_current_ModuleSha1Map(sha1) - - trans = TransPyC.TransPyC(code=code, triple=self.triple, datalayout=self.datalayout) - trans.translator.CurrentFile = src_path - # 函数级 DWARF(--dwarf):透传给 Translator,由其传给每个新 LlvmCodeGenerator; - # 未启用时不设置 → 与改动前逐字节一致(零影响) - if dwarf_enabled: - trans.translator.DwarfEnabled = True - # Ctrl+C 调用栈(--traceback):仅当本文件就是 traceback 模块时置位—— - # 只有该模块需要注入 @llvm.global_ctors 构造器调用自己的 Install() - # (见 BaseGen._RegisterTracebackInstallViaCtor)。其它模块不设置该属性 - # → Gen.traceback_ctor 保持 False,IR 与改动前逐字节一致(零影响)。 - _traceback_ctor: bool = bool( - self.traceback_enabled - and os.path.basename(src_path) in _TRACEBACK_MODULE_FILES) - if _traceback_ctor: - trans.translator.TracebackCtor = True - trans.SliceLevel = self.slice_level - trans.translator.SliceLevel = self.slice_level - trans.translator.SliceCount = 0 - trans.translator.SliceInfos = [] - trans.translator.LlvmGen = None - trans.translator._module_sha1 = sha1 - trans.translator._line_offset = _line_offset - trans.translator._ModuleSha1Map = ModuleSha1Map - trans.translator._struct_sha1_map = self.struct_sha1_map - trans.translator._global_function_default_args = self.function_default_args - trans.translator._global_function_param_names = self.function_param_names - trans.translator._temp_dir = self.temp_dir - # includes 只认 project.vpj 配置的 include 目录(不再回退仓库 TransPyCRoot/includes): - # 目录重组/缺失时避免声明解析从仓库或 sig 回退路径产生错误时序(memhub 未注册→i32) - _proj_inc = [os.path.abspath(d) for d in self.include_dirs if os.path.isdir(d)] - trans.translator.LibraryPaths = _proj_inc if _proj_inc else ['.'] - trans.translator._shared_doc_meta = self._shared_doc_meta - # 传递共享 vtable 状态 - trans.translator._shared_class_vtable = self._shared_class_vtable - trans.translator._shared_cross_module_vtable = self._shared_cross_module_vtable - trans.translator._shared_class_methods = self._shared_class_methods - trans.translator._shared_cross_module_novtable = self._shared_cross_module_novtable - - # 调用点热点统计与每函数计时(--hotspot-sites,Projectrans 多模块): - # 各模块处理器统一以追加方式写最终 ,注册策略为本模块 - # @llvm.global_ctors 自注册(见 BaseGen._InstallHotspotSitesModuleLocal); - # self.hotspot_sites_file 为 None 时不设置任何属性 → 与未启用时逐字节一致。 - if self.hotspot_sites_file: - trans.translator.HotspotSitesFile = self.hotspot_sites_file - trans.translator.HotspotSitesAll = self.hotspot_sites_all - trans.translator.HotspotCallees = self.hotspot_callees - trans.translator.HotspotSitesModuleLocal = True - - if self._shared_symbol_table is not None: - trans.translator.SymbolTable = self._shared_symbol_table - # 确保共享符号表的 translator 指向当前 translator,以便 import 别名解析正确 - if hasattr(self._shared_symbol_table, 'translator'): - self._shared_symbol_table.translator = trans.translator - trans.translator._source_module_sig_files = self._shared_source_module_sig_files - trans.translator._all_define_constants = self._shared_all_dc - trans.translator._define_decl_types = self._shared_all_decl_types - trans.translator._export_extern_funcs = self._shared_export_extern_funcs - if self._shared_generic_class_templates: - if not hasattr(trans.translator.ClassHandler, '_generic_class_templates'): - trans.translator.ClassHandler._generic_class_templates = {} - trans.translator.ClassHandler._generic_class_templates.update(self._shared_generic_class_templates) - else: - export_extern_funcs = {} - for sha1_key, sig_path in self.sig_files.items(): - if sha1_key == sha1: - continue - try: - sig_content = _ReadTextCached(sig_path) - sig_tree = ast.parse(sig_content, sig_path) - for node in ast.iter_child_nodes(sig_tree): - if isinstance(node, ast.FunctionDef): - if _check_annotation_for_export(node.returns): - export_extern_funcs[node.name] = sha1_key - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"收集导出外部函数声明失败: {_e}", "Exception") - if export_extern_funcs: - trans.translator._export_extern_funcs = export_extern_funcs - - for includes_dir in self.include_dirs: - if os.path.isdir(includes_dir): - for pyi_file in os.listdir(includes_dir): - if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'): - pyi_path = os.path.join(includes_dir, pyi_file) - module_name = os.path.splitext(pyi_file)[0] - trans.translator.SymbolTable.LoadModuleSymbols(pyi_path, module_name, lineno=0) - for py_file in os.listdir(includes_dir): - if (py_file.endswith('.vp') or py_file.endswith('.py')) and (not py_file.startswith('_') or py_file == '_list.vp' or py_file == '_list.py' or py_file == '_dict.vp' or py_file == '_dict.py'): - module_name = os.path.splitext(py_file)[0] - sha1_key = self.include_py_map.get(module_name) - if sha1_key and sha1_key in self.sig_files: - sig_path = self.sig_files[sha1_key] - trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) - # 收集需要重导出的包,等所有模块符号加载完后再处理 - _pending_reexports = [] - for sha1_key, sig_path in self.sig_files.items(): - rel_path = self.sha1_map.get(sha1_key, sha1_key) - if rel_path.startswith('includes/'): - mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') - mod_name = mod_name[len('includes/'):] - trans.translator.SymbolTable.LoadModuleSymbols(sig_path, mod_name, lineno=0) - if mod_name.endswith('.__init__'): - package_name = mod_name[:-len('.__init__')] - _pending_reexports.append((sig_path, package_name)) - - # 所有 includes 模块符号已加载,现在处理包重导出 - for sig_path, package_name in _pending_reexports: - self._register_package_reexports(trans, sig_path, package_name) - - for sha1_key, sig_path in self.sig_files.items(): - if sha1_key == sha1: - continue - rel_path = self.sha1_map.get(sha1_key, sha1_key) - if rel_path.startswith('includes/'): - continue - module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') - trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) - trans.translator._source_module_sig_files[module_name] = sig_path - short_name = module_name.split('.')[-1] if '.' in module_name else module_name - if short_name != module_name: - trans.translator._source_module_sig_files[short_name] = sig_path - - all_dc = {} - all_dt: dict[str, tuple[int, bool]] = {} - for sha1_key, stub_path in self.stub_files.items(): - if sha1_key == sha1: - continue - if os.path.exists(stub_path): - try: - stub_content = _ReadTextCached(stub_path) - for line in stub_content.splitlines(): - line = line.strip() - if line.startswith('@') and '= global' in line and 'i32' in line and not line.startswith('@llvm'): - var_name = line.split('=')[0].strip().lstrip('@') - val_part = line.split('i32')[-1].strip().rstrip(']').lstrip('[').lstrip('i32') - try: - val = int(val_part) - all_dc[var_name] = val - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - for sha1_key, pyi_path in self.sig_files.items(): - if sha1_key == sha1: - continue - _ExtractCDefineConstantsFromPyi(pyi_path, all_dc, trans.translator.SymbolTable, all_dt) - if all_dc: - trans.translator._all_define_constants = all_dc - if all_dt: - trans.translator._define_decl_types = all_dt - - for sym_name, sym_info in self.inline_func_symbols.items(): - if sym_name not in trans.translator.SymbolTable: - trans.translator.SymbolTable[sym_name] = sym_info - else: - existing = trans.translator.SymbolTable[sym_name] - if not getattr(existing, 'InlineBody', None) and getattr(sym_info, 'InlineBody', None): - existing.IsInline = sym_info.IsInline - existing.InlineBody = sym_info.InlineBody - existing.InlineParams = sym_info.InlineParams - - try: - result = trans.Convert( - OutputFilename=out_path, - SourceFilename=src_path, - target='llvm' - ) - except Exception as e: - error_stack = getattr(trans.translator, '_ErrorStack', []) - if error_stack: - self._last_ErrorStack = error_stack - # 语句栈渲染:error_stack 按"由内向外"入栈(内层语句先 append), - # 这里反转为"外层 → 内层"的调用路径;**异常消息只在最内层印一次**, - # 其余帧只印位置——否则同一段长 fail-fast 报文会被每个帧重复一遍 - # (旧格式把消息贴在每一帧后面,输出里同一句话出现 N 次,不可读)。 - entries: list = [entry for entry in error_stack if len(entry) >= 3] - chain_lines: list[str] = [] - for chain_idx, entry in enumerate(reversed(entries)): - exc_msg, line_info = entry[1], entry[2] - if chain_idx == len(entries) - 1: - chain_lines.append(" %s\n %s" % (line_info, exc_msg)) - else: - chain_lines.append(" %s" % (line_info,)) - if chain_lines: - enriched = str(e) + "\n" + "\n".join(chain_lines) - raise type(e)(enriched) from e - node_info = "" - if hasattr(trans.translator, 'LlvmGen') and trans.translator.LlvmGen: - node_info = trans.translator.LlvmGen._get_node_info() - if node_info: - enriched = "%s\n %s" % (str(e), node_info.strip()) - raise type(e)(enriched) from e - raise - - if result and isinstance(result, str): - for func_name, func_def in trans.translator.FunctionDefCache.items(): - if hasattr(func_def, 'args'): - mangled = trans.translator.LlvmGen._mangle_func_name(func_name) if trans.translator.LlvmGen else func_name - # 提取参数名 - if hasattr(func_def.args, 'args'): - param_names = [arg.arg for arg in func_def.args.args] - self.function_param_names[mangled] = param_names - self.function_param_names[func_name] = param_names - # 提取默认值 - if hasattr(func_def.args, 'defaults') and func_def.args.defaults: - defaults = [] - for d in func_def.args.defaults: - if isinstance(d, ast.Constant): - defaults.append(d.value) - else: - defaults.append(None) - self.function_default_args[mangled] = defaults - self.function_default_args[func_name] = defaults - for sym_name, sym_info in trans.translator.SymbolTable.items(): - if isinstance(sym_info, CTypeInfo) and getattr(sym_info, 'IsInline', False) and getattr(sym_info, 'InlineBody', None): - self.inline_func_symbols[sym_name] = sym_info - - out_dir = os.path.dirname(out_path) - if out_dir: - os.makedirs(out_dir, exist_ok=True) - # --traceback:核对注入钩子确实生效——本模块要求注入 @llvm.global_ctors - # 构造器(StructGen.finalize 里落成 .ll 文本之前),若产物中没有该全局 - # 说明钩子不可用(如序列化路径被改动绕过),显式报错终止,不静默产出 - # 缺注册的产物(那会让整个功能静默失效)。 - if _traceback_ctor and 'llvm.global_ctors' not in result: - raise RuntimeError( - '--traceback: traceback 模块未注入 @llvm.global_ctors ' - '(模块级收尾钩子未生效,不静默降级)') - # 修复 LLVM 22+: external global 不能有初值,改为 linkonce - result = _RE_EXTERNAL_GLOBAL_VALUE.sub(r'\1 = linkonce global \2 \3', result) - # 后处理:添加 llvmlite 不支持的 LLVM 属性(如 willreturn, mustprogress) - pending_str_attrs = getattr(trans.translator.LlvmGen, '_pending_str_attrs', None) - if pending_str_attrs: - for func_name, attrs in pending_str_attrs.items(): - attrs_str = ' '.join(attrs) - # 匹配 define ... @func_name(...) ... { 并在 { 之前添加属性 - pattern = rf'(define\s+[^@]*@{re.escape(func_name)}\s*\([^)]*\))([^{{]*)(\{{)' - result, n = re.subn(pattern, rf'\1\2 {attrs_str}\3', result, count=1) - if n == 0: - _vlog().warning(f"后处理添加属性失败: 未找到函数 {func_name} 的定义") - with open(out_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(result) - _InvalidateTextCache(out_path) - - stub_content, text_content = self._split_ll(result) - base = os.path.splitext(out_path)[0] - with open(base + '.stub.ll', 'w', encoding='utf-8', newline='\n') as f: - f.write(stub_content) - _InvalidateTextCache(base + '.stub.ll') - # 新 stub 已落盘 → 置脏,使本文件后续(及下个文件)的索引 miss 能重扫到它 - from lib.core.Handles.HandlesImports import ImportHandle as _IH2 - _IH2.MarkStubSetDirty() - with open(base + '.text.ll', 'w', encoding='utf-8', newline='\n') as f: - f.write(text_content) - _InvalidateTextCache(base + '.text.ll') - self._save_deps(sha1, ModuleSha1Map) - - # 进度日志由调用方统一打印(主进程按文件粒度 [x/total]),此处不再重复输出。 - else: - stub_path = self.stub_files.get(sha1) - if stub_path and os.path.exists(stub_path): - out_dir = os.path.dirname(out_path) - if out_dir: - os.makedirs(out_dir, exist_ok=True) - stub_content = _ReadTextCached(stub_path) - with open(out_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(stub_content) - # 进度日志由调用方统一打印。 - - def _register_package_reexports(self, trans, init_pyi_path: str, package_name: str) -> None: - """解析 __init__.pyi 中的 from .xxx import yyy 或 from __xxx import yyy,在包级别注册导出符号""" - try: - # 复用 parse_python_file 的 AST 缓存 - _, tree = parse_python_file(init_pyi_path) - if tree is None: - return - for node in ast.walk(tree): - if isinstance(node, ast.ImportFrom) and node.module: - sub_module = node.module.lstrip('.') - for alias in node.names: - symbol_name = alias.name - exported_name = alias.asname if alias.asname else symbol_name - source_keys = [ - f"{package_name}.{sub_module}.{symbol_name}", - f"{package_name}.__{sub_module}.{symbol_name}" if not sub_module.startswith('__') else None, - symbol_name, - ] - target_key = f"{package_name}.{exported_name}" - if target_key not in trans.translator.SymbolTable: - for source_key in source_keys: - if source_key and source_key in trans.translator.SymbolTable: - trans.translator.SymbolTable[target_key] = trans.translator.SymbolTable[source_key] - break - if exported_name not in trans.translator.SymbolTable: - for source_key in source_keys: - if source_key and source_key in trans.translator.SymbolTable: - trans.translator.SymbolTable[exported_name] = trans.translator.SymbolTable[source_key] - break - except Exception as e: - _vlog().warning(f" [警告] 解析包重导出失败 {init_pyi_path}: {e}") - - def _SanitizeIRFile(self, ll_path: str) -> str: - """送入 opt/llc 之前,对该 .ll 做一次元数据规整(见 IRMetadataFix)。 - - App 模块的 IR 是「本模块 IR + 各依赖 .stub.ll 片段」直接拼接而成,而 - --dwarf(--asan 自动开启)给每份片段都加了从 !0 开始编号的 DI 元数据, - 合并文件里同一个 !N 被定义多次 → opt/llc 报 "Metadata id is already - used"、命名元数据 "defined more than once"。规整后编号全局唯一、 - 命名元数据只留一份。无元数据 / 已规整的文件逐字节不变(no-op), - 原地覆盖没有额外产物。 - """ - with open(ll_path, 'r', encoding='utf-8', newline='') as f: - text = f.read() - fixed = SanitizeIRMetadata(text) - if fixed != text: - with open(ll_path, 'w', encoding='utf-8', newline='') as f: - f.write(fixed) - _InvalidateTextCache(ll_path) - return ll_path - - def _llc_compile(self, ll_path: str, obj_path: str, - cwd: str | None = None) -> subprocess.CompletedProcess: - """编译单个 .ll 为 obj。 - - ASan 模式(--asan)下先对 IR 执行 opt 插桩: - 检测 stdlib 堆/栈/全局越界写与垃圾指针解引用(MemBuddy pool 内部 - 由块尾 canary 覆盖,两者互补)。opt 失败返回其结果(returncode!=0)。 - - LLVM >= 20 起 opt -passes=asan 只对带 sanitize_address 函数属性的 - 函数插桩(clang -fsanitize=address 自动加;手写 IR 没有)—— - 先 forceattrs 批量加属性再 asan,否则只链接 runtime 不插桩。 - """ - ll_path = self._SanitizeIRFile(ll_path) - compile_input = ll_path - if self.asan_mode: - asan_ll = ll_path[:-3] + '_asan.ll' - # -asan-globals=false:TPC 对全局变量(如各模块 _g_gen)用直接地址寻址, - # ASan 全局 redzone 重定位后引用错位会导致读到垃圾指针(如 - # register_struct 的 self=0xbebebebe...)。先关闭全局插桩排除该因素。 - opt_cmd = ['opt', '-passes=forceattrs,asan', '-force-attribute=sanitize_address', - '-asan-globals=false', ll_path, '-o', asan_ll] - opt_r = subprocess.run(opt_cmd, capture_output=True, text=True, cwd=cwd) - if opt_r.returncode != 0: - _vlog().error(f" opt ASan 插桩失败: {opt_r.stderr.strip()}") - return opt_r - compile_input = asan_ll - cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input] - if self.traceback_enabled: - # --traceback:让 llc 保留帧指针(-frame-pointer=all)。dbghelp 主路径 - # 不依赖帧指针,但符号表不可用时要靠 RBP 链兜底——llc 默认 - # omit-frame-pointer 会让 RBP 链只走出 1~2 帧(实测"共 2 帧")。 - # 只在开启该开关时加,关闭时命令与改前逐字一致。 - cmd = [self.compile_cmd] + self.compile_flags \ - + ['-frame-pointer=all', '-o', obj_path, compile_input] - return subprocess.run(cmd, capture_output=True, text=True, cwd=cwd) - - def _compile_ll_files(self, active_sha1s: set[str]) -> None: - """将 output_dir 中属于 active_sha1s 的 .ll 文件编译为 .obj(跳过已编译过的 include 文件)""" - # 第一次 walk:清理 stale 文件(.obj/.deps.json/纯 .ll,排除 .stub.ll/.text.ll) - stale_files = [] - for root, dirs, files in os.walk(self.output_dir): - for file in files: - if file.endswith('.obj'): - sha1 = file[:-4] - if sha1 not in active_sha1s: - stale_files.append(os.path.join(root, file)) - elif file.endswith('.deps.json'): - sha1 = file[:-10] - if sha1 not in active_sha1s: - stale_files.append(os.path.join(root, file)) - elif file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): - sha1 = file[:-3] - if sha1 not in active_sha1s: - stale_files.append(os.path.join(root, file)) - if stale_files: - for fpath in stale_files: - try: - os.remove(fpath) - # 删除也是"本进程改变了该路径":不失效会让后续读拿到已删文件的旧文本 - _InvalidateTextCache(fpath) - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - _vlog().info(f"[清理] 删除 {len(stale_files)} 个过时文件") - - # 第二次 walk:收集需要编译的 .ll 文件(排除 .stub.ll/.text.ll) - ll_files = [] - for root, dirs, files in os.walk(self.output_dir): - for file in files: - if file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): - sha1 = file[:-3] - if sha1 in active_sha1s: - fpath = os.path.join(root, file) - obj_path = os.path.join(root, file[:-3] + '.obj') - if not _CachedIsFile(obj_path): - ll_files.append(fpath) - if not ll_files: - _vlog().info("[编译] 无 .ll 文件需要重新编译") - else: - _vlog().info(f"\n[编译] 找到 {len(ll_files)} 个 .ll 文件需要编译") - - # 第三次 walk:收集 all_ll_files(用于 alwaysinline 检测,排除 include sha1s) - all_ll_files = [] - for root, dirs, files in os.walk(self.output_dir): - for file in files: - if file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): - sha1 = file[:-3] - if sha1 in active_sha1s and sha1 not in self._include_sha1s: - all_ll_files.append(os.path.join(root, file)) - - has_inline = False - for ll_path in all_ll_files: - try: - content = _ReadTextCached(ll_path) - if 'alwaysinline' in content: - has_inline = True - break - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - - if has_inline: - llvm_link = shutil.which('llvm-link') or shutil.which('llvm-link.exe') - opt_cmd = shutil.which('opt') or shutil.which('opt.exe') - if llvm_link and opt_cmd: - _vlog().info(" [内联] 检测到 alwaysinline 函数,执行跨模块内联优化...") - for ll_path in all_ll_files: - try: - content = _ReadTextCached(ll_path) - type_defs = [] - type_def_indices = set() - for i, line in enumerate(content.splitlines()): - stripped = line.strip() - if stripped.startswith('%') and '= type' in stripped: - type_defs.append(line) - type_def_indices.add(i) - if type_def_indices: - lines = content.splitlines(True) - content_no_types = ''.join(line for i, line in enumerate(lines) if i not in type_def_indices) - header_end = 0 - for i, line in enumerate(content_no_types.splitlines()): - stripped = line.strip() - if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: - header_end = i + 1 - continue - break - lines2 = content_no_types.splitlines(True) - fixed = ''.join(lines2[:header_end]) + '\n'.join(type_defs) + '\n' + ''.join(lines2[header_end:]) - with open(ll_path, 'w', encoding='utf-8') as f: - f.write(fixed) - _InvalidateTextCache(ll_path) - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - merged_ll = os.path.join(self.output_dir, '_merged.ll') - optimized_ll = os.path.join(self.output_dir, '_merged_opt.ll') - try: - link_cmd = [llvm_link] + all_ll_files + ['-o', merged_ll] - result = subprocess.run(link_cmd, capture_output=True, text=True) - if result.returncode != 0: - _vlog().warning(f" llvm-link 失败: {result.stderr.strip()},回退到单独编译") - has_inline = False - else: - opt_result_cmd = [opt_cmd, '-passes=always-inline', merged_ll, '-S', '-o', optimized_ll] - result = subprocess.run(opt_result_cmd, capture_output=True, text=True) - if result.returncode != 0: - _vlog().warning(f" opt 失败: {result.stderr.strip()},回退到单独编译") - has_inline = False - else: - _vlog().info(" [内联] 跨模块内联优化完成") - opt_content = _ReadTextCached(optimized_ll) - opt_content = _RE_DEFINE_DECLARE_LINKAGE.sub(r'\1 \2 dso_local ', opt_content) - opt_content = _RE_DEFINE_DECLARE_NO_LINKAGE_AT.sub(r'\1 dso_local ', opt_content) - # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 - opt_content = _RE_EXTERNAL_GLOBAL_VALUE_PLAIN.sub(r'\1 = linkonce global \2 \3', opt_content) - opt_content = _RE_GLOBAL_LINKAGE.sub(r'\1 = \2 dso_local \3', opt_content) - opt_content = _RE_GLOBAL_DSOLOCAL.sub(r'\1 = dso_local \2', opt_content) - opt_content = _AddWeakComdat(opt_content) - with open(optimized_ll, 'w', encoding='utf-8') as f: - f.write(opt_content) - merged_obj = os.path.join(self.output_dir, '_merged.obj') - try: - result = self._llc_compile(optimized_ll, merged_obj, self.output_dir) - if result.returncode == 0: - _vlog().success(f" 合并模块编译完成") - for ll_path in ll_files: - obj_path = ll_path[:-3] + '.obj' - sha1_obj = os.path.join(self.output_dir, os.path.basename(ll_path)[:-3] + '.obj') - if _CachedIsFile(sha1_obj): - os.remove(sha1_obj) - return - else: - _vlog().warning(f" 合并模块编译失败: {result.stderr.strip()},回退到单独编译") - has_inline = False - except Exception as e: - _vlog().warning(f" 合并模块编译异常: {e},回退到单独编译") - has_inline = False - except FileNotFoundError: - _vlog().warning(f" 找不到 llvm-link 或 opt,回退到单独编译") - has_inline = False - else: - _vlog().warning(" 未找到 llvm-link/opt 工具,回退到单独编译(alwaysinline 可能不生效)") - - def _compile_one(ll_path: str) -> tuple[str, str, str]: - rel = os.path.relpath(ll_path, self.output_dir) - obj_path = ll_path[:-3] + '.obj' - if _CachedIsFile(obj_path): - return (rel, 'cached', '') - try: - ll_content = _ReadTextCached(ll_path) - sha1 = os.path.basename(ll_path)[:-3] - deps = self._LoadDeps(sha1) - stub_prefix = '' - if deps: - # 去重:同一个全局变量如果同时有 external 声明和实际定义, - # 移除 external 声明,避免 "redefinition of global" 错误 - # 注意:此去重必须在收集现有符号之前执行,否则行号偏移会导致 - # lines_to_remove 误删其他行(如全局变量定义) - ll_lines_for_dedup = ll_content.splitlines() - external_globals: dict[str, int] = {} - non_external_globals: dict[str, int] = {} - for i, line in enumerate(ll_lines_for_dedup): - stripped = line.strip() - if stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): - gm = _RE_AT_NAME.match(stripped) - if gm: - gname = gm.group(1) - is_external = _RE_AT_EXTERNAL.match(stripped) is not None - if is_external: - external_globals[gname] = i - else: - non_external_globals[gname] = i - lines_to_remove_global_dedup: set[int] = set() - for gname in external_globals: - if gname in non_external_globals: - lines_to_remove_global_dedup.add(external_globals[gname]) - if lines_to_remove_global_dedup: - ll_content = '\n'.join( - line for i, line in enumerate(ll_lines_for_dedup) - if i not in lines_to_remove_global_dedup - ) + '\n' - existing_symbols: dict[str, str] = {} - opaque_line_indices: dict[str, int] = {} - existing_declares: set[str] = set() - existing_declare_lines: dict[str, set[int]] = {} - for i, line in enumerate(ll_content.splitlines()): - stripped = line.strip() - if stripped.startswith('%') and '= type' in stripped: - name = Phase2Translator._extract_llvm_type_name(stripped) - if name: - if '= type opaque' in stripped: - existing_symbols[name] = 'opaque' - opaque_line_indices[name] = i - else: - existing_symbols[name] = 'full' - elif stripped.startswith('declare'): - dm = _RE_DECLARE_NAME.match(stripped) - if dm: - fname = dm.group(1) - existing_declares.add(fname) - existing_declare_lines.setdefault(fname, set()).add(i) - seen_stub_sha1: set[str] = set() - replaced_opaque_names: set[str] = set() - replaced_declare_names: set[str] = set() - all_stub_lines: list[str | None] = [] - stub_type_map: dict[str, tuple[bool, int]] = {} - stub_declare_names: set[str] = set() - stub_declare_lines: dict[str, str] = {} - existing_defines: set[str] = set() - existing_global_defs: set[str] = set() - stub_global_defs: set[str] = set() - for line in ll_content.splitlines(): - stripped = line.strip() - if stripped.startswith('define'): - dm = _RE_DEFINE_NAME.match(stripped) - if dm: - existing_defines.add(dm.group(1)) - elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): - gm = _RE_AT_NAME.match(stripped) - if gm: - existing_global_defs.add(gm.group(1)) - for dep_name, dep_sha1 in deps.items(): - if dep_sha1 in seen_stub_sha1: - continue - seen_stub_sha1.add(dep_sha1) - stub_path = os.path.join(self.output_dir, f"{dep_sha1}.stub.ll") - if not _CachedIsFile(stub_path): - stub_path = os.path.join(self.temp_dir, f"{dep_sha1}.stub.ll") - if _CachedIsFile(stub_path): - stub_content = _ReadTextCached(stub_path) - for line in stub_content.splitlines(): - stripped = line.strip() - if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: - continue - if stripped.startswith('declare'): - dm = _RE_DECLARE_NAME.match(stripped) - if dm: - fname = dm.group(1) - if 'zdef_alloc' in fname: - _vlog().debug(f"[DEBUG] stub declare for {fname}: existing_declares={fname in existing_declares}, stub_declare_names={fname in stub_declare_names}, has_struct={'%' in stripped}, existing={existing_declare_lines.get(fname)}") - if fname in existing_defines: - continue - if fname in existing_declares or fname in stub_declare_names: - existing_decl_line = stub_declare_lines.get(fname) - if existing_decl_line is not None: - has_struct = '%' in stripped - existing_has_struct = '%' in existing_decl_line if existing_decl_line else False - if has_struct and not existing_has_struct: - for i, l in enumerate(all_stub_lines): - if l is not None and l.strip().startswith('declare'): - em = _RE_DECLARE_NAME.match(l.strip()) - if em and em.group(1) == fname: - all_stub_lines[i] = line - break - continue - elif fname in existing_declares: - has_struct = '%' in stripped - if has_struct: - replaced_declare_names.add(fname) - stub_declare_names.add(fname) - stub_declare_lines[fname] = line - all_stub_lines.append(line) - continue - continue - stub_declare_names.add(fname) - stub_declare_lines[fname] = line - all_stub_lines.append(line) - continue - if stripped.startswith('%') and '= type' in stripped: - name = Phase2Translator._extract_llvm_type_name(stripped) - if name: - is_opaque = '= type opaque' in stripped - if name in existing_symbols: - if existing_symbols[name] == 'opaque' and not is_opaque: - replaced_opaque_names.add(name) - existing_symbols[name] = 'full' - idx = len(all_stub_lines) - all_stub_lines.append(line) - stub_type_map[name] = (is_opaque, idx) - continue - if name in stub_type_map: - prev_opaque, prev_idx = stub_type_map[name] - if prev_opaque and not is_opaque: - all_stub_lines[prev_idx] = None - idx = len(all_stub_lines) - all_stub_lines.append(line) - stub_type_map[name] = (is_opaque, idx) - continue - idx = len(all_stub_lines) - all_stub_lines.append(line) - stub_type_map[name] = (is_opaque, idx) - else: - all_stub_lines.append(line) - elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): - gm = _RE_AT_NAME.match(stripped) - if gm and (gm.group(1) in existing_global_defs or gm.group(1) in stub_global_defs): - continue - if gm: - stub_global_defs.add(gm.group(1)) - all_stub_lines.append(line) - else: - all_stub_lines.append(line) - filtered = [l for l in all_stub_lines if l is not None] - if filtered: - stub_prefix = '\n'.join(filtered) + '\n' - lines_to_remove = set() - if replaced_opaque_names: - for name in replaced_opaque_names: - if name in opaque_line_indices: - lines_to_remove.add(opaque_line_indices[name]) - if replaced_declare_names: - _vlog().debug(f"[DEBUG] replaced_declare_names: {replaced_declare_names}") - for fname in replaced_declare_names: - if fname in existing_declare_lines: - lines_to_remove.update(existing_declare_lines[fname]) - _vlog().debug(f"[DEBUG] removing lines {existing_declare_lines[fname]} for {fname}") - else: - _vlog().debug(f"[DEBUG] fname {fname} not in existing_declare_lines: {list(existing_declare_lines.keys())}") - if lines_to_remove: - ll_lines = ll_content.splitlines(True) - ll_content = ''.join(line for i, line in enumerate(ll_lines) if i not in lines_to_remove) - type_defs_in_ll = [] - type_def_line_indices = set() - for i, line in enumerate(ll_content.splitlines()): - stripped = line.strip() - if stripped.startswith('%') and '= type' in stripped: - type_defs_in_ll.append(line) - type_def_line_indices.add(i) - if type_def_line_indices: - ll_lines = ll_content.splitlines(True) - ll_content = ''.join(line for i, line in enumerate(ll_lines) if i not in type_def_line_indices) - if type_defs_in_ll: - stub_prefix += '\n'.join(type_defs_in_ll) + '\n' - if stub_prefix: - header_end = 0 - for i, line in enumerate(ll_content.splitlines()): - stripped = line.strip() - if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: - header_end = i + 1 - continue - break - ll_lines = ll_content.splitlines(True) - ll_content = ''.join(ll_lines[:header_end]) + stub_prefix + ''.join(ll_lines[header_end:]) - ll_content = _RE_DEFINE_DECLARE_LINKAGE.sub(r'\1 \2 dso_local ', ll_content) - ll_content = _RE_DEFINE_DECLARE_NO_LINKAGE_AT.sub(r'\1 dso_local ', ll_content) - # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 - ll_content = _RE_EXTERNAL_GLOBAL_VALUE.sub(r'\1 = linkonce global \2 \3', ll_content) - ll_content = _RE_GLOBAL_LINKAGE_MULTILINE.sub(r'\1 = \2 dso_local \3', ll_content) - ll_content = _RE_GLOBAL_DSOLOCAL_MULTILINE.sub(r'\1 = dso_local \2', ll_content) - ll_content = _AddWeakComdat(ll_content) - # LLVM 22+: 移除同一模块中已有 define 的 declare 语句(LLVM 22 将 declare+define 视为重复定义) - defined_funcs: set[str] = set() - for m in _RE_DEFINE_HEAD.finditer(ll_content): - defined_funcs.add(m.group(1)) - if defined_funcs: - lines: list[str] = ll_content.splitlines(True) - kept_lines: list[str] = [] - for line in lines: - m = _RE_DECLARE_FULL.match(line) - if m and m.group(1) in defined_funcs: - continue - kept_lines.append(line) - ll_content = ''.join(kept_lines) - # 移除重复的 declare 语句(同一函数多次 declare 但没有 define) - seen_declares: set[str] = set() - dedup_lines: list[str] = ll_content.splitlines(True) - dedup_kept: list[str] = [] - for line in dedup_lines: - dm = _RE_DECLARE_FULL.match(line) - if dm: - fname = dm.group(1) - if fname in seen_declares: - continue - seen_declares.add(fname) - dedup_kept.append(line) - ll_content = ''.join(dedup_kept) - final_type_defs = [] - final_type_def_indices = set() - seen_type_names_main = set() - for i, line in enumerate(ll_content.splitlines()): - stripped = line.strip() - if stripped.startswith('%') and '= type' in stripped: - type_name_main = stripped.split('=')[0].strip() - final_type_def_indices.add(i) - if type_name_main in seen_type_names_main: - continue - seen_type_names_main.add(type_name_main) - final_type_defs.append(line) - if final_type_def_indices: - final_lines = ll_content.splitlines(True) - ll_content_no_types = ''.join(line for i, line in enumerate(final_lines) if i not in final_type_def_indices) - header_end = 0 - for i, line in enumerate(ll_content_no_types.splitlines()): - stripped = line.strip() - if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: - header_end = i + 1 - continue - break - final_lines2 = ll_content_no_types.splitlines(True) - ll_content = ''.join(final_lines2[:header_end]) + '\n'.join(final_type_defs) + '\n' + ''.join(final_lines2[header_end:]) - # 写盘前先规整元数据:本文件的 IR 是「本模块 + 各依赖 .stub.ll/ - # .text.ll 片段」拼接而成,每份片段都自带从 !0 起编号的 DI 元数据, - # 未规整直接落盘的话,任何读该文件的 opt/llc(含另一并发构建进程) - # 都会报 "Metadata id is already used"。规整后盘上产物本身即合法 IR, - # _llc_compile 内的规整随之成为幂等 no-op。 - ll_content = SanitizeIRMetadata(ll_content) - # 拼接片段各自带一份同名 comdat 声明 ⇒ 落盘前统一去重: - # llc 对同一 comdat 声明出现两次即报 redefinition of comdat - ll_content = _DedupComdatDecls(ll_content) - dso_ll_path = ll_path[:-3] + '_dso.ll' - with open(dso_ll_path, 'w', encoding='utf-8') as f: - f.write(ll_content) - result = self._llc_compile(dso_ll_path, obj_path, os.path.dirname(ll_path) or '.') - if result.returncode == 0: - return (rel, 'ok', '') - else: - return (rel, 'error', result.stderr.strip()) - except FileNotFoundError: - return (rel, 'error', f'找不到编译器: {self.compile_cmd}') - except Exception as e: - return (rel, 'error', str(e)) - - need_compile = [] - for ll_path in ll_files: - obj_path = ll_path[:-3] + '.obj' - if not _CachedIsFile(obj_path): - need_compile.append(ll_path) - - if need_compile: - n_workers = _PickWorkerCount(len(need_compile), 400) - _vlog().info(f" 并行编译 {len(need_compile)} 个文件 (workers={n_workers})") - errors = [] - _compile_total = len(need_compile) - _compile_done = 0 - with ThreadPoolExecutor(max_workers=n_workers) as executor: - futures = {} - for ll_path in need_compile: - futures[executor.submit(_compile_one, ll_path)] = (ll_path, time.time()) - for future in as_completed(futures): - ll_path, _t_sub = futures[future] - rel, status, err = future.result() - _compile_done += 1 - _elapsed_c = time.time() - _t_sub - _sha1_c = os.path.basename(ll_path)[:-3] - _src_c = self.sha1_map.get(_sha1_c, '?') - if status == 'ok': - _vlog().success(f" [{_compile_done}/{_compile_total}] {os.path.basename(ll_path)} {_src_c} ({_elapsed_c:.1f}s)") - elif status == 'error': - _vlog().error(f" [{_compile_done}/{_compile_total}] {rel}: {err}") - errors.append((rel, err)) - elif status == 'cached': - pass - if errors: - _vlog().error(f"\n{len(errors)} 个文件编译失败") - sys.exit(1) - - def _precollect_vtable_state(self) -> None: - """预扫描 includes .py 文件,收集 @t.CVTable 类的虚表信息。 - - 并行编译(ProcessPoolExecutor)创建独立内存的 worker 进程, - vtable 状态必须预收集并通过 pickle 共享。 - 复用 parse_python_file 的 AST 缓存。 - """ - for includes_dir in self.include_dirs: - if not os.path.isdir(includes_dir): - continue - for root, dirs, files in os.walk(includes_dir): - dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] - for fname in files: - if not (fname.endswith('.vp') or fname.endswith('.py')): - continue - py_path: str = os.path.join(root, fname) - code, tree = parse_python_file(py_path) - # 字符串预过滤:不含 CVTable 的文件跳过 AST 遍历。 - # `virtual class` / `virtual def` 修饰符在源码里写的是 `virtual` - # (AST 上才变成 @t.CVTable 装饰器),故预过滤必须同时放行 - # `virtual`/`novirtual`,否则含修饰符的 includes 类会被整段跳过—— - # 静默不预收集虚表状态(与本题"不得静默丢弃修饰符"同族问题)。 - if tree is None or ('CVTable' not in code and 'virtual' not in code): - continue - try: - for node in ast.iter_child_nodes(tree): - if not isinstance(node, ast.ClassDef): - continue - has_cvtable: bool = False - for dec in node.decorator_list: - if isinstance(dec, ast.Attribute): - if getattr(dec.value, 'id', None) == 't' and dec.attr == 'CVTable': - has_cvtable = True - elif isinstance(dec, ast.Name): - if dec.id == 'CVTable': - has_cvtable = True - # 方法级 virtual 触发类级 vtable(与 - # HandlesClassDef._resolve_class_flags 同口径:类级 - # NoVTable / 无真实父类两种可判定形态)。 - # 记录"类级 CVTable"单独一份:它是 vtable 槽筛选的 - # VtableByClassLevel(忽略方法级标记),两者不可混用。 - class_level_cvtable: bool = has_cvtable - if not has_cvtable: - _has_real_parent: bool = False - for _b in node.bases: - _bn: str = None - if hasattr(_b, 'id'): - _bn = _b.id - elif hasattr(_b, 'attr'): - _bn = _b.attr - if _bn and _bn not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): - _has_real_parent = True - break - if ClassHasFuncLevelCVTable(node) and not _has_real_parent: - has_cvtable = True - if not has_cvtable: - continue - class_name: str = node.name - has_methods: bool = False - if class_name not in self._shared_class_methods: - self._shared_class_methods[class_name] = [] - for item in node.body: - if isinstance(item, ast.FunctionDef): - has_methods = True - method_name: str = item.name - # 构造函数不放入 vtable(避免子类与基类 vtable 大小不一致破坏多态分派) - if method_name in ('__new__', '__init__', '__before_init__'): - continue - # 方法级修饰符:`novirtual def`(⇒ @t.NoVTable)不进槽; - # 判据唯一源 = SymbolUtils.IsVtableSlotMethod,与 - # HandlesClassDef / TPV _should_method_be_virtual 同口径。 - if not IsVtableSlotMethod(item, class_level_cvtable): - continue - if method_name == '__init__': - func_full_name: str = f"{class_name}.__init__" - elif method_name == '__call__': - func_full_name = f"{class_name}.__call__" - else: - func_full_name = f"{class_name}.{method_name}" - if func_full_name not in self._shared_class_methods[class_name]: - self._shared_class_methods[class_name].append(func_full_name) - if has_methods: - self._shared_class_vtable.add(class_name) - self._shared_cross_module_vtable.add(class_name) - except Exception: - pass - - def _build_include_py_index(self, includes_dir: str) -> set[str]: - """预扫描 includes 目录,构建所有 .py 文件路径集合 - - 用 set 查询替代 os.path.isfile,避免 3490 次磁盘 stat(83s→<0.1s)。 - 同时规范化路径为小写(Windows 不区分大小写)以加速查找。 - """ - py_index: set[str] = set() - for root, dirs, files in os.walk(includes_dir): - dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] - for fname in files: - if fname.endswith('.vp') or fname.endswith('.py'): - py_index.add(os.path.normcase(os.path.join(root, fname))) - return py_index - - def _scan_include_libraries(self) -> None: - """扫描 includes 目录,检测被使用的模块对应的库文件和源文件""" - # --traceback:强制纳入 traceback 模块 —— 无任何用户代码 import 它, - # 不纳入就不会被翻译 ⇒ 产物没有 Install 符号可注入启动期构造器。 - # 直接借用既有的 used_includes 收集路径(下面按同一套 py_index / - # 传递依赖逻辑把它加入 extra_py_files,再交给 _compile_include_py_files - # 编译并把 .obj 追加进 extra_link_files)。开关关闭时不触碰 used_includes - # → 本函数行为与改动前逐字一致(零影响)。 - if self.traceback_enabled: - self.used_includes.add('traceback') - if not self.used_includes: - return - - for includes_dir in self.include_dirs: - if not os.path.isdir(includes_dir): - continue - - # 预构建 .py 文件索引,避免递归发现时反复 os.path.isfile - py_index = self._build_include_py_index(includes_dir) - - for module_name in self.used_includes: - module_dir = os.path.join(includes_dir, module_name) - if os.path.isdir(module_dir): - self._scan_dir_for_libs(module_dir, module_name) - for root, dirs, files in os.walk(module_dir): - dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] - for fname in files: - if fname.endswith('.vp') or fname.endswith('.py'): - py_path = os.path.join(root, fname) - if py_path not in self.extra_py_files: - self.extra_py_files.append(py_path) - _vlog().info(f" [includes] 发现 Python 包文件: {os.path.relpath(py_path, includes_dir)}") - - for ext in self.INCLUDE_LIB_EXTENSIONS: - lib_path = os.path.join(includes_dir, module_name + ext) - if _CachedIsFile(lib_path): - self.extra_link_files.append(lib_path) - _vlog().info(f" [includes] 发现库文件: {os.path.relpath(lib_path, includes_dir)}") - - for ext in self.INCLUDE_SRC_EXTENSIONS: - src_path = os.path.join(includes_dir, module_name + ext) - if _CachedIsFile(src_path): - self.extra_compile_files.append(src_path) - _vlog().info(f" [includes] 发现源文件: {os.path.relpath(src_path, includes_dir)}") - - py_path = os.path.join(includes_dir, module_name + '.vp') - if os.path.normcase(py_path) not in py_index: - # 回退到 .py(向后兼容) - py_path = os.path.join(includes_dir, module_name + '.py') - if os.path.normcase(py_path) in py_index: - self.extra_py_files.append(py_path) - _vlog().info(f" [includes] 发现 Python 源文件: {os.path.relpath(py_path, includes_dir)}") - - discovered = set() - for ef in list(self.extra_py_files): - self._discover_transitive_deps(ef, includes_dir, discovered, py_index) - - if self.extra_link_files: - _vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个") - if self.extra_compile_files: - _vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个") - # --traceback:断言 traceback 源文件确实被纳入待编译集合。找不到就必然 - # 得不到 Install 符号、构造器无处注入(功能静默失效)——显式报错终止, - # 不静默跳过(多半是 project.json 的 includes 未包含仓库的 includes 目录)。 - if self.traceback_enabled: - _traceback_found: bool = any( - os.path.basename(_p) in _TRACEBACK_MODULE_FILES - for _p in self.extra_py_files) - if not _traceback_found: - raise RuntimeError( - '--traceback: 在 includes 目录中找不到 traceback.vp/.py,' - '无法编译并链接 Install()(请确认 project.json 的 includes ' - f'包含仓库 includes 目录;已扫描: {self.include_dirs})') - - def _scan_dir_for_libs(self, directory: str, module_name: str) -> None: - """递归扫描目录中的库文件和源文件 - - 模块目录含 `.no_autobuild` 标记时直接跳过:该模块的 C/C++ 源码 - 与库文件由 project.vpj linker.static_libs 显式链接(如 fltk 内嵌 - 完整 FLTK/C 源码树,避免误编译整个第三方库)。递归时跳过常见 - 构建/缓存目录(build、cmake-build*、.git、__pycache__、. 开头)。 - """ - if _CachedIsFile(os.path.join(directory, '.no_autobuild')): - return - for root, dirs, files in os.walk(directory): - dirs[:] = [ - d for d in dirs - if d not in ('.git', '__pycache__', 'build', 'cmake-build') - and not d.startswith('cmake-build-') - and not d.startswith('.') - ] - for fname in files: - fpath = os.path.join(root, fname) - _, ext = os.path.splitext(fname) - ext = ext.lower() - if ext in self.INCLUDE_LIB_EXTENSIONS: - self.extra_link_files.append(fpath) - _vlog().info(f" [includes] 发现库文件: {os.path.relpath(fpath, directory)}") - elif ext in self.INCLUDE_SRC_EXTENSIONS: - self.extra_compile_files.append(fpath) - _vlog().info(f" [includes] 发现源文件: {os.path.relpath(fpath, directory)}") - - def _discover_transitive_deps(self, py_path: str, includes_dir: str, discovered: set[str], py_index: set[str] | None = None) -> None: - """递归发现 Python 文件的间接依赖 - - 复用 parse_python_file 的 AST 缓存,避免重复 open+read+ast.parse。 - py_index 为预构建的 .py 文件路径集合,避免 os.path.isfile 磁盘 stat。 - """ - if py_path in discovered: - return - discovered.add(py_path) - _, tree = parse_python_file(py_path) - if tree is None: - return - try: - for node in ast.walk(tree): - if isinstance(node, ast.Import): - for alias in node.names: - self._try_add_include_dep(alias.name, includes_dir, discovered, py_index) - elif isinstance(node, ast.ImportFrom): - if node.module and node.level == 0: - self._try_add_include_dep(node.module, includes_dir, discovered, py_index) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"发现 include 传递依赖失败: {_e}", "Exception") - - def _try_add_include_dep(self, module_name: str, includes_dir: str, discovered: set, py_index: set[str] | None = None): - """尝试将模块添加为 include 依赖(支持子包,如 win32.win32base) - - py_index 非空时用 set 查询替代 os.path.isfile(O(1) vs 24ms/次)。 - """ - # 候选路径:子包路径(win32/win32base.vp/.py)、顶层模块(win32.vp/.py)、子包 __init__.vp/__init__.py - candidates: list[str] = [] - # 子包路径:win32.win32base → win32/win32base.vp / .py - candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep) + '.vp')) - candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep) + '.py')) - # 子包 __init__.vp/__init__.py:win32.win32base → win32/win32base/__init__.vp / .py - candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep), '__init__.vp')) - candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep), '__init__.py')) - # 顶层模块:取第一个组件 → win32.vp / .py - top_module = module_name.split('.')[0] - if top_module != module_name: - candidates.append(os.path.join(includes_dir, top_module + '.vp')) - candidates.append(os.path.join(includes_dir, top_module + '.py')) - - for py_path in candidates: - exists: bool - if py_index is not None: - exists = os.path.normcase(py_path) in py_index - else: - exists = _CachedIsFile(py_path) - if exists and py_path not in self.extra_py_files: - self.extra_py_files.append(py_path) - _vlog().info(f" [includes] 发现间接依赖: {os.path.relpath(py_path, includes_dir)}") - self._discover_transitive_deps(py_path, includes_dir, discovered, py_index) - return # 找到第一个匹配即可 - - def _is_decl_only_file(self, src_path: str) -> bool: - """复用 parse_python_file 的 AST 缓存""" - _, tree = parse_python_file(src_path) - if tree is None: - return False - try: - for node in ast.walk(tree): - if isinstance(node, ast.ClassDef): - return False - if isinstance(node, ast.FunctionDef): - body = node.body - if len(body) == 1: - stmt = body[0] - if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant) and stmt.value.value is ...: - continue - if isinstance(stmt, ast.Pass): - continue - return False - return True - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"判断是否为声明文件失败: {_e}", "Exception") - return False - - def _sort_include_files_by_deps(self, file_list: list[str]) -> tuple[list[str], dict[str, set[str]]]: - """按依赖关系对 include 文件进行拓扑排序,确保被依赖的文件先编译。 - - 返回 (排序后的文件列表, 依赖字典)。 - 依赖字典: {fpath: set(依赖的fpath)},仅包含 file_list 中的依赖。 - """ - # 构建文件名到路径的映射 - name_to_path = {} - for fpath in file_list: - basename = os.path.splitext(os.path.basename(fpath))[0] - name_to_path[basename] = fpath - - # 也支持包内模块: vpsdk/window -> path - for fpath in file_list: - # 尝试从 includes 目录推断模块全名 - for includes_dir in self.include_dirs: - try: - rel = os.path.relpath(fpath, includes_dir) - mod_name = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') - name_to_path[mod_name] = fpath - except ValueError: - pass - - # 分析每个文件的 import 依赖(复用 parse_python_file 的 AST 缓存) - deps = {} # path -> set of paths it depends on - for fpath in file_list: - deps[fpath] = set() - try: - _, tree = parse_python_file(fpath) - if tree is None: - continue - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.Import): - for alias in node.names: - # 尝试完整路径和各级前缀,确保同包内子模块依赖能被正确解析 - # 例如 import zlib.zhuff -> 应先匹配 zlib.zhuff,再回退到 zlib - parts = alias.name.split('.') - for i in range(len(parts), 0, -1): - mod = '.'.join(parts[:i]) - if mod in name_to_path and name_to_path[mod] != fpath: - deps[fpath].add(name_to_path[mod]) - break - elif isinstance(node, ast.ImportFrom): - if node.module and node.level == 0: - # 绝对导入:同样尝试完整路径和各级前缀 - parts = node.module.split('.') - for i in range(len(parts), 0, -1): - mod = '.'.join(parts[:i]) - if mod in name_to_path and name_to_path[mod] != fpath: - deps[fpath].add(name_to_path[mod]) - break - elif node.level > 0: - # 相对导入:from .xxx import yyy 或 from ..xxx import yyy - # 根据当前文件位置推断目标模块的完整路径 - current_dir = os.path.dirname(fpath) - # node.level == 1 表示当前包,node.level == 2 表示上一级包 - package_dir = current_dir - for _ in range(node.level - 1): - package_dir = os.path.dirname(package_dir) - # 推断当前包名(从 includes 目录的相对路径) - package_name: str | None = None - for includes_dir in self.include_dirs: - try: - rel = os.path.relpath(package_dir, includes_dir) - if rel and rel != '.': - package_name = rel.replace(os.sep, '.').replace('/', '.') - break - except ValueError: - pass - # 构建目标模块名并查找 - target_mod: str | None = None - if node.module: - target_mod = f"{package_name}.{node.module}" if package_name else node.module - else: - # from . import yyy:yyy 是子模块,在 names 中 - target_mod = package_name - if target_mod: - parts = target_mod.split('.') - for i in range(len(parts), 0, -1): - mod = '.'.join(parts[:i]) - if mod in name_to_path and name_to_path[mod] != fpath: - deps[fpath].add(name_to_path[mod]) - break - # from . import yyy:yyy 可能是子模块 - if node.level > 0 and not node.module: - for alias in node.names: - sub_mod = f"{package_name}.{alias.name}" if package_name else alias.name - if sub_mod in name_to_path and name_to_path[sub_mod] != fpath: - deps[fpath].add(name_to_path[sub_mod]) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"分析 include 文件导入依赖失败: {_e}", "Exception") - - # 拓扑排序 (Kahn's algorithm) - # in_degree 只计入 file_list 中的依赖——decl-only 文件(如 stdint.py) - # 不在 file_list 中,不应计入入度,否则依赖它们的文件入度永远不为 0, - # 被误判为循环依赖通过 fallback 添加,导致编译顺序错误。 - file_set = set(file_list) - in_degree = {fpath: sum(1 for dep in deps[fpath] if dep in file_set) for fpath in file_list} - # 反向邻接表:如果 A 依赖 B,则 B -> A - reverse_adj = {fpath: [] for fpath in file_list} - for fpath in file_list: - for dep in deps[fpath]: - if dep in reverse_adj: - reverse_adj[dep].append(fpath) - - result = [] - queue = [fpath for fpath in file_list if in_degree[fpath] == 0] - # 对队列排序以保持确定性 - queue.sort(key=lambda x: x) - - while queue: - current = queue.pop(0) - result.append(current) - for neighbor in reverse_adj[current]: - in_degree[neighbor] -= 1 - if in_degree[neighbor] == 0: - queue.append(neighbor) - queue.sort(key=lambda x: x) - - # 如果有循环依赖,把剩余文件也加入 - for fpath in file_list: - if fpath not in result: - result.append(fpath) - - # 仅保留 file_list 内的依赖(用于波次并行翻译) - file_set_result = set(file_list) - deps_in_set = {fpath: (deps[fpath] & file_set_result) for fpath in file_list} - return result, deps_in_set - - def _get_precompiled_obj_path(self, src_path: str, sha1: str) -> str | None: - """计算 include 文件在 includes.binary/ 中的预编译 .obj 路径。 - - 预编译目录位于 include 目录同级,命名为 {basename}.binary/。 - 文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。 - - --traceback:include 模块也带 DWARF 行表(栈帧要能落到 includes/* 的源行), - 此类 .obj 不得回写进跨项目共享的 includes.binary(会污染其它项目的预编译 - 缓存),改落**兄弟目录** {basename}.binary + 后缀(不新增层级,形态与 - ASan 变体目录 includes.binary_asan 一致)。 - 关闭时后缀为空 → 路径与改动前逐字一致(含 --dwarf/--asan 单独开启的情形)。 - --asan 与 --traceback 同开:ASan 插桩也会改变 include obj 内容(见 - _llc_compile),两者产物不可互相复用,故再叠加 _asan 后缀区分 - (优先级:是否隔离由 --traceback 决定,--asan 只决定后缀再叠加 _asan)。 - """ - AbsSrcPath: str = os.path.abspath(src_path) - RelModel: str = 'static' - for _flag in self.compile_flags: - if _flag.startswith('-relocation-model='): - RelModel = _flag[len('-relocation-model='):] - break - for inc_dir in self.include_dirs: - abs_inc: str = os.path.abspath(inc_dir) - try: - rel: str = os.path.relpath(AbsSrcPath, abs_inc) - if rel.startswith('..'): - continue - inc_parent: str = os.path.dirname(abs_inc) - inc_base: str = os.path.basename(abs_inc) - _iso_suffix: str = '' - if self.traceback_enabled: - _iso_suffix = '_traceback_asan' if self.asan_mode else '_traceback' - precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary' + _iso_suffix) - triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_') - return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj") - except ValueError: - continue - return None - - def _compile_include_py_files(self, active_sha1s: set[str]) -> None: - """通过 TransPyC 翻译器编译 includes 目录中发现的 .py 文件 - - 优化流程:预注册符号 → 并行翻译 → 串行后处理 → 并行编译 → 注册+缓存 - """ - if not self.extra_py_files: - return - - # 按依赖关系排序 include 文件,确保被依赖的文件先编译 - sorted_files, file_deps = self._sort_include_files_by_deps(self.extra_py_files) - _vlog().info(f"\n[includes 编译] 找到 {len(sorted_files)} 个 Python 源文件") - - # --hotspot-sites:include 模块同样被插桩(hotspot_sites_file 经 worker 透传), - # 故启用时必须让旧 .obj 失效并强制重译,否则会复用上一次未插桩的目标文件, - # 使 include 模块的热点计数/计时 IR 静默缺失。未启用时 hotspot_on=False, - # 本函数行为与改动前逐字节一致(零影响)。 - hotspot_on: bool = bool(self.hotspot_sites_file) - - # --traceback:include 模块也必须带 DWARF 行表(否则运行时 Ctrl+C 的栈里 - # 落在 includes/*(memhub/string/viperlib/win32…)的帧取不到源行,只能落到 - # 邻行或 "+0x…(unknown)")。为此翻译时给 include 透传 dwarf_enabled=True - # (见下方 worker/串行四处的 dwarf_enabled=self.traceback_enabled),并把 - # 旧目标文件(output 目录 {sha1}.obj 及隔离缓存 includes.binary_traceback - # 内的旧 .obj)一并失效重译——复用上一次无 DI 的产物会让行表静默缺失。 - # 与 hotspot_on 处理方式一致;同时覆盖 traceback.vp(其 .obj 还须含 - # @llvm.global_ctors 启动期构造器)。取舍:开启本开关时 include 目标文件每 - # 次构建都重译(放弃隔离缓存的复用,换取"必然带 DWARF"的正确性保证), - # 仅调试模式可接受。开关关闭时 include_dwarf=False,本函数行为与改动前 - # 逐字节一致(零影响,含 --dwarf/--asan 单独开启的情形)。 - include_dwarf: bool = bool(self.traceback_enabled) - - # 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射) - include_ModuleSha1Map = self._build_current_ModuleSha1Map(None) - need_translate = [] # [(src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)] - for src_path in sorted_files: - module_name = os.path.splitext(os.path.basename(src_path))[0] - # 复用 parse_python_file 缓存的 content,避免重复 open+read - py_content, _ = parse_python_file(src_path) - sha1 = compute_sha1(py_content) - self.include_py_map[module_name] = sha1 - active_sha1s.add(sha1) - self._include_sha1s.add(sha1) - - for includes_dir in self.include_dirs: - try: - rel = os.path.relpath(src_path, includes_dir) - mod_full = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') - include_ModuleSha1Map[mod_full] = sha1 - include_ModuleSha1Map[module_name] = sha1 - # 包入口文件:注册父包名(如 'os.__init__' -> 'os') - if module_name == '__init__' and '.' in mod_full: - parent_pkg = mod_full.rsplit('.', 1)[0] - include_ModuleSha1Map[parent_pkg] = sha1 - except ValueError: - pass - include_ModuleSha1Map[module_name] = sha1 - ll_path = os.path.join(self.output_dir, f"{sha1}.ll") - obj_path = os.path.join(self.output_dir, f"{sha1}.obj") - precompiled_obj = self._get_precompiled_obj_path(src_path, sha1) - - if self._is_decl_only_file(src_path): - _vlog().info(f" 跳过(声明文件): {module_name}.py") - # 声明文件(如 stdint.py)虽不编译为 .obj,但其 typedef/类型别名 - # 必须注册到共享符号表,否则依赖它的 include 文件编译时 - # 类型解析失败(如 UINT32PTR 被当作命名结构体而非 i32*) - self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) - self._collect_inline_symbols(src_path) - continue - - # --hotspot-sites / --traceback:include 的 .py 未变时下面两处会因旧 - # .obj 存在而直接跳过,复用上一次(未插桩 / 不含 DI)的目标文件。 - # 启用时先让这两类旧 .obj 一并失效,使其必然走入 need_translate 重新 - # 翻译并嵌入热点计数/计时 IR 或 DWARF 行表。 - # 用 os.path.isfile 而非 _CachedIsFile:避免把"已存在"写入存在性缓存后又被 - # 本处删除,使下面两处判定读到过期缓存而漏编(与 App 主循环同一写法)。 - if hotspot_on or include_dwarf: - for _stale_obj in (precompiled_obj, obj_path): - if _stale_obj and os.path.isfile(_stale_obj): - try: - os.remove(_stale_obj) - except OSError as e: - # 不中断构建,但真实错误(如权限不足)必须打印告警,不静默吞掉 - # (报文保持原样:关闭开关时与改动前逐字一致) - _vlog().warning( - f"Phase2Translator: 删除热点插桩 include 模块的旧目标文件失败: {_stale_obj}: {e}") - - # 优先检查 includes.binary 预编译缓存(跨项目共享) - if not self.force_recompile_includes and precompiled_obj and _CachedIsFile(precompiled_obj): - _vlog().info(f" 跳过(预编译): {module_name}.py -> {os.path.basename(precompiled_obj)}") - # 预编译跳过的文件仍需注册 .pyi/.stub.ll 声明和符号, - # 否则依赖它的 include 文件(如 vpui.py import vpsdk.window) - # 在 _EmitModuleDeclarationsLlvm 中找不到声明,导致 Undefined symbol - self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) - self._collect_inline_symbols(src_path) - self.extra_link_files.append(precompiled_obj) - continue - - if not self.force_recompile_includes and _CachedIsFile(obj_path): - _vlog().info(f" 跳过(缓存): {module_name}.py -> {sha1}.obj") - self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) - self._collect_inline_symbols(src_path) - self.extra_link_files.append(obj_path) - continue - - need_translate.append((src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)) - - if not need_translate: - return - - # 预注册所有待翻译文件的符号(从 Phase1 缓存加载 .pyi / .stub.ll) - # 这样并行翻译时所有跨模块符号都已可用 - for src_path, sha1, module_name, ll_path, obj_path, precompiled_obj in need_translate: - self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) - - # 预收集 stub_decls(所有文件共用,避免重复扫描 .stub.ll) - stub_decls = [] - for other_sha1, other_stub in self.stub_files.items(): - if os.path.exists(other_stub): - try: - for sline in _ReadTextCached(other_stub).splitlines(True): - sl = sline.strip() - if sl.startswith('%') and '= type' in sl and 'opaque' not in sl: - struct_name = Phase2Translator._extract_llvm_type_name(sl) - if struct_name: - stub_decls.append(sl) - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - all_type_defs = {} - for sdecl in stub_decls: - tname = Phase2Translator._extract_llvm_type_name(sdecl) - if tname: - all_type_defs[tname] = sdecl - - # ========== 阶段1:并行翻译 ========== - _vlog().info(f" 翻译 {len(need_translate)} 个文件") - if len(need_translate) > 1: - # 序列化共享符号表供 worker 使用 - shared_pickle_path = os.path.join(self.output_dir, '_shared_sym_includes.pickle') - try: - # symbol_table 只以 pickle 字节传递(symbol_table_bytes),不重复序列化 - # 对象本身(原双重序列化使文件体积/时间翻倍,4.65MB 中近半冗余)。 - shared_data = { - 'symbol_table_bytes': pickle.dumps(self._shared_symbol_table), - 'source_module_sig_files': self._shared_source_module_sig_files, - 'all_dc': self._shared_all_dc, - 'all_decl_types': self._shared_all_decl_types, - 'export_extern_funcs': self._shared_export_extern_funcs, - 'inline_func_symbols': self.inline_func_symbols, - 'function_default_args': self.function_default_args, - 'function_param_names': self.function_param_names, - 'generic_class_templates': self._shared_generic_class_templates, - 'shared_doc_meta': self._shared_doc_meta, - 'class_vtable': self._shared_class_vtable, - 'cross_module_vtable': self._shared_cross_module_vtable, - 'class_methods': self._shared_class_methods, - 'cross_module_novtable': self._shared_cross_module_novtable, - } - with open(shared_pickle_path, 'wb') as f: - f.write(pickle.dumps(shared_data)) - except Exception as e: - _vlog().warning(f"符号表序列化失败({e}),include将串行翻译") - shared_pickle_path = '' - - if shared_pickle_path and os.path.exists(shared_pickle_path): - n_workers = _PickWorkerCount(len(need_translate)) - errors = [] - # 波次并行翻译:按依赖关系分批提交,确保被依赖的文件先翻译完成。 - # 根因:_TryLoadStructFromStub 扫描 output 目录加载跨模块 struct 定义, - # 若依赖文件尚未完成翻译(output stub 未写入),只能取到 Phase1 temp - # 中的不完整 stub(缺少跨模块继承字段),导致运行时崩溃。 - item_by_path = {item[0]: item for item in need_translate} - translate_paths = set(item_by_path.keys()) - # 每个文件的依赖(仅限本轮待翻译集合内) - translate_deps = { - p: (file_deps.get(p, set()) & translate_paths) - for p in translate_paths - } - # 复用 self._executor_pool(与 App 源文件阶段共享),避免 GC 阻塞 - if self._executor_pool is None: - # 新池:刷新 pyi pickle 缓存,把 _register_compiled_include 新增的 - # include .vpi 一并落盘,使 worker 预热包含它们(签名未变时零开销) - if os.environ.get('TPC_WORKER_WARMUP', '1') == '1': - _BuildPyiCacheFile(self.temp_dir) - self._executor_pool = ProcessPoolExecutor(max_workers=n_workers, - initializer=_PyiCacheWorkerInitializer, - initargs=(self.temp_dir,)) - executor = self._executor_pool - try: - completed_paths: set[str] = set() - remaining = list(need_translate) # 保持拓扑序 - in_progress: dict = {} # future -> item - done_count = 0 - - while remaining or in_progress: - # 提交所有依赖已完成的文件 - ready = [ - item for item in remaining - if translate_deps[item[0]] <= completed_paths - ] - for item in ready: - remaining.remove(item) - src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item - future = executor.submit( - _parallel_translate_worker, - src_path, ll_path, - self.src_root, self.temp_dir, self.output_dir, - self.include_dirs, self.triple, self.datalayout, - self.sha1_map, self.sig_files, self.stub_files, - self.include_py_map, self.slice_level, - shared_pickle_path, self.struct_sha1_map, - include_ModuleSha1Map, - hotspot_sites_file=self.hotspot_sites_file, - hotspot_sites_all=self.hotspot_sites_all, - hotspot_callees=self.hotspot_callees, - traceback_enabled=self.traceback_enabled, - dwarf_enabled=self.traceback_enabled - ) - in_progress[future] = item - - if not in_progress: - # 无可提交且无进行中:循环依赖 fallback - for item in remaining: - src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item - future = executor.submit( - _parallel_translate_worker, - src_path, ll_path, - self.src_root, self.temp_dir, self.output_dir, - self.include_dirs, self.triple, self.datalayout, - self.sha1_map, self.sig_files, self.stub_files, - self.include_py_map, self.slice_level, - shared_pickle_path, self.struct_sha1_map, - include_ModuleSha1Map, - hotspot_sites_file=self.hotspot_sites_file, - hotspot_sites_all=self.hotspot_sites_all, - hotspot_callees=self.hotspot_callees, - traceback_enabled=self.traceback_enabled, - dwarf_enabled=self.traceback_enabled - ) - in_progress[future] = item - remaining.clear() - - # 等待至少一个完成 - done_set, _ = wait(in_progress, return_when=FIRST_COMPLETED) - for future in done_set: - item = in_progress.pop(future) - src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item - completed_paths.add(src_path) - done_count += 1 - try: - status, _, err = future.result() - if status == 'ok': - _vlog().info(f" [{done_count}/{len(need_translate)}] 翻译完成: {module_name}.py") - else: - # fail-fast:文件编译出错立即终止(worker 已 os._exit, - # 此处兜底处理残留的 error 返回) - raise RuntimeError( - f"include 翻译失败,立即终止: {module_name}.py: {err}") - except Exception as e: - _vlog().error(f" {module_name}.py: {e}") - for f2 in in_progress: - f2.cancel() - if self._executor_pool is not None: - self._executor_pool.shutdown(wait=False, cancel_futures=True) - self._executor_pool = None - raise RuntimeError( - f"include worker 异常终止,编译立即停止: {e}") - except Exception: - if self._executor_pool is not None: - self._executor_pool.shutdown(wait=False, cancel_futures=True) - self._executor_pool = None - raise - - if errors: - _vlog().error(f"\n{len(errors)} 个 include 文件翻译失败") - sys.exit(1) - else: - # 串行回退 - for item in need_translate: - src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item - _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") - self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled) - else: - item = need_translate[0] - src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item - _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") - self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled) - - # ========== 阶段2:串行后处理(dso_local + stub_decls 注入)========== - compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)] - for item in need_translate: - src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item - if not _CachedIsFile(ll_path): - _vlog().error(f" 翻译未生成 .ll 文件: {module_name}.py") - continue - - ll_content = _ReadTextCached(ll_path) - - # --traceback 提醒:include 模块若发射了 define 却不带 DI(!llvm.dbg.cu), - # 其 .debug_line 行为空 ⇒ 运行时 Ctrl+C 栈里该 include 帧取不到源行 - # (会退化为 +0x… (unknown))。 - # **只警告、不报错**:像 includes/stdarg.vp 这类模块的 define 是"合成"的 - # (由 extern/sugar 派生、无 .vp 函数体),本就不会有源级 DI —— 按 fail-fast - # 报错会把这类模块全部误杀;而"行号缺失"只降低可读性,不是编译正确性问题。 - if self.traceback_enabled and _RE_DEFINE_LINE.search(ll_content) \ - and '!llvm.dbg.cu' not in ll_content: - _vlog().warning( - f"--traceback: include 模块 {module_name}.py 发射了 define 但 IR 未携带 " - f"DWARF (!llvm.dbg.cu)——该模块内联帧在 Ctrl+C 栈里将只显示地址: {ll_path}") - - # dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare) - ll_content = _RE_DEFINE_DECLARE_LINKAGE.sub(r'\1 \2 dso_local ', ll_content) - ll_content = _RE_DEFINE_DECLARE_NO_LINKAGE.sub(r'\1 dso_local ', ll_content) - # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 - ll_content = _RE_EXTERNAL_GLOBAL_VALUE.sub(r'\1 = linkonce global \2 \3', ll_content) - ll_content = _RE_GLOBAL_LINKAGE_MULTILINE.sub(r'\1 = \2 dso_local \3', ll_content) - ll_content = _RE_GLOBAL_DSOLOCAL_MULTILINE.sub(r'\1 = dso_local \2', ll_content) - ll_content = _AddWeakComdat(ll_content) - - # stub_decls 注入(复用预收集的 stub_decls) - if stub_decls: - for sdecl in stub_decls: - sname = Phase2Translator._extract_llvm_type_name(sdecl) - if sname: - short = sname.split('.')[-1] if '.' in sname else sname - ll_content = re.sub( - r'%"?' + re.escape(sname) + r'"?\s*=\s*type\s*opaque', - sdecl, ll_content) - if short != sname: - ll_content = re.sub( - r'%"?' + re.escape(short) + r'"?\s*=\s*type\s*opaque', - sdecl, ll_content) - - max_iterations = 10 - for _ in range(max_iterations): - defined_types = set() - for line in ll_content.splitlines(): - sl = line.strip() - if sl.startswith('%') and '= type' in sl: - tname = Phase2Translator._extract_llvm_type_name(sl) - if tname: - defined_types.add(tname) - # 同时添加短名,防止 missing 循环因全名/短名不匹配而重复添加 - if '.' in tname: - parts = tname.split('.', 1) - if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): - defined_types.add(parts[1]) - - referenced_types = Phase2Translator._find_all_type_refs(ll_content) - missing = referenced_types - defined_types - new_defs = [] - for tname in sorted(missing): - if tname in all_type_defs: - new_defs.append(all_type_defs[tname]) - else: - short_key = tname.split('.')[-1] if '.' in tname else tname - for full_name, defn in all_type_defs.items(): - if full_name.split('.')[-1] == short_key: - if short_key not in defined_types: - new_defs.append(defn) - break - else: - # 未在 stub 中找到定义(如泛型类 list[T]), - # 声明为 opaque 以满足 llc 的类型前向声明要求 - new_defs.append(f'%"{tname}" = type opaque') - - if not new_defs: - break - - inject_block = '\n'.join(new_defs) + '\n' - lines = ll_content.splitlines(True) - insert_pos = 0 - for i, line in enumerate(lines): - stripped = line.strip() - if stripped and not stripped.startswith(';') and not stripped.startswith('target ') and not stripped.startswith('source_filename') and not stripped.startswith('declare ') and not stripped.startswith('@') and not stripped.startswith('define ') and not stripped.startswith('attributes ') and not stripped.startswith('!') and not stripped.startswith('module '): - insert_pos = i - break - if insert_pos == 0: - insert_pos = len(lines) - ll_content = ''.join(lines[:insert_pos]) + inject_block + ''.join(lines[insert_pos:]) - - # 将所有类型定义移到 declares 之前 - # (llc 要求 by-value struct 参数的类型在 declare 前定义,否则报 invalid type for function argument) - lines = ll_content.splitlines(True) - type_def_lines = [] - other_lines = [] - seen_type_names_inc = set() - for line in lines: - stripped = line.strip() - if stripped.startswith('%') and '= type' in stripped: - type_name_inc = stripped.split('=')[0].strip() - if type_name_inc in seen_type_names_inc: - continue - seen_type_names_inc.add(type_name_inc) - type_def_lines.append(line) - else: - other_lines.append(line) - if type_def_lines: - insert_pos = 0 - for i, line in enumerate(other_lines): - stripped = line.strip() - if stripped and not stripped.startswith(';') and not stripped.startswith('target ') and not stripped.startswith('source_filename') and not stripped.startswith('!'): - insert_pos = i - break - if insert_pos == 0: - insert_pos = len(other_lines) - ll_content = ''.join(other_lines[:insert_pos]) + ''.join(type_def_lines) + ''.join(other_lines[insert_pos:]) - - # 同 App 路径:写盘前规整元数据(拼接片段各自带 !0..!N 的 DI 元数据), - # 让盘上产物本身即合法 IR,不依赖 _llc_compile 里那次事后规整。 - ll_content = SanitizeIRMetadata(ll_content) - # 同 App 路径:拼接片段各自带一份同名 comdat 声明 ⇒ 落盘前统一去重 - ll_content = _DedupComdatDecls(ll_content) - dso_ll_path = ll_path[:-3] + '_dso.ll' - with open(dso_ll_path, 'w', encoding='utf-8') as f: - f.write(ll_content) - _InvalidateTextCache(dso_ll_path) - compile_tasks.append((dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)) - - if not compile_tasks: - return - - # ========== 阶段3:并行编译(llc)========== - _vlog().info(f" 编译 {len(compile_tasks)} 个 .ll 文件") - compile_results = {} # task -> result - - def _compile_one_include(task): - dso_ll_path, obj_path = task[0], task[1] - result = self._llc_compile(dso_ll_path, obj_path, self.output_dir) - return (task, result) - - if len(compile_tasks) > 1: - n_workers = _PickWorkerCount(len(compile_tasks), 400) - with ThreadPoolExecutor(max_workers=n_workers) as executor: - futures = {executor.submit(_compile_one_include, task): task for task in compile_tasks} - done_count = 0 - for future in as_completed(futures): - done_count += 1 - try: - task, result = future.result() - module_name = task[4] - if result.returncode == 0: - _vlog().success(f" [{done_count}/{len(compile_tasks)}] 编译成功: {module_name}.py") - else: - _vlog().warning(f" [{done_count}/{len(compile_tasks)}] 编译失败: {module_name}.py: {result.stderr.strip()}") - compile_results[task] = result - except Exception as e: - task = futures[future] - module_name = task[4] - _vlog().error(f" [{done_count}/{len(compile_tasks)}] 编译异常: {module_name}.py: {e}") - compile_results[task] = None - else: - task = compile_tasks[0] - task_result, result = _compile_one_include(task) - module_name = task[4] - if result.returncode == 0: - _vlog().success(f" 编译成功: {module_name}.py") - else: - _vlog().warning(f" 编译失败: {module_name}.py: {result.stderr.strip()}") - compile_results[task] = result - - # ========== 阶段4:注册 + 缓存 ========== - has_error = False - for task in compile_tasks: - dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content = task - result = compile_results.get(task) - if result and result.returncode == 0: - self.extra_link_files.append(obj_path) - # 复制到 includes.binary 预编译目录(--traceback 时 precompiled_obj - # 指向隔离目录 includes.binary_traceback,不会污染共享缓存) - if precompiled_obj: - precompiled_dir = os.path.dirname(precompiled_obj) - try: - if not os.path.exists(precompiled_dir): - os.makedirs(precompiled_dir, exist_ok=True) - shutil.copy2(obj_path, precompiled_obj) - except Exception as _e: - # --traceback fail-fast:隔离目录创建 / 写回失败必须显式终止, - # 不静默降级(否则本次构建的 include obj 拿不到、或落到共享缓存 - # 之外的位置,DWARF 行表与后续复用的前提都被破坏)。 - if self.traceback_enabled: - raise RuntimeError( - f"--traceback: include 预编译缓存写入隔离目录失败: " - f"{precompiled_obj}: {_e}") from _e - if _config_mode == "strict": - _vlog().warning(f"预编译缓存写入失败: {_e}") - # 注册符号 - self._register_compiled_include(src_path, sha1, ll_content, include_ModuleSha1Map) - # 清理临时文件(仅成功时清理,失败时保留以便调试) - # DEBUG: 保留 .ll 文件以便静态分析 - # for ext in ['.ll', '_dso.ll']: - # p = ll_path[:-3] + ext - # if _CachedIsFile(p): - # os.remove(p) - else: - has_error = True - _vlog().warning(f" 保留失败文件以便调试: {ll_path}") - - if has_error: - _vlog().error(f"\nincludes 文件编译失败") - sys.exit(1) - - # 从 include 文件源码中提取函数参数名,供主项目编译时 keyword 参数匹配使用 - self._extract_include_param_names(sorted_files) - - def _extract_include_param_names_from_dirs(self) -> None: - """从 include 目录的 Python 源码中提取函数参数名与默认值。 - - 顶层 .vp/.py 直接解析;包子目录(如 includes/json)只在该包被 - used_includes 引用时递归解析——避免为每个工程全量递归 includes - (fltk/vqt6/openssl 等上百个文件)带来的解析开销。 - 默认值一并注册(跨模块缺参调用点据此填充,见 HandlesExprCall - ._HandleClosureCallLlvm),此前只注册参数名,导致 include 模块的 - 尾随默认参数在调用点被零值填充。 - """ - for includes_dir in self.include_dirs: - if not os.path.isdir(includes_dir): - continue - for entry in os.listdir(includes_dir): - entry_path = os.path.join(includes_dir, entry) - targets: list[str] = [] - if os.path.isdir(entry_path): - if not self.used_includes or entry not in self.used_includes: - continue - for root, dirs, files in os.walk(entry_path): - dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] - targets.extend(os.path.join(root, f) for f in files - if f.endswith('.vp') or f.endswith('.py')) - elif entry.endswith('.vp') or entry.endswith('.py'): - targets.append(entry_path) - for src_path in targets: - try: - source, tree = parse_python_file(src_path) - if tree is None: - continue - sha1 = compute_sha1(source) - self._collect_function_params(tree, sha1) - except Exception as e: - _vlog().warning(f" 提取参数名失败 {src_path}: {e}") - - def _collect_function_params(self, tree: ast.AST, sha1: str | None) -> None: - """单次递归遍历 AST 收集函数参数名(含类方法)与默认值,O(N) 复杂度 - - 遍历时维护 current_class 上下文:遇到 ClassDef 进入类作用域, - 遇到 FunctionDef 记录参数名并标注所属类,函数内部的嵌套定义 - 不再属于外层类。 - - 默认值只登记非方法(模块级函数)的裸名键,减少与其它模块同名函数的 - 误配;带 sha1 / 类名限定的键始终登记,查找时精确键优先。 - """ - def visit(node: ast.AST, current_class: str | None) -> None: - for child in ast.iter_child_nodes(node): - if isinstance(child, ast.ClassDef): - # 进入类作用域,递归处理类体 - visit(child, child.name) - elif isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)): - func_name = child.name - param_names = [arg.arg for arg in child.args.args] - self.function_param_names[func_name] = param_names - if sha1: - self.function_param_names[f'{sha1}.{func_name}'] = param_names - if current_class: - self.function_param_names[f'{current_class}.{func_name}'] = param_names - if sha1: - self.function_param_names[f'{sha1}.{current_class}.{func_name}'] = param_names - defaults = getattr(child.args, 'defaults', []) or [] - if defaults: - default_vals = [] - for d in defaults: - if isinstance(d, ast.Constant): - default_vals.append(d.value) - else: - default_vals.append(None) - if sha1: - self.function_default_args[f'{sha1}.{func_name}'] = default_vals - if current_class: - self.function_default_args[f'{current_class}.{func_name}'] = default_vals - if sha1: - self.function_default_args[f'{sha1}.{current_class}.{func_name}'] = default_vals - else: - self.function_default_args[func_name] = default_vals - # 函数内部的嵌套定义不属于外层类 - visit(child, None) - else: - visit(child, current_class) - visit(tree, None) - - def _extract_app_default_args(self, need_translate: list) -> None: - """从 App 源文件中提取函数默认参数,存入 function_default_args - - 在 Phase 2 并行编译开始前调用,确保跨模块调用参数检查能正确识别默认参数。 - need_translate 中每个元素是 (i, src_path, out_path, sha1, current_ModuleSha1Map) - 复用 parse_python_file 的 AST 缓存(run() 循环已 parse 过)。 - """ - for entry in need_translate: - src_path = entry[1] - sha1 = entry[3] - _, tree = parse_python_file(src_path) - if tree is None: - continue - try: - for node in ast.walk(tree): - if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): - func_name = node.name - defaults = getattr(node.args, 'defaults', []) - if defaults: - default_vals = [] - for d in defaults: - if isinstance(d, ast.Constant): - default_vals.append(d.value) - else: - default_vals.append(None) - # 存储多种键格式,确保跨模块调用检查能找到 - self.function_default_args[func_name] = default_vals - self.function_default_args[f'{sha1}.{func_name}'] = default_vals - # 也更新参数名(预扫描时补充) - param_names = [arg.arg for arg in node.args.args] - if func_name not in self.function_param_names: - self.function_param_names[func_name] = param_names - if f'{sha1}.{func_name}' not in self.function_param_names: - self.function_param_names[f'{sha1}.{func_name}'] = param_names - except Exception as e: - _vlog().warning(f" 提取默认参数失败 {src_path}: {e}") - - def _extract_include_param_names(self, sorted_files: list[str]) -> None: - """从 include 文件的 Python 源码中提取函数参数名,存入 function_param_names - - 优化:复用 _collect_function_params 的 O(N) 单次遍历 + parse_python_file 缓存。 - """ - for src_path in sorted_files: - if self._is_decl_only_file(src_path): - continue - try: - _, tree = parse_python_file(src_path) - if tree is None: - continue - module_name = os.path.splitext(os.path.basename(src_path))[0] - sha1 = self.include_py_map.get(module_name) - self._collect_function_params(tree, sha1) - except Exception as e: - _vlog().warning(f" 提取参数名失败 {src_path}: {e}") - - def _register_compiled_include(self, src_path: str, sha1: str, ll_content: str, include_ModuleSha1Map: dict[str, str]) -> None: - """编译 include 文件成功后,生成签名和 stub 文件并注册到符号表中""" - module_name = os.path.splitext(os.path.basename(src_path))[0] - - # 确定完整模块名(如 vpsdk.window) - mod_full = module_name - for includes_dir in self.include_dirs: - try: - rel = os.path.relpath(src_path, includes_dir) - mod_full = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') - break - except ValueError: - pass - - # 1. 生成 .pyi 签名文件(如果还没有的话) - sig_path = os.path.join(self.temp_dir, f"{sha1}.vpi") - if not _CachedIsFile(sig_path): - # 全局缓存检查(不被 --clean 清除) - _TryGlobalCache(sha1, 'pyi', sig_path) - if not _CachedIsFile(sig_path): - try: - py_content = _ReadTextCached(src_path) - sig_content = PythonToStubConverter.convert(py_content, mod_full) - os.makedirs(self.temp_dir, exist_ok=True) - with open(sig_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(sig_content) - _InvalidateTextCache(sig_path) - _SaveToGlobalCache(sha1, 'pyi', sig_path) - except Exception as e: - if _config_mode == "strict": - raise - _vlog().warning(f"签名生成失败不阻塞编译: {e}", "Exception") - - # 注册到 sig_files 和 _source_module_sig_files - if _CachedIsFile(sig_path): - self.sig_files[sha1] = sig_path - self.sha1_map[sha1] = f"includes/{mod_full.replace('.', '/')}.vp" - if self._shared_source_module_sig_files is not None: - self._shared_source_module_sig_files[mod_full] = sig_path - self._shared_source_module_sig_files[module_name] = sig_path - - # 将签名信息加载到共享符号表中 - if self._shared_symbol_table is not None: - try: - self._shared_symbol_table.LoadModuleSymbols(sig_path, mod_full, lineno=0) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"加载模块签名到共享符号表失败: {_e}", "Exception") - - # 2. 生成 .stub.ll 文件 - stub_path = os.path.join(self.output_dir, f"{sha1}.stub.ll") - if not _CachedIsFile(stub_path): - # 全局缓存检查(不被 --clean 清除) - _TryGlobalCache(sha1, 'stub.ll', stub_path) - if not _CachedIsFile(stub_path) and ll_content: - try: - stub_content, _ = self._split_ll(ll_content) - with open(stub_path, 'w', encoding='utf-8', newline='\n') as f: - f.write(stub_content) - _SaveToGlobalCache(sha1, 'stub.ll', stub_path) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"生成 .stub.ll 文件失败: {_e}", "Exception") - - # 注册到 stub_files - if _CachedIsFile(stub_path): - self.stub_files[sha1] = stub_path - # output/.stub.ll 存在(本步刚落盘或从全局缓存拷入)→ 置脏, - # 让后续索引 miss 重扫到新增的 *.stub.ll(见 _build_stub_index 快路径) - from lib.core.Handles.HandlesImports import ImportHandle as _IH3 - _IH3.MarkStubSetDirty() - elif _CachedIsFile(sig_path): - # 如果 stub 文件不存在,也尝试从 temp 目录查找 - temp_stub = os.path.join(self.temp_dir, f"{sha1}.stub.ll") - if _CachedIsFile(temp_stub): - self.stub_files[sha1] = temp_stub - else: - # 最后尝试从全局缓存直接读取 - _TryGlobalCache(sha1, 'stub.ll', temp_stub) - if _CachedIsFile(temp_stub): - self.stub_files[sha1] = temp_stub - - def _precollect_inline_symbols(self) -> None: - """在主翻译循环之前,扫描includes目录中used_includes相关的模块收集内联函数AST""" - if not self.used_includes: - return - for includes_dir in self.include_dirs: - if not os.path.isdir(includes_dir): - continue - for module_name in self.used_includes: - module_dir = os.path.join(includes_dir, module_name) - if os.path.isdir(module_dir): - for root, dirs, files in os.walk(module_dir): - dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] - for fname in files: - if fname.endswith('.vp') or fname.endswith('.py'): - src_path = os.path.join(root, fname) - self._collect_inline_symbols(src_path) - py_path = os.path.join(includes_dir, module_name + '.vp') - if not _CachedIsFile(py_path): - # 回退到 .py(向后兼容) - py_path = os.path.join(includes_dir, module_name + '.py') - if _CachedIsFile(py_path): - self._collect_inline_symbols(py_path) - for ef in self.extra_py_files: - self._collect_inline_symbols(ef) - - def _collect_inline_symbols(self, src_path: str): - """解析源文件,收集内联函数的AST信息到inline_func_symbols - - 复用 parse_python_file 的 AST 缓存,避免重复 open+read+ast.parse。 - """ - try: - _, tree = parse_python_file(src_path) - if tree is None: - return - module_name = os.path.splitext(os.path.basename(src_path))[0] - for node in ast.iter_child_nodes(tree): - if isinstance(node, ast.FunctionDef) and node.returns: - is_inline = False - if isinstance(node.returns, ast.BinOp) and isinstance(node.returns.op, ast.BitOr): - for part in [node.returns.left, node.returns.right]: - if isinstance(part, ast.Attribute) and part.attr == 'CInline': - is_inline = True - break - if isinstance(part, ast.Name) and part.id == 'CInline': - is_inline = True - break - elif isinstance(node.returns, ast.Attribute) and node.returns.attr == 'CInline': - is_inline = True - elif isinstance(node.returns, ast.Name) and node.returns.id == 'CInline': - is_inline = True - if is_inline: - func_name = node.name - sym_key = f"{module_name}.{func_name}" - info = CTypeInfo() - info.Name = func_name - info.IsFunction = True - info.IsInline = True - info.InlineBody = node.body - info.InlineParams = [arg.arg for arg in node.args.args] - info.Storage = t.CInline() - self.inline_func_symbols[func_name] = info - self.inline_func_symbols[sym_key] = info - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"预收集内联函数符号失败: {_e}", "Exception") - - def _build_shared_symbol_data(self) -> None: - """一次性构建所有文件共享的符号表数据,避免每个文件重复加载""" - t0 = time.time() - - _vlog().info("[缓存] 构建共享符号表数据...") - - base_trans = TransPyC.TransPyC(code="pass", triple=self.triple, datalayout=self.datalayout) - base_trans.translator.LlvmGen = None - base_trans.translator._ModuleSha1Map = {} - base_trans.translator._global_function_default_args = self.function_default_args - base_trans.translator._global_function_param_names = self.function_param_names - - # ---- 两阶段加载:先收集全部模块,再「①声明」全跑完 / 「②签名」全跑完 ---- - # 声明阶段把类/枚举/typedef 灌满符号表,签名阶段才解析函数签名 ⇒ 模块间 - # 类型引用(`from X import *` 的裸名如 posix 的 `INT`、跨模块类如 - # `rawptr[Constant]`)不再依赖 os.listdir 的字典序(此前 posix 排在 stdint - # 之前 ⇒ `INT` 解析不到,旧口径静默降级成 i32,fail-fast 后必须消除)。 - staged: list[tuple[str, str]] = [] # (声明文件路径, 模块名) - # 需要重导出的包(在全部模块符号加载完后统一处理;提到循环外避免多个 - # include_dir 时被反复覆盖/重复收集) - _pending_reexports: list[tuple[str, str]] = [] - for includes_dir in self.include_dirs: - if os.path.isdir(includes_dir): - for pyi_file in os.listdir(includes_dir): - if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'): - pyi_path = os.path.join(includes_dir, pyi_file) - module_name = os.path.splitext(pyi_file)[0] - staged.append((pyi_path, module_name)) - for py_file in os.listdir(includes_dir): - if (py_file.endswith('.vp') or py_file.endswith('.py')) and (not py_file.startswith('_') or py_file == '_list.vp' or py_file == '_list.py' or py_file == '_dict.vp' or py_file == '_dict.py'): - module_name = os.path.splitext(py_file)[0] - sha1_key = self.include_py_map.get(module_name) - if sha1_key and sha1_key in self.sig_files: - staged.append((self.sig_files[sha1_key], module_name)) - # 收集需要重导出的包,等所有模块符号加载完后再处理 - for sha1_key, sig_path in self.sig_files.items(): - rel_path = self.sha1_map.get(sha1_key, sha1_key) - if rel_path.startswith('includes/'): - mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') - mod_name = mod_name[len('includes/'):] - _entry_mod: tuple[str, str] = (sig_path, mod_name) - if _entry_mod not in staged: - staged.append(_entry_mod) - if mod_name.endswith('.__init__'): - package_name = mod_name[:-len('.__init__')] - _entry_pkg: tuple[str, str] = (sig_path, package_name) - if _entry_pkg not in _pending_reexports: - _pending_reexports.append(_entry_pkg) - - for sha1_key, sig_path in self.sig_files.items(): - rel_path = self.sha1_map.get(sha1_key, sha1_key) - if rel_path.startswith('includes/'): - continue - module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') - staged.append((sig_path, module_name)) - base_trans.translator._source_module_sig_files[module_name] = sig_path - short_name = module_name.split('.')[-1] if '.' in module_name else module_name - if short_name != module_name: - base_trans.translator._source_module_sig_files[short_name] = sig_path - - # ① 声明阶段(全部模块;多轮直到"本轮没有新增可解析的声明") - # 单轮仍会与模块顺序有关:typedef 右侧可以引用**其它模块**的 typedef, - # 例 win32base 的 `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint - # ⇒ 本模块先 stage 时右侧解析为空;下一轮把它重 stage 一遍(重 stage 覆盖 - # 登记,条目随依赖入表而变完整)。无进展即停,最多再跑一轮。 - _stage_map: dict[tuple[str, str], ModuleSymbols] = {} - _round_pending = list(staged) - _prev_miss: int | None = None - while _round_pending: - _miss_total: int = 0 - _next_round: list[tuple[str, str]] = [] - for _stage_path, _stage_mod in _round_pending: - _stage_ms = base_trans.translator.SymbolTable.StageModuleSymbols(_stage_path, _stage_mod) - if _stage_ms is None: - continue - _stage_map[(_stage_path, _stage_mod)] = _stage_ms - _miss: int = _CountUnresolvedTypedefs(_stage_ms) - _miss_total += _miss - if _miss: - _next_round.append((_stage_path, _stage_mod)) - if not _next_round or (_prev_miss is not None and _miss_total >= _prev_miss): - break - _prev_miss = _miss_total - _round_pending = _next_round - - # ② 签名阶段(全部模块;此时任一模块的签名都能看到全部声明) - for (_commit_path, _commit_mod), _commit_ms in _stage_map.items(): - base_trans.translator.SymbolTable.CommitModuleSymbols(_commit_ms, _commit_mod, _commit_path) - - # 所有 includes 模块符号已加载,现在处理包重导出 - for sig_path, package_name in _pending_reexports: - self._register_package_reexports(base_trans, sig_path, package_name) - - all_dc = {} - all_dt: dict[str, tuple[int, bool]] = {} - for sha1_key, stub_path in self.stub_files.items(): - if os.path.exists(stub_path): - try: - stub_content = _ReadTextCached(stub_path) - for line in stub_content.splitlines(): - line = line.strip() - if line.startswith('@') and '= global' in line and 'i32' in line and not line.startswith('@llvm'): - var_name = line.split('=')[0].strip().lstrip('@') - val_part = line.split('i32')[-1].strip().rstrip(']').lstrip('[').lstrip('i32') - try: - val = int(val_part) - all_dc[var_name] = val - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - except Exception as e: - _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) - - for sha1_key, pyi_path in self.sig_files.items(): - _ExtractCDefineConstantsFromPyi(pyi_path, all_dc, base_trans.translator.SymbolTable, all_dt) - - export_extern_funcs = {} - for sha1_key, sig_path in self.sig_files.items(): - try: - # 复用 parse_python_file 的 AST 缓存(sig 文件被多处解析) - _, sig_tree = parse_python_file(sig_path) - if sig_tree is None: - continue - for node in ast.iter_child_nodes(sig_tree): - if isinstance(node, ast.FunctionDef): - if _check_annotation_for_export(node.returns): - export_extern_funcs[node.name] = sha1_key - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"收集共享导出外部函数失败: {_e}", "Exception") - - for sym_name, sym_info in self.inline_func_symbols.items(): - if sym_name not in base_trans.translator.SymbolTable: - base_trans.translator.SymbolTable[sym_name] = sym_info - else: - existing = base_trans.translator.SymbolTable[sym_name] - if not getattr(existing, 'InlineBody', None) and getattr(sym_info, 'InlineBody', None): - existing.IsInline = sym_info.IsInline - existing.InlineBody = sym_info.InlineBody - existing.InlineParams = sym_info.InlineParams - - self._shared_symbol_table = base_trans.translator.SymbolTable - self._shared_source_module_sig_files = dict(base_trans.translator._source_module_sig_files) - self._shared_all_dc = all_dc - self._shared_all_decl_types = all_dt - self._shared_export_extern_funcs = export_extern_funcs - - generic_class_templates = {} - for includes_dir in self.include_dirs: - if os.path.isdir(includes_dir): - abs_includes = os.path.join(self.src_root, includes_dir) if not os.path.isabs(includes_dir) else includes_dir - if not os.path.isdir(abs_includes): - abs_includes = includes_dir - for py_file in os.listdir(abs_includes): - if (py_file.endswith('.vp') or py_file.endswith('.py')) and (not py_file.startswith('_') or py_file == '_list.vp' or py_file == '_list.py' or py_file == '_dict.vp' or py_file == '_dict.py'): - py_path = os.path.join(abs_includes, py_file) - try: - # 复用 parse_python_file 的 AST 缓存 - py_content, py_tree = parse_python_file(py_path) - if py_tree is None: - continue - # 计算 include 文件的 SHA1,供特化时统一命名空间 - py_sha1: str = compute_sha1(py_content) - for node in py_tree.body: - if isinstance(node, ast.ClassDef) and hasattr(node, 'type_params') and node.type_params: - type_params = [tp.name for tp in node.type_params] - generic_class_templates[node.name] = { - 'node': node, - 'type_params': type_params, - 'sha1': py_sha1, - } - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"收集泛型类模板失败: {_e}", "Exception") - self._shared_generic_class_templates = generic_class_templates - - # 加载所有 {sha1}.doc.json 到 _shared_doc_meta,供跨模块 __doc__ 查询 - shared_doc_meta: dict[str, dict[str, str]] = {} - if os.path.isdir(self.temp_dir): - for fname in os.listdir(self.temp_dir): - if fname.endswith('.doc.json'): - sha1_key = fname[:-len('.doc.json')] - try: - with open(os.path.join(self.temp_dir, fname), 'r', encoding='utf-8') as f: - data = json.load(f) - if isinstance(data, dict): - shared_doc_meta[sha1_key] = {str(k): str(v) for k, v in data.items()} - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"加载 {fname} 失败: {_e}", "Exception") - # 也从全局缓存加载 .doc.json(temp_dir 被 --clean 清空时补充) - if os.path.isdir(_GLOBAL_CACHE_DIR): - for fname in os.listdir(_GLOBAL_CACHE_DIR): - if fname.endswith('.doc.json'): - sha1_key = fname[:-len('.doc.json')] - if sha1_key in shared_doc_meta: - continue - try: - data = json.loads(_ReadTextCached(os.path.join(_GLOBAL_CACHE_DIR, fname))) - if isinstance(data, dict): - shared_doc_meta[sha1_key] = {str(k): str(v) for k, v in data.items()} - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"加载全局缓存 {fname} 失败: {_e}", "Exception") - self._shared_doc_meta = shared_doc_meta - - t1 = time.time() - _vlog().info(f"[缓存] 共享符号表构建完成 ({t1-t0:.2f}s, {len(self._shared_symbol_table)} 符号, {len(generic_class_templates)} 泛型模板, {len(shared_doc_meta)} doc 元数据)") - - def _compile_include_sources(self): - """扫描项目目录中的汇编存根,编译 includes 目录中发现的 C/C++ 源文件""" - extra_sources = list(self.extra_compile_files) - scan_dirs = [] - if self.src_root and os.path.isdir(self.src_root): - scan_dirs.append(self.src_root) - # 不再扫描 output_dir 的父目录:当 output_dir 位于顶层时, - # 父目录是整个仓库根,会误扫到 ViperOS/includes/vqt6 等无关 C/C++ 源文件。 - # 项目特定的 C/汇编文件应放在 src_root 下,或通过 extra_compile_files 指定。 - for scan_dir in scan_dirs: - for root, dirs, files in os.walk(scan_dir): - dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] - for fname in files: - if fname.endswith(('.S', '.s', '.c', '.cpp')): - if fname.startswith('test_'): - continue - fpath = os.path.join(root, fname) - if fpath not in extra_sources: - extra_sources.append(fpath) - if not extra_sources: - return - - cc_map = { - '.c': 'gcc', - '.cpp': 'g++', '.cc': 'g++', '.cxx': 'g++', - '.m': 'clang', '.mm': 'clang++', - '.s': 'gcc', '.S': 'gcc', - } - - _vlog().info(f"\n[includes 编译] 找到 {len(extra_sources)} 个源文件") - - for src_path in extra_sources: - _, ext = os.path.splitext(src_path) - ext = ext.lower() - obj_path = os.path.join(self.output_dir, os.path.splitext(os.path.basename(src_path))[0] + '.obj') - if ext in ('.s', '.S') and any('oformat' in f for f in self.linker_flags): - cc = 'clang' - compile_src = src_path - extra_flags = ['-c', '-target', 'x86_64-none-elf', '-o', obj_path, compile_src] - else: - cc = cc_map.get(ext, 'gcc') - extra_flags = ['-c', '-o', obj_path, src_path] - - _vlog().info(f" 编译: {os.path.basename(src_path)} -> {os.path.basename(obj_path)}") - - try: - cmd = [cc] + extra_flags - result = subprocess.run(cmd, capture_output=True, text=True) - if result.returncode == 0: - _vlog().success(" 编译成功") - self.extra_link_files.append(obj_path) - else: - _vlog().error(f" 编译失败: {result.stderr.strip()}") - _vlog().error(f"\nC/C++ 源文件编译失败,立即终止编译。") - sys.exit(1) - except FileNotFoundError: - _vlog().error(f" 找不到编译器: {cc}") - _vlog().error(f"\n找不到编译器,立即终止编译。") - sys.exit(1) - except Exception as e: - _vlog().error(f" {e}") - _vlog().error(f"\n编译异常,立即终止编译。") - sys.exit(1) - - def _strip_debug_sections(self, exe_path: str) -> None: - """剥离 .exe 中的调试段(跳过裸机二进制)""" - if not os.path.exists(exe_path): - return - ext = os.path.splitext(exe_path)[1].lower() - if ext not in ('.exe', '.dll', '.obj', '.o', '.elf'): - _vlog().info(f" [剥离] 跳过({ext} 格式无需剥离)") - return - for tool in ['llvm-strip', 'strip']: - strip_cmd = shutil.which(tool) - if strip_cmd: - try: - result = subprocess.run( - [strip_cmd, '--strip-debug', exe_path], - capture_output=True, text=True - ) - if result.returncode == 0: - _vlog().info(f" [剥离] 调试段已移除 ({tool})") - else: - _vlog().warning(f" [剥离] 警告: {result.stderr.strip()}") - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"剥离可执行文件调试段失败: {_e}", "Exception") - break - - def _WriteStackMap(self, exe_path: str) -> None: - """--traceback:链接成功后导出"地址→源行"旁文件 .stackmap。 - - 用 llvm-dwarfdump 解析最终 PE 的 .debug_line(地址→文件/行)与 - .debug_info(DISubprogram 的 low/high_pc + DW_AT_name → 函数名), - 换算为 RVA 后落盘。旁文件规格(全部小端 little-endian): - - 头部 20 字节 = magic(4)="TPCS" + version(u32=1) + entry_count(u32) - + str_count(u32) + str_bytes(u32) - 记录 entry_count × 20 字节(按 rva 升序): - rva(u64) + file_off(u32) + line(u32) + func_off(u32) - 字符串池 str_bytes 字节(紧接记录区,UTF-8,全部以 '\\0' 结尾) - - 口径(必须与运行期严格一致):运行期 traceback.vp 取 - `rva = 帧地址 − GetModuleHandleA(NULL)`,即"链接期 ImageBase 之上的偏移", - 故写端唯一正确口径是 **`RVA = dwarfdump 打印的地址 − PE.ImageBase`**。 - - ⚠ 实测 TransPyV.exe 的 DWARF 里同时存在两个地址域: - * 绝大多数行/子程序地址是**已重定位的 VA**(0x14000xxxx,含 ImageBase); - * 另有 22638 条行记录 / 756 个 subprogram 的地址**恰为 0**——它们是**被 - 链接器按 COMDAT 去重丢弃的泛型实例副本**(实测 36 个 `list[T].*` 名字, - 每个都有 1 份 VA 副本 + 15~21 份 0 地址副本),并非解析错误。 - 旧实现按"整段最小地址是否 >= ImageBase"**整表**判定:只要有一行 0,就整表都不 - 减基址 ⇒ 全部 VA 行留在 0x140000000+ 域、与运行期 RVA 域相差一个 ImageBase, - 运行期下界查找对任意帧都落到同一条记录上(用户实测所有帧都显示同一行)。 - 现改为**逐条**归一化:地址落在映像 VA 区间 [ImageBase, ImageBase+SizeOfImage) - 的记录保留(RVA = 地址 − ImageBase);其余(未重定位的 0、越界值)一律**丢弃并 - 统计**(不静默留垃圾条目)。同一 RVA 只保留行号最大的一条。 - - 写出前自检(任一不满足即 fail-fast 抛异常,遵循 --traceback 既有风格): - 记录数 / min~max RVA 与 SizeOfImage 的关系 / 所有 RVA ∈ [0, SizeOfImage) / - RVA 严格升序 / 至少若干条落在运行期可能出现的 RVA 区间 - (粗判据 [.text 节 RVA, SizeOfImage))。 - - 任何一步失败都直接抛异常终止编译(--traceback 的硬要求,无兜底)。 - """ - import struct - import bisect - - def _Fail(msg: str) -> None: - full: str = msg + '(这是 --traceback 的要求)' - _vlog().error(full) - raise RuntimeError(full) - - layout = _ReadPEImageLayout(exe_path) - if layout is None: - _Fail(f"--traceback 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE") - image_base, size_of_image, sections = layout - if size_of_image <= 0: - _Fail(f"--traceback 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域") - img_lo: int = image_base - img_hi: int = image_base + size_of_image - # 运行期可能出现的 RVA 区间的**粗判据**下界:.text 节 RVA(缺失时退化为最小节 RVA) - text_rva: int = min((v[0] for v in sections.values()), default=0x1000) - if '.text' in sections: - text_rva = sections['.text'][0] - max_sec_end: int = max((v[0] + v[1] for v in sections.values()), default=size_of_image) - - dwarfdump: str | None = shutil.which('llvm-dwarfdump') or shutil.which('llvm-dwarfdump.exe') - if not dwarfdump: - _Fail("--traceback 找不到 llvm-dwarfdump(请确认 PATH 内含 LLVM 工具)") - - try: - proc = subprocess.run( - [dwarfdump, '--debug-line', '--debug-info', exe_path], - capture_output=True) - except Exception as e: - _Fail(f"--traceback 调用 llvm-dwarfdump 失败: {e}") - if proc.returncode != 0: - _Fail(f"--traceback 的 llvm-dwarfdump 返回 {proc.returncode}: " - + proc.stderr.decode('utf-8', errors='replace').strip()) - dump_text: str = proc.stdout.decode('utf-8', errors='replace') - - rows: list[tuple[int, str, int]] = _ParseDwarfLineRows(dump_text) - if not rows: - _Fail(f"--traceback 未能从 {exe_path} 解析出任何 DWARF 行记录(.debug_line 为空?)") - - # ---- 行记录:逐条归一化为"运行期 RVA";不能归一的一律丢弃并统计 ---- - dropped_rows: int = 0 - dropped_row_samples: list[int] = [] - best: dict[int, tuple[str, int]] = {} - for addr, path, line in rows: - if not (img_lo <= addr < img_hi): - dropped_rows += 1 - if len(dropped_row_samples) < 5: - dropped_row_samples.append(addr) - continue - rva: int = addr - image_base - cur = best.get(rva) - if cur is None or line > cur[1]: - best[rva] = (path, line) - if dropped_rows: - _vlog().warning( - f" [--traceback] 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录" - f"(地址不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;判定原因:多为被连接器" - f" COMDAT 去重丢弃的泛型副本,其地址恰为 0)示例地址: " - + ", ".join(f"0x{a:x}" for a in dropped_row_samples)) - if not best: - _Fail(f"--traceback 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域") - - # ---- 子程序(函数名):同样逐条归一化;不能归一的丢弃 ---- - funcs: list[tuple[int, int, str]] = _ParseDwarfSubprograms(dump_text) - func_ranges: list[tuple[int, int, str]] = [] - dropped_funcs: int = 0 - for low, high, name in funcs: - if not (img_lo <= low < img_hi): - dropped_funcs += 1 - continue - lo_rva: int = low - image_base - hi_rva: int = high - image_base - if hi_rva <= lo_rva: - hi_rva = lo_rva + 1 - if hi_rva > size_of_image: - hi_rva = size_of_image - func_ranges.append((lo_rva, hi_rva, name)) - if dropped_funcs: - _vlog().warning( - f" [--traceback] 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序" - f"(low_pc 不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;同上为去重副本)") - func_ranges.sort(key=lambda f: f[0]) - func_lows: list[int] = [f[0] for f in func_ranges] - - def _FuncNameFor(rva: int) -> str | None: - i: int = bisect.bisect_right(func_lows, rva) - 1 - if i >= 0: - low, high, name = func_ranges[i] - if low <= rva < high: - return name - return None - - # 字符串池:偏移 0 固定为一个空串('\0')哨兵,使 func_off=0 可无歧义表示 - # "无函数名";真实串偏移恒 >= 1。 - pool: bytearray = bytearray(b'\x00') - str_offsets: dict[str, int] = {'': 0} - - def _Intern(s: str) -> int: - off: int | None = str_offsets.get(s) - if off is None: - off = len(pool) - pool.extend(s.encode('utf-8')) - pool.append(0) - str_offsets[s] = off - return off - - records: list[tuple[int, int, int, int]] = [] - for rva in sorted(best.keys()): - path, line = best[rva] - name = _FuncNameFor(rva) - records.append((rva, _Intern(path), line, _Intern(name) if name else 0)) - - # ---- 写端自检(fail-fast;与运行期 LoadMapFile 的校验口径一一对应)---- - rvas: list[int] = [r[0] for r in records] - min_rva: int = rvas[0] - max_rva: int = rvas[-1] - for i in range(1, len(rvas)): - if rvas[i] <= rvas[i - 1]: - _Fail(f"--traceback 自检失败:记录 RVA 非严格升序" - f"(第 {i} 条 0x{rvas[i]:x} <= 前一条 0x{rvas[i - 1]:x},会破坏运行期二分查找)") - outside: int = sum(1 for r in rvas if not (0 <= r < size_of_image)) - if outside: - _Fail(f"--traceback 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外") - # 运行期可能出现的 RVA 区间(粗判据):[.text 节 RVA, SizeOfImage)。一条都没有 ⇒ 口径又错了。 - plausible: int = sum(1 for r in rvas if text_rva <= r < size_of_image) - if plausible <= 0: - _Fail(f"--traceback 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间" - f" [0x{text_rva:x}(.text RVA), 0x{size_of_image:x}(SizeOfImage)) 内" - f"(RVA 口径疑似又错了)") - _vlog().info( - f" [--traceback] 自检: 记录={len(records)} RVA范围=[0x{min_rva:x}, 0x{max_rva:x}]" - f" PE.SizeOfImage=0x{size_of_image:x}({size_of_image})" - f" 各节最高结束 RVA=0x{max_sec_end:x}" - f" | 全部∈[0,SizeOfImage)=是 严格升序=是" - f" | 落在[.text 0x{text_rva:x},SizeOfImage)内={plausible} 条") - - out: bytearray = bytearray() - out.extend(b'TPCS') - out.extend(struct.pack(' None: - """将 active_sha1s 对应的 .obj 文件和 includes 库文件链接为可执行文件""" - obj_files = [] - merged_obj = os.path.join(self.output_dir, '_merged.obj') - if _CachedIsFile(merged_obj): - obj_files.append(merged_obj) - else: - for root, dirs, files in os.walk(self.output_dir): - for file in files: - if file.endswith('.obj'): - sha1 = file[:-4] - if sha1 in active_sha1s and sha1 not in self._include_sha1s: - obj_files.append(os.path.join(root, file)) - - if not obj_files and not self.extra_link_files: - _vlog().info("[链接] 无文件可链接") - return - - _vlog().info(f"\n[链接] 找到 {len(obj_files)} 个 .obj 文件") - - all_link_files = obj_files + self.extra_link_files - seen = set() - all_link_files = [f for f in all_link_files if not (f in seen or seen.add(f))] - if self.extra_link_files: - _vlog().info(f"[链接] 额外库文件: {len(self.extra_link_files)} 个") - for lib in self.extra_link_files: - _vlog().info(f" - {os.path.basename(lib)}") - - if not self.linker_cmd: - _vlog().info("[链接] 未配置链接器") - return - - exe_path = os.path.join(self.output_dir, self.linker_output) if self.linker_output else os.path.join(self.output_dir, 'a.exe') - is_binary = self.linker_output and self.linker_output.endswith('.bin') - - # 检查是否直接输出二进制(使用 --oformat binary / -oformat binary 标志) - is_direct_binary = is_binary and any('oformat' in f for f in self.linker_flags) - - if is_binary and not is_direct_binary: - link_output = exe_path[:-4] + '_pe.exe' - else: - link_output = exe_path - - # 自动复制或生成 linker.ld 到输出目录 - linker_ld_src = os.path.join(os.path.dirname(self.output_dir), 'linker.ld') - linker_ld_dst = os.path.join(self.output_dir, 'linker.ld') - if _CachedIsFile(linker_ld_src) and not _CachedIsFile(linker_ld_dst): - shutil.copy2(linker_ld_src, linker_ld_dst) - _vlog().info(f" [链接] 使用链接脚本: linker.ld") - elif not _CachedIsFile(linker_ld_dst) and any('-T' in f or '-T' in f.replace(',', ' ') for f in self.linker_flags): - default_ld = """ENTRY(_start) - -SECTIONS { - . = 0x100000; - - .text : { - *(.text) - *(.text.*) - } - - .rodata : { - *(.rodata) - *(.rodata.*) - } - - .data : { - *(.data) - *(.data.*) - } - - .bss : { - *(.bss) - *(.bss.*) - } - - /DISCARD/ : { - *(.comment) - *(.note) - *(.eh_frame) - *(.eh_frame_hdr) - *(.reloc) - *(.rela) - *(.debug*) - } -} -""" - with open(linker_ld_dst, 'w', encoding='utf-8', newline='\n') as f: - f.write(default_ld) - _InvalidateTextCache(linker_ld_dst) - _vlog().info(f" [链接] 自动创建默认链接脚本: linker.ld") - - try: - # 将 linker_flags 拆分为非库标志和库标志(-l/-L),库标志放在目标文件之后 - # 这样 Unix 风格链接器才能正确解析库中的符号 - # -Wl, 选项是链接器全局选项,放在目标文件之前 - non_lib_flags = [] - lib_flags = [] - i = 0 - while i < len(self.linker_flags): - flag = self.linker_flags[i] - if flag in ('-l', '-L') and i + 1 < len(self.linker_flags): - # -l xxx / -L xxx 分开的形式 - lib_flags.extend([flag, self.linker_flags[i + 1]]) - i += 2 - elif flag.startswith('-l') or flag.startswith('-L'): - # -lxxx / -Lxxx 合并的形式 - lib_flags.append(flag) - i += 1 - else: - # 其他所有标志(包括 -Wl, 选项)放在目标文件之前 - non_lib_flags.append(flag) - i += 1 - # ASan 模式:链接 ASan 运行库(配合各 .ll 的 opt -passes=asan 插桩) - # 不能传 -fsanitize=address:那会让 clang++ 驱动去找 - # /lib/x86_64-w64-windows-gnu/libclang_rt.asan_dynamic.dll.a, - # 而该目录是空的(上游 LLVM 不为这个 triple 构建 ASan runtime), - # 直接报 "could not open ... No such file or directory"。 - # 改为显式链接 clang64 的那两份(与 TPV / README §8.1 同口径)。 - # 它们是库,必须排在目标文件之后,所以追加到 lib_flags 而不是 non_lib_flags。 - if self.asan_mode: - if self.asan_rt_dir is None: - _vlog().error( - "ASan 模式在 PATH 里找不到 clang64 的 ASan 运行库目录 " - "(\\clang64\\lib\\clang\\\\lib\\windows\\" - "libclang_rt.asan_dynamic-x86_64.dll.a)\n" - " 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装," - "且 \\clang64(或其 bin)已在 PATH 中。") - sys.exit(1) - lib_flags.append('-L' + self.asan_rt_dir) - lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a') - lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a') - # Ctrl+C 栈转储(--traceback):includes/traceback.vp 的主回溯路径用 - # StackWalk64 / SymInitialize / SymFunctionTableAccess64 / SymGetModuleBase64 - # (均在 dbghelp),故开启该开关时必须显式链接 -ldbghelp,否则链接期报未定义符号。 - # 与 ASan 同理:库要排在目标文件之后 ⇒ 追加到 lib_flags。 - # (若确实无法链 dbghelp:把 includes/traceback.vp 的 TRACEBACK_USE_DBGHELP 置 0, - # 退化为纯 RBP 链回溯,此时不该再带本 flag。) - if self.traceback_enabled: - lib_flags.append('-ldbghelp') - cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags - _vlog().info(f" 执行: {' '.join(cmd)}") - result = subprocess.run( - cmd, - capture_output=True, - text=True, - cwd=self.output_dir or '.' - ) - if result.returncode == 0: - if is_binary and not is_direct_binary: - objcopy = shutil.which('llvm-objcopy') or shutil.which('objcopy') - if objcopy: - conv = subprocess.run( - [objcopy, '-O', 'binary', link_output, exe_path], - capture_output=True, text=True - ) - if conv.returncode == 0: - os.remove(link_output) - _vlog().success(f" 生成裸机二进制: {exe_path}") - file_size = os.path.getsize(exe_path) - _vlog().info(f" [大小] {file_size} 字节") - else: - _vlog().error(f" 二进制转换失败: {conv.stderr.strip()}") - sys.exit(1) - else: - _vlog().error(" 找不到 llvm-objcopy,无法生成裸机二进制") - sys.exit(1) - else: - _vlog().success(f" 生成: {exe_path}") - file_size = os.path.getsize(exe_path) - _vlog().info(f" [大小] {file_size} 字节") - # --dwarf 构建(含 --asan 自动开启)必须保留 DWARF: - # 该模式存在的唯一意义是崩溃时反查 ".vp:行", - # 而 _strip_debug_sections 会 llvm-strip --strip-debug 掉全部 - # .debug_* 段(实测 exe 体积 13378048 → 11895296, - # llvm-symbolizer 对含 main 在内的所有地址返回 ??:0:0)。 - # 非 dwarf 构建保持原有剥离行为(产物零变化)。 - if not is_binary and not self.dwarf_enabled: - self._strip_debug_sections(exe_path) - else: - _vlog().error(f" 链接失败: {result.stderr.strip()}") - _vlog().error(f"\n链接失败,立即终止编译。") - sys.exit(1) - except FileNotFoundError: - _vlog().error(f" 找不到链接器: {self.linker_cmd}") - _vlog().error(f"\n找不到链接器,立即终止编译。") - sys.exit(1) - except Exception as e: - _vlog().error(f" {e}") - _vlog().error(f"\n链接异常,立即终止编译。") - sys.exit(1) - - # --traceback:链接成功后导出"地址→源行"旁文件(.stackmap)。 - # 刻意放在上面的链接 try/except 之外,使导出阶段的 fail-fast RuntimeError - # 直接向上抛出终止编译,而不被"链接异常兜底"吞掉。 - if self.traceback_enabled: - self._WriteStackMap(exe_path) +from __future__ import annotations + +import sys +import os +import re +import shutil +import subprocess +import vast as ast +import traceback +import json +import pickle +import time +import cProfile +from collections import OrderedDict +from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor, as_completed, wait, FIRST_COMPLETED +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.SymbolData import ModuleSymbols +from lib.includes import t +from lib.core.SymbolUtils import AnnotationContainsName, ExpandSoftKeywords, DefineDeclIntTypeFromAnnotation +from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod +from lib.Projectrans.Utils import compute_sha1, _check_annotation_for_export, find_reachable_files_from_entries, topological_sort_files, parse_python_file +from lib.Projectrans.Phase1Generator import _TryGlobalCache, _SaveToGlobalCache, _GLOBAL_CACHE_DIR +import TransPyC +from StubGen import PythonToStubConverter +from lib.core.LLVMCG.IRMetadataFix import SanitizeIRMetadata + + +# PROF 热点:nt._path_isfile 2.6 万次 16.4s(Windows 磁盘 stat ~0.6ms/次)。 +# 编译进程内文件状态稳定(obj/ll 一旦生成不删除、不被移动),用模块级缓存 +# 避免重复 stat。仅用于"存在性检查"类调用(不用于内容变化的判断)。 +_FILE_STAT_CACHE: dict[str, bool] = {} + + +# --traceback(Ctrl+C 调用栈)强制纳入编译+链接的 include 模块源文件名。 +# 该模块对外只有 Install() 入口,且无任何用户代码 import 它 —— 若不强制纳入, +# 产物里既没有 Install 符号、也没有调用它的启动期构造器,功能静默失效。 +# .vp 优先,.py 仅为向后兼容的回退名。 +_TRACEBACK_MODULE_FILES: tuple[str, ...] = ('traceback.vp', 'traceback.py') + + +def _CachedIsFile(path: str) -> bool: + r: bool | None = _FILE_STAT_CACHE.get(path) + if r is None: + r = os.path.isfile(path) + _FILE_STAT_CACHE[path] = r + return r + + +# --------------------------------------------------------------- +# 文本读缓存(性能):同一个文件在一次构建里被**多个阶段反复读** +# (prof 基线 PROF/20260930_224857:_io.open 12,740 次 / self 28.8s、 +# read 11,902 次 / self 30.4s、TextIOWrapper.__exit__ 16.6s —— 128 个模块 +# 却产生上万次读,主要是同一份 .stub.ll / .ll / deps.json 被 +# _compile_one / _recombine_ll / 依赖解析等重复打开)。 +# 键 = **路径**(不做 stat / 时间戳校验)。性能前提(用户口径):TPC 单次运行内 +# 非本进程写出的文件内容不变 —— 因此不需要"每次读都 stat 一次"来判新旧; +# 若再 stat,等于把省下的 1.2 万次 open 换成 1.2 万次 stat(仍是系统调用热点)。 +# 正确性前提:**本进程写出/改写/删除过的文件,一律在该动作后调用 +# _InvalidateTextCache**(写点见文件内全部 open(..., 'w') 与 os.remove 处)。 +# 只缓存 newline=None 的默认换行翻译读,与直接 open(...).read() 语义逐字相同。 +# 内存:按总字节预算 + LRU 淘汰;单文件超过 _TEXT_CACHE_FILE_MAX 不入缓存 +# (合并后的 .ll 可达数十 MB,避免把内存吃光)。 +# --------------------------------------------------------------- +_TEXT_CACHE_MAX_BYTES: int = 64 * 1024 * 1024 +_TEXT_CACHE_FILE_MAX: int = 32 * 1024 * 1024 +_TextReadCache: OrderedDict[str, str] = OrderedDict() +_TextReadCacheBytes: int = 0 + + +def _ReadTextCached(path: str, newline: str | None = None) -> str: + """读文本文件(按路径缓存,零 stat);newline 非 None 时退化为直接读(不进缓存)。""" + global _TextReadCacheBytes + if newline is not None: + # 逐字节保真的读(newline='' 等)语义特殊:不进缓存,避免与默认换行翻译混用 + with open(path, 'r', encoding='utf-8', newline=newline) as _f: + return _f.read() + _hit: str | None = _TextReadCache.get(path) + if _hit is not None: + _TextReadCache.move_to_end(path) + return _hit + with open(path, 'r', encoding='utf-8') as _f: + _text: str = _f.read() + if len(_text) <= _TEXT_CACHE_FILE_MAX: + _TextReadCache[path] = _text + _TextReadCacheBytes += len(_text) + while _TextReadCacheBytes > _TEXT_CACHE_MAX_BYTES and len(_TextReadCache) > 1: + _, _old = _TextReadCache.popitem(last=False) + _TextReadCacheBytes -= len(_old) + return _text + + +def _InvalidateTextCache(path: str) -> None: + """丢弃 path 的文本读缓存条目:本进程**写出/改写/删除**该文件后必须调用。 + + 这是本缓存唯一的正确性保证(读侧不再 stat 判新旧):漏掉任一写点,后续读会 + 拿到该路径的旧文本。典型必失效场景:先读 → 同一 tick 内原地改写 → 再读, + 且改写前后字节数相同(实例:_compile_one 前的 type 定义重排,行集合不变仅次序变; + _recombine_ll 的旧 sha1 前缀替换,16 位 hex 等长替换)。 + """ + global _TextReadCacheBytes + _old = _TextReadCache.pop(path, None) + if _old is not None: + _TextReadCacheBytes -= len(_old) + + +_ASAN_DLL_A = 'libclang_rt.asan_dynamic-x86_64.dll.a' + + +def _FindAsanRuntime() -> tuple[str, str] | None: + """通过 PATH 定位 clang64 的 ASan 运行库(按 LLVM 大版本取最新的那个) + + mingw64 的 clang 只为 x86_64-w64-windows-gnu 目标提供 builtins / fuzzer / + profile / orc 运行库,**没有 ASan runtime**(/lib/ + x86_64-w64-windows-gnu/ 是空目录),所以不能靠 `-fsanitize=address` 让驱动 + 自动去找 —— 那只会得到 + "could not open .../libclang_rt.asan_dynamic.dll.a: No such file or directory"。 + clang64(MSVC ABI) 那边才有 libclang_rt.asan_dynamic-x86_64.dll.a 与 + libclang_rt.asan_dynamic_runtime_thunk-x86_64.a,只能借用这两份。 + + 路径**不再写死**:遍历 PATH 的每个目录 E,按 `E` 与 `dirname(E)` 两种可能取 base, + 拼出 `/lib/clang/<大版本>/lib/windows`,命中即为链接库目录;对应的运行期 + DLL 目录取 `/bin`。 + + Returns: + (链接库目录, 运行期 DLL 目录);都找不到返回 None + """ + hits: list[tuple[str, str]] = [] + seen: set[str] = set() + for entry in os.environ.get('PATH', '').split(os.pathsep): + if not entry: + continue + for base in (entry, os.path.dirname(entry)): + if not base: + continue + root = os.path.join(base, 'lib', 'clang') + if root in seen or not os.path.isdir(root): + continue + seen.add(root) + for name in os.listdir(root): + cand = os.path.join(root, name, 'lib', 'windows') + if os.path.isfile(os.path.join(cand, _ASAN_DLL_A)): + hits.append((cand, os.path.join(base, 'bin'))) + if not hits: + return None + + def _Ver(item: tuple[str, str]) -> int: + # 目录名是 LLVM 大版本号(如 "22")——按数值比,否则 "9" 会排在 "22" 之后 + v = item[0].split(os.sep)[-3] + return int(v) if v.isdigit() else 0 + + hits.sort(key=_Ver) + return hits[-1] + + +# ---------- --traceback:地址→源行 旁文件(.stackmap)---------- +# +# 开启 --traceback 时,链接成功后用 llvm-dwarfdump 解析最终 PE 的 +# .debug_line/.debug_info,把已有的"函数级/语句级 DWARF 行标记"导出成 +# .stackmap,供 exe 内运行时处理器在 Ctrl+C/Ctrl+Break 时把 CRT 到当前 +# 的返回地址逐帧翻译回 ".vp:行/函数"。以下函数只在开关开启时被调用;未开启时 +# 不引入任何新依赖、任何新行为。 + +_PE_MAGIC: bytes = b'PE\0\0' + + +def _ReadPEImageLayout(exe_path: str) -> tuple[int, int, dict[str, tuple[int, int]]] | None: + """读取 PE 的 (ImageBase, SizeOfImage, 节表) ;非 PE / 解析失败返回 None。 + + 节表为 {节名: (节 RVA, 节 VirtualSize)}。写端需要这三样才能把 DWARF 地址严格 + 归一到"运行期 RVA"(运行期 traceback.vp 用 `帧地址 − GetModuleHandleA(NULL)`): + * 合法行 ⇔ 地址落在映像 VA 区间 [ImageBase, ImageBase + SizeOfImage); + * "运行期可能出现的 RVA 区间"的粗判据 = [.text 节 RVA, SizeOfImage)。 + (原 _ReadPEImageBase 只返回 ImageBase,不足以做上述判定,故扩展为本函数。) + """ + import struct + try: + with open(exe_path, 'rb') as f: + head: bytes = f.read(0x1000) + except Exception: + return None + if len(head) < 0x40 or head[:2] != b'MZ': + return None + e_lfanew: int = struct.unpack_from(' len(head) or head[e_lfanew:e_lfanew + 4] != _PE_MAGIC: + return None + coff_off: int = e_lfanew + 4 + n_sections: int = struct.unpack_from(' len(head): + return None + magic: int = struct.unpack_from(' len(head): + break + name: str = head[base:base + 8].split(b'\x00', 1)[0].decode('ascii', 'replace') + sections[name] = (struct.unpack_from(' list[tuple[int, str, int]]: + """从 llvm-dwarfdump 输出解析出 [(地址, 文件绝对路径, 行号)]。 + + 逐个 CU(debug_line[0x..])重建目录/文件表;行记录过滤掉 line==0 的 + end_sequence 行。文件路径优先用文件表里的绝对 name;name 为相对时按 dir_index + 指向的目录拼接(DWARF v5 取 directory_names,v4 取 include_directories)。 + + ⚠ 地址口径:**原样返回 dwarfdump 打印的地址**(本例为含 ImageBase 的 VA + 0x14000xxxx,但被链接器按 COMDAT 去重丢弃的泛型副本其地址恰为 0)。 + 本函数**不**做"整表 VA/RVA"的判定——那正是旧实现失真的根因(全局最小值一旦为 + 0,整表都不减基址)。归一化(逐条 `地址 − ImageBase`,并丢弃不能归一的条目) + 由调用方 `_WriteStackMap` 依 PE.ImageBase/SizeOfImage 完成。 + """ + rows: list[tuple[int, str, int]] = [] + dirs: dict[int, str] = {} + files: dict[int, tuple[str, int]] = {} + pending_kind: str | None = None + pending_idx: int = -1 + pending_name: str | None = None + pending_dir_index: int = 0 + in_line_section: bool = False + + def _Unquote(text: str) -> str: + text = text.strip() + if len(text) >= 2 and text[0] == '"' and text[-1] == '"': + text = text[1:-1] + return text + + def _Commit() -> None: + nonlocal pending_kind, pending_name, pending_dir_index + if pending_kind == 'file' and pending_name is not None: + files[pending_idx] = (pending_name, pending_dir_index) + pending_kind = None + pending_name = None + pending_dir_index = 0 + + def _Resolve(idx: int) -> str: + entry = files.get(idx) + if entry is None: + return '??' + name, dir_index = entry + if os.path.isabs(name): + return name + base: str = dirs.get(dir_index, '') + if base and not os.path.isabs(base): + root: str = dirs.get(0, '') + if root and os.path.isabs(root): + base = os.path.join(root, base) + return os.path.normpath(os.path.join(base, name)) if base else name + + for line in dump_text.splitlines(): + stripped: str = line.strip() + if not stripped: + continue + if 'debug_line contents' in stripped: + in_line_section = True + continue + if 'contents:' in stripped and 'debug_' in stripped: + in_line_section = False + continue + if not in_line_section: + continue + if stripped.startswith('debug_line['): + _Commit() + dirs = {} + files = {} + continue + m = _DWARF_FILENAMES_RE.match(line) + if m: + _Commit() + pending_kind = 'file' + pending_idx = int(m.group(1)) + continue + m = _DWARF_DIRNAMES_RE.match(line) + if m: + _Commit() + value: str = _Unquote(m.group(2)) + if value: + dirs[int(m.group(1))] = value + else: + pending_kind = 'dir' + pending_idx = int(m.group(1)) + continue + m = _DWARF_NAME_RE.match(line) + if m and pending_kind is not None: + name: str = _Unquote(m.group(1)) + if pending_kind == 'file': + pending_name = name + else: + dirs[pending_idx] = name + pending_kind = None + continue + m = _DWARF_DIRINDEX_RE.match(line) + if m and pending_kind == 'file': + pending_dir_index = int(m.group(1)) + continue + if _DWARF_LINE_HEADER_RE.match(line): + _Commit() + continue + m = _DWARF_LINE_ROW_RE.match(line) + if m: + _Commit() + row_line: int = int(m.group(2)) + if row_line > 0: + rows.append((int(m.group(1), 16), _Resolve(int(m.group(4))), row_line)) + return rows + + +def _ParseDwarfSubprograms(dump_text: str) -> list[tuple[int, int, str]]: + """从 llvm-dwarfdump --debug-info 输出解析 [(low_pc, high_pc, 函数名)]。 + + DW_AT_high_pc 有两种形态且 dwarfdump 都打成 `(0x…)`:`DW_FORM_addr`(绝对 + 结束地址,值 > low_pc)与常量形式(相对 low_pc 的**长度**,值通常 < low_pc)。 + 本函数以 `high_pc <= low_pc` 判定后者并按 `low_pc + 长度` 还原;缺 high_pc 时 + 退化为 [low_pc, low_pc + 1)。 + + ⚠ 地址口径同 `_ParseDwarfLineRows`:**原样返回**,不做整表 VA/RVA 判定;被 + 链接器 COMDAT 去重丢弃的泛型副本其 low_pc 恰为 0(实测 viperc.exe 有 756 个), + 由调用方 `_WriteStackMap` 逐条归一化并丢弃。 + """ + result: list[tuple[int, int, str]] = [] + cur_low: int | None = None + cur_high: int | None = None + cur_name: str | None = None + in_prog: bool = False + + def _Flush() -> None: + nonlocal cur_low, cur_high, cur_name, in_prog + if in_prog and cur_low is not None and cur_name: + hi: int = cur_high if cur_high is not None else cur_low + 1 + if hi <= cur_low: + hi = cur_low + cur_high # high_pc 为 offset 形式 + if hi > cur_low: + result.append((cur_low, hi, cur_name)) + cur_low = None + cur_high = None + cur_name = None + in_prog = False + + for line in dump_text.splitlines(): + if _DWARF_TAG_ANY_RE.search(line): + if _DWARF_TAG_SUBPROGRAM_RE.search(line): + _Flush() + in_prog = True + else: + _Flush() + continue + if not in_prog: + continue + m = _DWARF_LOWPC_RE.search(line) + if m: + cur_low = int(m.group(1), 16) + continue + m = _DWARF_HIGHPC_RE.search(line) + if m: + cur_high = int(m.group(1), 16) + continue + m = _DWARF_ATNAME_RE.search(line) + if m and cur_name is None: + cur_name = m.group(1) + _Flush() + return result + + +# 显式并行度覆盖:TPC_JOBS=N(N>=1)——使用者明确指定 worker 数并**自行承担** +# 内存风险(含 N=1 即显式接受串行)。设了它就不再走内存校验/预分配。 +_JOBS_ENV: str = 'TPC_JOBS' +# 已通过"预分配校验"的预算(MB):同一预算不重复校验(翻译/编译两阶段各查一次) +_MemProbeDone: set[int] = set() +# 预分配校验的分块大小(MB):分块提交并**全部持有**到校验结束(峰值 = 预算), +# 结束后立即整体释放 —— 紧接其后就创建 worker 进程,OS 直接复用这批刚释放的页。 +_MEM_PROBE_CHUNK_MB: int = 256 +# 自动并行度上限(用户 2026-10-01 定:16 worker 太多)。只约束**自动**取值, +# 显式 TPC_JOBS=N 仍按硬件/文件数上限生效。 +_MAX_WORKERS: int = 8 + + +def _MemStatus() -> tuple[int, int]: + """(可用物理内存 MB, 物理内存总量 MB);读取失败返回 (-1, -1),调用方按"未知"处理""" + try: + import ctypes + + class _MemStat(ctypes.Structure): + _fields_ = [ + ("dwLength", ctypes.c_ulong), + ("dwMemoryLoad", ctypes.c_ulong), + ("ullTotalPhys", ctypes.c_ulonglong), + ("ullAvailPhys", ctypes.c_ulonglong), + ("ullTotalPageFile", ctypes.c_ulonglong), + ("ullAvailPageFile", ctypes.c_ulonglong), + ("ullTotalVirtual", ctypes.c_ulonglong), + ("ullAvailVirtual", ctypes.c_ulonglong), + ("ullAvailExtendedVirtual", ctypes.c_ulonglong), + ] + + stat = _MemStat() + stat.dwLength = ctypes.sizeof(_MemStat) + if ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat)): + return (stat.ullAvailPhys // (1024 * 1024), + stat.ullTotalPhys // (1024 * 1024)) + except Exception: + pass + return (-1, -1) + + +def _ProbeReserveMb(mb: int) -> bool: + """预分配校验:一次性提交 mb MB(分块持有后整体释放),成功返回 True。 + + 为什么要"预分配"而不是只读 ullAvailPhys:可用物理内存含大量**可回收**的 + 缓存页/其他进程随时会吃掉的余量,只按它决策会出现"这一秒够、真跑起来不够" + 的波动(编译时间在 400s / 1180s 两档之间随机跳的直接来源)。先真正把预算 + 拿到手(页级提交)再放掉,等于向 OS 确认过这笔预算确实可分配。 + """ + if mb <= 0: + return True + Bufs = [] + Left = mb + try: + while Left > 0: + Cur = min(_MEM_PROBE_CHUNK_MB, Left) + Bufs.append(bytearray(Cur * 1024 * 1024)) + Left -= Cur + return True + except MemoryError: + return False + finally: + del Bufs + + +def _PickWorkerCount(n_files: int, per_worker_mb: int = 350) -> int: + """并行 worker 数的**唯一**决策入口(CPU 上限 + 内存预分配双约束)。 + + 口径(用户 2026-10-01 定,取代旧"内存紧就降级串行"): + · **自动上限 `_MAX_WORKERS`=8**:16 worker 收益不足、内存压力翻倍(用户 2026-10-01 定); + 显式 TPC_JOBS 不受此限(只受硬件/文件数约束)。 + 1. **不降级**:内存不足**绝不**静默退回单 worker 串行 —— 旧实现 `by_mem = + max(1, avail_mb // per_worker_mb)` 在可用内存稍紧时就整体降为串行, + 同一条命令 76 文件逐个翻译 1180s、并行 ~400s,时间随机跳 2-3 倍 + (profile 实证:224857 那次主进程 0 次 _translate_file=并行,438s; + 231916/235354 两次主进程 75 次 _translate_file=串行,1048s/1168s)。 + 2. **预分配 + 强制预留**:先按 base×per_worker_mb 做一次性提交校验 + (_ProbeReserveMb),拿到就并行、拿不到就 fail-fast 报出"可用/需要" + 数字,由使用者释放内存或**显式** TPC_JOBS=N 接受其它并行度。 + 3. 显式 TPC_JOBS=N 优先且跳过校验(使用者自行承担内存风险,N=1 即显式串行)。 + + 单 worker 预估 per_worker_mb(Python 解释器 + llvmlite module + 共享符号表 + 快照);实测翻译阶段 worker 稳态 ~110MB、峰值 ~300MB,故取 350(编译阶段 + 调用方传 400)。Windows 用 GlobalMemoryStatusEx 读物理内存。 + """ + hw_max = min(os.cpu_count() or 4, n_files) + base = min(hw_max, _MAX_WORKERS) + if base <= 1: + return base + env_jobs: str | None = os.environ.get(_JOBS_ENV) + if env_jobs: + try: + want = int(env_jobs) + except ValueError: + want = base + # 显式指定只受硬件/文件数约束(不受 _MAX_WORKERS 限制),并跳过内存校验 + want = max(1, min(want, hw_max)) + _vlog().info(f" [并行] {_JOBS_ENV}={env_jobs} → workers={want}(显式指定,跳过校验)") + return want + avail_mb, total_mb = _MemStatus() + need_mb = base * per_worker_mb + if avail_mb < 0: + # 读不到内存信息:不阻塞编译(按上限并行),但必须显式告知 + _vlog().warning(f" [并行] 读不到物理内存,取 workers={base}(预算 {need_mb}MB)") + return base + if avail_mb < need_mb: + raise RuntimeError( + f"[并行翻译] 内存不足:需 {need_mb}MB({base}×{per_worker_mb}MB)," + f"可用 {avail_mb}MB/{total_mb}MB;释放内存或设 {_JOBS_ENV}=N 重跑") + if need_mb not in _MemProbeDone: + _t0: float = _time.time() + if not _ProbeReserveMb(need_mb): + raise RuntimeError( + f"[并行翻译] 预分配 {need_mb}MB 失败({base}×{per_worker_mb}MB," + f"可用 {avail_mb}MB/{total_mb}MB);释放内存或设 {_JOBS_ENV}=N 重跑") + _MemProbeDone.add(need_mb) + _vlog().info(f" [并行] 预分配 {need_mb}MB 通过({_time.time() - _t0:.2f}s)") + _vlog().info(f" [并行] workers={base}(预算 {need_mb}MB / 可用 {avail_mb}MB)") + return base + + +def _ExtractCDefineConstantsFromPyi(pyi_path: str, all_dc: dict[str, object], + symtab: object | None = None, + all_dt: dict[str, tuple[int, bool]] | None = None) -> None: + """从 .pyi 文件中提取 CDefine 常量到 all_dc。文件不存在时静默跳过。 + + 治本修复:原实现只支持 ast.Constant 和 float() 调用, + 无法处理 t.CUnsignedLong(-11)(ast.Call)和 + FOREGROUND_GREEN | FOREGROUND_INTENSITY(ast.BinOp)等复杂表达式。 + 现使用 ConstEvaluator.eval_full 统一求值,支持: + - ast.Constant: 简单常量 (0x0002) + - ast.Call: 类型构造 (t.CUnsignedLong(-11)) + - ast.BinOp: 位运算 (FOREGROUND_RED | FOREGROUND_GREEN) + - ast.UnaryOp: 一元运算 (-11) + - ast.Name: 引用其他 CDefine 常量 + """ + if not os.path.exists(pyi_path): + return + try: + # 复用 parse_python_file 的 AST 缓存(.pyi 文件被多处解析) + _, tree = parse_python_file(pyi_path) + if tree is None: + return + # 治本修复:使用 ConstEvaluator 处理复杂表达式 + # 创建 mock Gen 对象,其 _define_constants 指向 all_dc, + # 使 ConstEvaluator 能查找同文件中已提取的常量(如 FOREGROUND_RED 引用 FOREGROUND_BLUE) + from lib.core.ConstEvaluator import ConstEvaluator, EvalContext + + class _MockGen: + """轻量 mock,仅提供 _define_constants 供 ConstEvaluator 查找""" + def __init__(self) -> None: + self._define_constants: dict[str, object] = all_dc + + mock_gen: _MockGen = _MockGen() + ctx: EvalContext = EvalContext(Gen=mock_gen) + + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + if AnnotationContainsName(node.annotation, 'CDefine') and node.value: + val: object = ConstEvaluator.eval_full(node.value, ctx) + if val is None and isinstance(node.value, ast.Call) and len(node.value.args) == 1: + # 类型构造的**裸别名**形态(如 `DWORD(-11)`,非 `t.CUnsignedLong(-11)`): + # eval_full 只认 `t.X(...)` / `from t import X`(见 ConstEvaluator + # _eval_compile_time_call),裸 typedef 别名既不在 _t_module 也不在 + # mock ctx 的 _t_type_symbols 里 → 返回 None → 该常量被整条丢弃。 + # 现场:win32file.vp 的 STD_INPUT/OUTPUT/ERROR_HANDLE 是全仓**唯一**用 + # 别名构造写的 define → 唯独这三个进不了 _all_define_constants,导致 + # 模块属性访问 `wf.STD_ERROR_HANDLE` 解析落空、退化成未定义的全局符号 + # (链接失败)。CDefine 关心的是常量值,故回退求值其唯一实参(位型一致)。 + val = ConstEvaluator.eval_full(node.value.args[0], ctx) + if val is not None and isinstance(val, (int, float, str)): + all_dc[node.target.id] = val + # 带类型注册:记录该 define 的**声明整数类型**(如 `DWORD` → (32, False)), + # 供使用点按声明类型物化(u32 用 i32 + 位型,而不是按值大小猜 i32/i64)。 + if all_dt is not None and isinstance(val, int) and not isinstance(val, bool): + decl_type: tuple[int, bool] | None = DefineDeclIntTypeFromAnnotation(symtab, node.annotation) + if decl_type is not None: + all_dt[node.target.id] = decl_type + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + + +def _CountUnresolvedTypedefs(module_symbols: object) -> int: + """该模块"有值注解却解析不出类型"的 typedef 个数(供声明阶段判断是否要下一轮)。 + + typedef 右侧可以引用**其它模块**的 typedef:例 win32base 的 + `HANDLE: t.CTypedef = VOIDPTR`(VOIDPTR 在 stdint)——本模块先 stage 时右侧 + 解析为空(original_type_kind/original_class 均为 None),要等依赖模块入表后 + 重 stage 才能补全。返回 0 表示本模块声明已完整(不再需要重跑)。 + """ + miss: int = 0 + for td in getattr(module_symbols, 'typedefs', []): + if getattr(td, 'value', None) is None and getattr(td, 'annotation', None) is None: + continue + if getattr(td, 'original_type_kind', None) is None and getattr(td, 'original_class', None) is None: + miss += 1 + return miss + + +def _parallel_translate_worker(src_path: str, out_path: str, src_root: str, temp_dir: str, output_dir: str, + include_dirs: list[str], triple: str | None, datalayout: str | None, + sha1_map: dict[str, str], sig_files: dict[str, str], + stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int, + shared_sym_pickle_path: str, struct_sha1_map: dict[str, str], + prebuilt_ModuleSha1Map: dict[str, str] | None = None, + hotspot_sites_file: str | None = None, + hotspot_sites_all: bool = False, + hotspot_callees: list[str] | None = None, + traceback_enabled: bool = False, + dwarf_enabled: bool = False) -> tuple[str, str, str]: + # 注意:不可跨任务复用 Phase2Translator/共享符号表!_translate_file 编译过程中 + # 会向共享 SymbolTable 写入符号(import insert/类注册等),复用导致前文件的 + # 符号污染后文件 → 跨模块 struct 类型前缀错位(如 7cadfe07e983fd90.CompilerFactory + # 混入 _list 前缀,Type of #1 arg mismatch)。每任务必须从 pickle 反序列化出 + # 独立副本(原实现;worker 复用省掉的 pickle.load 时间仅 ~0.5s/次,远小于 + # worker 内翻译计算,不值得以正确性为代价)。 + try: + trans = Phase2Translator(src_root, temp_dir, output_dir, + compile_cmd='llc', include_dirs=include_dirs, + target_triple=triple, target_datalayout=datalayout, + build_pyi_cache=False) + trans.sha1_map = sha1_map + trans.sig_files = sig_files + trans.stub_files = stub_files + trans.include_py_map = include_py_map + trans.slice_level = slice_level + trans.struct_sha1_map = struct_sha1_map + # 调用点热点统计与每函数计时(--hotspot-sites):worker 子进程无父进程属性, + # 须显式透传(None=未启用,零影响);生成 IR 时各模块处理器统一追加写最终 + trans.hotspot_sites_file = hotspot_sites_file + trans.hotspot_sites_all = hotspot_sites_all + trans.hotspot_callees = hotspot_callees + # Ctrl+C 调用栈(--traceback):worker 子进程无父进程属性,须显式透传 + # (False=未启用,零影响);traceback 模块据此注入 Install() 启动期构造器 + trans.traceback_enabled = traceback_enabled + # 函数级 DWARF(--traceback 时 include 模块也必须带行表,否则栈里落到 + # includes/* 的帧取不到源行):worker 子进程无父进程属性,须显式透传 + # (False=未启用,零影响),仅用于 _translate_file 的 dwarf_enabled 形参 + trans.dwarf_enabled = dwarf_enabled + + if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path): + # 使用 pickle.load(f) 流式加载,避免 pickle.loads(f.read()) 一次性 + # 将整个 pickle 文件读入内存(磁盘副本 + 反序列化对象同时占用内存)导致 MemoryError。 + # 同时提高递归限制,防止深层嵌套对象反序列化失败。 + sys.setrecursionlimit(max(sys.getrecursionlimit(), 100000)) + with open(shared_sym_pickle_path, 'rb') as f: + shared_data = pickle.load(f) + _sym_bytes: bytes | None = shared_data.get('symbol_table_bytes') + if _sym_bytes is not None: + trans._shared_symbol_table = pickle.loads(_sym_bytes) + trans._shared_source_module_sig_files = shared_data['source_module_sig_files'] + trans._shared_all_dc = shared_data['all_dc'] + trans._shared_all_decl_types = shared_data.get('all_decl_types', {}) + trans._shared_export_extern_funcs = shared_data['export_extern_funcs'] + trans.inline_func_symbols = shared_data['inline_func_symbols'] + trans.function_default_args = shared_data['function_default_args'] + trans.function_param_names = shared_data.get('function_param_names', {}) + trans._shared_generic_class_templates = shared_data.get('generic_class_templates', {}) + trans._shared_doc_meta = shared_data.get('shared_doc_meta', {}) + trans._shared_class_vtable = shared_data.get('class_vtable', set()) + trans._shared_cross_module_vtable = shared_data.get('cross_module_vtable', set()) + trans._shared_class_methods = shared_data.get('class_methods', {}) + trans._shared_cross_module_novtable = shared_data.get('cross_module_novtable', set()) + else: + trans._precollect_inline_symbols() + trans._build_shared_symbol_data() + + trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=prebuilt_ModuleSha1Map, dwarf_enabled=dwarf_enabled) + return ('ok', src_path, '') + except Exception as e: + # fail-fast(2026-08-18):include 文件编译出错立即终止整个编译进程 + # (主进程收到 BrokenProcessPool 后取消剩余任务退出)。 + sys.stderr.write( + f"[FATAL] include 翻译失败,立即终止编译: {src_path}\n" + f"{traceback.format_exc()}\n") + sys.stderr.flush() + os._exit(88) + + +def _parallel_translate_batch(batch: list, src_root: str, temp_dir: str, output_dir: str, + include_dirs: list[str], triple: str | None, datalayout: str | None, + sha1_map: dict[str, str], sig_files: dict[str, str], + stub_files: dict[str, str], include_py_map: dict[str, str], slice_level: int, + shared_sym_pickle_path: str, struct_sha1_map: dict[str, str], + hotspot_sites_file: str | None = None, + hotspot_sites_all: bool = False, + hotspot_callees: list[str] | None = None, + dwarf_enabled: bool = False) -> tuple[list, list]: + """批次子任务:worker 内创建一个 Phase2Translator + pickle.load 一次,顺序翻译批内全部文件。 + + batch 每项: (i, src_path, out_path, sha1, prebuilt_ModuleSha1Map) + + 防污染:SymbolTable 是编译期唯一被写的共享对象(import insert/类注册), + 批内每个文件翻译前从 symbol_table_bytes 反序列化出干净副本,前文件的 + 符号写入不会残留到后文件(否则跨模块 struct 类型前缀错位)。 + 返回 (错误列表[(src_path, err)], batch, per_file_times[(src_path, elapsed, rel)])。 + """ + try: + trans = Phase2Translator(src_root, temp_dir, output_dir, + compile_cmd='llc', include_dirs=include_dirs, + target_triple=triple, target_datalayout=datalayout, + build_pyi_cache=False) + trans.sha1_map = sha1_map + trans.sig_files = sig_files + trans.stub_files = stub_files + trans.include_py_map = include_py_map + trans.slice_level = slice_level + trans.struct_sha1_map = struct_sha1_map + # 调用点热点统计与每函数计时(--hotspot-sites):批内每文件同样透传 + trans.hotspot_sites_file = hotspot_sites_file + trans.hotspot_sites_all = hotspot_sites_all + trans.hotspot_callees = hotspot_callees + + symbol_table_bytes: bytes | None = None + if shared_sym_pickle_path and os.path.exists(shared_sym_pickle_path): + sys.setrecursionlimit(max(sys.getrecursionlimit(), 100000)) + with open(shared_sym_pickle_path, 'rb') as f: + shared_data = pickle.load(f) + symbol_table_bytes = shared_data.get('symbol_table_bytes') + if symbol_table_bytes is not None: + trans._shared_symbol_table = pickle.loads(symbol_table_bytes) + trans._shared_source_module_sig_files = shared_data['source_module_sig_files'] + trans._shared_all_dc = shared_data['all_dc'] + trans._shared_all_decl_types = shared_data.get('all_decl_types', {}) + trans._shared_export_extern_funcs = shared_data['export_extern_funcs'] + trans.inline_func_symbols = shared_data['inline_func_symbols'] + trans.function_default_args = shared_data['function_default_args'] + trans.function_param_names = shared_data.get('function_param_names', {}) + trans._shared_generic_class_templates = shared_data.get('generic_class_templates', {}) + trans._shared_doc_meta = shared_data.get('shared_doc_meta', {}) + trans._shared_class_vtable = shared_data.get('class_vtable', set()) + trans._shared_cross_module_vtable = shared_data.get('cross_module_vtable', set()) + trans._shared_class_methods = shared_data.get('class_methods', {}) + trans._shared_cross_module_novtable = shared_data.get('cross_module_novtable', set()) + else: + trans._precollect_inline_symbols() + trans._build_shared_symbol_data() + + batch_errors: list = [] + per_file_times: list[tuple[str, float, str]] = [] + for _i, src_path, out_path, _sha1, msm in batch: + _f0: float = time.time() + try: + # 每文件前重置 SymbolTable 为干净副本(防跨文件污染) + if symbol_table_bytes is not None: + trans._shared_symbol_table = pickle.loads(symbol_table_bytes) + trans._shared_symbol_table.translator = None + trans._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm, dwarf_enabled=dwarf_enabled) + per_file_times.append((src_path, time.time() - _f0, os.path.relpath(src_path, src_root))) + except Exception as e: + # fail-fast(2026-08-18):任意文件编译出错立即终止整个编译进程, + # 不再收集错误继续翻译批内剩余文件(避免浪费大量时间)。主进程 + # 收到 BrokenProcessPool/future 异常后取消全部剩余批次退出。 + sys.stderr.write( + f"[FATAL] 文件翻译失败,立即终止编译: {src_path}\n" + f"{traceback.format_exc()}\n") + sys.stderr.flush() + os._exit(88) + return (batch_errors, batch, per_file_times) + except Exception as e: + return ([(item[1], f'{e}\n{traceback.format_exc()}') for item in batch], batch, []) + + +# ================================================================== +# 子进程性能剖析:主进程 cProfile 只能看到进程池等待(WaitForMultipleObjects/ +# as_completed),看不到 worker 进程内部 CPU 分布(vast parse 等热点)。 +# 固定启用(不设环境变量开关),worker 每个任务导出 cProfile dump +# (worker_prof_{pid}_{seq}.prof),主进程导出 main_prof_{pid}.prof + main_time.txt。 +# 输出落在 PROF_DIR/<启动时间>/ 下的**本次编译专属目录**(2026-09-29 起)。 +# ================================================================== +import functools as _functools +import time as _time + +# 硬编码 PROF 根目录(2026-08-18 起不再用 TPC_WORKER_PROF 环境变量控制) +PROF_DIR: str = r'D:\Users\TermiNexus\Desktop\TransPyC\PROF' + +# 本次编译的 run 目录:按启动时间在 PROF_DIR 下新建子目录,**不清理旧目录** +# (2026-09-29 用户要求保留历次 profile 以便跨次对比,旧行为是每次编译删掉 +# PROF 根目录下全部 *.prof ⇒ 只能看最近一次)。 +# 子目录名经环境变量传给 worker:ProcessPoolExecutor 用 spawn 起新解释器, +# 进程内模块态不共享;环境变量随子进程继承 ⇒ 同一次编译的所有 worker 落在 +# 同一个 run 目录,不会各建各的目录。 +_PROF_RUN_ENV: str = 'TPC_PROF_RUN_DIR' +_prof_run_dir: str | None = None + + +def StartProfRun() -> str: + """每次编译开始时调用一次:创建本次编译专属的 PROF 子目录,返回其路径。 + + 目录名 = PROF 根目录下 `YYYYmmdd_HHMMSS`(编译启动时刻);同名(同一秒内 + 二次启动)补 `_N` 序号。创建失败时回落到 PROF 根目录,保证不因 profiling + 影响编译本身。成功后把目录写进环境变量,供 worker 子进程复用。 + """ + global _prof_run_dir + if _prof_run_dir: + return _prof_run_dir + _name: str = _time.strftime('%Y%m%d_%H%M%S') + _dir: str = os.path.join(PROF_DIR, _name) + _seq: int = 1 + while os.path.isdir(_dir): + _seq += 1 + _dir = os.path.join(PROF_DIR, f'{_name}_{_seq}') + try: + os.makedirs(_dir, exist_ok=True) + except OSError: + return PROF_DIR + _prof_run_dir = _dir + os.environ[_PROF_RUN_ENV] = _dir + return _dir + + +def GetProfRunDir() -> str: + """当前进程写 profile 应落的目录。 + + 主进程 = StartProfRun 选定的 run 目录;worker = 继承来的环境变量指向的目录。 + 两者都取不到(未先调 StartProfRun,如 worker 被单独拉起)时回落到 PROF 根目录, + 宁可文件落在根目录也不丢 dump。 + """ + global _prof_run_dir + if _prof_run_dir: + return _prof_run_dir + _env_dir: str = os.environ.get(_PROF_RUN_ENV, '') + if _env_dir: + _prof_run_dir = _env_dir + return _env_dir + return PROF_DIR + + +def _ProfileWorker(_fn): + @_functools.wraps(_fn) + def _wrapper(*args, **kwargs): + _prof = None + try: + # cProfile 默认不启用(环境变量 TPC_WORKER_PROF=1 开启): + # Python 只允许一个 profiling tool 同时活跃,TPC 的 cProfile 与 + # viztracer --log_multiprocess 冲突(cProfile.enable() 检测到已激活 + # profiler 抛 "Another profiling tool is already active" → worker 崩溃 + # → batch 异常 → 编译终止)。外部 profiler 活跃(sys.getprofile() + # 非 None)时即使开启也跳过让位;enable() 异常兜底吞掉保编译。 + if os.environ.get('TPC_WORKER_PROF', '0') == '1' and sys.getprofile() is None: + _prof = cProfile.Profile() + try: + _prof.enable() + except RuntimeError: + _prof = None + return _fn(*args, **kwargs) + finally: + if _prof is not None: + _prof.disable() + _seq: int = getattr(_wrapper, '_seq', 0) + 1 + _wrapper._seq = _seq + try: + _out_dir: str = GetProfRunDir() + if not os.path.isdir(_out_dir): + os.makedirs(_out_dir, exist_ok=True) + _out: str = os.path.join(_out_dir, f'worker_prof_{os.getpid()}_{_seq}.prof') + _prof.dump_stats(_out) + except Exception: + pass + + return _wrapper + + +_parallel_translate_worker = _ProfileWorker(_parallel_translate_worker) +_parallel_translate_batch = _ProfileWorker(_parallel_translate_batch) + + +# ================================================================== +# worker pyi 缓存预热:ProcessPoolExecutor(spawn) 下每个 worker 重新 +# import 模块后,HandlesImports.ImportHandle 的类级 _pyi_cache 全空, +# 每进程首次任务重新 vast.parse 全部依赖 .vpi/.pyi +# (cProfile: 4479 次 parse / 245s)。 +# 方案:主进程把 temp 下全部 .vpi 的 vast AST 树 pickle 落盘 +# (temp/_pyi_cache_v1.pkl,任务 B),worker initializer 启动时 +# pickle.load 一次填充 _pyi_cache(任务 A);load 失败回退逐文件 +# vast.parse。文件系统共享即为"共用缓存",不做跨进程共享内存(任务 C)。 +# 环境变量 TPC_WORKER_WARMUP=0 可关闭(回归对比基线)。 +# ================================================================== +_PYI_CACHE_PKL_NAME: str = '_pyi_cache_v1.pkl' +_PYI_CACHE_SIG_NAME: str = '_pyi_cache_v1.sig.json' +_PYI_CACHE_VERSION: int = 1 + + +def _ScanPyiFiles(temp_dir: str) -> list[str]: + """扫描 temp 目录下全部 .vpi/.pyi 文件,返回绝对路径列表(与 + _TryLoadClassMembersFromPyi 的 _pyi_cache 键格式一致)。""" + try: + names: list[str] = os.listdir(temp_dir) + except OSError: + return [] + paths: list[str] = [] + for fname in sorted(names): + if fname.startswith('_'): + continue + if fname.endswith('.vpi') or fname.endswith('.pyi'): + paths.append(os.path.join(temp_dir, fname)) + return paths + + +def _PyiCacheSignature(paths: list[str]) -> list[tuple[str, int, float]]: + """vpi 清单签名:(basename, size, mtime) 元组列表,用于缓存失效判断。""" + sig: list[tuple[str, int, float]] = [] + for p in paths: + try: + st = os.stat(p) + sig.append((os.path.basename(p), st.st_size, st.st_mtime)) + except OSError: + sig.append((os.path.basename(p), -1, -1.0)) + return sig + + +def _LoadPyiCachePkl(temp_dir: str) -> dict | None: + """pickle.load temp/_pyi_cache_v1.pkl,返回 {abs_path: vast树|None}。 + 文件不存在/损坏/版本不符时返回 None(调用方回退逐文件解析)。""" + pkl_path: str = os.path.join(temp_dir, _PYI_CACHE_PKL_NAME) + try: + with open(pkl_path, 'rb') as f: + data: dict = pickle.load(f) + if not isinstance(data, dict) or data.get('version') != _PYI_CACHE_VERSION: + return None + cache = data.get('cache') + if not isinstance(cache, dict): + return None + return cache + except Exception: + return None + + +def _BuildPyiCacheFile(temp_dir: str) -> str | None: + """构建/刷新 temp/_pyi_cache_v1.pkl:遍历全部 .vpi 做 vast.parse 后 + pickle.dump(含版本号与签名)。签名(basename,size,mtime)未变时跳过。 + 返回 pkl 路径;失败(无 vpi / pickle 不兼容等)返回 None。 + 注意:vast AST 可 pickle 性由 Nodes.py 侧保证;若实际 pickle.dump + 失败,此处静默回退,worker 的 initializer 将改为逐文件 vast.parse。""" + paths: list[str] = _ScanPyiFiles(temp_dir) + if not paths: + return None + sig: list[tuple[str, int, float]] = _PyiCacheSignature(paths) + pkl_path: str = os.path.join(temp_dir, _PYI_CACHE_PKL_NAME) + sig_path: str = os.path.join(temp_dir, _PYI_CACHE_SIG_NAME) + try: + if _CachedIsFile(pkl_path) and _CachedIsFile(sig_path): + stored = json.loads(_ReadTextCached(sig_path)) + # json 往返后 tuple→list,需规范化再比较 + if stored == [[n, s, m] for n, s, m in sig]: + return pkl_path + except Exception: + pass + cache: dict[str, object] = {} + # 增量复用:Phase2 过程会新增 include .vpi(签名每次变 → 全量重建 12.2s)。 + # 加载旧 pkl,按 (basename, size, mtime) 匹配未变化的文件复用 vast 树, + # 只对新增/变化的文件重新 parse。 + old_by_name: dict[str, tuple[int, float, object | None]] = {} + try: + old = _LoadPyiCachePkl(temp_dir) + if isinstance(old, dict): + old_sig = old.get('signature') or [] + old_trees = list((old.get('cache') or {}).values()) + for _i, (_name, _size, _mtime) in enumerate(old_sig): + _tree = old_trees[_i] if _i < len(old_trees) else None + old_by_name[_name] = (_size, _mtime, _tree) + except Exception: + pass + for p, (name, size, mtime) in zip(paths, sig): + old_entry = old_by_name.get(name) + if (old_entry is not None and old_entry[0] == size + and old_entry[1] == mtime and old_entry[2] is not None): + cache[p] = old_entry[2] + continue + try: + # 走 LRU 文本缓存:同一 .vpi 在增量复用/多 worker 预热路径会被反复读 + cache[p] = ast.parse(_ReadTextCached(p), p) + except Exception: + cache[p] = None + try: + with open(pkl_path, 'wb') as f: + pickle.dump({'version': _PYI_CACHE_VERSION, 'signature': sig, 'cache': cache}, f) + with open(sig_path, 'w', encoding='utf-8') as f: + json.dump(sig, f) + return pkl_path + except Exception as e: + _vlog().warning(f"Phase2Translator: pyi 缓存落盘失败({e}),worker 将回退逐文件解析") + return None + + +def _PyiCacheWorkerInitializer(temp_dir: str) -> None: + """worker 进程启动时预热 ImportHandle._pyi_cache(类级缓存)。 + + spawn 下每 worker 重新 import 模块后 _pyi_cache 为空,首次任务会 + 重新 vast.parse 全部依赖 .vpi(4479 次 parse / 245s)。此处启动时 + 一次性填充:优先 pickle.load 主进程落盘的 _pyi_cache_v1.pkl; + 失败(文件不存在/损坏/版本不符/pickle 不兼容)回退逐文件 vast.parse。 + 预热只解析 .vpi(pyi 树小);stub 索引保留 _build_stub_index 懒加载。 + TPC_WORKER_WARMUP=0 时关闭(便于回归对比)。 + """ + if os.environ.get('TPC_WORKER_WARMUP', '1') != '1': + return + if not os.path.isdir(temp_dir): + return + from lib.core.Handles.HandlesImports import ImportHandle + cache: dict | None = _LoadPyiCachePkl(temp_dir) + if cache is not None: + ImportHandle._pyi_cache = cache + ImportHandle._pyi_cache_dir = os.path.abspath(temp_dir) + return + # 回退:逐文件 vast.parse 预热(worker 单线程,无竞争) + try: + new_cache: dict[str, object] = {} + for p in _ScanPyiFiles(temp_dir): + try: + new_cache[p] = ast.parse(_ReadTextCached(p), p) + except Exception: + new_cache[p] = None + if new_cache: + ImportHandle._pyi_cache = new_cache + ImportHandle._pyi_cache_dir = os.path.abspath(temp_dir) + except Exception: + pass + + +def _count_braces_outside_strings(text: str) -> int: + """统计花括号差值,跳过 c"..." 字符串字面量内部的括号 + + 快路径(prof 基线:本函数 102,397 次调用 / self 2.0s,占 _split_ll cumtime + 3.35s 的 83%,是全流程 str.strip 之后第二大的"纯 Python 逐字符"热点): + 行内不含 `c"` 时,"跳过字符串内部括号"这一语义退化为"整行括号计数", + 直接用 C 级 str.count 一次算完。含 `c"` 的行(主要见于 c"..." 全局串常量) + 仍走下面的逐字符扫描,语义逐字不变。 + """ + if 'c"' not in text: + return text.count('{') - text.count('}') + count = 0 + i = 0 + while i < len(text): + if text[i:i+2] == 'c"' and (i == 0 or text[i-1] != '\\'): + # 跳过 c"..." 字面量 + i += 2 + while i < len(text): + if text[i] == '"' and (i == 0 or text[i-1] != '\\'): + i += 1 + break + i += 1 + continue + if text[i] == '{': + count += 1 + elif text[i] == '}': + count -= 1 + i += 1 + return count + + +# LLVM IR "无引号标识符"的精确规则(LangRef "Identifiers";实现即 +# LLLexer::ReadVarName):首字符 ∈ [-a-zA-Z$._],后续字符 ∈ [-a-zA-Z$._0-9]。 +# 首字符**不允许是数字**:`$884f3d94ea4d0123.md5_F` 会让 LLLexer::LexDollar +# 走到 ReadVarName() 返回 false ⇒ 产出 error token ⇒ 顶层解析报 +# "expected top-level entity"。故 comdat 名必须以同一规则判定是否加引号 +# (与 LLVM 自身 AsmWriter 的输出形态一致:`$"884f3d94ea4d0123.md5_F"`)。 +# 另:引号名 @"..." 不支持转义,名字含 `"`/`\` 时两种形态都非法 ⇒ 见 +# _AddWeakComdat 内的 fail-fast(不静默替换、不跳过)。 +_LLVM_UNQUOTED_NAME_RE: re.Pattern[str] = re.compile( + r'[-a-zA-Z$._][-a-zA-Z$._0-9]*\Z') + +# comdat 声明行:`$name = comdat any` / `$"name" = comdat any`(允许行首缩进) +_RE_COMDAT_DECL: re.Pattern[str] = re.compile(r'^\s*\$("?)([^"=\s]+)\1\s*=\s*comdat\b') + + +def _DedupComdatDecls(ll_content: str) -> str: + """删除重复的 `$name = comdat any` 声明(保留首次出现)。 + + 落盘的 `_dso.ll` 是「本模块 + 各依赖 .stub.ll/.text.ll 片段」拼接而成, + 每个片段各自带一份同名 comdat 声明(片段生成时也会各自补 comdat)。同名 + 声明出现两次即 llc 报 `redefinition of comdat '$…'`(现场:泛型特化 + `list[.AST*].__before_init__` 在同一文件里出现 4 次)。comdat 声明 + 在顶层可位于任意位置,保留首次出现即可,语义不变。 + """ + if '= comdat' not in ll_content: + return ll_content + seen: set[str] = set() + kept: list[str] = [] + for line in ll_content.splitlines(True): + m = _RE_COMDAT_DECL.match(line) + if m is not None: + name: str = m.group(2) + if name in seen: + continue + seen.add(name) + kept.append(line) + return ''.join(kept) + + +def _AddWeakComdat(ll_content: str) -> str: + """为 weak/linkonce 系列 函数/全局 定义补 COMDAT(COFF/PE 必需)。 + + COFF 下无 COMDAT 的 weak/linkonce 定义会被 llc 展开成强符号 + `.weak..default`(weak external 的实现副本)。同一弱定义出现在 + 多个模块时(泛型特化方法如 `e94ef9e139461915.list[rawptr[AST]]. + __before_init__`、`linkonce`/`weak` 标注的跨模块共享常量),副本同名 → + ld.lld 报 duplicate symbol。补 COMDAT(any) 后 llc 输出单一 COMDAT + 成员,链接器自动去重(llc/ld.lld 端到端已实验验证)。 + """ + if (' linkonce ' not in ll_content and ' linkonce_odr ' not in ll_content + and ' weak_odr ' not in ll_content and ' weak ' not in ll_content): + return _DedupComdatDecls(ll_content) + # 拼接而来的片段可能各自已带同名 comdat 声明 ⇒ 先去重,再做本次补全 + ll_content = _DedupComdatDecls(ll_content) + weak_links = 'linkonce|linkonce_odr|weak|weak_odr|extern_weak' + comdat_names: list[str] = [] + + # 1) 函数定义:define [dso_local] ... @name(...) { → @name(...) comdat { + def _fn_repl(m: re.Match) -> str: + comdat_names.append(m.group(3)) + head: str = m.group(1) + # comdat 是函数头关键字,必须写在 !dbg 等元数据附着**之前**:LLVM 解析器 + # 读完 `!...` 附着后就只接受 `{`,附着之后写 comdat 会报 + # "expected '{' in function body"(`[^{]*` 会跨行吞掉换行,--dwarf 给 + # 每个 define 行尾加了 !dbg 后插入点就落到了附着之后)。故插入点前移到 + # 第一个元数据附着之前。 + attach = _RE_METADATA_ATTACH.search(head) + if attach is None: + return head + ' comdat ' + m.group(4) + return head[:attach.start()] + ' comdat' + head[attach.start():] + m.group(4) + ll_content = re.sub( + r'^(define\s+(?:dso_local\s+)?(?:%s)\s+[^@]*@("?)([^"\s\(]+)\2[^{]*)(\{)' % weak_links, + _fn_repl, ll_content, flags=re.MULTILINE) + + # 2) 全局定义:@name = [dso_local] (global|constant) → 值后补 ', comdat' + # (comdat 需位于 align/section 等尾部属性之前,符合 LLVM IR 文法) + def _g_repl(m: re.Match) -> str: + line = m.group(0) + if ' comdat' in line: + return line + comdat_names.append(m.group(3)) + ins = _RE_COMDAT_TAIL_ATTRS.search(line) + if ins: + return line[:ins.start()] + ', comdat' + line[ins.start():] + return line + ', comdat' + ll_content = re.sub( + r'^(@("?)([^"=\s]+)\2\s*=\s*(?:%s)\s+(?:dso_local\s+)?(?:global|constant)[^\n]*)' % weak_links, + _g_repl, ll_content, flags=re.MULTILINE) + + if not comdat_names: + return ll_content + decls: list[str] = [] + for name in dict.fromkeys(comdat_names): + # fail-fast:LLVM 的引号标识符 @"..." **不支持转义**(LLLexer 读到 `"` 即认为名字 + # 结束),故名字里含 `"` / `\` / 换行时,加不加引号都产不出合法 IR。此处不静默 + # 跳过、也不做字符替换(那会把符号名改得与源码/其它模块不一致,链接期更难查), + # 直接指名报错。名字来自弱符号命名 `.<标识符>`,出现这些字符说明上游符号名 + # 构造有误,应在上游修正。 + if '"' in name or '\\' in name or '\n' in name or '\r' in name: + raise RuntimeError( + f'comdat 名无法表达为 LLVM 标识符(含双引号/反斜杠/换行): {name!r};' + 'LLVM 引号名 @"..." 不支持转义,产出即非法 IR(opt 解析报 ' + '"expected top-level entity")。请在上游符号名构造处修正。') + if _LLVM_UNQUOTED_NAME_RE.match(name): + decls.append(f'${name} = comdat any') + else: + decls.append(f'$"{name}" = comdat any') + comdat_block = '\n'.join(decls) + '\n' + lines = ll_content.splitlines(True) + header_end = 0 + for i, line in enumerate(lines): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + return ''.join(lines[:header_end]) + comdat_block + ''.join(lines[header_end:]) + + +class Phase2Translator: + """阶段二:使用声明接口翻译源文件""" + + INCLUDE_LIB_EXTENSIONS: tuple[str, ...] = ('.dll', '.ll', '.so', '.o', '.a', '.lib') + INCLUDE_SRC_EXTENSIONS: tuple[str, ...] = ('.c', '.cpp', '.cc', '.cxx', '.m', '.mm') + # includes 只认项目配置(project.vpj includes),禁止仓库硬编码回退。 + # INCLUDE_DIRS 保留为空以兼容旧引用;实际目录由 __init__ 写入 PROJECT_INCLUDE_DIRS。 + INCLUDE_DIRS: list[str] = [] + PROJECT_INCLUDE_DIRS: list[str] = [] + # include 源文件 sha1/存在性缓存(_build_current_ModuleSha1Map): + # 编译期间 include .vp/.py 源文件不变,避免每文件重复 isfile + open + + # compute_sha1(PROF 显示 9306 isfile + 4653 open + TextIOWrapper.read + # 35.9s 的大头来源,99 次调用 × 全量 include 重算)。 + _include_sha1_cache: dict[str, str] = {} + _include_exist_cache: dict[str, bool] = {} + + def __init__(self, src_root: str, temp_dir: str, output_dir: str, compile_cmd: str = 'llc', compile_flags: list[str] | None = None, linker_cmd: str | None = None, linker_flags: list[str] | None = None, linker_output: str | None = None, static_libs: list[str] | None = None, dynamic_libs: list[str] | None = None, include_dirs: list[str] | None = None, entry_files: list[str] | None = None, target_triple: str | None = None, target_datalayout: str | None = None, force_recompile_includes: bool = False, build_pyi_cache: bool = True, asan_mode: bool = False, dwarf_enabled: bool = False, traceback_enabled: bool = False) -> None: + self.src_root = os.path.abspath(src_root) + self.temp_dir = os.path.abspath(temp_dir) + self.output_dir = os.path.abspath(output_dir) + self.compile_cmd = compile_cmd + self.compile_flags = compile_flags or ['-filetype=obj'] + self.triple = target_triple + self.datalayout = target_datalayout + if not self.triple: + for i, flag in enumerate(self.compile_flags): + if flag.startswith('-mtriple='): + self.triple = flag[len('-mtriple='):] + elif flag == '-mtriple' and i + 1 < len(self.compile_flags): + self.triple = self.compile_flags[i + 1] + self.linker_cmd = linker_cmd + self.linker_flags = linker_flags or [] + self.linker_output = linker_output + # ASan 模式:由 Config 的 --asan 参数直传(不再用 TPC_ASAN 环境变量, + # 环境变量在 worker 子进程/外部调用链下不可靠)。 + # 检测 stdlib 堆/栈/全局越界与垃圾指针(MemBuddy pool 内部由 canary 覆盖)。 + # 注意:mingw64 的 clang 不提供 x86_64-w64-windows-gnu 目标的 ASan runtime, + # 只能借用 clang64(MSVC ABI) 的那两份:PATH 末尾追加 clang64/bin(运行期 DLL), + # 链接时显式 -l: 那两份 .a/.dll.a(见 _FindAsanRuntime 的说明)。 + self.asan_mode = asan_mode + self.asan_rt_dir: str | None = None + if self.asan_mode: + _rt = _FindAsanRuntime() + if _rt is not None: + self.asan_rt_dir, _asan_bin = _rt + # clang64/bin 必须**追加**到 PATH 末尾:既要让运行期能找到 + # libclang_rt.asan_dynamic-x86_64.dll,又不能前置 —— 前置会让裸名 + # clang++.exe / llc / opt 解析到 clang64 的 libc++ 版本,与 includes/ + # 里 mingw64(libstdc++) 编的静态库链接不上。 + if _asan_bin and os.path.isdir(_asan_bin): + os.environ['PATH'] = os.environ.get('PATH', '') + os.pathsep + _asan_bin + self.slice_level = 3 + # 函数级 DWARF 调试元数据(--dwarf;--asan 时自动开启):False=未启用 + # (零影响,不构造任何 DI 元数据);True=App 源模块的 .ll 携带 + # !llvm.dbg.cu + 每函数 DISubprogram + 入口 DILocation,供 ASan / + # llvm-symbolizer 反查 ".vp:函数起始行"。 + self.dwarf_enabled: bool = dwarf_enabled + # Ctrl+C 调用栈(--traceback):False=未启用(零影响,不调用任何外部 + # 工具、不写任何旁文件);True=链接成功后额外用 llvm-dwarfdump 把最终 + # exe 的 DWARF 行表导出为 ".stackmap"(地址→源行旁文件,供 exe 内 + # 运行时在 Ctrl+C/Ctrl+Break 时逐帧翻译返回地址)。要求 --dwarf 已开启 + # (Config 侧联动,否则行表为空),且目标为 Windows PE。 + self.traceback_enabled: bool = traceback_enabled + # 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数 + # 落盘文件路径(每函数计时落同目录 hotspot_time.txt)。各模块运行时自带 + # Ctrl+C 处理器、统一以追加方式写该路径;含 main 的模块在 main 入口清空一次 + # → 每次运行一份结果。 + self.hotspot_sites_file: str | None = None + # 是否对全部直接 call 点插桩(忽略白名单);白名单被调函数基名列表(None=默认) + self.hotspot_sites_all: bool = False + self.hotspot_callees: list[str] | None = None + self.sha1_map: dict[str, str] = {} + self.sig_files: dict[str, str] = {} + self.stub_files: dict[str, str] = {} + self.include_py_map: dict[str, str] = {} + self.used_includes: set[str] = set() + self._last_ErrorStack: list | None = None + self.function_default_args: dict[str, list] = {} + self.function_param_names: dict[str, list] = {} + self.inline_func_symbols: dict[str, CTypeInfo] = {} + self.extra_link_files: list[str] = [] + self.extra_compile_files: list[str] = [] + self.extra_py_files: list[str] = [] + # project.vpj linker.static_libs:显式静态链接文件(.a/.obj), + # 加入 extra_link_files 参与最终链接(位于 -o 之后、-l 库标志之前)。 + if static_libs: + for lib in static_libs: + if lib not in self.extra_link_files: + self.extra_link_files.append(lib) + # project.vpj linker.dynamic_libs:显式动态链接文件(.dll/.so), + # 同样作为链接输入(GNU ld 从 DLL/SO 提取导入符号);运行时需 + # DLL/SO 位于 exe 同目录 / PATH / LD_LIBRARY_PATH。 + if dynamic_libs: + for lib in dynamic_libs: + if lib not in self.extra_link_files: + self.extra_link_files.append(lib) + self._include_sha1s: set[str] = set() + # 持有 ProcessPoolExecutor 引用,避免局部变量 GC 时 finalizer 阻塞 ~99s + # (join_executor_internals 在 GC 时同步等待 worker 退出队列) + self._executor_pool: ProcessPoolExecutor | None = None + self.entry_files: list[str] | None = entry_files + self.force_recompile_includes: bool = force_recompile_includes + # pyi cache 构建开关:仅主进程编译启动时构建一次(_LoadSha1Map 内), + # worker 子进程只读主进程落盘的 temp/_pyi_cache_v1.pkl(_PyiCacheWorkerInitializer), + # 不重复构建——worker 每任务重建 pkl 的 pickle.load/dump(43 次 × ~0.5s ≈ 23s + # + pickle.loads 10.4s)纯属浪费;worker 缺 includes vpi 时运行期按需 parse 回退。 + self._build_pyi_cache: bool = build_pyi_cache + self._shared_symbol_table: object | None = None + self._shared_source_module_sig_files: dict[str, str] = {} + self._shared_all_dc: dict[str, dict] = {} + self._shared_all_decl_types: dict[str, tuple[int, bool]] = {} + self._shared_export_extern_funcs: dict[str, str] = {} + self.include_dirs: list[str] = [] + self.struct_sha1_map: dict[str, str] = {} + self._shared_generic_class_templates: dict[str, dict] = {} + self._shared_doc_meta: dict[str, dict[str, str]] = {} + # 共享 vtable 状态跨模块(修复 pool.alloc(48) 虚表分派失败导致段错误) + self._shared_class_vtable: set[str] = set() + self._shared_cross_module_vtable: set[str] = set() + self._shared_class_methods: dict[str, list[str]] = {} + self._shared_cross_module_novtable: set[str] = set() + default_dirs: list[str] = [] + if include_dirs: + seen = set() + self.include_dirs = [] + for d in list(include_dirs) + default_dirs: + d = os.path.abspath(d) + if d not in seen and os.path.isdir(d): + self.include_dirs.append(d) + seen.add(d) + else: + self.include_dirs = default_dirs + # 严格:只认项目 includes;供 builtins.vp 扫描等静态路径使用 + Phase2Translator.PROJECT_INCLUDE_DIRS = list(self.include_dirs) + self._LoadSha1Map() + + def _LoadSha1Map(self) -> None: + """从 temp_dir 加载 SHA1 映射、签名文件和 stub 声明文件列表。 + temp_dir 中缺失的文件会从全局缓存补充。 + """ + if not os.path.exists(self.temp_dir): + _vlog().error(f"声明目录不存在: {self.temp_dir}") + return + + for fname in os.listdir(self.temp_dir): + fpath = os.path.join(self.temp_dir, fname) + if fname.startswith('_'): + continue + if fname.endswith('.vpi') or fname.endswith('.pyi'): + sha1 = fname[:-4] + self.sig_files[sha1] = fpath + elif fname.endswith('.stub.ll'): + sha1 = fname[:-8] + self.stub_files[sha1] = fpath + + # 从内存存储器读取 SHA1 映射(_sha1_map.txt 仅人类可读,不再作为机器数据源) + # 延迟导入避免循环依赖(Config 导入 Phase2Translator) + from lib.Projectrans.Config import get_sha1_map_store + mem_store: dict[str, str] = get_sha1_map_store() + if mem_store: + for sha1, rel in mem_store.items(): + self.sha1_map[sha1] = rel + if rel.startswith('includes/'): + module_name = os.path.splitext(os.path.basename(rel))[0] + self.include_py_map[module_name] = sha1 + else: + # 内存存储器为空(独立运行 Phase2 的回退):从 stub 文件名推断 + for sha1, stub_path in self.stub_files.items(): + self.sha1_map[sha1] = sha1 + + # 从全局缓存补充 temp_dir 中缺失的 .vpi / .stub.ll。 + # 只补充"当前编译有效"的 sha1(在 sha1_map 中):.transpyc_cache 会残留 + # 旧版源码的缓存(如旧 __builder.vp 的 9026a429f5e2d0e0.pyi/.stub.ll 与新 + # d9b3d06934cd1898 共存),不加过滤会把旧版 .vpi/.stub.ll 也加载进 + # sig_files/stub_files → 同名类(IRBuilder)按旧 sha1 前缀注册 → + # ld.lld undefined symbol: 9026a429f5e2d0e0.new_builder。 + if os.path.isdir(_GLOBAL_CACHE_DIR): + for fname in os.listdir(_GLOBAL_CACHE_DIR): + if fname.startswith('_'): + continue + if fname.endswith('.vpi') or fname.endswith('.pyi'): + sha1 = fname[:-4] + if sha1 in self.sha1_map and sha1 not in self.sig_files: + self.sig_files[sha1] = os.path.join(_GLOBAL_CACHE_DIR, fname) + elif fname.endswith('.stub.ll'): + sha1 = fname[:-8] + if sha1 in self.sha1_map and sha1 not in self.stub_files: + self.stub_files[sha1] = os.path.join(_GLOBAL_CACHE_DIR, fname) + + # 构建 pyi AST pickle 缓存(temp/_pyi_cache_v1.pkl)供 worker 预热: + # Phase1 已生成全部 .vpi,此处把 vast 树落盘,避免每个 worker 首次任务 + # 重新 vast.parse(4479 次 parse / 245s)。签名未变时零开销跳过。 + # 仅主进程构建(build_pyi_cache=True);worker 只读 pkl,不重复构建。 + if self._build_pyi_cache and os.environ.get('TPC_WORKER_WARMUP', '1') == '1': + _BuildPyiCacheFile(self.temp_dir) + + def _build_struct_sha1_map(self) -> dict[str, str]: + struct_sha1_map = {} + valid_sha1_keys = set(self.sha1_map.keys()) + # 收集 temp_dir 和全局缓存中的 .vpi/.pyi 文件 + pyi_dirs: list[str] = [self.temp_dir] + if os.path.isdir(_GLOBAL_CACHE_DIR): + pyi_dirs.append(_GLOBAL_CACHE_DIR) + _scanned_sha1: set[str] = set() + for scan_dir in pyi_dirs: + if not os.path.isdir(scan_dir): + continue + for fname in os.listdir(scan_dir): + if not (fname.endswith('.vpi') or fname.endswith('.pyi')): + continue + sha1_key = fname.replace('.vpi', '').replace('.pyi', '') + if sha1_key not in valid_sha1_keys: + continue + # 同一模块在两处目录都有文件时只解析一次(与下面的类名归属判据无关) + if sha1_key in _scanned_sha1: + continue + _scanned_sha1.add(sha1_key) + pyi_path = os.path.join(scan_dir, fname) + # 复用 parse_python_file 的 AST 缓存(.pyi 文件可能被多处解析) + _, tree = parse_python_file(pyi_path) + if tree is None: + continue + for node in ast.iter_child_nodes(tree): + # 每类只登记一次,**先扫到的为准**:判据必须是**类名**。 + # 原实现写成 `if sha1_key in struct_sha1_map` —— 用模块 SHA1 去查一张 + # 按**类名**建的表,判据恒假 ⇒ 实际是"后扫到的覆盖先扫到的", + # 同名类的归属随 os.listdir 顺序漂移(现场:includes/ast/astaux.vp 与 + # includes/ast/tokens.vp 各有一个 `class Keyword`,定义端拿到的前缀 + # 可能是**另一个模块**的 sha1 ⇒ 同一类出现两个 struct 实例、 + # `__before_init__` 声明与调用点分叉)。 + if isinstance(node, ast.ClassDef): + # 嵌套类按扁平名 `{外类}_{内类}` 登记(与定义端 + # HandlesClassDef._EmitNestedClassDefs、Phase1Generator. + # _RegisterNestedStructNames 同源)。此前用 ast.walk 会把嵌套类按 + # **短名** `Inner` 登记 —— 既占住平坦命名空间,又与定义端的 + # `外类_内类` 不同源(跨模块前缀分裂)。 + self._RegisterStructSha1Rec(node, node.name, sha1_key, struct_sha1_map) + return struct_sha1_map + + def _RegisterStructSha1Rec(self, Node, FlatName: str, Sha1Key: str, + StructSha1Map: dict) -> None: + """递归登记"类名 → 定义模块 SHA1"(嵌套类用扁平名 `{外类}_{内类}`)""" + if not FlatName: + return + if FlatName not in StructSha1Map: + StructSha1Map[FlatName] = Sha1Key + Body = getattr(Node, 'body', None) + if not Body: + return + for Item in Body: + if not isinstance(Item, ast.ClassDef): + continue + InnerName: str | None = getattr(Item, 'name', None) + if not InnerName: + continue + self._RegisterStructSha1Rec(Item, f'{FlatName}_{InnerName}', Sha1Key, StructSha1Map) + + def run(self) -> None: + """扫描源目录,翻译并生成 .ll 含代码文件(导入遍历)""" + import sys as _sys + if self.entry_files: + py_files = list(self.entry_files) + else: + main_py = os.path.join(self.src_root, 'main.vp') + if not os.path.exists(main_py): + # 回退到 main.py(向后兼容) + main_py = os.path.join(self.src_root, 'main.py') + if os.path.exists(main_py): + py_files = [main_py] + else: + py_files = [] + for root, dirs, files in os.walk(self.src_root): + dirs[:] = [d for d in dirs if d not in ('__pycache__',)] + for file in files: + if file.endswith('.vp') or file.endswith('.py'): + py_files.append(os.path.join(root, file)) + + if not py_files: + _vlog().info("未找到入口文件或源文件") + return + + reachable = find_reachable_files_from_entries(self.src_root, py_files) + py_files = list(reachable) + + py_files = topological_sort_files(py_files, self.src_root) + + _vlog().info(f"找到 {len(py_files)} 个可达源文件(已按依赖排序)") + _vlog().info("处理顺序:") + for i, f in enumerate(py_files[:10], 1): + rel = os.path.relpath(f, self.src_root) + _vlog().info(f" {i}. {rel}") + os.makedirs(self.output_dir, exist_ok=True) + + valid_sha1s = set(self.sha1_map.keys()) + old_stub_count = len(self.stub_files) + old_sig_count = len(self.sig_files) + self.stub_files = {k: v for k, v in self.stub_files.items() if k in valid_sha1s} + self.sig_files = {k: v for k, v in self.sig_files.items() if k in valid_sha1s} + if old_stub_count != len(self.stub_files) or old_sig_count != len(self.sig_files): + _vlog().info(f"过滤旧文件: stub {old_stub_count}->{len(self.stub_files)}, sig {old_sig_count}->{len(self.sig_files)}") + + active_sha1s = set() + self._precollect_inline_symbols() + self._build_shared_symbol_data() + self._precollect_vtable_state() + self.struct_sha1_map = self._build_struct_sha1_map() + + need_translate = [] + # --hotspot-sites:缓存 .ll / .stub.ll+.text.ll 可能不含热点计数/计时 IR, + # 启用时一律重译,保证每个模块都嵌入 internal 计数器、计时器与 + # @llvm.global_ctors 自注册。未启用时 hotspot_on=False,缓存与重组分支 + # 行为与改动前完全一致(零影响)。 + # --dwarf(函数级 DWARF)同理:缓存 .ll/.obj 不含 !llvm.dbg.cu,必须一并 + # 强制重译,否则开启开关后仍复用旧产物、看不到任何 .vp 行号。 + # --traceback 依赖同一份 .debug_line 行表(Config 已联动开启 --dwarf), + # 这里显式纳入,避免将来联动改动后静默复用无 DI 的旧产物。 + hotspot_on: bool = bool(self.hotspot_sites_file) or bool(self.dwarf_enabled) or bool(self.traceback_enabled) + for i, src_path in enumerate(py_files, 1): + rel = os.path.relpath(src_path, self.src_root) + # 复用 parse_python_file 的 AST 缓存(get_file_dependencies 已 parse 过) + content, tree = parse_python_file(src_path) + sha1 = compute_sha1(content) + active_sha1s.add(sha1) + out_path = os.path.join(self.output_dir, f"{sha1}.ll") + + current_ModuleSha1Map = self._build_current_ModuleSha1Map(sha1) + + if tree is not None: + try: + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + self.used_includes.add(alias.name.split('.')[0]) + elif isinstance(node, ast.ImportFrom): + if node.module: + self.used_includes.add(node.module.split('.')[0]) + # 检测 assert 使用,自动添加 testcheck 到 used_includes: + # assert 译为 testcheck.check(...),该库须被发现、编译并链接。 + if isinstance(node, ast.Assert): + self.used_includes.add('testcheck') + # 检测 list/dict 容器使用,自动添加 _list/_dict/json 到 used_includes + # 以便 _scan_include_libraries 能发现并编译这些库文件 + if isinstance(node, ast.Subscript) and isinstance(node.value, ast.Name): + if node.value.id == 'dict': + self.used_includes.add('_dict') + self.used_includes.add('json') + elif node.value.id == 'list': + self.used_includes.add('_list') + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): + if node.func.id == 'dict': + self.used_includes.add('_dict') + self.used_includes.add('json') + elif node.func.id == 'list': + self.used_includes.add('_list') + # 检测类型注解 d: dict / l: list (非泛型形式) + if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): + if node.annotation.id == 'dict': + self.used_includes.add('_dict') + self.used_includes.add('json') + elif node.annotation.id == 'list': + self.used_includes.add('_list') + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集源文件导入的 include 模块失败: {_e}", "Exception") + + # --hotspot-sites:上面两个缓存/重组分支在 hotspot_on 时被跳过, + # 本轮每个模块都会强制重译 .ll;此时必须让旧的 {sha1}.obj 一并失效, + # 否则 _compile_ll_files 会因 .obj 已存在而跳过重编,复用上一次未插桩 + # 的目标文件,导致部分模块热点计时/计数静默缺失、耗时被计入调用者 self。 + # 这里用 os.path.isfile 而非 _CachedIsFile:避免把"已存在"写进存在性 + # 缓存后又被本函数删除,使后续 _compile_ll_files 读到过期缓存而漏编。 + if hotspot_on: + obj_path = os.path.join(self.output_dir, f"{sha1}.obj") + if os.path.isfile(obj_path): + try: + os.remove(obj_path) + except OSError as e: + # 不中断构建,但真实错误(如权限不足)必须打印告警,不静默吞掉 + _vlog().warning( + f"Phase2Translator: 删除热点插桩模块的旧目标文件失败: {obj_path}: {e}") + + if not hotspot_on and _CachedIsFile(out_path): + deps_changed = self._check_deps_changed(sha1, current_ModuleSha1Map) + if not deps_changed: + _vlog().info(f"[{i}/{len(py_files)}] 跳过(缓存): {rel} -> {sha1}.ll") + continue + else: + if self._recombine_ll(sha1, current_ModuleSha1Map): + _vlog().info(f"[{i}/{len(py_files)}] 重组(依赖变化): {rel} -> {sha1}.ll") + continue + _vlog().info(f"[{i}/{len(py_files)}] 重译(依赖变化): {rel} -> {sha1}.ll") + + elif not hotspot_on: + stub_cache = os.path.join(self.output_dir, f"{sha1}.stub.ll") + text_cache = os.path.join(self.output_dir, f"{sha1}.text.ll") + if _CachedIsFile(stub_cache) and _CachedIsFile(text_cache): + if self._recombine_ll(sha1, current_ModuleSha1Map): + _vlog().info(f"[{i}/{len(py_files)}] 重组(缓存恢复): {rel} -> {sha1}.ll") + continue + + need_translate.append((i, src_path, out_path, sha1, current_ModuleSha1Map)) + + if need_translate: + # 提前从 include 文件源码中提取函数参数名,供主项目编译时 keyword 参数匹配使用 + self._extract_include_param_names_from_dirs() + # 提前从 App 源文件中提取函数默认参数,供跨模块调用参数检查使用 + # (避免并行编译时,被调用函数的文件尚未编译完,导致默认参数信息缺失) + self._extract_app_default_args(need_translate) + + _vlog().info(f"\n需要翻译 {len(need_translate)} 个文件") + t_start = time.time() + + n_workers = _PickWorkerCount(len(need_translate)) + + if n_workers > 1 and len(need_translate) > 1: + _vlog().info(f" 并行翻译 (workers={n_workers})") + + shared_pickle_path = os.path.join(self.temp_dir, '_shared_sym.json') + try: + # symbol_table_bytes:SymbolTable 独立 pickle 字节,供批次 worker + # 每文件前反序列化出干净副本(SymbolTable 是编译期唯一被写的共享 + # 对象,批次内复用 trans 时必须在每个文件前重置,防止前文件 import + # 写入的符号污染后文件 → 跨模块 struct 类型前缀错位)。 + symbol_table_bytes: bytes = pickle.dumps(self._shared_symbol_table) + shared_data = { + 'symbol_table_bytes': symbol_table_bytes, + 'source_module_sig_files': self._shared_source_module_sig_files, + 'all_dc': self._shared_all_dc, + 'all_decl_types': self._shared_all_decl_types, + 'export_extern_funcs': self._shared_export_extern_funcs, + 'inline_func_symbols': self.inline_func_symbols, + 'function_default_args': self.function_default_args, + 'function_param_names': self.function_param_names, + 'generic_class_templates': self._shared_generic_class_templates, + 'shared_doc_meta': self._shared_doc_meta, + 'class_vtable': self._shared_class_vtable, + 'cross_module_vtable': self._shared_cross_module_vtable, + 'class_methods': self._shared_class_methods, + 'cross_module_novtable': self._shared_cross_module_novtable, + } + with open(shared_pickle_path, 'wb') as f: + f.write(pickle.dumps(shared_data)) + _vlog().info(f" 共享符号表已序列化 ({os.path.getsize(shared_pickle_path)//1024}KB)") + except Exception as e: + _vlog().warning(f"符号表序列化失败({e}),worker将自行构建") + shared_pickle_path = '' + + errors = [] + # 复用 self._executor_pool:避免局部变量 GC 时 finalizer 阻塞 ~99s + # (join_executor_internals 在 GC 时同步等待 worker 退出队列) + if self._executor_pool is None: + # initializer 预热 ImportHandle._pyi_cache(pickle.load 落盘缓存), + # 避免每个 worker 首次任务重新 vast.parse 全部依赖 .vpi + self._executor_pool = ProcessPoolExecutor(max_workers=n_workers, + initializer=_PyiCacheWorkerInitializer, + initargs=(self.temp_dir,)) + executor = self._executor_pool + try: + # 批次子任务模型:多个文件包装为一个子任务(batch), + # 每 worker 处理一批——Phase2Translator 创建 + pickle.load 只做 + # 一次,批内文件顺序翻译(每文件前重置 SymbolTable 防污染)。 + # 相比"一文件一任务":减少进程池调度/通信/构造开销 + # (200+ 文件 → n_workers 个批次任务)。 + batches: list[list] = [] + # 按源文件大小加权分桶(LPT 贪心:最大文件优先放入当前总成本最小的桶), + # 使各批次总成本均匀。原按文件数均分(batch_size=ceil(N/n))导致大文件 + # 集中的批次拖长尾,其他 worker 提前完成空等(任务颗粒度不均匀)。 + if len(need_translate) <= n_workers: + batches = [[item] for item in need_translate] + else: + sized_items: list = [] + for item in need_translate: + try: + _size: int = os.path.getsize(item[1]) + except OSError: + _size = 0 + sized_items.append((item, _size)) + buckets: list[list] = [[] for _ in range(n_workers)] + bucket_cost: list[int] = [0] * n_workers + for item, _size in sorted(sized_items, key=lambda t: t[1], reverse=True): + target: int = min(range(n_workers), key=lambda w: bucket_cost[w]) + buckets[target].append(item) + bucket_cost[target] += _size + batches = [b for b in buckets if b] + futures = {} + for bidx, batch in enumerate(batches): + rel_first = os.path.relpath(batch[0][1], self.src_root) + future = executor.submit( + _parallel_translate_batch, + batch, + self.src_root, self.temp_dir, self.output_dir, + self.include_dirs, self.triple, self.datalayout, + self.sha1_map, self.sig_files, self.stub_files, + self.include_py_map, self.slice_level, + shared_pickle_path, self.struct_sha1_map, + hotspot_sites_file=self.hotspot_sites_file, + hotspot_sites_all=self.hotspot_sites_all, + hotspot_callees=self.hotspot_callees, + dwarf_enabled=self.dwarf_enabled, + ) + futures[future] = (bidx, rel_first, len(batch)) + + done_files = 0 + total_files = len(need_translate) + for future in as_completed(futures): + bidx, rel_first, bcount = futures[future] + try: + batch_errors, batch, per_file_times = future.result() + for src, elapsed, rel in per_file_times: + done_files += 1 + _vlog().info(f" [{done_files}/{total_files}] {rel} ({elapsed:.1f}s)") + for src, err in batch_errors: + rel = os.path.relpath(src, self.src_root) + _vlog().error(f" {rel}: {err}") + errors.append((rel, err)) + _vlog().info(f" [批次完成] {bcount} 文件, 首个 {rel_first}") + except Exception as e: + # fail-fast:worker 已 os._exit(文件编译出错)或进程池 + # 已 broken → 立即取消全部剩余批次并终止编译,不空等。 + _vlog().error(f" 批次 {bidx} 异常(worker 已终止): {e}") + for fut in futures: + if not fut.done(): + fut.cancel() + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + raise RuntimeError( + f"批次 {bidx} worker 异常终止,编译立即停止: {e}") + except Exception: + # 异常时立即清理,正常流程不 shutdown(留给 includes 阶段复用) + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + raise + + if errors: + _vlog().error(f"\n{len(errors)} 个文件翻译失败,立即终止编译") + for rel, err in errors: + _vlog().error(f" {rel}: {err}") + raise RuntimeError(f"{len(errors)} 个文件翻译失败,已终止编译") + else: + for idx, (i, src_path, out_path, sha1, msm) in enumerate(need_translate): + rel = os.path.relpath(src_path, self.src_root) + t0 = time.time() + try: + self._translate_file(src_path, out_path, prebuilt_ModuleSha1Map=msm, dwarf_enabled=self.dwarf_enabled) + t1 = time.time() + _vlog().info(f" [{idx+1}/{len(need_translate)}] {rel} ({t1-t0:.1f}s)") + except Exception as e: + _vlog().error(f" {rel}: {e}") + traceback.print_exc() + raise RuntimeError(f"翻译 {rel} 失败,已终止编译: {e}") from e + t_end = time.time() + _vlog().info(f" 翻译总耗时: {t_end-t_start:.1f}s") + + _vlog().info(f"\n[阶段二完成] .ll 文件生成到: {self.output_dir}") + self._scan_include_libraries() + self._compile_include_py_files(active_sha1s) + self._compile_ll_files(active_sha1s) + self._compile_include_sources() + if self.linker_cmd: + self._link_obj_files(active_sha1s) + # 清理 ProcessPoolExecutor:所有翻译已完成,shutdown(wait=False) 让 worker + # 后台退出。避免 GC finalizer 在程序末尾阻塞 ~99s(join_executor_internals) + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + + def _build_current_ModuleSha1Map(self, self_sha1: str | None) -> dict[str, str]: + """构建当前模块的依赖 SHA1 映射(与 _translate_file 中逻辑一致)""" + ModuleSha1Map = {} + for sha1_key, sig_path in self.sig_files.items(): + if sha1_key == self_sha1: + continue + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + inc_mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + inc_mod_name = inc_mod_name[len('includes/'):] + inc_short_name = inc_mod_name.split('.')[-1] if '.' in inc_mod_name else inc_mod_name + actual_sha1 = sha1_key + for inc_dir in self.include_dirs: + if os.path.isdir(inc_dir): + py_path = os.path.join(inc_dir, rel_path[len('includes/'):].replace(os.sep, '/')) + py_path = os.path.splitext(py_path)[0] + '.vp' + # 存在性/内容缓存:编译期 include 源文件不变,命中跳过磁盘 I/O + exists = Phase2Translator._include_exist_cache.get(py_path) + if exists is None: + exists = _CachedIsFile(py_path) + Phase2Translator._include_exist_cache[py_path] = exists + if not exists: + # 回退到 .py(向后兼容) + py_path = os.path.splitext(py_path)[0] + '.py' + exists = Phase2Translator._include_exist_cache.get(py_path) + if exists is None: + exists = _CachedIsFile(py_path) + Phase2Translator._include_exist_cache[py_path] = exists + if exists: + py_sha1 = Phase2Translator._include_sha1_cache.get(py_path) + if py_sha1 is None: + py_sha1 = compute_sha1(_ReadTextCached(py_path)) + Phase2Translator._include_sha1_cache[py_path] = py_sha1 + actual_sha1 = py_sha1 + break + ModuleSha1Map[inc_mod_name] = actual_sha1 + ModuleSha1Map[inc_short_name] = actual_sha1 + # Also add parent package name (e.g., 'hashlib' for 'hashlib.__init__') + if inc_short_name == '__init__' and '.' in inc_mod_name: + parent_pkg = inc_mod_name.rsplit('.', 1)[0] + ModuleSha1Map[parent_pkg] = actual_sha1 + continue + module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + ModuleSha1Map[module_name] = sha1_key + short_name = module_name.split('.')[-1] if '.' in module_name else module_name + ModuleSha1Map[short_name] = sha1_key + # 包入口文件:注册父包名(如 'os.__init__' -> 'os'),使 import os 能解析到包级符号 + if short_name == '__init__' and '.' in module_name: + parent_pkg = module_name.rsplit('.', 1)[0] + ModuleSha1Map[parent_pkg] = sha1_key + return ModuleSha1Map + + @staticmethod + def _extract_llvm_type_name(line: str) -> str | None: + """从 LLVM IR 类型定义行提取类型名(字符串操作,不使用正则) + + 支持 list[str]、dict[str, int] 等含特殊字符的类型名。 + 例如: + %"18e6990aa81cc5b6.list[str]" = type { ... } → "18e6990aa81cc5b6.list[str]" + %struct.foo = type { ... } → "struct.foo" + """ + stripped = line.strip() + if not stripped.startswith('%'): + return None + # 处理带引号的情况: %"name" = type ... + if stripped.startswith('%"'): + end_quote = stripped.find('"', 2) + if end_quote > 0: + return stripped[2:end_quote] + return None + # 处理不带引号的情况: %name = type ... + eq_pos = stripped.find('=') + if eq_pos > 1: + return stripped[1:eq_pos].strip() + return None + + @staticmethod + def _find_all_type_refs(text: str) -> set[str]: + """查找文本中所有 %"..."/%name 类型引用(字符串操作,不使用正则) + + 支持 list[str]、dict[str, int] 等含特殊字符的类型名。 + """ + refs: set[str] = set() + i: int = 0 + n: int = len(text) + while i < n: + if text[i] == '%': + if i + 1 < n and text[i + 1] == '"': + # 带引号: %"name" + end: int = text.find('"', i + 2) + if end > 0: + name: str = text[i + 2:end] + if name: + refs.add(name) + i = end + 1 + continue + else: + # 不带引号: %name (仅匹配字母、数字、下划线、点) + j: int = i + 1 + while j < n and (text[j].isalnum() or text[j] in '._'): + j += 1 + if j > i + 1: + refs.add(text[i + 1:j]) + i = j + continue + i += 1 + return refs + + @staticmethod + def _split_ll(ll_content: str) -> tuple[str, str]: + """将 .ll 内容拆分为 stub(声明)和 text(代码)两部分 + + stub: target, 注释, %type = type, declare, @xxx = external global + text: define ... { ... }, @xxx = global/constant (带初始化器) + """ + stub_lines = [] + text_lines = [] + in_define = False + brace_depth = 0 + module_sha1 = None + defined_names = set() + + for line in ll_content.splitlines(True): + # 第一遍只关心 `define ...` 行,而合并后的 .ll 里这类行占比极低。 + # `'define' not in line` 是 C 级子串扫描 ⇒ 绝大多数行连 strip 都不做 + # (prof 实测 str.strip 1.79 亿次 / str.startswith 2.37 亿次是全流程 + # 最大的 Python 侧热点,本函数两遍逐行扫描是其主要来源)。 + if 'define' not in line: + continue + stripped = line.strip() + + if stripped.startswith('define '): + dm = _RE_DEFINE_SHA1.match(stripped) + if dm and module_sha1 is None: + module_sha1 = dm.group(1) + dm2 = _RE_DEFINE_NAME.match(stripped) + if dm2: + defined_names.add(dm2.group(1)) + + in_global_def = False + global_var_name = None + global_type_parts = [] + global_brace_depth = 0 + + for line in ll_content.splitlines(True): + # 续行(函数体指令 / 全局初始化器)在合并 .ll 里占绝大多数,而这两支 + # 只需要"行内容 + 花括号计数" ⇒ 不进 strip(前导/尾随空白不含花括号, + # _count_braces_outside_strings 的结果逐字不变)。prof 实测 str.strip + # 1.79 亿次为最大 Python 侧热点,逐行 strip 是其主要来源。 + if in_global_def: + text_lines.append(line) + global_type_parts.append(line.strip()) + # 预筛:无花括号的行必然贡献 0(与计数器返回值逐字等价,见其快路径) + if '{' in line or '}' in line: + global_brace_depth += _count_braces_outside_strings(line) + if global_brace_depth <= 0: + full_type = ' '.join(global_type_parts).strip() + depth = 0 + type_end = len(full_type) + for ci, cc in enumerate(full_type): + if cc in ('{', '['): + depth += 1 + elif cc in ('}', ']'): + depth -= 1 + elif depth == 0 and cc == ' ': + type_end = ci + break + var_type = full_type[:type_end].strip() + if var_type: + stub_lines.append(f'{global_var_name} = external global {var_type}\n') + in_global_def = False + global_var_name = None + global_type_parts = [] + global_brace_depth = 0 + continue + + if in_define: + text_lines.append(line) + # 预筛:函数体指令行绝大多数不含花括号,跳过即省掉一次函数调用 + # (prof:本函数 102k 次调用;有花括号的行仍按原语义计数) + if '{' in line or '}' in line: + brace_depth += _count_braces_outside_strings(line) + if brace_depth <= 0: + in_define = False + continue + + stripped = line.strip() + if stripped.startswith('define '): + text_lines.append(line) + in_define = True + brace_depth = _count_braces_outside_strings(stripped) + decl_line = _RE_DEFINE_PREFIX.sub('declare ', stripped) + decl_line = _RE_DEFINE_LINKAGE.sub('', decl_line) + decl_line = _RE_ALWAYSINLINE.sub('', decl_line) + paren_pos = decl_line.rfind(')') + if paren_pos >= 0: + decl_line = decl_line[:paren_pos + 1] + stub_lines.append(decl_line + '\n') + continue + + if stripped == '{' and text_lines and not in_define: + prev_stripped = text_lines[-1].strip() if text_lines else '' + if prev_stripped.startswith('define ') or prev_stripped.endswith('noredzone') or prev_stripped.endswith(')'): + text_lines.append(line) + brace_depth = 1 + in_define = True + if prev_stripped.startswith('define '): + # 与上面 define 分支同一组预编译正则(模式串一字未改), + # 避免 re 模块级的"模式串哈希→缓存查找"逐行开销 + decl_line = _RE_DEFINE_PREFIX.sub('declare ', prev_stripped) + decl_line = _RE_DEFINE_LINKAGE.sub('', decl_line) + decl_line = _RE_ALWAYSINLINE.sub('', decl_line) + paren_pos = decl_line.rfind(')') + if paren_pos >= 0: + decl_line = decl_line[:paren_pos + 1] + stub_lines.append(decl_line + '\n') + continue + + if stripped.startswith('@') and ' global ' in stripped and 'external global' not in stripped: + text_lines.append(line) + # LLVM 保留全局(如 --hotspot-sites 的 @llvm.global_ctors;名字含点故 + # 最终 IR 保留引号)是 appending 定义,禁止改写为 "external global" + # 存根(否则 .ll 重组后 llc 报错) + if _RE_LLVM_INTRINSIC.match(stripped): + continue + if ' internal ' not in stripped and ' private ' not in stripped: + name_match = _RE_AT_NAME.match(stripped) + if name_match: + var_name = name_match.group(1) + global_pos = stripped.find(' global ') + after_global = stripped[global_pos + 8:] + depth = 0 + type_end = len(after_global) + for ci, cc in enumerate(after_global): + if cc in ('{', '['): + depth += 1 + elif cc in ('}', ']'): + depth -= 1 + elif depth == 0 and cc == ' ': + type_end = ci + break + var_type = after_global[:type_end].strip() + brace_depth_gv = 0 + for cc in var_type: + if cc == '{': + brace_depth_gv += 1 + elif cc == '}': + brace_depth_gv -= 1 + if brace_depth_gv > 0: + in_global_def = True + global_var_name = var_name + global_type_parts = [var_type] + global_brace_depth = brace_depth_gv + elif var_type: + stub_lines.append(f'{var_name} = external global {var_type}\n') + continue + if stripped.startswith('@') and ' constant ' in stripped: + text_lines.append(line) + continue + + if stripped.startswith('declare'): + dm = _RE_DECLARE_NAME.match(stripped) + if dm: + fname = dm.group(1) + if fname in defined_names: + stub_lines.append(line) + continue + dot_pos = fname.find('.') + if dot_pos > 0: + fname_sha1 = fname[:dot_pos] + if module_sha1 and fname_sha1 != module_sha1 and len(fname_sha1) >= 16: + continue + if _RE_SHA1_16_DOT.match(fname): + continue + stub_lines.append(line) + continue + + stub_lines.append(line) + + return ''.join(stub_lines), ''.join(text_lines) + + def _save_deps(self, sha1: str, ModuleSha1Map: dict[str, str]) -> None: + """保存依赖指纹到 .deps.json""" + deps_path = os.path.join(self.output_dir, f"{sha1}.deps.json") + deps = {} + for mod_name, dep_sha1 in ModuleSha1Map.items(): + deps[mod_name] = dep_sha1 + with open(deps_path, 'w', encoding='utf-8') as f: + json.dump(deps, f) + _InvalidateTextCache(deps_path) + + def _LoadDeps(self, sha1: str) -> dict[str, str] | None: + """加载依赖指纹""" + deps_path = os.path.join(self.output_dir, f"{sha1}.deps.json") + if _CachedIsFile(deps_path): + try: + return json.loads(_ReadTextCached(deps_path)) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载依赖指纹 JSON 失败: {_e}", "Exception") + return None + + def _check_deps_changed(self, sha1: str, current_ModuleSha1Map: dict[str, str]) -> bool: + """检查依赖指纹是否变化""" + saved_deps = self._LoadDeps(sha1) + if saved_deps is None: + return True + for mod_name, dep_sha1 in current_ModuleSha1Map.items(): + if saved_deps.get(mod_name) != dep_sha1: + return True + for mod_name, dep_sha1 in saved_deps.items(): + if current_ModuleSha1Map.get(mod_name) != dep_sha1: + return True + return False + + def _recombine_ll(self, sha1: str, current_ModuleSha1Map: dict[str, str]) -> bool: + """依赖变化时重新组合 .ll:更新 .stub.ll 中的 SHA1 前缀,拼接 .stub.ll + .text.ll""" + saved_deps = self._LoadDeps(sha1) + if saved_deps is None: + return False + + stub_path = os.path.join(self.output_dir, f"{sha1}.stub.ll") + text_path = os.path.join(self.output_dir, f"{sha1}.text.ll") + ll_path = os.path.join(self.output_dir, f"{sha1}.ll") + + if not _CachedIsFile(stub_path) or not _CachedIsFile(text_path): + return False + + stub_content = _ReadTextCached(stub_path) + text_content = _ReadTextCached(text_path) + + sha1_replacements = {} + for mod_name, old_sha1 in saved_deps.items(): + new_sha1 = current_ModuleSha1Map.get(mod_name) + if new_sha1 and old_sha1 != new_sha1: + sha1_replacements[old_sha1] = new_sha1 + + if sha1_replacements: + for old_sha1, new_sha1 in sha1_replacements.items(): + stub_content = stub_content.replace(old_sha1, new_sha1) + text_content = text_content.replace(old_sha1, new_sha1) + + combined = stub_content + if not combined.endswith('\n'): + combined += '\n' + combined += text_content + + with open(ll_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(combined) + with open(stub_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + with open(text_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(text_content) + _InvalidateTextCache(ll_path) + _InvalidateTextCache(stub_path) + _InvalidateTextCache(text_path) + + self._save_deps(sha1, current_ModuleSha1Map) + + obj_path = os.path.join(self.output_dir, f"{sha1}.obj") + if _CachedIsFile(obj_path): + os.remove(obj_path) + + return True + + def _inject_auto_imports(self, code: str, src_path: str) -> str: + """检测源码中对 list/dict 容器的使用,自动注入 import _list / import _dict。 + 当使用 dict 时,同时注入 import json (用于 dict 的 JSON 处理功能)。 + 检测到 assert 语句时自动注入 import testcheck(assert 译为 + testcheck.check(...),需该模块的跨模块符号声明)。 + + 检测模式: + - list[T] / dict[V] 作为类型注解 (ast.Subscript, value=Name(id='list'/'dict')) + - list[T](...) / dict[V](...) 作为构造函数调用 (ast.Call, func=Subscript) + - list(...) / dict(...) 作为构造函数调用 (ast.Call, func=Name) + - assert 语句 (ast.Assert) + """ + try: + # `.vp` 源码须先过 Viper 软关键字层(`enum X:` / `fn` 等)再解析: + # 否则此处 SyntaxError 会静默 return 原 code(自动注入 import _list/_dict + # 失效),与主翻译路径(Shell/TransPyC 已展开)口径不一致。 + tree = ast.parse(ExpandSoftKeywords(code), src_path) + except SyntaxError: + return code + + # 检查是否已经导入了 _list / _dict / json / testcheck + has_list_import: bool = False + has_dict_import: bool = False + has_json_import: bool = False + has_testcheck_import: bool = False + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name == '_list': + has_list_import = True + elif alias.name == '_dict': + has_dict_import = True + elif alias.name == 'json': + has_json_import = True + elif alias.name == 'testcheck': + has_testcheck_import = True + elif isinstance(node, ast.ImportFrom): + if node.module == '_list': + has_list_import = True + elif node.module == '_dict': + has_dict_import = True + elif node.module == 'json': + has_json_import = True + elif node.module == 'testcheck': + has_testcheck_import = True + if has_list_import and has_dict_import and has_json_import and has_testcheck_import: + break + + # 检查是否使用了 list / dict 容器,以及是否含 assert 语句 + uses_list: bool = False + uses_dict: bool = False + uses_assert: bool = False + for node in ast.walk(tree): + if isinstance(node, ast.Assert): + uses_assert = True + if isinstance(node, ast.Subscript): + if isinstance(node.value, ast.Name): + if node.value.id == 'list': + uses_list = True + elif node.value.id == 'dict': + uses_dict = True + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name): + if node.func.id == 'list': + uses_list = True + elif node.func.id == 'dict': + uses_dict = True + # 检测类型注解 d: dict / l: list (非泛型形式) + if isinstance(node, ast.AnnAssign) and isinstance(node.annotation, ast.Name): + if node.annotation.id == 'dict': + uses_dict = True + elif node.annotation.id == 'list': + uses_list = True + + # 注入 import 到源码开头 + prefix: str = '' + if uses_list and not has_list_import: + prefix += 'import _list\n' + if uses_dict and not has_dict_import: + prefix += 'import _dict\n' + if uses_dict and not has_json_import: + prefix += 'import json\n' + # 含 assert 的模块等价于在文件顶部写下 import testcheck(已导入则不重复) + if uses_assert and not has_testcheck_import: + prefix += 'import testcheck\n' + if prefix: + return prefix + code + return code + + def _translate_file(self, src_path: str, out_path: str, prebuilt_ModuleSha1Map: dict[str, str] | None = None, dwarf_enabled: bool = False) -> None: + """翻译单个源文件,嵌入相关 .stub.ll 声明 + + dwarf_enabled: 函数级 DWARF 调试元数据(--dwarf),默认 False(零影响)。 + App 源模块在 --dwarf/--asan/--traceback 时传 True。include 文件默认 + 传 False(其 .obj 会落进跨项目共享的 includes.binary 预编译缓存,注入 DI + 会污染该缓存);仅 --traceback 时传 True——此时 include 的 .obj 改落 + 隔离目录(includes.binary_traceback,见 _get_precompiled_obj_path), + 不会污染共享缓存。 + """ + _t0: float = time.time() + # 每个文件翻译入口置脏:本文件/其它文件在此之间新写的 *.stub.ll 全部可见 + # (ImportHandle._build_stub_index 的零 syscall 快路径据此判断是否重扫目录; + # 顺序模式与 worker 模式的每文件粒度皆覆盖,见 MarkStubSetDirty)。 + from lib.core.Handles.HandlesImports import ImportHandle as _IH + _IH.MarkStubSetDirty() + code = _ReadTextCached(src_path) + + # SHA1 以主流程 out_path 文件名为准(= 主流程 compute_sha1(parse_python_file 内容)), + # 必须在自动导入注入之前确定。不可在此重读文件重算:主流程收集(need_translate) + # 与本次实际翻译之间若文件内容变化(如编辑器保存),重算会得到不同 sha1, + # 导致函数定义前缀 ≠ 文件名/调用方引用前缀 → 链接 undefined symbol + # (实例:696d41ca86e0a29a.BuildTopoSortedIndices 定义被生成成 5536… 前缀)。 + sha1 = os.path.splitext(os.path.basename(out_path))[0] + cur_sha1 = compute_sha1(code) + if cur_sha1 != sha1: + _vlog().warning( + f"模块内容在收集后已变化(文件名sha1={sha1}, 当前sha1={cur_sha1}): {src_path}," + f"仍按主流程 sha1 生成前缀以保证链接一致;下次构建将采用新 sha1", "SHA1") + + # 自动导入: 检测 list 容器使用,自动注入 import _list + # 注入行会让 AST 节点 lineno 整体下移 N(磁盘真实行号 = AST 行号 - N); + # 记下 N 供报错渲染还原磁盘行号(如 _RaiseUnresolvedExpr)。 + _raw_newlines: int = code.count('\n') + code = self._inject_auto_imports(code, src_path) + _line_offset: int = code.count('\n') - _raw_newlines + + if prebuilt_ModuleSha1Map is not None: + ModuleSha1Map = prebuilt_ModuleSha1Map + else: + ModuleSha1Map = self._build_current_ModuleSha1Map(sha1) + + trans = TransPyC.TransPyC(code=code, triple=self.triple, datalayout=self.datalayout) + trans.translator.CurrentFile = src_path + # 函数级 DWARF(--dwarf):透传给 Translator,由其传给每个新 LlvmCodeGenerator; + # 未启用时不设置 → 与改动前逐字节一致(零影响) + if dwarf_enabled: + trans.translator.DwarfEnabled = True + # Ctrl+C 调用栈(--traceback):仅当本文件就是 traceback 模块时置位—— + # 只有该模块需要注入 @llvm.global_ctors 构造器调用自己的 Install() + # (见 BaseGen._RegisterTracebackInstallViaCtor)。其它模块不设置该属性 + # → Gen.traceback_ctor 保持 False,IR 与改动前逐字节一致(零影响)。 + _traceback_ctor: bool = bool( + self.traceback_enabled + and os.path.basename(src_path) in _TRACEBACK_MODULE_FILES) + if _traceback_ctor: + trans.translator.TracebackCtor = True + trans.SliceLevel = self.slice_level + trans.translator.SliceLevel = self.slice_level + trans.translator.SliceCount = 0 + trans.translator.SliceInfos = [] + trans.translator.LlvmGen = None + trans.translator._module_sha1 = sha1 + trans.translator._line_offset = _line_offset + trans.translator._ModuleSha1Map = ModuleSha1Map + trans.translator._struct_sha1_map = self.struct_sha1_map + trans.translator._global_function_default_args = self.function_default_args + trans.translator._global_function_param_names = self.function_param_names + trans.translator._temp_dir = self.temp_dir + # includes 只认 project.vpj 配置的 include 目录(不再回退仓库 TransPyCRoot/includes): + # 目录重组/缺失时避免声明解析从仓库或 sig 回退路径产生错误时序(memhub 未注册→i32) + _proj_inc = [os.path.abspath(d) for d in self.include_dirs if os.path.isdir(d)] + trans.translator.LibraryPaths = _proj_inc if _proj_inc else ['.'] + trans.translator._shared_doc_meta = self._shared_doc_meta + # 传递共享 vtable 状态 + trans.translator._shared_class_vtable = self._shared_class_vtable + trans.translator._shared_cross_module_vtable = self._shared_cross_module_vtable + trans.translator._shared_class_methods = self._shared_class_methods + trans.translator._shared_cross_module_novtable = self._shared_cross_module_novtable + + # 调用点热点统计与每函数计时(--hotspot-sites,Projectrans 多模块): + # 各模块处理器统一以追加方式写最终 ,注册策略为本模块 + # @llvm.global_ctors 自注册(见 BaseGen._InstallHotspotSitesModuleLocal); + # self.hotspot_sites_file 为 None 时不设置任何属性 → 与未启用时逐字节一致。 + if self.hotspot_sites_file: + trans.translator.HotspotSitesFile = self.hotspot_sites_file + trans.translator.HotspotSitesAll = self.hotspot_sites_all + trans.translator.HotspotCallees = self.hotspot_callees + trans.translator.HotspotSitesModuleLocal = True + + if self._shared_symbol_table is not None: + trans.translator.SymbolTable = self._shared_symbol_table + # 确保共享符号表的 translator 指向当前 translator,以便 import 别名解析正确 + if hasattr(self._shared_symbol_table, 'translator'): + self._shared_symbol_table.translator = trans.translator + trans.translator._source_module_sig_files = self._shared_source_module_sig_files + trans.translator._all_define_constants = self._shared_all_dc + trans.translator._define_decl_types = self._shared_all_decl_types + trans.translator._export_extern_funcs = self._shared_export_extern_funcs + if self._shared_generic_class_templates: + if not hasattr(trans.translator.ClassHandler, '_generic_class_templates'): + trans.translator.ClassHandler._generic_class_templates = {} + trans.translator.ClassHandler._generic_class_templates.update(self._shared_generic_class_templates) + else: + export_extern_funcs = {} + for sha1_key, sig_path in self.sig_files.items(): + if sha1_key == sha1: + continue + try: + sig_content = _ReadTextCached(sig_path) + sig_tree = ast.parse(sig_content, sig_path) + for node in ast.iter_child_nodes(sig_tree): + if isinstance(node, ast.FunctionDef): + if _check_annotation_for_export(node.returns): + export_extern_funcs[node.name] = sha1_key + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集导出外部函数声明失败: {_e}", "Exception") + if export_extern_funcs: + trans.translator._export_extern_funcs = export_extern_funcs + + for includes_dir in self.include_dirs: + if os.path.isdir(includes_dir): + for pyi_file in os.listdir(includes_dir): + if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'): + pyi_path = os.path.join(includes_dir, pyi_file) + module_name = os.path.splitext(pyi_file)[0] + trans.translator.SymbolTable.LoadModuleSymbols(pyi_path, module_name, lineno=0) + for py_file in os.listdir(includes_dir): + if (py_file.endswith('.vp') or py_file.endswith('.py')) and (not py_file.startswith('_') or py_file == '_list.vp' or py_file == '_list.py' or py_file == '_dict.vp' or py_file == '_dict.py'): + module_name = os.path.splitext(py_file)[0] + sha1_key = self.include_py_map.get(module_name) + if sha1_key and sha1_key in self.sig_files: + sig_path = self.sig_files[sha1_key] + trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) + # 收集需要重导出的包,等所有模块符号加载完后再处理 + _pending_reexports = [] + for sha1_key, sig_path in self.sig_files.items(): + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + mod_name = mod_name[len('includes/'):] + trans.translator.SymbolTable.LoadModuleSymbols(sig_path, mod_name, lineno=0) + if mod_name.endswith('.__init__'): + package_name = mod_name[:-len('.__init__')] + _pending_reexports.append((sig_path, package_name)) + + # 所有 includes 模块符号已加载,现在处理包重导出 + for sig_path, package_name in _pending_reexports: + self._register_package_reexports(trans, sig_path, package_name) + + for sha1_key, sig_path in self.sig_files.items(): + if sha1_key == sha1: + continue + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + continue + module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + trans.translator.SymbolTable.LoadModuleSymbols(sig_path, module_name, lineno=0) + trans.translator._source_module_sig_files[module_name] = sig_path + short_name = module_name.split('.')[-1] if '.' in module_name else module_name + if short_name != module_name: + trans.translator._source_module_sig_files[short_name] = sig_path + + all_dc = {} + all_dt: dict[str, tuple[int, bool]] = {} + for sha1_key, stub_path in self.stub_files.items(): + if sha1_key == sha1: + continue + if os.path.exists(stub_path): + try: + stub_content = _ReadTextCached(stub_path) + for line in stub_content.splitlines(): + line = line.strip() + if line.startswith('@') and '= global' in line and 'i32' in line and not line.startswith('@llvm'): + var_name = line.split('=')[0].strip().lstrip('@') + val_part = line.split('i32')[-1].strip().rstrip(']').lstrip('[').lstrip('i32') + try: + val = int(val_part) + all_dc[var_name] = val + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + for sha1_key, pyi_path in self.sig_files.items(): + if sha1_key == sha1: + continue + _ExtractCDefineConstantsFromPyi(pyi_path, all_dc, trans.translator.SymbolTable, all_dt) + if all_dc: + trans.translator._all_define_constants = all_dc + if all_dt: + trans.translator._define_decl_types = all_dt + + for sym_name, sym_info in self.inline_func_symbols.items(): + if sym_name not in trans.translator.SymbolTable: + trans.translator.SymbolTable[sym_name] = sym_info + else: + existing = trans.translator.SymbolTable[sym_name] + if not getattr(existing, 'InlineBody', None) and getattr(sym_info, 'InlineBody', None): + existing.IsInline = sym_info.IsInline + existing.InlineBody = sym_info.InlineBody + existing.InlineParams = sym_info.InlineParams + + try: + result = trans.Convert( + OutputFilename=out_path, + SourceFilename=src_path, + target='llvm' + ) + except Exception as e: + error_stack = getattr(trans.translator, '_ErrorStack', []) + if error_stack: + self._last_ErrorStack = error_stack + # 语句栈渲染:error_stack 按"由内向外"入栈(内层语句先 append), + # 这里反转为"外层 → 内层"的调用路径;**异常消息只在最内层印一次**, + # 其余帧只印位置——否则同一段长 fail-fast 报文会被每个帧重复一遍 + # (旧格式把消息贴在每一帧后面,输出里同一句话出现 N 次,不可读)。 + entries: list = [entry for entry in error_stack if len(entry) >= 3] + chain_lines: list[str] = [] + for chain_idx, entry in enumerate(reversed(entries)): + exc_msg, line_info = entry[1], entry[2] + if chain_idx == len(entries) - 1: + chain_lines.append(" %s\n %s" % (line_info, exc_msg)) + else: + chain_lines.append(" %s" % (line_info,)) + if chain_lines: + enriched = str(e) + "\n" + "\n".join(chain_lines) + raise type(e)(enriched) from e + node_info = "" + if hasattr(trans.translator, 'LlvmGen') and trans.translator.LlvmGen: + node_info = trans.translator.LlvmGen._get_node_info() + if node_info: + enriched = "%s\n %s" % (str(e), node_info.strip()) + raise type(e)(enriched) from e + raise + + if result and isinstance(result, str): + for func_name, func_def in trans.translator.FunctionDefCache.items(): + if hasattr(func_def, 'args'): + mangled = trans.translator.LlvmGen._mangle_func_name(func_name) if trans.translator.LlvmGen else func_name + # 提取参数名 + if hasattr(func_def.args, 'args'): + param_names = [arg.arg for arg in func_def.args.args] + self.function_param_names[mangled] = param_names + self.function_param_names[func_name] = param_names + # 提取默认值 + if hasattr(func_def.args, 'defaults') and func_def.args.defaults: + defaults = [] + for d in func_def.args.defaults: + if isinstance(d, ast.Constant): + defaults.append(d.value) + else: + defaults.append(None) + self.function_default_args[mangled] = defaults + self.function_default_args[func_name] = defaults + for sym_name, sym_info in trans.translator.SymbolTable.items(): + if isinstance(sym_info, CTypeInfo) and getattr(sym_info, 'IsInline', False) and getattr(sym_info, 'InlineBody', None): + self.inline_func_symbols[sym_name] = sym_info + + out_dir = os.path.dirname(out_path) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + # --traceback:核对注入钩子确实生效——本模块要求注入 @llvm.global_ctors + # 构造器(StructGen.finalize 里落成 .ll 文本之前),若产物中没有该全局 + # 说明钩子不可用(如序列化路径被改动绕过),显式报错终止,不静默产出 + # 缺注册的产物(那会让整个功能静默失效)。 + if _traceback_ctor and 'llvm.global_ctors' not in result: + raise RuntimeError( + '--traceback: traceback 模块未注入 @llvm.global_ctors ' + '(模块级收尾钩子未生效,不静默降级)') + # 修复 LLVM 22+: external global 不能有初值,改为 linkonce + result = _RE_EXTERNAL_GLOBAL_VALUE.sub(r'\1 = linkonce global \2 \3', result) + # 后处理:添加 llvmlite 不支持的 LLVM 属性(如 willreturn, mustprogress) + pending_str_attrs = getattr(trans.translator.LlvmGen, '_pending_str_attrs', None) + if pending_str_attrs: + for func_name, attrs in pending_str_attrs.items(): + attrs_str = ' '.join(attrs) + # 匹配 define ... @func_name(...) ... { 并在 { 之前添加属性 + pattern = rf'(define\s+[^@]*@{re.escape(func_name)}\s*\([^)]*\))([^{{]*)(\{{)' + result, n = re.subn(pattern, rf'\1\2 {attrs_str}\3', result, count=1) + if n == 0: + _vlog().warning(f"后处理添加属性失败: 未找到函数 {func_name} 的定义") + with open(out_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(result) + _InvalidateTextCache(out_path) + + stub_content, text_content = self._split_ll(result) + base = os.path.splitext(out_path)[0] + with open(base + '.stub.ll', 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + _InvalidateTextCache(base + '.stub.ll') + # 新 stub 已落盘 → 置脏,使本文件后续(及下个文件)的索引 miss 能重扫到它 + from lib.core.Handles.HandlesImports import ImportHandle as _IH2 + _IH2.MarkStubSetDirty() + with open(base + '.text.ll', 'w', encoding='utf-8', newline='\n') as f: + f.write(text_content) + _InvalidateTextCache(base + '.text.ll') + self._save_deps(sha1, ModuleSha1Map) + + # 进度日志由调用方统一打印(主进程按文件粒度 [x/total]),此处不再重复输出。 + else: + stub_path = self.stub_files.get(sha1) + if stub_path and os.path.exists(stub_path): + out_dir = os.path.dirname(out_path) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + stub_content = _ReadTextCached(stub_path) + with open(out_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + # 进度日志由调用方统一打印。 + + def _register_package_reexports(self, trans, init_pyi_path: str, package_name: str) -> None: + """解析 __init__.pyi 中的 from .xxx import yyy 或 from __xxx import yyy,在包级别注册导出符号""" + try: + # 复用 parse_python_file 的 AST 缓存 + _, tree = parse_python_file(init_pyi_path) + if tree is None: + return + for node in ast.walk(tree): + if isinstance(node, ast.ImportFrom) and node.module: + sub_module = node.module.lstrip('.') + for alias in node.names: + symbol_name = alias.name + exported_name = alias.asname if alias.asname else symbol_name + source_keys = [ + f"{package_name}.{sub_module}.{symbol_name}", + f"{package_name}.__{sub_module}.{symbol_name}" if not sub_module.startswith('__') else None, + symbol_name, + ] + target_key = f"{package_name}.{exported_name}" + if target_key not in trans.translator.SymbolTable: + for source_key in source_keys: + if source_key and source_key in trans.translator.SymbolTable: + trans.translator.SymbolTable[target_key] = trans.translator.SymbolTable[source_key] + break + if exported_name not in trans.translator.SymbolTable: + for source_key in source_keys: + if source_key and source_key in trans.translator.SymbolTable: + trans.translator.SymbolTable[exported_name] = trans.translator.SymbolTable[source_key] + break + except Exception as e: + _vlog().warning(f" [警告] 解析包重导出失败 {init_pyi_path}: {e}") + + def _SanitizeIRFile(self, ll_path: str) -> str: + """送入 opt/llc 之前,对该 .ll 做一次元数据规整(见 IRMetadataFix)。 + + App 模块的 IR 是「本模块 IR + 各依赖 .stub.ll 片段」直接拼接而成,而 + --dwarf(--asan 自动开启)给每份片段都加了从 !0 开始编号的 DI 元数据, + 合并文件里同一个 !N 被定义多次 → opt/llc 报 "Metadata id is already + used"、命名元数据 "defined more than once"。规整后编号全局唯一、 + 命名元数据只留一份。无元数据 / 已规整的文件逐字节不变(no-op), + 原地覆盖没有额外产物。 + """ + with open(ll_path, 'r', encoding='utf-8', newline='') as f: + text = f.read() + fixed = SanitizeIRMetadata(text) + if fixed != text: + with open(ll_path, 'w', encoding='utf-8', newline='') as f: + f.write(fixed) + _InvalidateTextCache(ll_path) + return ll_path + + def _llc_compile(self, ll_path: str, obj_path: str, + cwd: str | None = None) -> subprocess.CompletedProcess: + """编译单个 .ll 为 obj。 + + ASan 模式(--asan)下先对 IR 执行 opt 插桩: + 检测 stdlib 堆/栈/全局越界写与垃圾指针解引用(MemBuddy pool 内部 + 由块尾 canary 覆盖,两者互补)。opt 失败返回其结果(returncode!=0)。 + + LLVM >= 20 起 opt -passes=asan 只对带 sanitize_address 函数属性的 + 函数插桩(clang -fsanitize=address 自动加;手写 IR 没有)—— + 先 forceattrs 批量加属性再 asan,否则只链接 runtime 不插桩。 + """ + ll_path = self._SanitizeIRFile(ll_path) + compile_input = ll_path + if self.asan_mode: + asan_ll = ll_path[:-3] + '_asan.ll' + # -asan-globals=false:TPC 对全局变量(如各模块 _g_gen)用直接地址寻址, + # ASan 全局 redzone 重定位后引用错位会导致读到垃圾指针(如 + # register_struct 的 self=0xbebebebe...)。先关闭全局插桩排除该因素。 + opt_cmd = ['opt', '-passes=forceattrs,asan', '-force-attribute=sanitize_address', + '-asan-globals=false', ll_path, '-o', asan_ll] + opt_r = subprocess.run(opt_cmd, capture_output=True, text=True, cwd=cwd) + if opt_r.returncode != 0: + _vlog().error(f" opt ASan 插桩失败: {opt_r.stderr.strip()}") + return opt_r + compile_input = asan_ll + cmd = [self.compile_cmd] + self.compile_flags + ['-o', obj_path, compile_input] + if self.traceback_enabled: + # --traceback:让 llc 保留帧指针(-frame-pointer=all)。dbghelp 主路径 + # 不依赖帧指针,但符号表不可用时要靠 RBP 链兜底——llc 默认 + # omit-frame-pointer 会让 RBP 链只走出 1~2 帧(实测"共 2 帧")。 + # 只在开启该开关时加,关闭时命令与改前逐字一致。 + cmd = [self.compile_cmd] + self.compile_flags \ + + ['-frame-pointer=all', '-o', obj_path, compile_input] + return subprocess.run(cmd, capture_output=True, text=True, cwd=cwd) + + def _compile_ll_files(self, active_sha1s: set[str]) -> None: + """将 output_dir 中属于 active_sha1s 的 .ll 文件编译为 .obj(跳过已编译过的 include 文件)""" + # 第一次 walk:清理 stale 文件(.obj/.deps.json/纯 .ll,排除 .stub.ll/.text.ll) + stale_files = [] + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.obj'): + sha1 = file[:-4] + if sha1 not in active_sha1s: + stale_files.append(os.path.join(root, file)) + elif file.endswith('.deps.json'): + sha1 = file[:-10] + if sha1 not in active_sha1s: + stale_files.append(os.path.join(root, file)) + elif file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): + sha1 = file[:-3] + if sha1 not in active_sha1s: + stale_files.append(os.path.join(root, file)) + if stale_files: + for fpath in stale_files: + try: + os.remove(fpath) + # 删除也是"本进程改变了该路径":不失效会让后续读拿到已删文件的旧文本 + _InvalidateTextCache(fpath) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + _vlog().info(f"[清理] 删除 {len(stale_files)} 个过时文件") + + # 第二次 walk:收集需要编译的 .ll 文件(排除 .stub.ll/.text.ll) + ll_files = [] + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): + sha1 = file[:-3] + if sha1 in active_sha1s: + fpath = os.path.join(root, file) + obj_path = os.path.join(root, file[:-3] + '.obj') + if not _CachedIsFile(obj_path): + ll_files.append(fpath) + if not ll_files: + _vlog().info("[编译] 无 .ll 文件需要重新编译") + else: + _vlog().info(f"\n[编译] 找到 {len(ll_files)} 个 .ll 文件需要编译") + + # 第三次 walk:收集 all_ll_files(用于 alwaysinline 检测,排除 include sha1s) + all_ll_files = [] + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.ll') and not file.endswith('.stub.ll') and not file.endswith('.text.ll'): + sha1 = file[:-3] + if sha1 in active_sha1s and sha1 not in self._include_sha1s: + all_ll_files.append(os.path.join(root, file)) + + has_inline = False + for ll_path in all_ll_files: + try: + content = _ReadTextCached(ll_path) + if 'alwaysinline' in content: + has_inline = True + break + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + + if has_inline: + llvm_link = shutil.which('llvm-link') or shutil.which('llvm-link.exe') + opt_cmd = shutil.which('opt') or shutil.which('opt.exe') + if llvm_link and opt_cmd: + _vlog().info(" [内联] 检测到 alwaysinline 函数,执行跨模块内联优化...") + for ll_path in all_ll_files: + try: + content = _ReadTextCached(ll_path) + type_defs = [] + type_def_indices = set() + for i, line in enumerate(content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_defs.append(line) + type_def_indices.add(i) + if type_def_indices: + lines = content.splitlines(True) + content_no_types = ''.join(line for i, line in enumerate(lines) if i not in type_def_indices) + header_end = 0 + for i, line in enumerate(content_no_types.splitlines()): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + lines2 = content_no_types.splitlines(True) + fixed = ''.join(lines2[:header_end]) + '\n'.join(type_defs) + '\n' + ''.join(lines2[header_end:]) + with open(ll_path, 'w', encoding='utf-8') as f: + f.write(fixed) + _InvalidateTextCache(ll_path) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + merged_ll = os.path.join(self.output_dir, '_merged.ll') + optimized_ll = os.path.join(self.output_dir, '_merged_opt.ll') + try: + link_cmd = [llvm_link] + all_ll_files + ['-o', merged_ll] + result = subprocess.run(link_cmd, capture_output=True, text=True) + if result.returncode != 0: + _vlog().warning(f" llvm-link 失败: {result.stderr.strip()},回退到单独编译") + has_inline = False + else: + opt_result_cmd = [opt_cmd, '-passes=always-inline', merged_ll, '-S', '-o', optimized_ll] + result = subprocess.run(opt_result_cmd, capture_output=True, text=True) + if result.returncode != 0: + _vlog().warning(f" opt 失败: {result.stderr.strip()},回退到单独编译") + has_inline = False + else: + _vlog().info(" [内联] 跨模块内联优化完成") + opt_content = _ReadTextCached(optimized_ll) + opt_content = _RE_DEFINE_DECLARE_LINKAGE.sub(r'\1 \2 dso_local ', opt_content) + opt_content = _RE_DEFINE_DECLARE_NO_LINKAGE_AT.sub(r'\1 dso_local ', opt_content) + # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 + opt_content = _RE_EXTERNAL_GLOBAL_VALUE_PLAIN.sub(r'\1 = linkonce global \2 \3', opt_content) + opt_content = _RE_GLOBAL_LINKAGE.sub(r'\1 = \2 dso_local \3', opt_content) + opt_content = _RE_GLOBAL_DSOLOCAL.sub(r'\1 = dso_local \2', opt_content) + opt_content = _AddWeakComdat(opt_content) + with open(optimized_ll, 'w', encoding='utf-8') as f: + f.write(opt_content) + merged_obj = os.path.join(self.output_dir, '_merged.obj') + try: + result = self._llc_compile(optimized_ll, merged_obj, self.output_dir) + if result.returncode == 0: + _vlog().success(f" 合并模块编译完成") + for ll_path in ll_files: + obj_path = ll_path[:-3] + '.obj' + sha1_obj = os.path.join(self.output_dir, os.path.basename(ll_path)[:-3] + '.obj') + if _CachedIsFile(sha1_obj): + os.remove(sha1_obj) + return + else: + _vlog().warning(f" 合并模块编译失败: {result.stderr.strip()},回退到单独编译") + has_inline = False + except Exception as e: + _vlog().warning(f" 合并模块编译异常: {e},回退到单独编译") + has_inline = False + except FileNotFoundError: + _vlog().warning(f" 找不到 llvm-link 或 opt,回退到单独编译") + has_inline = False + else: + _vlog().warning(" 未找到 llvm-link/opt 工具,回退到单独编译(alwaysinline 可能不生效)") + + def _compile_one(ll_path: str) -> tuple[str, str, str]: + rel = os.path.relpath(ll_path, self.output_dir) + obj_path = ll_path[:-3] + '.obj' + if _CachedIsFile(obj_path): + return (rel, 'cached', '') + try: + ll_content = _ReadTextCached(ll_path) + sha1 = os.path.basename(ll_path)[:-3] + deps = self._LoadDeps(sha1) + stub_prefix = '' + if deps: + # 去重:同一个全局变量如果同时有 external 声明和实际定义, + # 移除 external 声明,避免 "redefinition of global" 错误 + # 注意:此去重必须在收集现有符号之前执行,否则行号偏移会导致 + # lines_to_remove 误删其他行(如全局变量定义) + ll_lines_for_dedup = ll_content.splitlines() + external_globals: dict[str, int] = {} + non_external_globals: dict[str, int] = {} + for i, line in enumerate(ll_lines_for_dedup): + stripped = line.strip() + if stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): + gm = _RE_AT_NAME.match(stripped) + if gm: + gname = gm.group(1) + is_external = _RE_AT_EXTERNAL.match(stripped) is not None + if is_external: + external_globals[gname] = i + else: + non_external_globals[gname] = i + lines_to_remove_global_dedup: set[int] = set() + for gname in external_globals: + if gname in non_external_globals: + lines_to_remove_global_dedup.add(external_globals[gname]) + if lines_to_remove_global_dedup: + ll_content = '\n'.join( + line for i, line in enumerate(ll_lines_for_dedup) + if i not in lines_to_remove_global_dedup + ) + '\n' + existing_symbols: dict[str, str] = {} + opaque_line_indices: dict[str, int] = {} + existing_declares: set[str] = set() + existing_declare_lines: dict[str, set[int]] = {} + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + name = Phase2Translator._extract_llvm_type_name(stripped) + if name: + if '= type opaque' in stripped: + existing_symbols[name] = 'opaque' + opaque_line_indices[name] = i + else: + existing_symbols[name] = 'full' + elif stripped.startswith('declare'): + dm = _RE_DECLARE_NAME.match(stripped) + if dm: + fname = dm.group(1) + existing_declares.add(fname) + existing_declare_lines.setdefault(fname, set()).add(i) + seen_stub_sha1: set[str] = set() + replaced_opaque_names: set[str] = set() + replaced_declare_names: set[str] = set() + all_stub_lines: list[str | None] = [] + stub_type_map: dict[str, tuple[bool, int]] = {} + stub_declare_names: set[str] = set() + stub_declare_lines: dict[str, str] = {} + existing_defines: set[str] = set() + existing_global_defs: set[str] = set() + stub_global_defs: set[str] = set() + for line in ll_content.splitlines(): + stripped = line.strip() + if stripped.startswith('define'): + dm = _RE_DEFINE_NAME.match(stripped) + if dm: + existing_defines.add(dm.group(1)) + elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): + gm = _RE_AT_NAME.match(stripped) + if gm: + existing_global_defs.add(gm.group(1)) + for dep_name, dep_sha1 in deps.items(): + if dep_sha1 in seen_stub_sha1: + continue + seen_stub_sha1.add(dep_sha1) + stub_path = os.path.join(self.output_dir, f"{dep_sha1}.stub.ll") + if not _CachedIsFile(stub_path): + stub_path = os.path.join(self.temp_dir, f"{dep_sha1}.stub.ll") + if _CachedIsFile(stub_path): + stub_content = _ReadTextCached(stub_path) + for line in stub_content.splitlines(): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + continue + if stripped.startswith('declare'): + dm = _RE_DECLARE_NAME.match(stripped) + if dm: + fname = dm.group(1) + if 'zdef_alloc' in fname: + _vlog().debug(f"[DEBUG] stub declare for {fname}: existing_declares={fname in existing_declares}, stub_declare_names={fname in stub_declare_names}, has_struct={'%' in stripped}, existing={existing_declare_lines.get(fname)}") + if fname in existing_defines: + continue + if fname in existing_declares or fname in stub_declare_names: + existing_decl_line = stub_declare_lines.get(fname) + if existing_decl_line is not None: + has_struct = '%' in stripped + existing_has_struct = '%' in existing_decl_line if existing_decl_line else False + if has_struct and not existing_has_struct: + for i, l in enumerate(all_stub_lines): + if l is not None and l.strip().startswith('declare'): + em = _RE_DECLARE_NAME.match(l.strip()) + if em and em.group(1) == fname: + all_stub_lines[i] = line + break + continue + elif fname in existing_declares: + has_struct = '%' in stripped + if has_struct: + replaced_declare_names.add(fname) + stub_declare_names.add(fname) + stub_declare_lines[fname] = line + all_stub_lines.append(line) + continue + continue + stub_declare_names.add(fname) + stub_declare_lines[fname] = line + all_stub_lines.append(line) + continue + if stripped.startswith('%') and '= type' in stripped: + name = Phase2Translator._extract_llvm_type_name(stripped) + if name: + is_opaque = '= type opaque' in stripped + if name in existing_symbols: + if existing_symbols[name] == 'opaque' and not is_opaque: + replaced_opaque_names.add(name) + existing_symbols[name] = 'full' + idx = len(all_stub_lines) + all_stub_lines.append(line) + stub_type_map[name] = (is_opaque, idx) + continue + if name in stub_type_map: + prev_opaque, prev_idx = stub_type_map[name] + if prev_opaque and not is_opaque: + all_stub_lines[prev_idx] = None + idx = len(all_stub_lines) + all_stub_lines.append(line) + stub_type_map[name] = (is_opaque, idx) + continue + idx = len(all_stub_lines) + all_stub_lines.append(line) + stub_type_map[name] = (is_opaque, idx) + else: + all_stub_lines.append(line) + elif stripped.startswith('@') and (' global ' in stripped or ' constant ' in stripped): + gm = _RE_AT_NAME.match(stripped) + if gm and (gm.group(1) in existing_global_defs or gm.group(1) in stub_global_defs): + continue + if gm: + stub_global_defs.add(gm.group(1)) + all_stub_lines.append(line) + else: + all_stub_lines.append(line) + filtered = [l for l in all_stub_lines if l is not None] + if filtered: + stub_prefix = '\n'.join(filtered) + '\n' + lines_to_remove = set() + if replaced_opaque_names: + for name in replaced_opaque_names: + if name in opaque_line_indices: + lines_to_remove.add(opaque_line_indices[name]) + if replaced_declare_names: + _vlog().debug(f"[DEBUG] replaced_declare_names: {replaced_declare_names}") + for fname in replaced_declare_names: + if fname in existing_declare_lines: + lines_to_remove.update(existing_declare_lines[fname]) + _vlog().debug(f"[DEBUG] removing lines {existing_declare_lines[fname]} for {fname}") + else: + _vlog().debug(f"[DEBUG] fname {fname} not in existing_declare_lines: {list(existing_declare_lines.keys())}") + if lines_to_remove: + ll_lines = ll_content.splitlines(True) + ll_content = ''.join(line for i, line in enumerate(ll_lines) if i not in lines_to_remove) + type_defs_in_ll = [] + type_def_line_indices = set() + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_defs_in_ll.append(line) + type_def_line_indices.add(i) + if type_def_line_indices: + ll_lines = ll_content.splitlines(True) + ll_content = ''.join(line for i, line in enumerate(ll_lines) if i not in type_def_line_indices) + if type_defs_in_ll: + stub_prefix += '\n'.join(type_defs_in_ll) + '\n' + if stub_prefix: + header_end = 0 + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + ll_lines = ll_content.splitlines(True) + ll_content = ''.join(ll_lines[:header_end]) + stub_prefix + ''.join(ll_lines[header_end:]) + ll_content = _RE_DEFINE_DECLARE_LINKAGE.sub(r'\1 \2 dso_local ', ll_content) + ll_content = _RE_DEFINE_DECLARE_NO_LINKAGE_AT.sub(r'\1 dso_local ', ll_content) + # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 + ll_content = _RE_EXTERNAL_GLOBAL_VALUE.sub(r'\1 = linkonce global \2 \3', ll_content) + ll_content = _RE_GLOBAL_LINKAGE_MULTILINE.sub(r'\1 = \2 dso_local \3', ll_content) + ll_content = _RE_GLOBAL_DSOLOCAL_MULTILINE.sub(r'\1 = dso_local \2', ll_content) + ll_content = _AddWeakComdat(ll_content) + # LLVM 22+: 移除同一模块中已有 define 的 declare 语句(LLVM 22 将 declare+define 视为重复定义) + defined_funcs: set[str] = set() + for m in _RE_DEFINE_HEAD.finditer(ll_content): + defined_funcs.add(m.group(1)) + if defined_funcs: + lines: list[str] = ll_content.splitlines(True) + kept_lines: list[str] = [] + for line in lines: + m = _RE_DECLARE_FULL.match(line) + if m and m.group(1) in defined_funcs: + continue + kept_lines.append(line) + ll_content = ''.join(kept_lines) + # 移除重复的 declare 语句(同一函数多次 declare 但没有 define) + seen_declares: set[str] = set() + dedup_lines: list[str] = ll_content.splitlines(True) + dedup_kept: list[str] = [] + for line in dedup_lines: + dm = _RE_DECLARE_FULL.match(line) + if dm: + fname = dm.group(1) + if fname in seen_declares: + continue + seen_declares.add(fname) + dedup_kept.append(line) + ll_content = ''.join(dedup_kept) + final_type_defs = [] + final_type_def_indices = set() + seen_type_names_main = set() + for i, line in enumerate(ll_content.splitlines()): + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_name_main = stripped.split('=')[0].strip() + final_type_def_indices.add(i) + if type_name_main in seen_type_names_main: + continue + seen_type_names_main.add(type_name_main) + final_type_defs.append(line) + if final_type_def_indices: + final_lines = ll_content.splitlines(True) + ll_content_no_types = ''.join(line for i, line in enumerate(final_lines) if i not in final_type_def_indices) + header_end = 0 + for i, line in enumerate(ll_content_no_types.splitlines()): + stripped = line.strip() + if stripped.startswith(';') or stripped.startswith('target ') or stripped.startswith('source_filename') or not stripped: + header_end = i + 1 + continue + break + final_lines2 = ll_content_no_types.splitlines(True) + ll_content = ''.join(final_lines2[:header_end]) + '\n'.join(final_type_defs) + '\n' + ''.join(final_lines2[header_end:]) + # 写盘前先规整元数据:本文件的 IR 是「本模块 + 各依赖 .stub.ll/ + # .text.ll 片段」拼接而成,每份片段都自带从 !0 起编号的 DI 元数据, + # 未规整直接落盘的话,任何读该文件的 opt/llc(含另一并发构建进程) + # 都会报 "Metadata id is already used"。规整后盘上产物本身即合法 IR, + # _llc_compile 内的规整随之成为幂等 no-op。 + ll_content = SanitizeIRMetadata(ll_content) + # 拼接片段各自带一份同名 comdat 声明 ⇒ 落盘前统一去重: + # llc 对同一 comdat 声明出现两次即报 redefinition of comdat + ll_content = _DedupComdatDecls(ll_content) + dso_ll_path = ll_path[:-3] + '_dso.ll' + with open(dso_ll_path, 'w', encoding='utf-8') as f: + f.write(ll_content) + result = self._llc_compile(dso_ll_path, obj_path, os.path.dirname(ll_path) or '.') + if result.returncode == 0: + return (rel, 'ok', '') + else: + return (rel, 'error', result.stderr.strip()) + except FileNotFoundError: + return (rel, 'error', f'找不到编译器: {self.compile_cmd}') + except Exception as e: + return (rel, 'error', str(e)) + + need_compile = [] + for ll_path in ll_files: + obj_path = ll_path[:-3] + '.obj' + if not _CachedIsFile(obj_path): + need_compile.append(ll_path) + + if need_compile: + n_workers = _PickWorkerCount(len(need_compile), 400) + _vlog().info(f" 并行编译 {len(need_compile)} 个文件 (workers={n_workers})") + errors = [] + _compile_total = len(need_compile) + _compile_done = 0 + with ThreadPoolExecutor(max_workers=n_workers) as executor: + futures = {} + for ll_path in need_compile: + futures[executor.submit(_compile_one, ll_path)] = (ll_path, time.time()) + for future in as_completed(futures): + ll_path, _t_sub = futures[future] + rel, status, err = future.result() + _compile_done += 1 + _elapsed_c = time.time() - _t_sub + _sha1_c = os.path.basename(ll_path)[:-3] + _src_c = self.sha1_map.get(_sha1_c, '?') + if status == 'ok': + _vlog().success(f" [{_compile_done}/{_compile_total}] {os.path.basename(ll_path)} {_src_c} ({_elapsed_c:.1f}s)") + elif status == 'error': + _vlog().error(f" [{_compile_done}/{_compile_total}] {rel}: {err}") + errors.append((rel, err)) + elif status == 'cached': + pass + if errors: + _vlog().error(f"\n{len(errors)} 个文件编译失败") + sys.exit(1) + + def _precollect_vtable_state(self) -> None: + """预扫描 includes .py 文件,收集 @t.CVTable 类的虚表信息。 + + 并行编译(ProcessPoolExecutor)创建独立内存的 worker 进程, + vtable 状态必须预收集并通过 pickle 共享。 + 复用 parse_python_file 的 AST 缓存。 + """ + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for root, dirs, files in os.walk(includes_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if not (fname.endswith('.vp') or fname.endswith('.py')): + continue + py_path: str = os.path.join(root, fname) + code, tree = parse_python_file(py_path) + # 字符串预过滤:不含 CVTable 的文件跳过 AST 遍历。 + # `virtual class` / `virtual def` 修饰符在源码里写的是 `virtual` + # (AST 上才变成 @t.CVTable 装饰器),故预过滤必须同时放行 + # `virtual`/`novirtual`,否则含修饰符的 includes 类会被整段跳过—— + # 静默不预收集虚表状态(与本题"不得静默丢弃修饰符"同族问题)。 + if tree is None or ('CVTable' not in code and 'virtual' not in code): + continue + try: + for node in ast.iter_child_nodes(tree): + if not isinstance(node, ast.ClassDef): + continue + has_cvtable: bool = False + for dec in node.decorator_list: + if isinstance(dec, ast.Attribute): + if getattr(dec.value, 'id', None) == 't' and dec.attr == 'CVTable': + has_cvtable = True + elif isinstance(dec, ast.Name): + if dec.id == 'CVTable': + has_cvtable = True + # 方法级 virtual 触发类级 vtable(与 + # HandlesClassDef._resolve_class_flags 同口径:类级 + # NoVTable / 无真实父类两种可判定形态)。 + # 记录"类级 CVTable"单独一份:它是 vtable 槽筛选的 + # VtableByClassLevel(忽略方法级标记),两者不可混用。 + class_level_cvtable: bool = has_cvtable + if not has_cvtable: + _has_real_parent: bool = False + for _b in node.bases: + _bn: str = None + if hasattr(_b, 'id'): + _bn = _b.id + elif hasattr(_b, 'attr'): + _bn = _b.attr + if _bn and _bn not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): + _has_real_parent = True + break + if ClassHasFuncLevelCVTable(node) and not _has_real_parent: + has_cvtable = True + if not has_cvtable: + continue + class_name: str = node.name + has_methods: bool = False + if class_name not in self._shared_class_methods: + self._shared_class_methods[class_name] = [] + for item in node.body: + if isinstance(item, ast.FunctionDef): + has_methods = True + method_name: str = item.name + # 构造函数不放入 vtable(避免子类与基类 vtable 大小不一致破坏多态分派) + if method_name in ('__new__', '__init__', '__before_init__'): + continue + # 方法级修饰符:`novirtual def`(⇒ @t.NoVTable)不进槽; + # 判据唯一源 = SymbolUtils.IsVtableSlotMethod,与 + # HandlesClassDef / VPC _should_method_be_virtual 同口径。 + if not IsVtableSlotMethod(item, class_level_cvtable): + continue + if method_name == '__init__': + func_full_name: str = f"{class_name}.__init__" + elif method_name == '__call__': + func_full_name = f"{class_name}.__call__" + else: + func_full_name = f"{class_name}.{method_name}" + if func_full_name not in self._shared_class_methods[class_name]: + self._shared_class_methods[class_name].append(func_full_name) + if has_methods: + self._shared_class_vtable.add(class_name) + self._shared_cross_module_vtable.add(class_name) + except Exception: + pass + + def _build_include_py_index(self, includes_dir: str) -> set[str]: + """预扫描 includes 目录,构建所有 .py 文件路径集合 + + 用 set 查询替代 os.path.isfile,避免 3490 次磁盘 stat(83s→<0.1s)。 + 同时规范化路径为小写(Windows 不区分大小写)以加速查找。 + """ + py_index: set[str] = set() + for root, dirs, files in os.walk(includes_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.vp') or fname.endswith('.py'): + py_index.add(os.path.normcase(os.path.join(root, fname))) + return py_index + + def _scan_include_libraries(self) -> None: + """扫描 includes 目录,检测被使用的模块对应的库文件和源文件""" + # --traceback:强制纳入 traceback 模块 —— 无任何用户代码 import 它, + # 不纳入就不会被翻译 ⇒ 产物没有 Install 符号可注入启动期构造器。 + # 直接借用既有的 used_includes 收集路径(下面按同一套 py_index / + # 传递依赖逻辑把它加入 extra_py_files,再交给 _compile_include_py_files + # 编译并把 .obj 追加进 extra_link_files)。开关关闭时不触碰 used_includes + # → 本函数行为与改动前逐字一致(零影响)。 + if self.traceback_enabled: + self.used_includes.add('traceback') + if not self.used_includes: + return + + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + + # 预构建 .py 文件索引,避免递归发现时反复 os.path.isfile + py_index = self._build_include_py_index(includes_dir) + + for module_name in self.used_includes: + module_dir = os.path.join(includes_dir, module_name) + if os.path.isdir(module_dir): + self._scan_dir_for_libs(module_dir, module_name) + for root, dirs, files in os.walk(module_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.vp') or fname.endswith('.py'): + py_path = os.path.join(root, fname) + if py_path not in self.extra_py_files: + self.extra_py_files.append(py_path) + _vlog().info(f" [includes] 发现 Python 包文件: {os.path.relpath(py_path, includes_dir)}") + + for ext in self.INCLUDE_LIB_EXTENSIONS: + lib_path = os.path.join(includes_dir, module_name + ext) + if _CachedIsFile(lib_path): + self.extra_link_files.append(lib_path) + _vlog().info(f" [includes] 发现库文件: {os.path.relpath(lib_path, includes_dir)}") + + for ext in self.INCLUDE_SRC_EXTENSIONS: + src_path = os.path.join(includes_dir, module_name + ext) + if _CachedIsFile(src_path): + self.extra_compile_files.append(src_path) + _vlog().info(f" [includes] 发现源文件: {os.path.relpath(src_path, includes_dir)}") + + py_path = os.path.join(includes_dir, module_name + '.vp') + if os.path.normcase(py_path) not in py_index: + # 回退到 .py(向后兼容) + py_path = os.path.join(includes_dir, module_name + '.py') + if os.path.normcase(py_path) in py_index: + self.extra_py_files.append(py_path) + _vlog().info(f" [includes] 发现 Python 源文件: {os.path.relpath(py_path, includes_dir)}") + + discovered = set() + for ef in list(self.extra_py_files): + self._discover_transitive_deps(ef, includes_dir, discovered, py_index) + + if self.extra_link_files: + _vlog().info(f"\n[includes] 需要链接的库文件: {len(self.extra_link_files)} 个") + if self.extra_compile_files: + _vlog().info(f"[includes] 需要编译的源文件: {len(self.extra_compile_files)} 个") + # --traceback:断言 traceback 源文件确实被纳入待编译集合。找不到就必然 + # 得不到 Install 符号、构造器无处注入(功能静默失效)——显式报错终止, + # 不静默跳过(多半是 project.json 的 includes 未包含仓库的 includes 目录)。 + if self.traceback_enabled: + _traceback_found: bool = any( + os.path.basename(_p) in _TRACEBACK_MODULE_FILES + for _p in self.extra_py_files) + if not _traceback_found: + raise RuntimeError( + '--traceback: 在 includes 目录中找不到 traceback.vp/.py,' + '无法编译并链接 Install()(请确认 project.json 的 includes ' + f'包含仓库 includes 目录;已扫描: {self.include_dirs})') + + def _scan_dir_for_libs(self, directory: str, module_name: str) -> None: + """递归扫描目录中的库文件和源文件 + + 模块目录含 `.no_autobuild` 标记时直接跳过:该模块的 C/C++ 源码 + 与库文件由 project.vpj linker.static_libs 显式链接(如 fltk 内嵌 + 完整 FLTK/C 源码树,避免误编译整个第三方库)。递归时跳过常见 + 构建/缓存目录(build、cmake-build*、.git、__pycache__、. 开头)。 + """ + if _CachedIsFile(os.path.join(directory, '.no_autobuild')): + return + for root, dirs, files in os.walk(directory): + dirs[:] = [ + d for d in dirs + if d not in ('.git', '__pycache__', 'build', 'cmake-build') + and not d.startswith('cmake-build-') + and not d.startswith('.') + ] + for fname in files: + fpath = os.path.join(root, fname) + _, ext = os.path.splitext(fname) + ext = ext.lower() + if ext in self.INCLUDE_LIB_EXTENSIONS: + self.extra_link_files.append(fpath) + _vlog().info(f" [includes] 发现库文件: {os.path.relpath(fpath, directory)}") + elif ext in self.INCLUDE_SRC_EXTENSIONS: + self.extra_compile_files.append(fpath) + _vlog().info(f" [includes] 发现源文件: {os.path.relpath(fpath, directory)}") + + def _discover_transitive_deps(self, py_path: str, includes_dir: str, discovered: set[str], py_index: set[str] | None = None) -> None: + """递归发现 Python 文件的间接依赖 + + 复用 parse_python_file 的 AST 缓存,避免重复 open+read+ast.parse。 + py_index 为预构建的 .py 文件路径集合,避免 os.path.isfile 磁盘 stat。 + """ + if py_path in discovered: + return + discovered.add(py_path) + _, tree = parse_python_file(py_path) + if tree is None: + return + try: + for node in ast.walk(tree): + if isinstance(node, ast.Import): + for alias in node.names: + self._try_add_include_dep(alias.name, includes_dir, discovered, py_index) + elif isinstance(node, ast.ImportFrom): + if node.module and node.level == 0: + self._try_add_include_dep(node.module, includes_dir, discovered, py_index) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"发现 include 传递依赖失败: {_e}", "Exception") + + def _try_add_include_dep(self, module_name: str, includes_dir: str, discovered: set, py_index: set[str] | None = None): + """尝试将模块添加为 include 依赖(支持子包,如 win32.win32base) + + py_index 非空时用 set 查询替代 os.path.isfile(O(1) vs 24ms/次)。 + """ + # 候选路径:子包路径(win32/win32base.vp/.py)、顶层模块(win32.vp/.py)、子包 __init__.vp/__init__.py + candidates: list[str] = [] + # 子包路径:win32.win32base → win32/win32base.vp / .py + candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep) + '.vp')) + candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep) + '.py')) + # 子包 __init__.vp/__init__.py:win32.win32base → win32/win32base/__init__.vp / .py + candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep), '__init__.vp')) + candidates.append(os.path.join(includes_dir, module_name.replace('.', os.sep), '__init__.py')) + # 顶层模块:取第一个组件 → win32.vp / .py + top_module = module_name.split('.')[0] + if top_module != module_name: + candidates.append(os.path.join(includes_dir, top_module + '.vp')) + candidates.append(os.path.join(includes_dir, top_module + '.py')) + + for py_path in candidates: + exists: bool + if py_index is not None: + exists = os.path.normcase(py_path) in py_index + else: + exists = _CachedIsFile(py_path) + if exists and py_path not in self.extra_py_files: + self.extra_py_files.append(py_path) + _vlog().info(f" [includes] 发现间接依赖: {os.path.relpath(py_path, includes_dir)}") + self._discover_transitive_deps(py_path, includes_dir, discovered, py_index) + return # 找到第一个匹配即可 + + def _is_decl_only_file(self, src_path: str) -> bool: + """复用 parse_python_file 的 AST 缓存""" + _, tree = parse_python_file(src_path) + if tree is None: + return False + try: + for node in ast.walk(tree): + if isinstance(node, ast.ClassDef): + return False + if isinstance(node, ast.FunctionDef): + body = node.body + if len(body) == 1: + stmt = body[0] + if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Constant) and stmt.value.value is ...: + continue + if isinstance(stmt, ast.Pass): + continue + return False + return True + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"判断是否为声明文件失败: {_e}", "Exception") + return False + + def _sort_include_files_by_deps(self, file_list: list[str]) -> tuple[list[str], dict[str, set[str]]]: + """按依赖关系对 include 文件进行拓扑排序,确保被依赖的文件先编译。 + + 返回 (排序后的文件列表, 依赖字典)。 + 依赖字典: {fpath: set(依赖的fpath)},仅包含 file_list 中的依赖。 + """ + # 构建文件名到路径的映射 + name_to_path = {} + for fpath in file_list: + basename = os.path.splitext(os.path.basename(fpath))[0] + name_to_path[basename] = fpath + + # 也支持包内模块: vpsdk/window -> path + for fpath in file_list: + # 尝试从 includes 目录推断模块全名 + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(fpath, includes_dir) + mod_name = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') + name_to_path[mod_name] = fpath + except ValueError: + pass + + # 分析每个文件的 import 依赖(复用 parse_python_file 的 AST 缓存) + deps = {} # path -> set of paths it depends on + for fpath in file_list: + deps[fpath] = set() + try: + _, tree = parse_python_file(fpath) + if tree is None: + continue + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.Import): + for alias in node.names: + # 尝试完整路径和各级前缀,确保同包内子模块依赖能被正确解析 + # 例如 import zlib.zhuff -> 应先匹配 zlib.zhuff,再回退到 zlib + parts = alias.name.split('.') + for i in range(len(parts), 0, -1): + mod = '.'.join(parts[:i]) + if mod in name_to_path and name_to_path[mod] != fpath: + deps[fpath].add(name_to_path[mod]) + break + elif isinstance(node, ast.ImportFrom): + if node.module and node.level == 0: + # 绝对导入:同样尝试完整路径和各级前缀 + parts = node.module.split('.') + for i in range(len(parts), 0, -1): + mod = '.'.join(parts[:i]) + if mod in name_to_path and name_to_path[mod] != fpath: + deps[fpath].add(name_to_path[mod]) + break + elif node.level > 0: + # 相对导入:from .xxx import yyy 或 from ..xxx import yyy + # 根据当前文件位置推断目标模块的完整路径 + current_dir = os.path.dirname(fpath) + # node.level == 1 表示当前包,node.level == 2 表示上一级包 + package_dir = current_dir + for _ in range(node.level - 1): + package_dir = os.path.dirname(package_dir) + # 推断当前包名(从 includes 目录的相对路径) + package_name: str | None = None + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(package_dir, includes_dir) + if rel and rel != '.': + package_name = rel.replace(os.sep, '.').replace('/', '.') + break + except ValueError: + pass + # 构建目标模块名并查找 + target_mod: str | None = None + if node.module: + target_mod = f"{package_name}.{node.module}" if package_name else node.module + else: + # from . import yyy:yyy 是子模块,在 names 中 + target_mod = package_name + if target_mod: + parts = target_mod.split('.') + for i in range(len(parts), 0, -1): + mod = '.'.join(parts[:i]) + if mod in name_to_path and name_to_path[mod] != fpath: + deps[fpath].add(name_to_path[mod]) + break + # from . import yyy:yyy 可能是子模块 + if node.level > 0 and not node.module: + for alias in node.names: + sub_mod = f"{package_name}.{alias.name}" if package_name else alias.name + if sub_mod in name_to_path and name_to_path[sub_mod] != fpath: + deps[fpath].add(name_to_path[sub_mod]) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"分析 include 文件导入依赖失败: {_e}", "Exception") + + # 拓扑排序 (Kahn's algorithm) + # in_degree 只计入 file_list 中的依赖——decl-only 文件(如 stdint.py) + # 不在 file_list 中,不应计入入度,否则依赖它们的文件入度永远不为 0, + # 被误判为循环依赖通过 fallback 添加,导致编译顺序错误。 + file_set = set(file_list) + in_degree = {fpath: sum(1 for dep in deps[fpath] if dep in file_set) for fpath in file_list} + # 反向邻接表:如果 A 依赖 B,则 B -> A + reverse_adj = {fpath: [] for fpath in file_list} + for fpath in file_list: + for dep in deps[fpath]: + if dep in reverse_adj: + reverse_adj[dep].append(fpath) + + result = [] + queue = [fpath for fpath in file_list if in_degree[fpath] == 0] + # 对队列排序以保持确定性 + queue.sort(key=lambda x: x) + + while queue: + current = queue.pop(0) + result.append(current) + for neighbor in reverse_adj[current]: + in_degree[neighbor] -= 1 + if in_degree[neighbor] == 0: + queue.append(neighbor) + queue.sort(key=lambda x: x) + + # 如果有循环依赖,把剩余文件也加入 + for fpath in file_list: + if fpath not in result: + result.append(fpath) + + # 仅保留 file_list 内的依赖(用于波次并行翻译) + file_set_result = set(file_list) + deps_in_set = {fpath: (deps[fpath] & file_set_result) for fpath in file_list} + return result, deps_in_set + + def _get_precompiled_obj_path(self, src_path: str, sha1: str) -> str | None: + """计算 include 文件在 includes.binary/ 中的预编译 .obj 路径。 + + 预编译目录位于 include 目录同级,命名为 {basename}.binary/。 + 文件命名为 {sha1}.{triple_safe}.{rel_model}.obj,区分目标平台和重定位模型。 + + --traceback:include 模块也带 DWARF 行表(栈帧要能落到 includes/* 的源行), + 此类 .obj 不得回写进跨项目共享的 includes.binary(会污染其它项目的预编译 + 缓存),改落**兄弟目录** {basename}.binary + 后缀(不新增层级,形态与 + ASan 变体目录 includes.binary_asan 一致)。 + 关闭时后缀为空 → 路径与改动前逐字一致(含 --dwarf/--asan 单独开启的情形)。 + --asan 与 --traceback 同开:ASan 插桩也会改变 include obj 内容(见 + _llc_compile),两者产物不可互相复用,故再叠加 _asan 后缀区分 + (优先级:是否隔离由 --traceback 决定,--asan 只决定后缀再叠加 _asan)。 + """ + AbsSrcPath: str = os.path.abspath(src_path) + RelModel: str = 'static' + for _flag in self.compile_flags: + if _flag.startswith('-relocation-model='): + RelModel = _flag[len('-relocation-model='):] + break + for inc_dir in self.include_dirs: + abs_inc: str = os.path.abspath(inc_dir) + try: + rel: str = os.path.relpath(AbsSrcPath, abs_inc) + if rel.startswith('..'): + continue + inc_parent: str = os.path.dirname(abs_inc) + inc_base: str = os.path.basename(abs_inc) + _iso_suffix: str = '' + if self.traceback_enabled: + _iso_suffix = '_traceback_asan' if self.asan_mode else '_traceback' + precompiled_dir: str = os.path.join(inc_parent, inc_base + '.binary' + _iso_suffix) + triple_safe: str = (self.triple or 'default').replace(os.sep, '_').replace('/', '_').replace('\\', '_').replace(':', '_') + return os.path.join(precompiled_dir, f"{sha1}.{triple_safe}.{RelModel}.obj") + except ValueError: + continue + return None + + def _compile_include_py_files(self, active_sha1s: set[str]) -> None: + """通过 TransPyC 翻译器编译 includes 目录中发现的 .py 文件 + + 优化流程:预注册符号 → 并行翻译 → 串行后处理 → 并行编译 → 注册+缓存 + """ + if not self.extra_py_files: + return + + # 按依赖关系排序 include 文件,确保被依赖的文件先编译 + sorted_files, file_deps = self._sort_include_files_by_deps(self.extra_py_files) + _vlog().info(f"\n[includes 编译] 找到 {len(sorted_files)} 个 Python 源文件") + + # --hotspot-sites:include 模块同样被插桩(hotspot_sites_file 经 worker 透传), + # 故启用时必须让旧 .obj 失效并强制重译,否则会复用上一次未插桩的目标文件, + # 使 include 模块的热点计数/计时 IR 静默缺失。未启用时 hotspot_on=False, + # 本函数行为与改动前逐字节一致(零影响)。 + hotspot_on: bool = bool(self.hotspot_sites_file) + + # --traceback:include 模块也必须带 DWARF 行表(否则运行时 Ctrl+C 的栈里 + # 落在 includes/*(memhub/string/viperlib/win32…)的帧取不到源行,只能落到 + # 邻行或 "+0x…(unknown)")。为此翻译时给 include 透传 dwarf_enabled=True + # (见下方 worker/串行四处的 dwarf_enabled=self.traceback_enabled),并把 + # 旧目标文件(output 目录 {sha1}.obj 及隔离缓存 includes.binary_traceback + # 内的旧 .obj)一并失效重译——复用上一次无 DI 的产物会让行表静默缺失。 + # 与 hotspot_on 处理方式一致;同时覆盖 traceback.vp(其 .obj 还须含 + # @llvm.global_ctors 启动期构造器)。取舍:开启本开关时 include 目标文件每 + # 次构建都重译(放弃隔离缓存的复用,换取"必然带 DWARF"的正确性保证), + # 仅调试模式可接受。开关关闭时 include_dwarf=False,本函数行为与改动前 + # 逐字节一致(零影响,含 --dwarf/--asan 单独开启的情形)。 + include_dwarf: bool = bool(self.traceback_enabled) + + # 构建 include 文件的模块 SHA1 映射(预计算所有文件,构建完整映射) + include_ModuleSha1Map = self._build_current_ModuleSha1Map(None) + need_translate = [] # [(src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)] + for src_path in sorted_files: + module_name = os.path.splitext(os.path.basename(src_path))[0] + # 复用 parse_python_file 缓存的 content,避免重复 open+read + py_content, _ = parse_python_file(src_path) + sha1 = compute_sha1(py_content) + self.include_py_map[module_name] = sha1 + active_sha1s.add(sha1) + self._include_sha1s.add(sha1) + + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(src_path, includes_dir) + mod_full = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') + include_ModuleSha1Map[mod_full] = sha1 + include_ModuleSha1Map[module_name] = sha1 + # 包入口文件:注册父包名(如 'os.__init__' -> 'os') + if module_name == '__init__' and '.' in mod_full: + parent_pkg = mod_full.rsplit('.', 1)[0] + include_ModuleSha1Map[parent_pkg] = sha1 + except ValueError: + pass + include_ModuleSha1Map[module_name] = sha1 + ll_path = os.path.join(self.output_dir, f"{sha1}.ll") + obj_path = os.path.join(self.output_dir, f"{sha1}.obj") + precompiled_obj = self._get_precompiled_obj_path(src_path, sha1) + + if self._is_decl_only_file(src_path): + _vlog().info(f" 跳过(声明文件): {module_name}.py") + # 声明文件(如 stdint.py)虽不编译为 .obj,但其 typedef/类型别名 + # 必须注册到共享符号表,否则依赖它的 include 文件编译时 + # 类型解析失败(如 UINT32PTR 被当作命名结构体而非 i32*) + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + self._collect_inline_symbols(src_path) + continue + + # --hotspot-sites / --traceback:include 的 .py 未变时下面两处会因旧 + # .obj 存在而直接跳过,复用上一次(未插桩 / 不含 DI)的目标文件。 + # 启用时先让这两类旧 .obj 一并失效,使其必然走入 need_translate 重新 + # 翻译并嵌入热点计数/计时 IR 或 DWARF 行表。 + # 用 os.path.isfile 而非 _CachedIsFile:避免把"已存在"写入存在性缓存后又被 + # 本处删除,使下面两处判定读到过期缓存而漏编(与 App 主循环同一写法)。 + if hotspot_on or include_dwarf: + for _stale_obj in (precompiled_obj, obj_path): + if _stale_obj and os.path.isfile(_stale_obj): + try: + os.remove(_stale_obj) + except OSError as e: + # 不中断构建,但真实错误(如权限不足)必须打印告警,不静默吞掉 + # (报文保持原样:关闭开关时与改动前逐字一致) + _vlog().warning( + f"Phase2Translator: 删除热点插桩 include 模块的旧目标文件失败: {_stale_obj}: {e}") + + # 优先检查 includes.binary 预编译缓存(跨项目共享) + if not self.force_recompile_includes and precompiled_obj and _CachedIsFile(precompiled_obj): + _vlog().info(f" 跳过(预编译): {module_name}.py -> {os.path.basename(precompiled_obj)}") + # 预编译跳过的文件仍需注册 .pyi/.stub.ll 声明和符号, + # 否则依赖它的 include 文件(如 vpui.py import vpsdk.window) + # 在 _EmitModuleDeclarationsLlvm 中找不到声明,导致 Undefined symbol + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + self._collect_inline_symbols(src_path) + self.extra_link_files.append(precompiled_obj) + continue + + if not self.force_recompile_includes and _CachedIsFile(obj_path): + _vlog().info(f" 跳过(缓存): {module_name}.py -> {sha1}.obj") + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + self._collect_inline_symbols(src_path) + self.extra_link_files.append(obj_path) + continue + + need_translate.append((src_path, sha1, module_name, ll_path, obj_path, precompiled_obj)) + + if not need_translate: + return + + # 预注册所有待翻译文件的符号(从 Phase1 缓存加载 .pyi / .stub.ll) + # 这样并行翻译时所有跨模块符号都已可用 + for src_path, sha1, module_name, ll_path, obj_path, precompiled_obj in need_translate: + self._register_compiled_include(src_path, sha1, None, include_ModuleSha1Map) + + # 预收集 stub_decls(所有文件共用,避免重复扫描 .stub.ll) + stub_decls = [] + for other_sha1, other_stub in self.stub_files.items(): + if os.path.exists(other_stub): + try: + for sline in _ReadTextCached(other_stub).splitlines(True): + sl = sline.strip() + if sl.startswith('%') and '= type' in sl and 'opaque' not in sl: + struct_name = Phase2Translator._extract_llvm_type_name(sl) + if struct_name: + stub_decls.append(sl) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + all_type_defs = {} + for sdecl in stub_decls: + tname = Phase2Translator._extract_llvm_type_name(sdecl) + if tname: + all_type_defs[tname] = sdecl + + # ========== 阶段1:并行翻译 ========== + _vlog().info(f" 翻译 {len(need_translate)} 个文件") + if len(need_translate) > 1: + # 序列化共享符号表供 worker 使用 + shared_pickle_path = os.path.join(self.output_dir, '_shared_sym_includes.pickle') + try: + # symbol_table 只以 pickle 字节传递(symbol_table_bytes),不重复序列化 + # 对象本身(原双重序列化使文件体积/时间翻倍,4.65MB 中近半冗余)。 + shared_data = { + 'symbol_table_bytes': pickle.dumps(self._shared_symbol_table), + 'source_module_sig_files': self._shared_source_module_sig_files, + 'all_dc': self._shared_all_dc, + 'all_decl_types': self._shared_all_decl_types, + 'export_extern_funcs': self._shared_export_extern_funcs, + 'inline_func_symbols': self.inline_func_symbols, + 'function_default_args': self.function_default_args, + 'function_param_names': self.function_param_names, + 'generic_class_templates': self._shared_generic_class_templates, + 'shared_doc_meta': self._shared_doc_meta, + 'class_vtable': self._shared_class_vtable, + 'cross_module_vtable': self._shared_cross_module_vtable, + 'class_methods': self._shared_class_methods, + 'cross_module_novtable': self._shared_cross_module_novtable, + } + with open(shared_pickle_path, 'wb') as f: + f.write(pickle.dumps(shared_data)) + except Exception as e: + _vlog().warning(f"符号表序列化失败({e}),include将串行翻译") + shared_pickle_path = '' + + if shared_pickle_path and os.path.exists(shared_pickle_path): + n_workers = _PickWorkerCount(len(need_translate)) + errors = [] + # 波次并行翻译:按依赖关系分批提交,确保被依赖的文件先翻译完成。 + # 根因:_TryLoadStructFromStub 扫描 output 目录加载跨模块 struct 定义, + # 若依赖文件尚未完成翻译(output stub 未写入),只能取到 Phase1 temp + # 中的不完整 stub(缺少跨模块继承字段),导致运行时崩溃。 + item_by_path = {item[0]: item for item in need_translate} + translate_paths = set(item_by_path.keys()) + # 每个文件的依赖(仅限本轮待翻译集合内) + translate_deps = { + p: (file_deps.get(p, set()) & translate_paths) + for p in translate_paths + } + # 复用 self._executor_pool(与 App 源文件阶段共享),避免 GC 阻塞 + if self._executor_pool is None: + # 新池:刷新 pyi pickle 缓存,把 _register_compiled_include 新增的 + # include .vpi 一并落盘,使 worker 预热包含它们(签名未变时零开销) + if os.environ.get('TPC_WORKER_WARMUP', '1') == '1': + _BuildPyiCacheFile(self.temp_dir) + self._executor_pool = ProcessPoolExecutor(max_workers=n_workers, + initializer=_PyiCacheWorkerInitializer, + initargs=(self.temp_dir,)) + executor = self._executor_pool + try: + completed_paths: set[str] = set() + remaining = list(need_translate) # 保持拓扑序 + in_progress: dict = {} # future -> item + done_count = 0 + + while remaining or in_progress: + # 提交所有依赖已完成的文件 + ready = [ + item for item in remaining + if translate_deps[item[0]] <= completed_paths + ] + for item in ready: + remaining.remove(item) + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + future = executor.submit( + _parallel_translate_worker, + src_path, ll_path, + self.src_root, self.temp_dir, self.output_dir, + self.include_dirs, self.triple, self.datalayout, + self.sha1_map, self.sig_files, self.stub_files, + self.include_py_map, self.slice_level, + shared_pickle_path, self.struct_sha1_map, + include_ModuleSha1Map, + hotspot_sites_file=self.hotspot_sites_file, + hotspot_sites_all=self.hotspot_sites_all, + hotspot_callees=self.hotspot_callees, + traceback_enabled=self.traceback_enabled, + dwarf_enabled=self.traceback_enabled + ) + in_progress[future] = item + + if not in_progress: + # 无可提交且无进行中:循环依赖 fallback + for item in remaining: + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + future = executor.submit( + _parallel_translate_worker, + src_path, ll_path, + self.src_root, self.temp_dir, self.output_dir, + self.include_dirs, self.triple, self.datalayout, + self.sha1_map, self.sig_files, self.stub_files, + self.include_py_map, self.slice_level, + shared_pickle_path, self.struct_sha1_map, + include_ModuleSha1Map, + hotspot_sites_file=self.hotspot_sites_file, + hotspot_sites_all=self.hotspot_sites_all, + hotspot_callees=self.hotspot_callees, + traceback_enabled=self.traceback_enabled, + dwarf_enabled=self.traceback_enabled + ) + in_progress[future] = item + remaining.clear() + + # 等待至少一个完成 + done_set, _ = wait(in_progress, return_when=FIRST_COMPLETED) + for future in done_set: + item = in_progress.pop(future) + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + completed_paths.add(src_path) + done_count += 1 + try: + status, _, err = future.result() + if status == 'ok': + _vlog().info(f" [{done_count}/{len(need_translate)}] 翻译完成: {module_name}.py") + else: + # fail-fast:文件编译出错立即终止(worker 已 os._exit, + # 此处兜底处理残留的 error 返回) + raise RuntimeError( + f"include 翻译失败,立即终止: {module_name}.py: {err}") + except Exception as e: + _vlog().error(f" {module_name}.py: {e}") + for f2 in in_progress: + f2.cancel() + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + raise RuntimeError( + f"include worker 异常终止,编译立即停止: {e}") + except Exception: + if self._executor_pool is not None: + self._executor_pool.shutdown(wait=False, cancel_futures=True) + self._executor_pool = None + raise + + if errors: + _vlog().error(f"\n{len(errors)} 个 include 文件翻译失败") + sys.exit(1) + else: + # 串行回退 + for item in need_translate: + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") + self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled) + else: + item = need_translate[0] + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + _vlog().info(f" 翻译: {module_name}.py -> {sha1}.ll") + self._translate_file(src_path, ll_path, prebuilt_ModuleSha1Map=include_ModuleSha1Map, dwarf_enabled=self.traceback_enabled) + + # ========== 阶段2:串行后处理(dso_local + stub_decls 注入)========== + compile_tasks = [] # [(dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)] + for item in need_translate: + src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item + if not _CachedIsFile(ll_path): + _vlog().error(f" 翻译未生成 .ll 文件: {module_name}.py") + continue + + ll_content = _ReadTextCached(ll_path) + + # --traceback 提醒:include 模块若发射了 define 却不带 DI(!llvm.dbg.cu), + # 其 .debug_line 行为空 ⇒ 运行时 Ctrl+C 栈里该 include 帧取不到源行 + # (会退化为 +0x… (unknown))。 + # **只警告、不报错**:像 includes/stdarg.vp 这类模块的 define 是"合成"的 + # (由 extern/sugar 派生、无 .vp 函数体),本就不会有源级 DI —— 按 fail-fast + # 报错会把这类模块全部误杀;而"行号缺失"只降低可读性,不是编译正确性问题。 + if self.traceback_enabled and _RE_DEFINE_LINE.search(ll_content) \ + and '!llvm.dbg.cu' not in ll_content: + _vlog().warning( + f"--traceback: include 模块 {module_name}.py 发射了 define 但 IR 未携带 " + f"DWARF (!llvm.dbg.cu)——该模块内联帧在 Ctrl+C 栈里将只显示地址: {ll_path}") + + # dso_local 正则标注(使用 ^ + MULTILINE 避免匹配字符串常量内容中的 define/declare) + ll_content = _RE_DEFINE_DECLARE_LINKAGE.sub(r'\1 \2 dso_local ', ll_content) + ll_content = _RE_DEFINE_DECLARE_NO_LINKAGE.sub(r'\1 dso_local ', ll_content) + # LLVM 22+: external 不允许在带初值的全局定义上显式写出,改为 linkonce 保留多模块合并语义 + ll_content = _RE_EXTERNAL_GLOBAL_VALUE.sub(r'\1 = linkonce global \2 \3', ll_content) + ll_content = _RE_GLOBAL_LINKAGE_MULTILINE.sub(r'\1 = \2 dso_local \3', ll_content) + ll_content = _RE_GLOBAL_DSOLOCAL_MULTILINE.sub(r'\1 = dso_local \2', ll_content) + ll_content = _AddWeakComdat(ll_content) + + # stub_decls 注入(复用预收集的 stub_decls) + if stub_decls: + for sdecl in stub_decls: + sname = Phase2Translator._extract_llvm_type_name(sdecl) + if sname: + short = sname.split('.')[-1] if '.' in sname else sname + ll_content = re.sub( + r'%"?' + re.escape(sname) + r'"?\s*=\s*type\s*opaque', + sdecl, ll_content) + if short != sname: + ll_content = re.sub( + r'%"?' + re.escape(short) + r'"?\s*=\s*type\s*opaque', + sdecl, ll_content) + + max_iterations = 10 + for _ in range(max_iterations): + defined_types = set() + for line in ll_content.splitlines(): + sl = line.strip() + if sl.startswith('%') and '= type' in sl: + tname = Phase2Translator._extract_llvm_type_name(sl) + if tname: + defined_types.add(tname) + # 同时添加短名,防止 missing 循环因全名/短名不匹配而重复添加 + if '.' in tname: + parts = tname.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + defined_types.add(parts[1]) + + referenced_types = Phase2Translator._find_all_type_refs(ll_content) + missing = referenced_types - defined_types + new_defs = [] + for tname in sorted(missing): + if tname in all_type_defs: + new_defs.append(all_type_defs[tname]) + else: + short_key = tname.split('.')[-1] if '.' in tname else tname + for full_name, defn in all_type_defs.items(): + if full_name.split('.')[-1] == short_key: + if short_key not in defined_types: + new_defs.append(defn) + break + else: + # 未在 stub 中找到定义(如泛型类 list[T]), + # 声明为 opaque 以满足 llc 的类型前向声明要求 + new_defs.append(f'%"{tname}" = type opaque') + + if not new_defs: + break + + inject_block = '\n'.join(new_defs) + '\n' + lines = ll_content.splitlines(True) + insert_pos = 0 + for i, line in enumerate(lines): + stripped = line.strip() + if stripped and not stripped.startswith(';') and not stripped.startswith('target ') and not stripped.startswith('source_filename') and not stripped.startswith('declare ') and not stripped.startswith('@') and not stripped.startswith('define ') and not stripped.startswith('attributes ') and not stripped.startswith('!') and not stripped.startswith('module '): + insert_pos = i + break + if insert_pos == 0: + insert_pos = len(lines) + ll_content = ''.join(lines[:insert_pos]) + inject_block + ''.join(lines[insert_pos:]) + + # 将所有类型定义移到 declares 之前 + # (llc 要求 by-value struct 参数的类型在 declare 前定义,否则报 invalid type for function argument) + lines = ll_content.splitlines(True) + type_def_lines = [] + other_lines = [] + seen_type_names_inc = set() + for line in lines: + stripped = line.strip() + if stripped.startswith('%') and '= type' in stripped: + type_name_inc = stripped.split('=')[0].strip() + if type_name_inc in seen_type_names_inc: + continue + seen_type_names_inc.add(type_name_inc) + type_def_lines.append(line) + else: + other_lines.append(line) + if type_def_lines: + insert_pos = 0 + for i, line in enumerate(other_lines): + stripped = line.strip() + if stripped and not stripped.startswith(';') and not stripped.startswith('target ') and not stripped.startswith('source_filename') and not stripped.startswith('!'): + insert_pos = i + break + if insert_pos == 0: + insert_pos = len(other_lines) + ll_content = ''.join(other_lines[:insert_pos]) + ''.join(type_def_lines) + ''.join(other_lines[insert_pos:]) + + # 同 App 路径:写盘前规整元数据(拼接片段各自带 !0..!N 的 DI 元数据), + # 让盘上产物本身即合法 IR,不依赖 _llc_compile 里那次事后规整。 + ll_content = SanitizeIRMetadata(ll_content) + # 同 App 路径:拼接片段各自带一份同名 comdat 声明 ⇒ 落盘前统一去重 + ll_content = _DedupComdatDecls(ll_content) + dso_ll_path = ll_path[:-3] + '_dso.ll' + with open(dso_ll_path, 'w', encoding='utf-8') as f: + f.write(ll_content) + _InvalidateTextCache(dso_ll_path) + compile_tasks.append((dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content)) + + if not compile_tasks: + return + + # ========== 阶段3:并行编译(llc)========== + _vlog().info(f" 编译 {len(compile_tasks)} 个 .ll 文件") + compile_results = {} # task -> result + + def _compile_one_include(task): + dso_ll_path, obj_path = task[0], task[1] + result = self._llc_compile(dso_ll_path, obj_path, self.output_dir) + return (task, result) + + if len(compile_tasks) > 1: + n_workers = _PickWorkerCount(len(compile_tasks), 400) + with ThreadPoolExecutor(max_workers=n_workers) as executor: + futures = {executor.submit(_compile_one_include, task): task for task in compile_tasks} + done_count = 0 + for future in as_completed(futures): + done_count += 1 + try: + task, result = future.result() + module_name = task[4] + if result.returncode == 0: + _vlog().success(f" [{done_count}/{len(compile_tasks)}] 编译成功: {module_name}.py") + else: + _vlog().warning(f" [{done_count}/{len(compile_tasks)}] 编译失败: {module_name}.py: {result.stderr.strip()}") + compile_results[task] = result + except Exception as e: + task = futures[future] + module_name = task[4] + _vlog().error(f" [{done_count}/{len(compile_tasks)}] 编译异常: {module_name}.py: {e}") + compile_results[task] = None + else: + task = compile_tasks[0] + task_result, result = _compile_one_include(task) + module_name = task[4] + if result.returncode == 0: + _vlog().success(f" 编译成功: {module_name}.py") + else: + _vlog().warning(f" 编译失败: {module_name}.py: {result.stderr.strip()}") + compile_results[task] = result + + # ========== 阶段4:注册 + 缓存 ========== + has_error = False + for task in compile_tasks: + dso_ll_path, obj_path, src_path, sha1, module_name, ll_path, precompiled_obj, ll_content = task + result = compile_results.get(task) + if result and result.returncode == 0: + self.extra_link_files.append(obj_path) + # 复制到 includes.binary 预编译目录(--traceback 时 precompiled_obj + # 指向隔离目录 includes.binary_traceback,不会污染共享缓存) + if precompiled_obj: + precompiled_dir = os.path.dirname(precompiled_obj) + try: + if not os.path.exists(precompiled_dir): + os.makedirs(precompiled_dir, exist_ok=True) + shutil.copy2(obj_path, precompiled_obj) + except Exception as _e: + # --traceback fail-fast:隔离目录创建 / 写回失败必须显式终止, + # 不静默降级(否则本次构建的 include obj 拿不到、或落到共享缓存 + # 之外的位置,DWARF 行表与后续复用的前提都被破坏)。 + if self.traceback_enabled: + raise RuntimeError( + f"--traceback: include 预编译缓存写入隔离目录失败: " + f"{precompiled_obj}: {_e}") from _e + if _config_mode == "strict": + _vlog().warning(f"预编译缓存写入失败: {_e}") + # 注册符号 + self._register_compiled_include(src_path, sha1, ll_content, include_ModuleSha1Map) + # 清理临时文件(仅成功时清理,失败时保留以便调试) + # DEBUG: 保留 .ll 文件以便静态分析 + # for ext in ['.ll', '_dso.ll']: + # p = ll_path[:-3] + ext + # if _CachedIsFile(p): + # os.remove(p) + else: + has_error = True + _vlog().warning(f" 保留失败文件以便调试: {ll_path}") + + if has_error: + _vlog().error(f"\nincludes 文件编译失败") + sys.exit(1) + + # 从 include 文件源码中提取函数参数名,供主项目编译时 keyword 参数匹配使用 + self._extract_include_param_names(sorted_files) + + def _extract_include_param_names_from_dirs(self) -> None: + """从 include 目录的 Python 源码中提取函数参数名与默认值。 + + 顶层 .vp/.py 直接解析;包子目录(如 includes/json)只在该包被 + used_includes 引用时递归解析——避免为每个工程全量递归 includes + (fltk/vqt6/openssl 等上百个文件)带来的解析开销。 + 默认值一并注册(跨模块缺参调用点据此填充,见 HandlesExprCall + ._HandleClosureCallLlvm),此前只注册参数名,导致 include 模块的 + 尾随默认参数在调用点被零值填充。 + """ + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for entry in os.listdir(includes_dir): + entry_path = os.path.join(includes_dir, entry) + targets: list[str] = [] + if os.path.isdir(entry_path): + if not self.used_includes or entry not in self.used_includes: + continue + for root, dirs, files in os.walk(entry_path): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + targets.extend(os.path.join(root, f) for f in files + if f.endswith('.vp') or f.endswith('.py')) + elif entry.endswith('.vp') or entry.endswith('.py'): + targets.append(entry_path) + for src_path in targets: + try: + source, tree = parse_python_file(src_path) + if tree is None: + continue + sha1 = compute_sha1(source) + self._collect_function_params(tree, sha1) + except Exception as e: + _vlog().warning(f" 提取参数名失败 {src_path}: {e}") + + def _collect_function_params(self, tree: ast.AST, sha1: str | None) -> None: + """单次递归遍历 AST 收集函数参数名(含类方法)与默认值,O(N) 复杂度 + + 遍历时维护 current_class 上下文:遇到 ClassDef 进入类作用域, + 遇到 FunctionDef 记录参数名并标注所属类,函数内部的嵌套定义 + 不再属于外层类。 + + 默认值只登记非方法(模块级函数)的裸名键,减少与其它模块同名函数的 + 误配;带 sha1 / 类名限定的键始终登记,查找时精确键优先。 + """ + def visit(node: ast.AST, current_class: str | None) -> None: + for child in ast.iter_child_nodes(node): + if isinstance(child, ast.ClassDef): + # 进入类作用域,递归处理类体 + visit(child, child.name) + elif isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)): + func_name = child.name + param_names = [arg.arg for arg in child.args.args] + self.function_param_names[func_name] = param_names + if sha1: + self.function_param_names[f'{sha1}.{func_name}'] = param_names + if current_class: + self.function_param_names[f'{current_class}.{func_name}'] = param_names + if sha1: + self.function_param_names[f'{sha1}.{current_class}.{func_name}'] = param_names + defaults = getattr(child.args, 'defaults', []) or [] + if defaults: + default_vals = [] + for d in defaults: + if isinstance(d, ast.Constant): + default_vals.append(d.value) + else: + default_vals.append(None) + if sha1: + self.function_default_args[f'{sha1}.{func_name}'] = default_vals + if current_class: + self.function_default_args[f'{current_class}.{func_name}'] = default_vals + if sha1: + self.function_default_args[f'{sha1}.{current_class}.{func_name}'] = default_vals + else: + self.function_default_args[func_name] = default_vals + # 函数内部的嵌套定义不属于外层类 + visit(child, None) + else: + visit(child, current_class) + visit(tree, None) + + def _extract_app_default_args(self, need_translate: list) -> None: + """从 App 源文件中提取函数默认参数,存入 function_default_args + + 在 Phase 2 并行编译开始前调用,确保跨模块调用参数检查能正确识别默认参数。 + need_translate 中每个元素是 (i, src_path, out_path, sha1, current_ModuleSha1Map) + 复用 parse_python_file 的 AST 缓存(run() 循环已 parse 过)。 + """ + for entry in need_translate: + src_path = entry[1] + sha1 = entry[3] + _, tree = parse_python_file(src_path) + if tree is None: + continue + try: + for node in ast.walk(tree): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + func_name = node.name + defaults = getattr(node.args, 'defaults', []) + if defaults: + default_vals = [] + for d in defaults: + if isinstance(d, ast.Constant): + default_vals.append(d.value) + else: + default_vals.append(None) + # 存储多种键格式,确保跨模块调用检查能找到 + self.function_default_args[func_name] = default_vals + self.function_default_args[f'{sha1}.{func_name}'] = default_vals + # 也更新参数名(预扫描时补充) + param_names = [arg.arg for arg in node.args.args] + if func_name not in self.function_param_names: + self.function_param_names[func_name] = param_names + if f'{sha1}.{func_name}' not in self.function_param_names: + self.function_param_names[f'{sha1}.{func_name}'] = param_names + except Exception as e: + _vlog().warning(f" 提取默认参数失败 {src_path}: {e}") + + def _extract_include_param_names(self, sorted_files: list[str]) -> None: + """从 include 文件的 Python 源码中提取函数参数名,存入 function_param_names + + 优化:复用 _collect_function_params 的 O(N) 单次遍历 + parse_python_file 缓存。 + """ + for src_path in sorted_files: + if self._is_decl_only_file(src_path): + continue + try: + _, tree = parse_python_file(src_path) + if tree is None: + continue + module_name = os.path.splitext(os.path.basename(src_path))[0] + sha1 = self.include_py_map.get(module_name) + self._collect_function_params(tree, sha1) + except Exception as e: + _vlog().warning(f" 提取参数名失败 {src_path}: {e}") + + def _register_compiled_include(self, src_path: str, sha1: str, ll_content: str, include_ModuleSha1Map: dict[str, str]) -> None: + """编译 include 文件成功后,生成签名和 stub 文件并注册到符号表中""" + module_name = os.path.splitext(os.path.basename(src_path))[0] + + # 确定完整模块名(如 vpsdk.window) + mod_full = module_name + for includes_dir in self.include_dirs: + try: + rel = os.path.relpath(src_path, includes_dir) + mod_full = os.path.splitext(rel)[0].replace(os.sep, '.').replace('/', '.') + break + except ValueError: + pass + + # 1. 生成 .pyi 签名文件(如果还没有的话) + sig_path = os.path.join(self.temp_dir, f"{sha1}.vpi") + if not _CachedIsFile(sig_path): + # 全局缓存检查(不被 --clean 清除) + _TryGlobalCache(sha1, 'pyi', sig_path) + if not _CachedIsFile(sig_path): + try: + py_content = _ReadTextCached(src_path) + sig_content = PythonToStubConverter.convert(py_content, mod_full) + os.makedirs(self.temp_dir, exist_ok=True) + with open(sig_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(sig_content) + _InvalidateTextCache(sig_path) + _SaveToGlobalCache(sha1, 'pyi', sig_path) + except Exception as e: + if _config_mode == "strict": + raise + _vlog().warning(f"签名生成失败不阻塞编译: {e}", "Exception") + + # 注册到 sig_files 和 _source_module_sig_files + if _CachedIsFile(sig_path): + self.sig_files[sha1] = sig_path + self.sha1_map[sha1] = f"includes/{mod_full.replace('.', '/')}.vp" + if self._shared_source_module_sig_files is not None: + self._shared_source_module_sig_files[mod_full] = sig_path + self._shared_source_module_sig_files[module_name] = sig_path + + # 将签名信息加载到共享符号表中 + if self._shared_symbol_table is not None: + try: + self._shared_symbol_table.LoadModuleSymbols(sig_path, mod_full, lineno=0) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载模块签名到共享符号表失败: {_e}", "Exception") + + # 2. 生成 .stub.ll 文件 + stub_path = os.path.join(self.output_dir, f"{sha1}.stub.ll") + if not _CachedIsFile(stub_path): + # 全局缓存检查(不被 --clean 清除) + _TryGlobalCache(sha1, 'stub.ll', stub_path) + if not _CachedIsFile(stub_path) and ll_content: + try: + stub_content, _ = self._split_ll(ll_content) + with open(stub_path, 'w', encoding='utf-8', newline='\n') as f: + f.write(stub_content) + _SaveToGlobalCache(sha1, 'stub.ll', stub_path) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"生成 .stub.ll 文件失败: {_e}", "Exception") + + # 注册到 stub_files + if _CachedIsFile(stub_path): + self.stub_files[sha1] = stub_path + # output/.stub.ll 存在(本步刚落盘或从全局缓存拷入)→ 置脏, + # 让后续索引 miss 重扫到新增的 *.stub.ll(见 _build_stub_index 快路径) + from lib.core.Handles.HandlesImports import ImportHandle as _IH3 + _IH3.MarkStubSetDirty() + elif _CachedIsFile(sig_path): + # 如果 stub 文件不存在,也尝试从 temp 目录查找 + temp_stub = os.path.join(self.temp_dir, f"{sha1}.stub.ll") + if _CachedIsFile(temp_stub): + self.stub_files[sha1] = temp_stub + else: + # 最后尝试从全局缓存直接读取 + _TryGlobalCache(sha1, 'stub.ll', temp_stub) + if _CachedIsFile(temp_stub): + self.stub_files[sha1] = temp_stub + + def _precollect_inline_symbols(self) -> None: + """在主翻译循环之前,扫描includes目录中used_includes相关的模块收集内联函数AST""" + if not self.used_includes: + return + for includes_dir in self.include_dirs: + if not os.path.isdir(includes_dir): + continue + for module_name in self.used_includes: + module_dir = os.path.join(includes_dir, module_name) + if os.path.isdir(module_dir): + for root, dirs, files in os.walk(module_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith('.vp') or fname.endswith('.py'): + src_path = os.path.join(root, fname) + self._collect_inline_symbols(src_path) + py_path = os.path.join(includes_dir, module_name + '.vp') + if not _CachedIsFile(py_path): + # 回退到 .py(向后兼容) + py_path = os.path.join(includes_dir, module_name + '.py') + if _CachedIsFile(py_path): + self._collect_inline_symbols(py_path) + for ef in self.extra_py_files: + self._collect_inline_symbols(ef) + + def _collect_inline_symbols(self, src_path: str): + """解析源文件,收集内联函数的AST信息到inline_func_symbols + + 复用 parse_python_file 的 AST 缓存,避免重复 open+read+ast.parse。 + """ + try: + _, tree = parse_python_file(src_path) + if tree is None: + return + module_name = os.path.splitext(os.path.basename(src_path))[0] + for node in ast.iter_child_nodes(tree): + if isinstance(node, ast.FunctionDef) and node.returns: + is_inline = False + if isinstance(node.returns, ast.BinOp) and isinstance(node.returns.op, ast.BitOr): + for part in [node.returns.left, node.returns.right]: + if isinstance(part, ast.Attribute) and part.attr == 'CInline': + is_inline = True + break + if isinstance(part, ast.Name) and part.id == 'CInline': + is_inline = True + break + elif isinstance(node.returns, ast.Attribute) and node.returns.attr == 'CInline': + is_inline = True + elif isinstance(node.returns, ast.Name) and node.returns.id == 'CInline': + is_inline = True + if is_inline: + func_name = node.name + sym_key = f"{module_name}.{func_name}" + info = CTypeInfo() + info.Name = func_name + info.IsFunction = True + info.IsInline = True + info.InlineBody = node.body + info.InlineParams = [arg.arg for arg in node.args.args] + info.Storage = t.CInline() + self.inline_func_symbols[func_name] = info + self.inline_func_symbols[sym_key] = info + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"预收集内联函数符号失败: {_e}", "Exception") + + def _build_shared_symbol_data(self) -> None: + """一次性构建所有文件共享的符号表数据,避免每个文件重复加载""" + t0 = time.time() + + _vlog().info("[缓存] 构建共享符号表数据...") + + base_trans = TransPyC.TransPyC(code="pass", triple=self.triple, datalayout=self.datalayout) + base_trans.translator.LlvmGen = None + base_trans.translator._ModuleSha1Map = {} + base_trans.translator._global_function_default_args = self.function_default_args + base_trans.translator._global_function_param_names = self.function_param_names + + # ---- 两阶段加载:先收集全部模块,再「①声明」全跑完 / 「②签名」全跑完 ---- + # 声明阶段把类/枚举/typedef 灌满符号表,签名阶段才解析函数签名 ⇒ 模块间 + # 类型引用(`from X import *` 的裸名如 posix 的 `INT`、跨模块类如 + # `rawptr[Constant]`)不再依赖 os.listdir 的字典序(此前 posix 排在 stdint + # 之前 ⇒ `INT` 解析不到,旧口径静默降级成 i32,fail-fast 后必须消除)。 + staged: list[tuple[str, str]] = [] # (声明文件路径, 模块名) + # 需要重导出的包(在全部模块符号加载完后统一处理;提到循环外避免多个 + # include_dir 时被反复覆盖/重复收集) + _pending_reexports: list[tuple[str, str]] = [] + for includes_dir in self.include_dirs: + if os.path.isdir(includes_dir): + for pyi_file in os.listdir(includes_dir): + if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'): + pyi_path = os.path.join(includes_dir, pyi_file) + module_name = os.path.splitext(pyi_file)[0] + staged.append((pyi_path, module_name)) + for py_file in os.listdir(includes_dir): + if (py_file.endswith('.vp') or py_file.endswith('.py')) and (not py_file.startswith('_') or py_file == '_list.vp' or py_file == '_list.py' or py_file == '_dict.vp' or py_file == '_dict.py'): + module_name = os.path.splitext(py_file)[0] + sha1_key = self.include_py_map.get(module_name) + if sha1_key and sha1_key in self.sig_files: + staged.append((self.sig_files[sha1_key], module_name)) + # 收集需要重导出的包,等所有模块符号加载完后再处理 + for sha1_key, sig_path in self.sig_files.items(): + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + mod_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + mod_name = mod_name[len('includes/'):] + _entry_mod: tuple[str, str] = (sig_path, mod_name) + if _entry_mod not in staged: + staged.append(_entry_mod) + if mod_name.endswith('.__init__'): + package_name = mod_name[:-len('.__init__')] + _entry_pkg: tuple[str, str] = (sig_path, package_name) + if _entry_pkg not in _pending_reexports: + _pending_reexports.append(_entry_pkg) + + for sha1_key, sig_path in self.sig_files.items(): + rel_path = self.sha1_map.get(sha1_key, sha1_key) + if rel_path.startswith('includes/'): + continue + module_name = os.path.splitext(rel_path)[0].replace(os.sep, '.').replace('/', '.') + staged.append((sig_path, module_name)) + base_trans.translator._source_module_sig_files[module_name] = sig_path + short_name = module_name.split('.')[-1] if '.' in module_name else module_name + if short_name != module_name: + base_trans.translator._source_module_sig_files[short_name] = sig_path + + # ① 声明阶段(全部模块;多轮直到"本轮没有新增可解析的声明") + # 单轮仍会与模块顺序有关:typedef 右侧可以引用**其它模块**的 typedef, + # 例 win32base 的 `HANDLE: t.CTypedef = VOIDPTR`,VOIDPTR 在 stdint + # ⇒ 本模块先 stage 时右侧解析为空;下一轮把它重 stage 一遍(重 stage 覆盖 + # 登记,条目随依赖入表而变完整)。无进展即停,最多再跑一轮。 + _stage_map: dict[tuple[str, str], ModuleSymbols] = {} + _round_pending = list(staged) + _prev_miss: int | None = None + while _round_pending: + _miss_total: int = 0 + _next_round: list[tuple[str, str]] = [] + for _stage_path, _stage_mod in _round_pending: + _stage_ms = base_trans.translator.SymbolTable.StageModuleSymbols(_stage_path, _stage_mod) + if _stage_ms is None: + continue + _stage_map[(_stage_path, _stage_mod)] = _stage_ms + _miss: int = _CountUnresolvedTypedefs(_stage_ms) + _miss_total += _miss + if _miss: + _next_round.append((_stage_path, _stage_mod)) + if not _next_round or (_prev_miss is not None and _miss_total >= _prev_miss): + break + _prev_miss = _miss_total + _round_pending = _next_round + + # ② 签名阶段(全部模块;此时任一模块的签名都能看到全部声明) + for (_commit_path, _commit_mod), _commit_ms in _stage_map.items(): + base_trans.translator.SymbolTable.CommitModuleSymbols(_commit_ms, _commit_mod, _commit_path) + + # 所有 includes 模块符号已加载,现在处理包重导出 + for sig_path, package_name in _pending_reexports: + self._register_package_reexports(base_trans, sig_path, package_name) + + all_dc = {} + all_dt: dict[str, tuple[int, bool]] = {} + for sha1_key, stub_path in self.stub_files.items(): + if os.path.exists(stub_path): + try: + stub_content = _ReadTextCached(stub_path) + for line in stub_content.splitlines(): + line = line.strip() + if line.startswith('@') and '= global' in line and 'i32' in line and not line.startswith('@llvm'): + var_name = line.split('=')[0].strip().lstrip('@') + val_part = line.split('i32')[-1].strip().rstrip(']').lstrip('[').lstrip('i32') + try: + val = int(val_part) + all_dc[var_name] = val + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + except Exception as e: + _vlog().warning(f"Phase2Translator: 忽略异常 {e}", exc_info=e) + + for sha1_key, pyi_path in self.sig_files.items(): + _ExtractCDefineConstantsFromPyi(pyi_path, all_dc, base_trans.translator.SymbolTable, all_dt) + + export_extern_funcs = {} + for sha1_key, sig_path in self.sig_files.items(): + try: + # 复用 parse_python_file 的 AST 缓存(sig 文件被多处解析) + _, sig_tree = parse_python_file(sig_path) + if sig_tree is None: + continue + for node in ast.iter_child_nodes(sig_tree): + if isinstance(node, ast.FunctionDef): + if _check_annotation_for_export(node.returns): + export_extern_funcs[node.name] = sha1_key + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集共享导出外部函数失败: {_e}", "Exception") + + for sym_name, sym_info in self.inline_func_symbols.items(): + if sym_name not in base_trans.translator.SymbolTable: + base_trans.translator.SymbolTable[sym_name] = sym_info + else: + existing = base_trans.translator.SymbolTable[sym_name] + if not getattr(existing, 'InlineBody', None) and getattr(sym_info, 'InlineBody', None): + existing.IsInline = sym_info.IsInline + existing.InlineBody = sym_info.InlineBody + existing.InlineParams = sym_info.InlineParams + + self._shared_symbol_table = base_trans.translator.SymbolTable + self._shared_source_module_sig_files = dict(base_trans.translator._source_module_sig_files) + self._shared_all_dc = all_dc + self._shared_all_decl_types = all_dt + self._shared_export_extern_funcs = export_extern_funcs + + generic_class_templates = {} + for includes_dir in self.include_dirs: + if os.path.isdir(includes_dir): + abs_includes = os.path.join(self.src_root, includes_dir) if not os.path.isabs(includes_dir) else includes_dir + if not os.path.isdir(abs_includes): + abs_includes = includes_dir + for py_file in os.listdir(abs_includes): + if (py_file.endswith('.vp') or py_file.endswith('.py')) and (not py_file.startswith('_') or py_file == '_list.vp' or py_file == '_list.py' or py_file == '_dict.vp' or py_file == '_dict.py'): + py_path = os.path.join(abs_includes, py_file) + try: + # 复用 parse_python_file 的 AST 缓存 + py_content, py_tree = parse_python_file(py_path) + if py_tree is None: + continue + # 计算 include 文件的 SHA1,供特化时统一命名空间 + py_sha1: str = compute_sha1(py_content) + for node in py_tree.body: + if isinstance(node, ast.ClassDef) and hasattr(node, 'type_params') and node.type_params: + type_params = [tp.name for tp in node.type_params] + generic_class_templates[node.name] = { + 'node': node, + 'type_params': type_params, + 'sha1': py_sha1, + } + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"收集泛型类模板失败: {_e}", "Exception") + self._shared_generic_class_templates = generic_class_templates + + # 加载所有 {sha1}.doc.json 到 _shared_doc_meta,供跨模块 __doc__ 查询 + shared_doc_meta: dict[str, dict[str, str]] = {} + if os.path.isdir(self.temp_dir): + for fname in os.listdir(self.temp_dir): + if fname.endswith('.doc.json'): + sha1_key = fname[:-len('.doc.json')] + try: + with open(os.path.join(self.temp_dir, fname), 'r', encoding='utf-8') as f: + data = json.load(f) + if isinstance(data, dict): + shared_doc_meta[sha1_key] = {str(k): str(v) for k, v in data.items()} + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载 {fname} 失败: {_e}", "Exception") + # 也从全局缓存加载 .doc.json(temp_dir 被 --clean 清空时补充) + if os.path.isdir(_GLOBAL_CACHE_DIR): + for fname in os.listdir(_GLOBAL_CACHE_DIR): + if fname.endswith('.doc.json'): + sha1_key = fname[:-len('.doc.json')] + if sha1_key in shared_doc_meta: + continue + try: + data = json.loads(_ReadTextCached(os.path.join(_GLOBAL_CACHE_DIR, fname))) + if isinstance(data, dict): + shared_doc_meta[sha1_key] = {str(k): str(v) for k, v in data.items()} + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"加载全局缓存 {fname} 失败: {_e}", "Exception") + self._shared_doc_meta = shared_doc_meta + + t1 = time.time() + _vlog().info(f"[缓存] 共享符号表构建完成 ({t1-t0:.2f}s, {len(self._shared_symbol_table)} 符号, {len(generic_class_templates)} 泛型模板, {len(shared_doc_meta)} doc 元数据)") + + def _compile_include_sources(self): + """扫描项目目录中的汇编存根,编译 includes 目录中发现的 C/C++ 源文件""" + extra_sources = list(self.extra_compile_files) + scan_dirs = [] + if self.src_root and os.path.isdir(self.src_root): + scan_dirs.append(self.src_root) + # 不再扫描 output_dir 的父目录:当 output_dir 位于顶层时, + # 父目录是整个仓库根,会误扫到 ViperOS/includes/vqt6 等无关 C/C++ 源文件。 + # 项目特定的 C/汇编文件应放在 src_root 下,或通过 extra_compile_files 指定。 + for scan_dir in scan_dirs: + for root, dirs, files in os.walk(scan_dir): + dirs[:] = [d for d in dirs if not d.startswith('.') and d != '__pycache__'] + for fname in files: + if fname.endswith(('.S', '.s', '.c', '.cpp')): + if fname.startswith('test_'): + continue + fpath = os.path.join(root, fname) + if fpath not in extra_sources: + extra_sources.append(fpath) + if not extra_sources: + return + + cc_map = { + '.c': 'gcc', + '.cpp': 'g++', '.cc': 'g++', '.cxx': 'g++', + '.m': 'clang', '.mm': 'clang++', + '.s': 'gcc', '.S': 'gcc', + } + + _vlog().info(f"\n[includes 编译] 找到 {len(extra_sources)} 个源文件") + + for src_path in extra_sources: + _, ext = os.path.splitext(src_path) + ext = ext.lower() + obj_path = os.path.join(self.output_dir, os.path.splitext(os.path.basename(src_path))[0] + '.obj') + if ext in ('.s', '.S') and any('oformat' in f for f in self.linker_flags): + cc = 'clang' + compile_src = src_path + extra_flags = ['-c', '-target', 'x86_64-none-elf', '-o', obj_path, compile_src] + else: + cc = cc_map.get(ext, 'gcc') + extra_flags = ['-c', '-o', obj_path, src_path] + + _vlog().info(f" 编译: {os.path.basename(src_path)} -> {os.path.basename(obj_path)}") + + try: + cmd = [cc] + extra_flags + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode == 0: + _vlog().success(" 编译成功") + self.extra_link_files.append(obj_path) + else: + _vlog().error(f" 编译失败: {result.stderr.strip()}") + _vlog().error(f"\nC/C++ 源文件编译失败,立即终止编译。") + sys.exit(1) + except FileNotFoundError: + _vlog().error(f" 找不到编译器: {cc}") + _vlog().error(f"\n找不到编译器,立即终止编译。") + sys.exit(1) + except Exception as e: + _vlog().error(f" {e}") + _vlog().error(f"\n编译异常,立即终止编译。") + sys.exit(1) + + def _strip_debug_sections(self, exe_path: str) -> None: + """剥离 .exe 中的调试段(跳过裸机二进制)""" + if not os.path.exists(exe_path): + return + ext = os.path.splitext(exe_path)[1].lower() + if ext not in ('.exe', '.dll', '.obj', '.o', '.elf'): + _vlog().info(f" [剥离] 跳过({ext} 格式无需剥离)") + return + for tool in ['llvm-strip', 'strip']: + strip_cmd = shutil.which(tool) + if strip_cmd: + try: + result = subprocess.run( + [strip_cmd, '--strip-debug', exe_path], + capture_output=True, text=True + ) + if result.returncode == 0: + _vlog().info(f" [剥离] 调试段已移除 ({tool})") + else: + _vlog().warning(f" [剥离] 警告: {result.stderr.strip()}") + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"剥离可执行文件调试段失败: {_e}", "Exception") + break + + def _WriteStackMap(self, exe_path: str) -> None: + """--traceback:链接成功后导出"地址→源行"旁文件 .stackmap。 + + 用 llvm-dwarfdump 解析最终 PE 的 .debug_line(地址→文件/行)与 + .debug_info(DISubprogram 的 low/high_pc + DW_AT_name → 函数名), + 换算为 RVA 后落盘。旁文件规格(全部小端 little-endian): + + 头部 20 字节 = magic(4)="TPCS" + version(u32=1) + entry_count(u32) + + str_count(u32) + str_bytes(u32) + 记录 entry_count × 20 字节(按 rva 升序): + rva(u64) + file_off(u32) + line(u32) + func_off(u32) + 字符串池 str_bytes 字节(紧接记录区,UTF-8,全部以 '\\0' 结尾) + + 口径(必须与运行期严格一致):运行期 traceback.vp 取 + `rva = 帧地址 − GetModuleHandleA(NULL)`,即"链接期 ImageBase 之上的偏移", + 故写端唯一正确口径是 **`RVA = dwarfdump 打印的地址 − PE.ImageBase`**。 + + ⚠ 实测 viperc.exe 的 DWARF 里同时存在两个地址域: + * 绝大多数行/子程序地址是**已重定位的 VA**(0x14000xxxx,含 ImageBase); + * 另有 22638 条行记录 / 756 个 subprogram 的地址**恰为 0**——它们是**被 + 链接器按 COMDAT 去重丢弃的泛型实例副本**(实测 36 个 `list[T].*` 名字, + 每个都有 1 份 VA 副本 + 15~21 份 0 地址副本),并非解析错误。 + 旧实现按"整段最小地址是否 >= ImageBase"**整表**判定:只要有一行 0,就整表都不 + 减基址 ⇒ 全部 VA 行留在 0x140000000+ 域、与运行期 RVA 域相差一个 ImageBase, + 运行期下界查找对任意帧都落到同一条记录上(用户实测所有帧都显示同一行)。 + 现改为**逐条**归一化:地址落在映像 VA 区间 [ImageBase, ImageBase+SizeOfImage) + 的记录保留(RVA = 地址 − ImageBase);其余(未重定位的 0、越界值)一律**丢弃并 + 统计**(不静默留垃圾条目)。同一 RVA 只保留行号最大的一条。 + + 写出前自检(任一不满足即 fail-fast 抛异常,遵循 --traceback 既有风格): + 记录数 / min~max RVA 与 SizeOfImage 的关系 / 所有 RVA ∈ [0, SizeOfImage) / + RVA 严格升序 / 至少若干条落在运行期可能出现的 RVA 区间 + (粗判据 [.text 节 RVA, SizeOfImage))。 + + 任何一步失败都直接抛异常终止编译(--traceback 的硬要求,无兜底)。 + """ + import struct + import bisect + + def _Fail(msg: str) -> None: + full: str = msg + '(这是 --traceback 的要求)' + _vlog().error(full) + raise RuntimeError(full) + + layout = _ReadPEImageLayout(exe_path) + if layout is None: + _Fail(f"--traceback 只支持 Windows PE 产物,但 {exe_path} 不是有效 PE") + image_base, size_of_image, sections = layout + if size_of_image <= 0: + _Fail(f"--traceback 读到的 PE.SizeOfImage 非正({size_of_image}),无法界定 RVA 域") + img_lo: int = image_base + img_hi: int = image_base + size_of_image + # 运行期可能出现的 RVA 区间的**粗判据**下界:.text 节 RVA(缺失时退化为最小节 RVA) + text_rva: int = min((v[0] for v in sections.values()), default=0x1000) + if '.text' in sections: + text_rva = sections['.text'][0] + max_sec_end: int = max((v[0] + v[1] for v in sections.values()), default=size_of_image) + + dwarfdump: str | None = shutil.which('llvm-dwarfdump') or shutil.which('llvm-dwarfdump.exe') + if not dwarfdump: + _Fail("--traceback 找不到 llvm-dwarfdump(请确认 PATH 内含 LLVM 工具)") + + try: + proc = subprocess.run( + [dwarfdump, '--debug-line', '--debug-info', exe_path], + capture_output=True) + except Exception as e: + _Fail(f"--traceback 调用 llvm-dwarfdump 失败: {e}") + if proc.returncode != 0: + _Fail(f"--traceback 的 llvm-dwarfdump 返回 {proc.returncode}: " + + proc.stderr.decode('utf-8', errors='replace').strip()) + dump_text: str = proc.stdout.decode('utf-8', errors='replace') + + rows: list[tuple[int, str, int]] = _ParseDwarfLineRows(dump_text) + if not rows: + _Fail(f"--traceback 未能从 {exe_path} 解析出任何 DWARF 行记录(.debug_line 为空?)") + + # ---- 行记录:逐条归一化为"运行期 RVA";不能归一的一律丢弃并统计 ---- + dropped_rows: int = 0 + dropped_row_samples: list[int] = [] + best: dict[int, tuple[str, int]] = {} + for addr, path, line in rows: + if not (img_lo <= addr < img_hi): + dropped_rows += 1 + if len(dropped_row_samples) < 5: + dropped_row_samples.append(addr) + continue + rva: int = addr - image_base + cur = best.get(rva) + if cur is None or line > cur[1]: + best[rva] = (path, line) + if dropped_rows: + _vlog().warning( + f" [--traceback] 丢弃 {dropped_rows} 条无法归一化的 DWARF 行记录" + f"(地址不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;判定原因:多为被连接器" + f" COMDAT 去重丢弃的泛型副本,其地址恰为 0)示例地址: " + + ", ".join(f"0x{a:x}" for a in dropped_row_samples)) + if not best: + _Fail(f"--traceback 从 {exe_path} 得到的行记录全部无法归一到运行期 RVA 域") + + # ---- 子程序(函数名):同样逐条归一化;不能归一的丢弃 ---- + funcs: list[tuple[int, int, str]] = _ParseDwarfSubprograms(dump_text) + func_ranges: list[tuple[int, int, str]] = [] + dropped_funcs: int = 0 + for low, high, name in funcs: + if not (img_lo <= low < img_hi): + dropped_funcs += 1 + continue + lo_rva: int = low - image_base + hi_rva: int = high - image_base + if hi_rva <= lo_rva: + hi_rva = lo_rva + 1 + if hi_rva > size_of_image: + hi_rva = size_of_image + func_ranges.append((lo_rva, hi_rva, name)) + if dropped_funcs: + _vlog().warning( + f" [--traceback] 丢弃 {dropped_funcs} 个无法归一化的 DWARF 子程序" + f"(low_pc 不在映像 VA 区间 [0x{img_lo:x}, 0x{img_hi:x}) 内;同上为去重副本)") + func_ranges.sort(key=lambda f: f[0]) + func_lows: list[int] = [f[0] for f in func_ranges] + + def _FuncNameFor(rva: int) -> str | None: + i: int = bisect.bisect_right(func_lows, rva) - 1 + if i >= 0: + low, high, name = func_ranges[i] + if low <= rva < high: + return name + return None + + # 字符串池:偏移 0 固定为一个空串('\0')哨兵,使 func_off=0 可无歧义表示 + # "无函数名";真实串偏移恒 >= 1。 + pool: bytearray = bytearray(b'\x00') + str_offsets: dict[str, int] = {'': 0} + + def _Intern(s: str) -> int: + off: int | None = str_offsets.get(s) + if off is None: + off = len(pool) + pool.extend(s.encode('utf-8')) + pool.append(0) + str_offsets[s] = off + return off + + records: list[tuple[int, int, int, int]] = [] + for rva in sorted(best.keys()): + path, line = best[rva] + name = _FuncNameFor(rva) + records.append((rva, _Intern(path), line, _Intern(name) if name else 0)) + + # ---- 写端自检(fail-fast;与运行期 LoadMapFile 的校验口径一一对应)---- + rvas: list[int] = [r[0] for r in records] + min_rva: int = rvas[0] + max_rva: int = rvas[-1] + for i in range(1, len(rvas)): + if rvas[i] <= rvas[i - 1]: + _Fail(f"--traceback 自检失败:记录 RVA 非严格升序" + f"(第 {i} 条 0x{rvas[i]:x} <= 前一条 0x{rvas[i - 1]:x},会破坏运行期二分查找)") + outside: int = sum(1 for r in rvas if not (0 <= r < size_of_image)) + if outside: + _Fail(f"--traceback 自检失败:有 {outside} 条记录的 RVA 落在 [0, SizeOfImage=0x{size_of_image:x}) 之外") + # 运行期可能出现的 RVA 区间(粗判据):[.text 节 RVA, SizeOfImage)。一条都没有 ⇒ 口径又错了。 + plausible: int = sum(1 for r in rvas if text_rva <= r < size_of_image) + if plausible <= 0: + _Fail(f"--traceback 自检失败:没有任何记录的 RVA 落在运行期可能出现的区间" + f" [0x{text_rva:x}(.text RVA), 0x{size_of_image:x}(SizeOfImage)) 内" + f"(RVA 口径疑似又错了)") + _vlog().info( + f" [--traceback] 自检: 记录={len(records)} RVA范围=[0x{min_rva:x}, 0x{max_rva:x}]" + f" PE.SizeOfImage=0x{size_of_image:x}({size_of_image})" + f" 各节最高结束 RVA=0x{max_sec_end:x}" + f" | 全部∈[0,SizeOfImage)=是 严格升序=是" + f" | 落在[.text 0x{text_rva:x},SizeOfImage)内={plausible} 条") + + out: bytearray = bytearray() + out.extend(b'TPCS') + out.extend(struct.pack(' None: + """将 active_sha1s 对应的 .obj 文件和 includes 库文件链接为可执行文件""" + obj_files = [] + merged_obj = os.path.join(self.output_dir, '_merged.obj') + if _CachedIsFile(merged_obj): + obj_files.append(merged_obj) + else: + for root, dirs, files in os.walk(self.output_dir): + for file in files: + if file.endswith('.obj'): + sha1 = file[:-4] + if sha1 in active_sha1s and sha1 not in self._include_sha1s: + obj_files.append(os.path.join(root, file)) + + if not obj_files and not self.extra_link_files: + _vlog().info("[链接] 无文件可链接") + return + + _vlog().info(f"\n[链接] 找到 {len(obj_files)} 个 .obj 文件") + + all_link_files = obj_files + self.extra_link_files + seen = set() + all_link_files = [f for f in all_link_files if not (f in seen or seen.add(f))] + if self.extra_link_files: + _vlog().info(f"[链接] 额外库文件: {len(self.extra_link_files)} 个") + for lib in self.extra_link_files: + _vlog().info(f" - {os.path.basename(lib)}") + + if not self.linker_cmd: + _vlog().info("[链接] 未配置链接器") + return + + exe_path = os.path.join(self.output_dir, self.linker_output) if self.linker_output else os.path.join(self.output_dir, 'a.exe') + is_binary = self.linker_output and self.linker_output.endswith('.bin') + + # 检查是否直接输出二进制(使用 --oformat binary / -oformat binary 标志) + is_direct_binary = is_binary and any('oformat' in f for f in self.linker_flags) + + if is_binary and not is_direct_binary: + link_output = exe_path[:-4] + '_pe.exe' + else: + link_output = exe_path + + # 自动复制或生成 linker.ld 到输出目录 + linker_ld_src = os.path.join(os.path.dirname(self.output_dir), 'linker.ld') + linker_ld_dst = os.path.join(self.output_dir, 'linker.ld') + if _CachedIsFile(linker_ld_src) and not _CachedIsFile(linker_ld_dst): + shutil.copy2(linker_ld_src, linker_ld_dst) + _vlog().info(f" [链接] 使用链接脚本: linker.ld") + elif not _CachedIsFile(linker_ld_dst) and any('-T' in f or '-T' in f.replace(',', ' ') for f in self.linker_flags): + default_ld = """ENTRY(_start) + +SECTIONS { + . = 0x100000; + + .text : { + *(.text) + *(.text.*) + } + + .rodata : { + *(.rodata) + *(.rodata.*) + } + + .data : { + *(.data) + *(.data.*) + } + + .bss : { + *(.bss) + *(.bss.*) + } + + /DISCARD/ : { + *(.comment) + *(.note) + *(.eh_frame) + *(.eh_frame_hdr) + *(.reloc) + *(.rela) + *(.debug*) + } +} +""" + with open(linker_ld_dst, 'w', encoding='utf-8', newline='\n') as f: + f.write(default_ld) + _InvalidateTextCache(linker_ld_dst) + _vlog().info(f" [链接] 自动创建默认链接脚本: linker.ld") + + try: + # 将 linker_flags 拆分为非库标志和库标志(-l/-L),库标志放在目标文件之后 + # 这样 Unix 风格链接器才能正确解析库中的符号 + # -Wl, 选项是链接器全局选项,放在目标文件之前 + non_lib_flags = [] + lib_flags = [] + i = 0 + while i < len(self.linker_flags): + flag = self.linker_flags[i] + if flag in ('-l', '-L') and i + 1 < len(self.linker_flags): + # -l xxx / -L xxx 分开的形式 + lib_flags.extend([flag, self.linker_flags[i + 1]]) + i += 2 + elif flag.startswith('-l') or flag.startswith('-L'): + # -lxxx / -Lxxx 合并的形式 + lib_flags.append(flag) + i += 1 + else: + # 其他所有标志(包括 -Wl, 选项)放在目标文件之前 + non_lib_flags.append(flag) + i += 1 + # ASan 模式:链接 ASan 运行库(配合各 .ll 的 opt -passes=asan 插桩) + # 不能传 -fsanitize=address:那会让 clang++ 驱动去找 + # /lib/x86_64-w64-windows-gnu/libclang_rt.asan_dynamic.dll.a, + # 而该目录是空的(上游 LLVM 不为这个 triple 构建 ASan runtime), + # 直接报 "could not open ... No such file or directory"。 + # 改为显式链接 clang64 的那两份(与 VPC / README §8.1 同口径)。 + # 它们是库,必须排在目标文件之后,所以追加到 lib_flags 而不是 non_lib_flags。 + if self.asan_mode: + if self.asan_rt_dir is None: + _vlog().error( + "ASan 模式在 PATH 里找不到 clang64 的 ASan 运行库目录 " + "(\\clang64\\lib\\clang\\\\lib\\windows\\" + "libclang_rt.asan_dynamic-x86_64.dll.a)\n" + " 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装," + "且 \\clang64(或其 bin)已在 PATH 中。") + sys.exit(1) + lib_flags.append('-L' + self.asan_rt_dir) + lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a') + lib_flags.append('-l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a') + # Ctrl+C 栈转储(--traceback):includes/traceback.vp 的主回溯路径用 + # StackWalk64 / SymInitialize / SymFunctionTableAccess64 / SymGetModuleBase64 + # (均在 dbghelp),故开启该开关时必须显式链接 -ldbghelp,否则链接期报未定义符号。 + # 与 ASan 同理:库要排在目标文件之后 ⇒ 追加到 lib_flags。 + # (若确实无法链 dbghelp:把 includes/traceback.vp 的 TRACEBACK_USE_DBGHELP 置 0, + # 退化为纯 RBP 链回溯,此时不该再带本 flag。) + if self.traceback_enabled: + lib_flags.append('-ldbghelp') + cmd = [self.linker_cmd] + non_lib_flags + ['-o', link_output] + all_link_files + lib_flags + _vlog().info(f" 执行: {' '.join(cmd)}") + result = subprocess.run( + cmd, + capture_output=True, + text=True, + cwd=self.output_dir or '.' + ) + if result.returncode == 0: + if is_binary and not is_direct_binary: + objcopy = shutil.which('llvm-objcopy') or shutil.which('objcopy') + if objcopy: + conv = subprocess.run( + [objcopy, '-O', 'binary', link_output, exe_path], + capture_output=True, text=True + ) + if conv.returncode == 0: + os.remove(link_output) + _vlog().success(f" 生成裸机二进制: {exe_path}") + file_size = os.path.getsize(exe_path) + _vlog().info(f" [大小] {file_size} 字节") + else: + _vlog().error(f" 二进制转换失败: {conv.stderr.strip()}") + sys.exit(1) + else: + _vlog().error(" 找不到 llvm-objcopy,无法生成裸机二进制") + sys.exit(1) + else: + _vlog().success(f" 生成: {exe_path}") + file_size = os.path.getsize(exe_path) + _vlog().info(f" [大小] {file_size} 字节") + # --dwarf 构建(含 --asan 自动开启)必须保留 DWARF: + # 该模式存在的唯一意义是崩溃时反查 ".vp:行", + # 而 _strip_debug_sections 会 llvm-strip --strip-debug 掉全部 + # .debug_* 段(实测 exe 体积 13378048 → 11895296, + # llvm-symbolizer 对含 main 在内的所有地址返回 ??:0:0)。 + # 非 dwarf 构建保持原有剥离行为(产物零变化)。 + if not is_binary and not self.dwarf_enabled: + self._strip_debug_sections(exe_path) + else: + _vlog().error(f" 链接失败: {result.stderr.strip()}") + _vlog().error(f"\n链接失败,立即终止编译。") + sys.exit(1) + except FileNotFoundError: + _vlog().error(f" 找不到链接器: {self.linker_cmd}") + _vlog().error(f"\n找不到链接器,立即终止编译。") + sys.exit(1) + except Exception as e: + _vlog().error(f" {e}") + _vlog().error(f"\n链接异常,立即终止编译。") + sys.exit(1) + + # --traceback:链接成功后导出"地址→源行"旁文件(.stackmap)。 + # 刻意放在上面的链接 try/except 之外,使导出阶段的 fail-fast RuntimeError + # 直接向上抛出终止编译,而不被"链接异常兜底"吞掉。 + if self.traceback_enabled: + self._WriteStackMap(exe_path) diff --git a/lib/StubGen/Converter.py b/lib/StubGen/Converter.py index 3daccb9..ac79b32 100644 --- a/lib/StubGen/Converter.py +++ b/lib/StubGen/Converter.py @@ -1,1133 +1,1133 @@ -"""StubGen 转换器模块 - 将 Python 文件转换为存根格式""" -from __future__ import annotations -import vast as ast -import re -import fnmatch -from typing import List, Dict -from lib.core.SymbolUtils import IsListAnnotation, ExpandSoftKeywords - - -# 模块级转换结果缓存:{ (PyContent, ModuleName): str } -# convert 是纯函数(类级配置 IncludeAliasMap/VarExcludeList/MacroExcludeList 运行期不变), -# 相同输入必然产生相同输出。结果字符串不可变,调用方仅将其写入文件或拼接出新字符串, -# 缓存结果可安全复用(无需深拷贝)。Set* 配置方法会清空缓存以防配置变化导致结果过期。 -_ConvertResultCache: dict[tuple[str, str], str] = {} -_ConvertCacheMaxSize: int = 4096 - -# 提取 import 语句行尾注释(`_GetImportString` / `_GetImportFromString`)用的 -# **预编译**正则(性能:字面量正则预编译避免 re 缓存查找与编译开销)。 -_RE_LINE_COMMENT: re.Pattern[str] = re.compile(r'#.*$') - - -class PythonToStubConverter: - """将 Python 文件转换为存根格式""" - - # __include 别名映射表 {alias: ModuleName} - IncludeAliasMap: Dict[str, str] = {} - - # 变量排除列表 - 匹配这些模式的变量不会被添加到存根文件 - VarExcludeList: List[str] = [] - - # 宏排除列表 - 匹配这些模式的宏不会被添加到存根文件 - MacroExcludeList: List[str] = [] - - @classmethod - def SetIncludeAliasMap(cls, alias_map: Dict[str, str]) -> None: - """设置 __include 别名映射表""" - cls.IncludeAliasMap = alias_map - _ConvertResultCache.clear() - - @classmethod - def SetVarExcludeList(cls, exclude_list: List[str]) -> None: - """设置变量排除列表""" - cls.VarExcludeList = exclude_list - _ConvertResultCache.clear() - - @classmethod - def SetMacroExcludeList(cls, exclude_list: List[str]) -> None: - """设置宏排除列表""" - cls.MacroExcludeList = exclude_list - _ConvertResultCache.clear() - - @classmethod - def _ShouldExcludeVar(cls, VarName: str) -> bool: - """检查变量是否应该被排除""" - for pattern in cls.VarExcludeList: - if fnmatch.fnmatch(VarName, pattern): - return True - return False - - @classmethod - def _ShouldExcludeMacro(cls, MacroName: str) -> bool: - """检查宏是否应该被排除""" - for pattern in cls.MacroExcludeList: - if fnmatch.fnmatch(MacroName, pattern): - return True - return False - - @staticmethod - def convert(PyContent: str, ModuleName: str) -> str: - """将 Python 代码转换为存根格式""" - # 软关键字展开:fn/fun/func/function -> def(行内等宽/更短,行号不变), - # 保证后续 ast.parse 与 SourceLines 回读行号一致。 - PyContent = ExpandSoftKeywords(PyContent) - - # 模块级缓存:同一 (内容, 模块名) 直接复用上次转换结果(纯函数输出,不可变字符串) - CacheKey: tuple[str, str] = (PyContent, ModuleName) - CachedResult: str | None = _ConvertResultCache.get(CacheKey) - if CachedResult is not None: - return CachedResult - - lines: list[str] = PyContent.split('\n') - StubLines: list[str] = [] - - # 添加文件头 - StubLines.append('"""') - StubLines.append(f'Auto-generated Python stub file from {ModuleName}.vp') - StubLines.append(f'Module: {ModuleName}') - StubLines.append('"""') - StubLines.append('') - - # 解析 Python 代码,检查是否已经导入了 t 和 c(AST 与下方主解析复用,避免重复 parse) - HasImportT: bool = False - HasImportC: bool = False - try: - # 传来源名:解析失败时 vast 的报错/回退诊断才带得上模块名(否则记为 ) - tree: ast.Module = ast.parse(PyContent, f'{ModuleName}.vp') - for node in tree.body: - if isinstance(node, ast.Import): - for alias in node.names: - if alias.name == 't': - HasImportT = True - elif alias.name == 'c': - HasImportC = True - except SyntaxError: - # 语法错误:同一内容两次解析结果必然相同,与主循环 fail-fast 行为一致 - raise - except Exception: - # 非语法类解析异常:保持原语义(第一遍吞掉,主循环重新解析后向上传播) - tree = None - - # 添加默认导入(如果源代码中没有) - if not HasImportT: - StubLines.append('import t') - if not HasImportC: - StubLines.append('import c') - if not HasImportT or not HasImportC: - StubLines.append('') - - StubLines.append('') - - # 解析 Python 代码,提取类型定义(复用上方的 parse 结果) - try: - if tree is None: - # 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义 - tree = ast.parse(PyContent, f'{ModuleName}.vp') - - # 第一遍扫描:收集 Postdefinition 变量 - PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]} - for node in tree.body: - if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): - TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation) - # 检查是否是 Postdefinition 类型 - if 't.Postdefinition' in TypeStr: - # 提取引用的类名 - ClassName: str | None = PythonToStubConverter._ExtractPostdefClass(node.annotation) - if ClassName: - if ClassName not in PostdefVars: - PostdefVars[ClassName] = [] - PostdefVars[ClassName].append((node.target.id, TypeStr, node)) - - # 按原始顺序处理节点,保持代码的原始顺序 - PrevType: str | None = None # 记录前一个节点的类型 - - for node in tree.body: - CurrentType: str | None = None - - if isinstance(node, ast.ClassDef): - CurrentType = 'class' - # 如果前一个不是类,添加空行分隔 - if PrevType is not None and PrevType != 'class': - StubLines.append('') - # 检查是否有对应的 Postdefinition 变量 - ClassPostdefs: list[tuple[str, str, ast.AnnAssign]] = PostdefVars.get(node.name, []) - ClassLines: list[str] = PythonToStubConverter._ConvertClass( - node, SourceLines=lines, PostdefVars=ClassPostdefs - ) - StubLines.extend(ClassLines) - elif isinstance(node, ast.FunctionDef): - CurrentType = 'function' - # 如果前一个是类或者是第一个函数,添加空行分隔 - if PrevType == 'class' or (PrevType is not None and PrevType != 'function'): - StubLines.append('') - FuncLines: list[str] = PythonToStubConverter._ConvertFunction(node, SourceLines=lines) - StubLines.extend(FuncLines) - StubLines.append('') # 函数后空行 - elif isinstance(node, ast.AnnAssign): - CurrentType = 'variable' - # 跳过 Postdefinition 变量(已经在类中处理) - TypeStr = PythonToStubConverter._GetTypeString(node.annotation) - if 't.Postdefinition' in TypeStr: - continue - # 检查是否带有 t.CStatic 标志,如果有则不排除 - HasStatic: bool = 't.CStatic' in TypeStr - # 检查变量是否应该被排除 - ShouldExclude: bool = False - if isinstance(node.target, ast.Name) and not HasStatic: - VarName: str = node.target.id - if PythonToStubConverter._ShouldExcludeVar(VarName): - ShouldExclude = True - if ShouldExclude: - continue - # 如果前一个不是变量,添加空行分隔 - if PrevType is not None and PrevType != 'variable': - StubLines.append('') - VarLines: list[str] = PythonToStubConverter._ConvertVariable(node, SourceLines=lines) - # 移除多余的空行 - VarLines = [line for line in VarLines if line.strip()] - StubLines.extend(VarLines) - elif isinstance(node, ast.Import): - # 处理导入语句 - CurrentType = 'import' - ImportStr: str = PythonToStubConverter._GetImportString(node, SourceLines=lines) - if ImportStr: - if PrevType is not None and PrevType != 'import': - StubLines.append('') - StubLines.append(ImportStr) - elif isinstance(node, ast.Assign): - CurrentType = 'variable' - # 处理模块级别的全局变量(无类型标注) - if PrevType is not None and PrevType != 'variable': - StubLines.append('') - AssignLines: list[str] = PythonToStubConverter._ConvertGlobalAssign(node) - StubLines.extend(AssignLines) - elif isinstance(node, ast.ImportFrom): - # 处理 from ... import ... 语句 - CurrentType = 'import' - ImportStr = PythonToStubConverter._GetImportFromString(node, SourceLines=lines) - if ImportStr: - if PrevType is not None and PrevType != 'import': - StubLines.append('') - StubLines.append(ImportStr) - elif isinstance(node, ast.Expr): - # 处理模块级别的宏调用,如 c.CUndef(...) / c.CPragma(...) - ExprStr: str = PythonToStubConverter._GetExprString(node.value) - if ExprStr: - CurrentType = 'macro' - StubLines.append(ExprStr) - StubLines.append('') # 宏后空行 - elif isinstance(node, ast.If): - # 处理模块级别的 cif 宏条件,如 `cif FF_MULTI_PARTITION:` - IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node) - if IfStr: - CurrentType = 'macro' - StubLines.append(IfStr) - StubLines.append('') # 宏后空行 - - if CurrentType: - PrevType = CurrentType - - except SyntaxError as e: - # 语法错误直接终止(fail-fast),不允许生成残缺 stub 被全局缓存命中。 - # 残缺 vpi 只含 import 头,函数/类型声明全缺,会在后续阶段以 - # undefined symbol / mangle 错误形式爆发(如 ExpandKeywordsTest 链接失败)。 - raise - - Result: str = '\n'.join(StubLines) - # 缓存上限保护:条目过多时整体清空,避免内存无限增长(结果可随时重算) - if len(_ConvertResultCache) >= _ConvertCacheMaxSize: - _ConvertResultCache.clear() - _ConvertResultCache[CacheKey] = Result - return Result - - @staticmethod - def _ConvertClass(node: ast.ClassDef, SourceLines: list[str] | None = None, PostdefVars: list[tuple[str, str, ast.AnnAssign]] | None = None, indent: int = 0) -> list[str]: - """转换类定义 - - Args: - node: 类定义节点 - SourceLines: 源代码行列表 - PostdefVars: Postdefinition 变量列表 [(VarName, TypeStr, node), ...] - indent: 缩进级别(用于嵌套类) - """ - lines: list[str] = [] - PostdefVars = PostdefVars or [] - IndentStr: str = ' ' * indent - - # 处理类装饰器 - for decorator in node.decorator_list: - DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator) - if DecoratorStr: - lines.append(f'{IndentStr}@{DecoratorStr}') - - # 检查是否是结构体、联合体或枚举 - BaseNames: list[str] = [base.attr if isinstance(base, ast.Attribute) else base.id - for base in node.bases - if isinstance(base, (ast.Name, ast.Attribute))] - - # 保留原始基类 - if node.bases: - BaseStrs: list[str] = [] - for base in node.bases: - if isinstance(base, ast.Name): - BaseStrs.append(base.id) - elif isinstance(base, ast.Attribute): - BaseStrs.append(f'{base.value.id}.{base.attr}') - elif isinstance(base, ast.Subscript): - # 处理泛型类型,如 memory_block_t[MAX_ORDER + 1] - BaseStrs.append(PythonToStubConverter._GetTypeString(base)) - ClassTypeParamStr: str = '' - if hasattr(node, 'type_params') and node.type_params: - param_names: list[str] = [tp.name for tp in node.type_params] - ClassTypeParamStr = f'[{", ".join(param_names)}]' - if BaseStrs: - lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}({", ".join(BaseStrs)}):') - else: - lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:') - else: - ClassTypeParamStr = '' - if hasattr(node, 'type_params') and node.type_params: - param_names = [tp.name for tp in node.type_params] - ClassTypeParamStr = f'[{", ".join(param_names)}]' - lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:') - - # 处理类成员 - HasMembers: bool = False - seen_member_names: set[str] = set() - for item in node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - seen_member_names.add(item.target.id) - init_members: list[ast.AnnAssign] = [] - for item in node.body: - if isinstance(item, ast.FunctionDef) and item.name == '__init__': - for stmt in item.body: - if (isinstance(stmt, ast.AnnAssign) - and isinstance(stmt.target, ast.Attribute) - and isinstance(stmt.target.value, ast.Name) - and stmt.target.value.id == 'self'): - init_members.append(stmt) - untyped_self_members: list[ast.Assign] = [] - for m_name in [m.target.attr for m in init_members if isinstance(m.target, ast.Attribute)]: - seen_member_names.add(m_name) - for item in node.body: - if isinstance(item, ast.FunctionDef): - for stmt in item.body: - if (isinstance(stmt, ast.Assign) - and len(stmt.targets) == 1 - and isinstance(stmt.targets[0], ast.Attribute) - and isinstance(stmt.targets[0].value, ast.Name) - and stmt.targets[0].value.id == 'self'): - attr_name: str = stmt.targets[0].attr - if attr_name not in seen_member_names: - seen_member_names.add(attr_name) - untyped_self_members.append(stmt) - for item in list(node.body) + init_members + untyped_self_members: - if isinstance(item, ast.Assign): - if (len(item.targets) == 1 - and isinstance(item.targets[0], ast.Attribute) - and isinstance(item.targets[0].value, ast.Name) - and item.targets[0].value.id == 'self'): - HasMembers = True - MemberIndent: str = IndentStr + ' ' - attr_name = item.targets[0].attr - if attr_name in seen_member_names: - continue - InferredType: str = 'int' - if item.value and isinstance(item.value, ast.Constant): - if isinstance(item.value.value, float): - InferredType = 'float' - elif isinstance(item.value.value, bool): - InferredType = 'bool' - elif isinstance(item.value.value, str): - InferredType = 'str' - lines.append(f'{MemberIndent}{attr_name}: {InferredType}') - else: - HasMembers = True - MemberIndent = IndentStr + ' ' - if SourceLines and hasattr(item, 'lineno'): - StartLine: int = item.lineno - 1 - EndLine: int = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1 - for i in range(StartLine, min(EndLine, len(SourceLines))): - lines.append(MemberIndent + SourceLines[i].lstrip()) - elif isinstance(item, ast.AnnAssign): - HasMembers = True - TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) - MemberIndent = IndentStr + ' ' - # __provides__/__requires__/__require_must__ 元数据:保留值以便跨模块加载 - if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'): - if item.value is not None: - try: - value_str: str = ast.unparse(item.value) - except Exception: - value_str = '' - lines.append(f'{MemberIndent}{item.target.id}: {TypeStr} = {value_str}') - else: - lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}') - continue - # 检查是否是宏变量(类型包含 t.CDefine) - if 't.CDefine' in TypeStr: - # 检查宏变量是否应该被排除 - if isinstance(item.target, ast.Name): - MacroName: str = item.target.id - if PythonToStubConverter._ShouldExcludeMacro(MacroName): - continue # 跳过该宏 - # 宏变量保持原样(包括赋值) - if SourceLines and hasattr(item, 'lineno'): - StartLine = item.lineno - 1 - EndLine = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1 - for i in range(StartLine, min(EndLine, len(SourceLines))): - lines.append(MemberIndent + SourceLines[i].lstrip()) - else: - lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}') - else: - if isinstance(item.target, ast.Attribute): - MemberName: str = item.target.attr - else: - MemberName = item.target.id - FinalTypeStr: str = TypeStr - if IsListAnnotation(item.annotation): - slice_node: ast.AST = item.annotation.slice - has_size: bool = False - if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: - count_node: ast.AST = slice_node.elts[1] - if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0: - has_size = True - else: - try: - count_expr: str = ast.unparse(count_node) - except Exception: - count_expr = '' - if count_expr and count_expr != 'None': - has_size = True - elem_type_str: str = PythonToStubConverter._GetTypeString(slice_node.elts[0]) - FinalTypeStr = f't.CArray[{elem_type_str}, {count_expr}]' - if not has_size: - elem_type_str = PythonToStubConverter._GetTypeString(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0]) - init_len: int = 0 - if item.value is not None: - if isinstance(item.value, ast.List): - init_len = len(item.value.elts) - elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str): - init_len = len(item.value.value) + 1 - if init_len > 0: - FinalTypeStr = f't.CArray[{elem_type_str}, {init_len}]' - else: - FinalTypeStr = f't.CArray[{elem_type_str}, None]' - if item.value is not None and isinstance(item.value, ast.Constant): - try: - value_repr: str = ast.unparse(item.value) - lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr} = {value_repr}') - except Exception: - lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}') - else: - lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}') - elif isinstance(item, ast.FunctionDef): - HasMembers = True - FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name) - lines.extend(FuncStub) - elif isinstance(item, ast.Expr): - # 处理宏调用,如 c.CUndef(...) / c.CPragma(...) - ExprStr: str = PythonToStubConverter._GetExprString(item.value) - if ExprStr: - lines.append(f'{IndentStr} {ExprStr}') - elif isinstance(item, ast.If): - # 处理 cif 宏条件(`cif ...:` / `celif ...:` / `celse:`) - IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1) - if IfStr: - lines.append(IfStr) - elif isinstance(item, ast.ClassDef): - # 处理嵌套类 - HasMembers = True - NestedClassLines: list[str] = PythonToStubConverter._ConvertClass(item, SourceLines=SourceLines, PostdefVars=[], indent=indent+1) - # 移除嵌套类的宏守卫(因为已经在父类的宏守卫内) - FilteredLines: list[str] = [] - for line in NestedClassLines: - # 跳过宏守卫相关行 - if line.strip().startswith('c.CIfndef(') or line.strip().startswith('c.CEndif()'): - continue - if ': t.CDefine' in line and '__' in line: - continue - FilteredLines.append(line) - lines.extend(FilteredLines) - - if not HasMembers: - lines.append(f'{IndentStr} pass') - - # 添加 Postdefinition 变量 - if PostdefVars: - lines.append('') - for VarName, TypeStr, _ in PostdefVars: - lines.append(f'{VarName}: {TypeStr}') - - return lines - - @staticmethod - def _ConvertFunction(node: ast.FunctionDef, indent: int = 0, SourceLines: list[str] | None = None, class_name: str | None = None) -> list[str]: - """转换函数定义""" - lines: list[str] = [] - IndentStr: str = ' ' * indent - - # 检查是否是宏函数(返回类型包含 t.CDefine) - is_macro_func: bool = False - if node.returns: - ReturnType: str = PythonToStubConverter._GetTypeString(node.returns) - if 't.CDefine' in ReturnType: - is_macro_func = True - - # 如果是宏函数,检查是否应该被排除 - if is_macro_func: - if PythonToStubConverter._ShouldExcludeMacro(node.name): - return lines # 返回空列表,排除该宏 - - # 保持原样(包括代码块) - if SourceLines: - StartLine: int = node.lineno - 1 - EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 - Copied: list[str] = [SourceLines[i] - for i in range(StartLine, min(EndLine, len(SourceLines)))] - # 丢弃尾随空行:解析器的 end_lineno 会被紧随的空行 NL 顶到下一行 - # (vast Parser._ParseSuite 单行套件分支返回前 `_SkipNL()` 吞掉空行 NL, - # 调用方 _FixLoc 取 _Prev() 即该 NL)。若不丢弃,`define f(...): return x` - # 这类内联体宏末尾会多出空白行,使下方"末行是否已有内联体"判据落空, - # 凭空补一行 ` pass` → 存根缩进非法(hashlib.__sha512 第 29 行)。 - while Copied and not Copied[-1].strip(): - Copied.pop() - lines.extend(Copied) - if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'): - pass - elif lines: - lines.append(f'{IndentStr} pass') - else: - # 源区间为空(理论不可达):产出合法占位,绝不落出 ` pass` 这类非法缩进 - lines.append(f'{IndentStr}pass') - return lines - - # 处理函数装饰器 - for decorator in node.decorator_list: - DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator) - if DecoratorStr: - lines.append(f'{IndentStr}@{DecoratorStr}') - - # 构建参数列表 - # 治本修复:原实现只遍历 node.args.args,完全忽略 node.args.defaults, - # 导致带默认值的参数(如 category: str = "")在 .pyi 中丢失默认值。 - # 下游 DeclarationGenerator 据此生成 stub.ll 时,会因缺少默认参数而 - # 生成参数数量不足的 declare,进而造成调用方 ABI 不匹配崩溃。 - # 修复:按 Python AST 语义,defaults 从右向左对应 args 末尾的参数, - # 对有默认值的参数附加 '= '。 - params: list[str] = [] - NumDefaults: int = len(node.args.defaults) if node.args.defaults else 0 - DefaultsStart: int = len(node.args.args) - NumDefaults - for arg_idx, arg in enumerate(node.args.args): - ArgName: str = arg.arg - if arg.annotation: - TypeStr: str = PythonToStubConverter._GetTypeString(arg.annotation) - ParamPart: str = f'{ArgName}: {TypeStr}' - elif class_name and arg_idx == 0 and ArgName == 'self': - ParamPart = f'self: {class_name}' - else: - ParamPart = ArgName - # 附加默认值:defaults[i] 对应 args[DefaultsStart + i] - if NumDefaults > 0 and arg_idx >= DefaultsStart: - DefaultIdx: int = arg_idx - DefaultsStart - try: - DefaultStr: str = ast.unparse(node.args.defaults[DefaultIdx]) - ParamPart = f'{ParamPart} = {DefaultStr}' - except Exception: - # unparse 失败时保留无默认值形式,避免阻塞生成 - pass - params.append(ParamPart) - - # 处理 *args - if node.args.vararg: - ArgName = node.args.vararg.arg - if node.args.vararg.annotation: - TypeStr = PythonToStubConverter._GetTypeString(node.args.vararg.annotation) - params.append(f'*{ArgName}: {TypeStr}') - else: - params.append(f'*{ArgName}') - - # 处理 **kwargs - if node.args.kwarg: - ArgName = node.args.kwarg.arg - if node.args.kwarg.annotation: - TypeStr = PythonToStubConverter._GetTypeString(node.args.kwarg.annotation) - params.append(f'**{ArgName}: {TypeStr}') - else: - params.append(f'**{ArgName}') - - ParamStr: str = ', '.join(params) - - # 返回类型 - 保持原始类型(不添加t.State) - if node.returns: - ReturnType = PythonToStubConverter._GetTypeString(node.returns) - else: - ReturnType = 't.CInt' - - TypeParamStr: str = '' - if hasattr(node, 'type_params') and node.type_params: - param_names: list[str] = [tp.name for tp in node.type_params] - TypeParamStr = f'[{", ".join(param_names)}]' - - lines.append(f'{IndentStr}def {node.name}{TypeParamStr}({ParamStr}) -> {ReturnType}: pass') - return lines - - @staticmethod - def _ConvertVariable(node: ast.AnnAssign, SourceLines: list[str] | None = None) -> list[str]: - """转换变量定义""" - lines: list[str] = [] - if isinstance(node.target, ast.Name): - VarName: str = node.target.id - TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation) - # 检查是否是宏变量(类型包含 t.CDefine) - if 't.CDefine' in TypeStr: - # 检查宏变量是否应该被排除 - if PythonToStubConverter._ShouldExcludeMacro(VarName): - return lines # 返回空列表,排除该宏 - # 宏变量保持原样(包括赋值) - if SourceLines and hasattr(node, 'lineno'): - StartLine: int = node.lineno - 1 - EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 - for i in range(StartLine, min(EndLine, len(SourceLines))): - lines.append(SourceLines[i]) - else: - lines.append(f'{VarName}: {TypeStr}') - return lines - # 非宏变量:不加 t.State - # 含有 typedef 的变量不加 extern,普通变量需要加 extern - has_typedef: bool = 't.CTypedef' in TypeStr or 'CTypedef' in TypeStr - if has_typedef and node.value is not None: - ValueTypeStr: str = PythonToStubConverter._GetTypeString(node.value) - lines.append(f'{VarName}: {TypeStr} = {ValueTypeStr}') - else: - if not has_typedef and 't.CExtern' not in TypeStr and 't.CStatic' not in TypeStr: - TypeStr = f't.CExtern | {TypeStr}' - lines.append(f'{VarName}: {TypeStr}') - return lines - - @staticmethod - def _ConvertGlobalAssign(node: ast.Assign) -> list[str]: - """转换模块级别的全局变量(无类型标注)""" - lines: list[str] = [] - if not node.targets or not isinstance(node.targets[0], ast.Name): - return lines - - VarName: str = node.targets[0].id - - # 跳过私有变量(以下划线开头) - if VarName.startswith('_'): - return lines - - # 从值推断类型 - inferred_type: str | None = PythonToStubConverter._InferTypeFromValue(node.value) - if inferred_type: - lines.append(f'{VarName}: {inferred_type}') - else: - lines.append(f'{VarName}: t.CInt') - - return lines - - @staticmethod - def _InferTypeFromValue(value: ast.AST) -> str | None: - """从值推断 C 类型""" - if isinstance(value, ast.Constant): - if isinstance(value.value, int): - return 't.CInt' - elif isinstance(value.value, float): - return 't.CDouble' - elif isinstance(value.value, str): - return 't.CCharPtr' - elif isinstance(value.value, bool): - return 't.CInt' - elif isinstance(value, ast.List): - return 't.CVoidPtr' - elif isinstance(value, ast.Dict): - return 't.CVoidPtr' - elif isinstance(value, ast.Name): - return 't.CInt' - elif isinstance(value, ast.BinOp): - left_type: str | None = PythonToStubConverter._InferTypeFromValue(value.left) - if left_type: - return left_type - return 't.CInt' - elif isinstance(value, ast.Call): - # 函数调用返回值,假设为指针类型 - if isinstance(value.func, ast.Name): - func_name: str = value.func.id - if func_name == 'malloc': - return 't.CVoidPtr' - elif func_name in ('ctypes.cast', 'cast'): - return 't.CVoidPtr' - return 't.CVoidPtr' - return None - - @staticmethod - def _GetTypeString(annotation: ast.AST) -> str: - """获取类型字符串 - - 支持集与 TPV(TransPyV/App/lib/Stub/StubGen/Converter.vp 的 _PyiTypeStr) - 等价:Name/Attribute/Subscript/BinOp/UnaryOp/Compare/BoolOp/Constant/ - List/Call(TPV 另支持裸 Tuple,本侧仅在 Subscript.slice 位置接受 Tuple)。 - 未支持的节点种类与运算符在此 fail-fast(不自造 't.CVoid' 回落)。 - """ - # 无注解(None):调用方未提供注解,沿用原语义返回 't.CVoid'; - # 这不是"未知节点"——未知节点走函数尾 fail-fast。 - if annotation is None: - return 't.CVoid' - if isinstance(annotation, ast.Name): - return annotation.id - elif isinstance(annotation, ast.Attribute): - if isinstance(annotation.value, ast.Name): - return f'{annotation.value.id}.{annotation.attr}' - else: - # 处理嵌套属性访问,如 t.attr.packed - ParentStr: str = PythonToStubConverter._GetTypeString(annotation.value) - return f'{ParentStr}.{annotation.attr}' - elif isinstance(annotation, ast.Subscript): - base: str = PythonToStubConverter._GetTypeString(annotation.value) - if isinstance(annotation.slice, ast.Tuple): - slice_strs: list[str] = [PythonToStubConverter._GetTypeString(e) for e in annotation.slice.elts] - SliceStr: str = ', '.join(slice_strs) - else: - SliceStr = PythonToStubConverter._GetTypeString(annotation.slice) - return f'{base}[{SliceStr}]' - elif isinstance(annotation, ast.BinOp): - # 处理 BinOp,如 t.CExtern | fd_table.__set_default__(...) - left: ast.AST = annotation.left - right: ast.AST = annotation.right - - # 值引用参数标记:联合形态 `T | t.CRef`(ref)/ `T | t.CTempRef`(temp) - # 还原为源码写法 `&T` / `&temp T`,使跨模块 .vpi 往返保留取址模式。 - if isinstance(annotation.op, ast.BitOr): - _ref_kind: str | None = None - _type_side: ast.AST | None = None - if isinstance(right, ast.Attribute) and right.attr in ('CRef', 'CTempRef'): - _ref_kind = right.attr - _type_side = left - elif isinstance(left, ast.Attribute) and left.attr in ('CRef', 'CTempRef'): - _ref_kind = left.attr - _type_side = right - if _ref_kind is not None and _type_side is not None: - _inner: str = PythonToStubConverter._GetTypeString(_type_side) - return f'&temp {_inner}' if _ref_kind == 'CTempRef' else f'&{_inner}' - - # 检查右侧是否是 __set_default__ 调用 - if isinstance(right, ast.Call) and isinstance(right.func, ast.Attribute) and right.func.attr in ('__set_default__', '__set_default'): - # 去除 __set_default__,保留左侧,右侧替换为函数名部分 - LeftStr: str = PythonToStubConverter._GetTypeString(left) - RightStr: str = PythonToStubConverter._GetTypeString(right.func.value) - if isinstance(annotation.op, ast.BitOr): - return f'{LeftStr} | {RightStr}' - - LeftStr = PythonToStubConverter._GetTypeString(left) - RightStr = PythonToStubConverter._GetTypeString(right) - if isinstance(annotation.op, ast.BitOr): - return f'{LeftStr} | {RightStr}' - elif isinstance(annotation.op, ast.Add): - return f'{LeftStr} + {RightStr}' - elif isinstance(annotation.op, ast.Sub): - return f'{LeftStr} - {RightStr}' - elif isinstance(annotation.op, ast.Mult): - return f'{LeftStr} * {RightStr}' - elif isinstance(annotation.op, ast.Div): - return f'{LeftStr} / {RightStr}' - elif isinstance(annotation.op, ast.Mod): - return f'{LeftStr} % {RightStr}' - # 位与/右移:条件编译实参(如 cif MASK_BYTE & 0x0F、 - # cif (MAX_SIZE >> SHIFT_BITS) > 0)。两运算符必须与 TPV 的 - # _PyiBinOpOpText 保持一致,否则同一份源码两侧写出的 vpi 文本不同。 - elif isinstance(annotation.op, ast.BitAnd): - return f'{LeftStr} & {RightStr}' - elif isinstance(annotation.op, ast.RShift): - return f'{LeftStr} >> {RightStr}' - # 其余二元运算符(LShift/BitXor/FloorDiv/Pow/MatMult 等)不支持: - # 落到函数尾 fail-fast(与 TPV 同语义) - elif isinstance(annotation, ast.UnaryOp): - if isinstance(annotation.op, ast.Not): - operand: str = PythonToStubConverter._GetTypeString(annotation.operand) - return f'not {operand}' - elif isinstance(annotation.op, ast.Invert): - operand = PythonToStubConverter._GetTypeString(annotation.operand) - return f'~{operand}' - elif isinstance(annotation.op, ast.USub): - operand = PythonToStubConverter._GetTypeString(annotation.operand) - return f'-{operand}' - elif isinstance(annotation, ast.Compare): - # 处理比较操作,如 FF_MAX_SS != FF_MIN_SS - left: str = PythonToStubConverter._GetTypeString(annotation.left) - comparators: list[str] = [PythonToStubConverter._GetTypeString(c) for c in annotation.comparators] - ops: list[str] = [] - for op in annotation.ops: - if isinstance(op, ast.Eq): - ops.append('==') - elif isinstance(op, ast.NotEq): - ops.append('!=') - elif isinstance(op, ast.Lt): - ops.append('<') - elif isinstance(op, ast.LtE): - ops.append('<=') - elif isinstance(op, ast.Gt): - ops.append('>') - elif isinstance(op, ast.GtE): - ops.append('>=') - else: - ops.append('?') - result: str = left - for i, op in enumerate(ops): - result += f' {op} {comparators[i]}' - return result - elif isinstance(annotation, ast.BoolOp): - # 布尔运算:条件编译实参(如 cif ENABLED and MAX_SIZE > 0)。 - # 文本与 TPV 的 BoolOp 分支一致:and → ' and '、or → ' or '。 - Joiner: str = ' or ' if isinstance(annotation.op, ast.Or) else ' and ' - return Joiner.join([PythonToStubConverter._GetTypeString(v) for v in annotation.values]) - elif isinstance(annotation, ast.Constant): - return repr(annotation.value) - elif isinstance(annotation, ast.Index): - return PythonToStubConverter._GetTypeString(annotation.value) - elif isinstance(annotation, ast.List): - # 处理列表,如 [MAX_FILE_DESCRIPTORS] - elements: list[str] = [PythonToStubConverter._GetTypeString(elem) for elem in annotation.elts] - return f'[{", ".join(elements)}]' - elif isinstance(annotation, ast.Call): - # 处理函数调用形式的类型注解,如 t.Postdefinition(xxa) 或 callable(t.CVoid, app_id = t.CInt) - # 如果是 __set_default__ 调用,只返回函数名部分 - if isinstance(annotation.func, ast.Attribute) and annotation.func.attr in ('__set_default__', '__set_default'): - return PythonToStubConverter._GetTypeString(annotation.func.value) - - FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func) - ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in annotation.args]) - # 处理关键字参数 - keywords_str: str = ', '.join([f'{kw.arg} = {PythonToStubConverter._GetTypeString(kw.value)}' for kw in annotation.keywords]) - # 合并位置参数和关键字参数 - if ArgsStr and keywords_str: - return f'{FuncStr}({ArgsStr}, {keywords_str})' - elif keywords_str: - return f'{FuncStr}({keywords_str})' - else: - return f'{FuncStr}({ArgsStr})' - # 未知/未支持的注解节点 fail-fast(与 TPV StubGen 的 _PyiTypeStr 同语义、 - # 同文案、同类型):静默回落 't.CVoid' 会把错误类型写进 stub 签名——实例: - # 源里的 t.Callable[[], int] 曾写成 t.Callable[int, int](见 - # ViperApps/ViperOS/VKernel/.tpv_build/temp/0b/0bb176432facf634.vpi:23), - # 下游按错误签名解析且无任何提示。 - # 归因信息:节点 kind + 行号;文件归因由调用方在日志里带上 - # (Generator.GenerateStub → "Failed to process <文件>", - # Phase1Generator → "处理 include 文件失败 <相对路径>")。 - KindName: str = type(annotation).__name__ - LineNo: int = getattr(annotation, 'lineno', 0) - raise TypeError(f'vpi 类型注解节点类型未知: {KindName} (line {LineNo})') - - @staticmethod - def _ExtractPostdefClass(annotation: ast.AST) -> str | None: - """从 Postdefinition 类型注解中提取类名 - - 例如:t.Postdefinition(__buddy_system) -> '__buddy_system' - t.Postdefinition(xxx) | t.CTypedef -> 'xxx' - """ - # 处理 BinOp (如: t.Postdefinition(xxx) | t.CTypedef) - if isinstance(annotation, ast.BinOp): - # 递归检查左操作数 - left_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.left) - if left_result: - return left_result - # 递归检查右操作数 - right_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.right) - if right_result: - return right_result - return None - - # 处理函数调用 (如: t.Postdefinition(__buddy_system)) - if isinstance(annotation, ast.Call): - FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func) - if FuncStr == 't.Postdefinition' and annotation.args: - # 提取第一个参数 - first_arg: ast.AST = annotation.args[0] - if isinstance(first_arg, ast.Name): - return first_arg.id - elif isinstance(first_arg, ast.Attribute): - return f'{first_arg.value.id}.{first_arg.attr}' - - return None - - @staticmethod - def _GetImportString(node: ast.Import, SourceLines: list[str] | None = None) -> str: - """获取导入语句字符串""" - parts: list[str] = [] - IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap - - for alias in node.names: - ModuleName: str = alias.name - # 检查是否是 __include.xxx 格式的导入 - if ModuleName.startswith('__include.'): - # 提取别名(xxx 部分) - SubModule: str = ModuleName[len('__include.'):] - if SubModule in IncludeAliasMap: - # 替换为实际模块路径 - RealModule: str = IncludeAliasMap[SubModule] - if alias.asname: - parts.append(f'{RealModule} as {alias.asname}') - else: - parts.append(f'{RealModule} as {SubModule}') - else: - # 不在映射表中,保持原样 - if alias.asname: - parts.append(f'{ModuleName} as {alias.asname}') - else: - parts.append(ModuleName) - else: - if alias.asname: - parts.append(f'{alias.name} as {alias.asname}') - else: - parts.append(alias.name) - - result: str = f'import {", ".join(parts)}' - - # 提取并保留注释 - if SourceLines and hasattr(node, 'lineno'): - LineIdx: int = node.lineno - 1 - if 0 <= LineIdx < len(SourceLines): - line: str = SourceLines[LineIdx] - CommentMatch: re.Match | None = _RE_LINE_COMMENT.search(line) - if CommentMatch: - result += ' ' + CommentMatch.group(0) - - return result - - @staticmethod - def _GetImportFromString(node: ast.ImportFrom, SourceLines: list[str] | None = None) -> str: - """获取 from ... import ... 语句字符串""" - module: str = node.module or '' - parts: list[str] = [] - IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap - - # 处理相对导入 (from . import xxx 或 from ..package import xxx) - if node.level > 0: - if module: - if node.level == 1: - full_module: str = f'.{module}' - else: - full_module = '.' * node.level + module - else: - full_module = '.' * node.level - for alias in node.names: - if alias.asname: - parts.append(f'{alias.name} as {alias.asname}') - else: - parts.append(alias.name) - return f'from {full_module} import {", ".join(parts)}' - - # 提取注释(在生成结果前获取) - comment: str = '' - if SourceLines and hasattr(node, 'lineno'): - LineIdx = node.lineno - 1 - if 0 <= LineIdx < len(SourceLines): - line = SourceLines[LineIdx] - CommentMatch: re.Match | None = _RE_LINE_COMMENT.search(line) - if CommentMatch: - comment = ' ' + CommentMatch.group(0) - - # 检查是否是 from __include import xxx 格式 - if module == '__include': - for alias in node.names: - name: str = alias.name - if name in IncludeAliasMap: - # 替换为实际模块路径 - RealModule: str = IncludeAliasMap[name] - if alias.asname: - parts.append(f'import {RealModule} as {alias.asname}{comment}') - else: - parts.append(f'import {RealModule} as {name}{comment}') - else: - # 不在映射表中,保持原样 - if alias.asname: - parts.append(f'from {module} import {name} as {alias.asname}{comment}') - else: - parts.append(f'from {module} import {name}{comment}') - return '\n'.join(parts) - - # 普通 from ... import ... 语句 - for alias in node.names: - if alias.asname: - parts.append(f'{alias.name} as {alias.asname}') - else: - parts.append(alias.name) - return f'from {module} import {", ".join(parts)}{comment}' - - @staticmethod - def _GetDecoratorString(decorator: ast.AST) -> str: - """获取装饰器字符串""" - if isinstance(decorator, ast.Name): - return decorator.id - elif isinstance(decorator, ast.Attribute): - if isinstance(decorator.value, ast.Name): - return f'{decorator.value.id}.{decorator.attr}' - return decorator.attr - elif isinstance(decorator, ast.Call): - # 处理带参数的装饰器,如 @c.Attribute(t.attr.packed) - FuncStr: str = PythonToStubConverter._GetDecoratorString(decorator.func) - ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in decorator.args]) - return f'{FuncStr}({ArgsStr})' - return '' - - @staticmethod - def _GetExprString(expr: ast.AST) -> str: - """获取表达式字符串(用于宏调用)""" - if isinstance(expr, ast.Call): - # 处理函数调用,如 c.CUndef(...), c.CPragma(...) - # (旧的 c.CIf/c.CElif/c.CElse 已删除:cif/celif/celse 由 _ProcessIfMacro 渲染) - FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func) - if FuncStr and ('CEndif' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr): - ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args]) - return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()' - return '' - - @staticmethod - def _RestoreDefinedMarkers(node: ast.AST) -> ast.AST: - """把内部标记 c.@CIfdef(N) 递归还原为 defined(N) 调用(供 stub 文本渲染)。 - - cif/celif 条件的 desugar 会把 defined(NAME) 变成 c.@CIfdef(NAME) 标记调用; - 生成 .vpi 文本时必须还原为用户可写的 defined(NAME),否则存根不可再解析。 - """ - if isinstance(node, ast.Call): - F: str = PythonToStubConverter._GetDecoratorString(node.func) - short: str = F.rsplit('.', 1)[-1] if F else '' - if short in ('@CIfdef', '@CIfndef') and len(node.args) == 1: - inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(node.args[0]) - call: ast.Call = ast.Call( - func=ast.Name(id='defined', ctx=ast.Load()), - args=[inner], keywords=[]) - if short == '@CIfndef': - return ast.UnaryOp(op=ast.Not(), operand=call) - return call - for field, value in ast.iter_fields(node): - if isinstance(value, list): - for i, item in enumerate(value): - if isinstance(item, ast.AST): - value[i] = PythonToStubConverter._RestoreDefinedMarkers(item) - elif isinstance(value, ast.AST): - setattr(node, field, PythonToStubConverter._RestoreDefinedMarkers(value)) - return node - - @staticmethod - def _CifConditionText(test: ast.AST) -> str | None: - """cif/celif 条件文本:内部标记 c.@CIf(EXPR) → EXPR;c.@CIfdef(N) → defined(N)。 - - 返回 None 表示 test 不是 cif 族标记(普通 if 的运行时条件)。 - """ - if isinstance(test, ast.Call): - F: str = PythonToStubConverter._GetDecoratorString(test.func) - short: str = F.rsplit('.', 1)[-1] if F else '' - if short == '@CIf' and len(test.args) == 1: - inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(test.args[0]) - return PythonToStubConverter._GetTypeString(inner) - if short in ('@CIfdef', '@CIfndef') and len(test.args) == 1: - return PythonToStubConverter._GetTypeString( - PythonToStubConverter._RestoreDefinedMarkers(test)) - return None - - @staticmethod - def _get_if_macro_string(node: ast.If, indent: int = 1) -> str: - """获取类内部的 cif 宏字符串(如 `cif :`,含 celif/celse 链)。""" - IndentStr: str = ' ' * indent - InnerIndent: str = ' ' * (indent + 1) - cond: str | None = PythonToStubConverter._CifConditionText(node.test) - if cond is None: - return '' - - def _body_lines(body: list, ind: str) -> list[str]: - out: list[str] = [] - for item in body: - if isinstance(item, ast.AnnAssign): - TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) - out.append(f'{ind}{item.target.id}: {TypeStr}') - elif isinstance(item, ast.Expr): - ExprStr: str = PythonToStubConverter._GetExprString(item.value) - if ExprStr: - out.append(f'{ind}{ExprStr}') - return out - - result: list[str] = [f'{IndentStr}cif {cond}:'] - result.extend(_body_lines(node.body, InnerIndent)) - current: ast.If = node - while current.orelse: - if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): - elif_node: ast.If = current.orelse[0] - econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test) - if econd is None: - break - result.append(f'{IndentStr}celif {econd}:') - result.extend(_body_lines(elif_node.body, InnerIndent)) - current = elif_node - continue - result.append(f'{IndentStr}celse:') - result.extend(_body_lines(current.orelse, InnerIndent)) - break - return '\n'.join(result) - - @staticmethod - def _GetModuleIfMacroString(node: ast.If) -> str: - """获取模块级别的 cif 宏字符串(如 `cif FF_MULTI_PARTITION:`)。""" - return PythonToStubConverter._ProcessIfMacro(node, indent=0) - - @staticmethod - def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str: - """处理 cif 宏节点,支持 celif / celse 链。""" - IndentStr: str = ' ' * indent - cond: str | None = PythonToStubConverter._CifConditionText(node.test) - if cond is None: - return '' - result: list[str] = [f'{IndentStr}cif {cond}:'] - for item in node.body: - result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - - current: ast.If = node - while current.orelse: - if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): - # celif 情况 - elif_node: ast.If = current.orelse[0] - econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test) - if econd is None: - break - result.append(f'{IndentStr}celif {econd}:') - for item in elif_node.body: - result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - current = elif_node - continue - # celse 情况 - result.append(f'{IndentStr}celse:') - for item in current.orelse: - result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) - break - return '\n'.join(result) - - @staticmethod - def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]: - """处理宏体内的单个语句""" - IndentStr: str = ' ' * indent - result: list[str] = [] - - if isinstance(item, ast.ClassDef): - class_stub: list[str] = PythonToStubConverter._ConvertClass(item) - for line in class_stub: - if line.strip(): - result.append(f'{IndentStr}{line}') - else: - result.append(line) - elif isinstance(item, ast.FunctionDef): - FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent) - result.extend(FuncStub) - elif isinstance(item, ast.AnnAssign): - TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) - result.append(f'{IndentStr}{item.target.id}: {TypeStr}') - elif isinstance(item, ast.Expr): - ExprStr: str = PythonToStubConverter._GetExprString(item.value) - if ExprStr: - result.append(f'{IndentStr}{ExprStr}') - elif isinstance(item, ast.If): - # 处理嵌套的 if 宏 - NestedIf: str = PythonToStubConverter._ProcessIfMacro(item, indent=indent) - if NestedIf: - result.append(NestedIf) - +"""StubGen 转换器模块 - 将 Python 文件转换为存根格式""" +from __future__ import annotations +import vast as ast +import re +import fnmatch +from typing import List, Dict +from lib.core.SymbolUtils import IsListAnnotation, ExpandSoftKeywords + + +# 模块级转换结果缓存:{ (PyContent, ModuleName): str } +# convert 是纯函数(类级配置 IncludeAliasMap/VarExcludeList/MacroExcludeList 运行期不变), +# 相同输入必然产生相同输出。结果字符串不可变,调用方仅将其写入文件或拼接出新字符串, +# 缓存结果可安全复用(无需深拷贝)。Set* 配置方法会清空缓存以防配置变化导致结果过期。 +_ConvertResultCache: dict[tuple[str, str], str] = {} +_ConvertCacheMaxSize: int = 4096 + +# 提取 import 语句行尾注释(`_GetImportString` / `_GetImportFromString`)用的 +# **预编译**正则(性能:字面量正则预编译避免 re 缓存查找与编译开销)。 +_RE_LINE_COMMENT: re.Pattern[str] = re.compile(r'#.*$') + + +class PythonToStubConverter: + """将 Python 文件转换为存根格式""" + + # __include 别名映射表 {alias: ModuleName} + IncludeAliasMap: Dict[str, str] = {} + + # 变量排除列表 - 匹配这些模式的变量不会被添加到存根文件 + VarExcludeList: List[str] = [] + + # 宏排除列表 - 匹配这些模式的宏不会被添加到存根文件 + MacroExcludeList: List[str] = [] + + @classmethod + def SetIncludeAliasMap(cls, alias_map: Dict[str, str]) -> None: + """设置 __include 别名映射表""" + cls.IncludeAliasMap = alias_map + _ConvertResultCache.clear() + + @classmethod + def SetVarExcludeList(cls, exclude_list: List[str]) -> None: + """设置变量排除列表""" + cls.VarExcludeList = exclude_list + _ConvertResultCache.clear() + + @classmethod + def SetMacroExcludeList(cls, exclude_list: List[str]) -> None: + """设置宏排除列表""" + cls.MacroExcludeList = exclude_list + _ConvertResultCache.clear() + + @classmethod + def _ShouldExcludeVar(cls, VarName: str) -> bool: + """检查变量是否应该被排除""" + for pattern in cls.VarExcludeList: + if fnmatch.fnmatch(VarName, pattern): + return True + return False + + @classmethod + def _ShouldExcludeMacro(cls, MacroName: str) -> bool: + """检查宏是否应该被排除""" + for pattern in cls.MacroExcludeList: + if fnmatch.fnmatch(MacroName, pattern): + return True + return False + + @staticmethod + def convert(PyContent: str, ModuleName: str) -> str: + """将 Python 代码转换为存根格式""" + # 软关键字展开:fn/fun/func/function -> def(行内等宽/更短,行号不变), + # 保证后续 ast.parse 与 SourceLines 回读行号一致。 + PyContent = ExpandSoftKeywords(PyContent) + + # 模块级缓存:同一 (内容, 模块名) 直接复用上次转换结果(纯函数输出,不可变字符串) + CacheKey: tuple[str, str] = (PyContent, ModuleName) + CachedResult: str | None = _ConvertResultCache.get(CacheKey) + if CachedResult is not None: + return CachedResult + + lines: list[str] = PyContent.split('\n') + StubLines: list[str] = [] + + # 添加文件头 + StubLines.append('"""') + StubLines.append(f'Auto-generated Python stub file from {ModuleName}.vp') + StubLines.append(f'Module: {ModuleName}') + StubLines.append('"""') + StubLines.append('') + + # 解析 Python 代码,检查是否已经导入了 t 和 c(AST 与下方主解析复用,避免重复 parse) + HasImportT: bool = False + HasImportC: bool = False + try: + # 传来源名:解析失败时 vast 的报错/回退诊断才带得上模块名(否则记为 ) + tree: ast.Module = ast.parse(PyContent, f'{ModuleName}.vp') + for node in tree.body: + if isinstance(node, ast.Import): + for alias in node.names: + if alias.name == 't': + HasImportT = True + elif alias.name == 'c': + HasImportC = True + except SyntaxError: + # 语法错误:同一内容两次解析结果必然相同,与主循环 fail-fast 行为一致 + raise + except Exception: + # 非语法类解析异常:保持原语义(第一遍吞掉,主循环重新解析后向上传播) + tree = None + + # 添加默认导入(如果源代码中没有) + if not HasImportT: + StubLines.append('import t') + if not HasImportC: + StubLines.append('import c') + if not HasImportT or not HasImportC: + StubLines.append('') + + StubLines.append('') + + # 解析 Python 代码,提取类型定义(复用上方的 parse 结果) + try: + if tree is None: + # 第一遍解析失败(非语法类异常):重试一次,保持原"主循环重新解析"语义 + tree = ast.parse(PyContent, f'{ModuleName}.vp') + + # 第一遍扫描:收集 Postdefinition 变量 + PostdefVars: dict[str, list[tuple[str, str, ast.AnnAssign]]] = {} # {ClassName: [(VarName, TypeStr, node), ...]} + for node in tree.body: + if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation) + # 检查是否是 Postdefinition 类型 + if 't.Postdefinition' in TypeStr: + # 提取引用的类名 + ClassName: str | None = PythonToStubConverter._ExtractPostdefClass(node.annotation) + if ClassName: + if ClassName not in PostdefVars: + PostdefVars[ClassName] = [] + PostdefVars[ClassName].append((node.target.id, TypeStr, node)) + + # 按原始顺序处理节点,保持代码的原始顺序 + PrevType: str | None = None # 记录前一个节点的类型 + + for node in tree.body: + CurrentType: str | None = None + + if isinstance(node, ast.ClassDef): + CurrentType = 'class' + # 如果前一个不是类,添加空行分隔 + if PrevType is not None and PrevType != 'class': + StubLines.append('') + # 检查是否有对应的 Postdefinition 变量 + ClassPostdefs: list[tuple[str, str, ast.AnnAssign]] = PostdefVars.get(node.name, []) + ClassLines: list[str] = PythonToStubConverter._ConvertClass( + node, SourceLines=lines, PostdefVars=ClassPostdefs + ) + StubLines.extend(ClassLines) + elif isinstance(node, ast.FunctionDef): + CurrentType = 'function' + # 如果前一个是类或者是第一个函数,添加空行分隔 + if PrevType == 'class' or (PrevType is not None and PrevType != 'function'): + StubLines.append('') + FuncLines: list[str] = PythonToStubConverter._ConvertFunction(node, SourceLines=lines) + StubLines.extend(FuncLines) + StubLines.append('') # 函数后空行 + elif isinstance(node, ast.AnnAssign): + CurrentType = 'variable' + # 跳过 Postdefinition 变量(已经在类中处理) + TypeStr = PythonToStubConverter._GetTypeString(node.annotation) + if 't.Postdefinition' in TypeStr: + continue + # 检查是否带有 t.CStatic 标志,如果有则不排除 + HasStatic: bool = 't.CStatic' in TypeStr + # 检查变量是否应该被排除 + ShouldExclude: bool = False + if isinstance(node.target, ast.Name) and not HasStatic: + VarName: str = node.target.id + if PythonToStubConverter._ShouldExcludeVar(VarName): + ShouldExclude = True + if ShouldExclude: + continue + # 如果前一个不是变量,添加空行分隔 + if PrevType is not None and PrevType != 'variable': + StubLines.append('') + VarLines: list[str] = PythonToStubConverter._ConvertVariable(node, SourceLines=lines) + # 移除多余的空行 + VarLines = [line for line in VarLines if line.strip()] + StubLines.extend(VarLines) + elif isinstance(node, ast.Import): + # 处理导入语句 + CurrentType = 'import' + ImportStr: str = PythonToStubConverter._GetImportString(node, SourceLines=lines) + if ImportStr: + if PrevType is not None and PrevType != 'import': + StubLines.append('') + StubLines.append(ImportStr) + elif isinstance(node, ast.Assign): + CurrentType = 'variable' + # 处理模块级别的全局变量(无类型标注) + if PrevType is not None and PrevType != 'variable': + StubLines.append('') + AssignLines: list[str] = PythonToStubConverter._ConvertGlobalAssign(node) + StubLines.extend(AssignLines) + elif isinstance(node, ast.ImportFrom): + # 处理 from ... import ... 语句 + CurrentType = 'import' + ImportStr = PythonToStubConverter._GetImportFromString(node, SourceLines=lines) + if ImportStr: + if PrevType is not None and PrevType != 'import': + StubLines.append('') + StubLines.append(ImportStr) + elif isinstance(node, ast.Expr): + # 处理模块级别的宏调用,如 c.CUndef(...) / c.CPragma(...) + ExprStr: str = PythonToStubConverter._GetExprString(node.value) + if ExprStr: + CurrentType = 'macro' + StubLines.append(ExprStr) + StubLines.append('') # 宏后空行 + elif isinstance(node, ast.If): + # 处理模块级别的 cif 宏条件,如 `cif FF_MULTI_PARTITION:` + IfStr: str = PythonToStubConverter._GetModuleIfMacroString(node) + if IfStr: + CurrentType = 'macro' + StubLines.append(IfStr) + StubLines.append('') # 宏后空行 + + if CurrentType: + PrevType = CurrentType + + except SyntaxError as e: + # 语法错误直接终止(fail-fast),不允许生成残缺 stub 被全局缓存命中。 + # 残缺 vpi 只含 import 头,函数/类型声明全缺,会在后续阶段以 + # undefined symbol / mangle 错误形式爆发(如 ExpandKeywordsTest 链接失败)。 + raise + + Result: str = '\n'.join(StubLines) + # 缓存上限保护:条目过多时整体清空,避免内存无限增长(结果可随时重算) + if len(_ConvertResultCache) >= _ConvertCacheMaxSize: + _ConvertResultCache.clear() + _ConvertResultCache[CacheKey] = Result + return Result + + @staticmethod + def _ConvertClass(node: ast.ClassDef, SourceLines: list[str] | None = None, PostdefVars: list[tuple[str, str, ast.AnnAssign]] | None = None, indent: int = 0) -> list[str]: + """转换类定义 + + Args: + node: 类定义节点 + SourceLines: 源代码行列表 + PostdefVars: Postdefinition 变量列表 [(VarName, TypeStr, node), ...] + indent: 缩进级别(用于嵌套类) + """ + lines: list[str] = [] + PostdefVars = PostdefVars or [] + IndentStr: str = ' ' * indent + + # 处理类装饰器 + for decorator in node.decorator_list: + DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator) + if DecoratorStr: + lines.append(f'{IndentStr}@{DecoratorStr}') + + # 检查是否是结构体、联合体或枚举 + BaseNames: list[str] = [base.attr if isinstance(base, ast.Attribute) else base.id + for base in node.bases + if isinstance(base, (ast.Name, ast.Attribute))] + + # 保留原始基类 + if node.bases: + BaseStrs: list[str] = [] + for base in node.bases: + if isinstance(base, ast.Name): + BaseStrs.append(base.id) + elif isinstance(base, ast.Attribute): + BaseStrs.append(f'{base.value.id}.{base.attr}') + elif isinstance(base, ast.Subscript): + # 处理泛型类型,如 memory_block_t[MAX_ORDER + 1] + BaseStrs.append(PythonToStubConverter._GetTypeString(base)) + ClassTypeParamStr: str = '' + if hasattr(node, 'type_params') and node.type_params: + param_names: list[str] = [tp.name for tp in node.type_params] + ClassTypeParamStr = f'[{", ".join(param_names)}]' + if BaseStrs: + lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}({", ".join(BaseStrs)}):') + else: + lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:') + else: + ClassTypeParamStr = '' + if hasattr(node, 'type_params') and node.type_params: + param_names = [tp.name for tp in node.type_params] + ClassTypeParamStr = f'[{", ".join(param_names)}]' + lines.append(f'{IndentStr}class {node.name}{ClassTypeParamStr}:') + + # 处理类成员 + HasMembers: bool = False + seen_member_names: set[str] = set() + for item in node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + seen_member_names.add(item.target.id) + init_members: list[ast.AnnAssign] = [] + for item in node.body: + if isinstance(item, ast.FunctionDef) and item.name == '__init__': + for stmt in item.body: + if (isinstance(stmt, ast.AnnAssign) + and isinstance(stmt.target, ast.Attribute) + and isinstance(stmt.target.value, ast.Name) + and stmt.target.value.id == 'self'): + init_members.append(stmt) + untyped_self_members: list[ast.Assign] = [] + for m_name in [m.target.attr for m in init_members if isinstance(m.target, ast.Attribute)]: + seen_member_names.add(m_name) + for item in node.body: + if isinstance(item, ast.FunctionDef): + for stmt in item.body: + if (isinstance(stmt, ast.Assign) + and len(stmt.targets) == 1 + and isinstance(stmt.targets[0], ast.Attribute) + and isinstance(stmt.targets[0].value, ast.Name) + and stmt.targets[0].value.id == 'self'): + attr_name: str = stmt.targets[0].attr + if attr_name not in seen_member_names: + seen_member_names.add(attr_name) + untyped_self_members.append(stmt) + for item in list(node.body) + init_members + untyped_self_members: + if isinstance(item, ast.Assign): + if (len(item.targets) == 1 + and isinstance(item.targets[0], ast.Attribute) + and isinstance(item.targets[0].value, ast.Name) + and item.targets[0].value.id == 'self'): + HasMembers = True + MemberIndent: str = IndentStr + ' ' + attr_name = item.targets[0].attr + if attr_name in seen_member_names: + continue + InferredType: str = 'int' + if item.value and isinstance(item.value, ast.Constant): + if isinstance(item.value.value, float): + InferredType = 'float' + elif isinstance(item.value.value, bool): + InferredType = 'bool' + elif isinstance(item.value.value, str): + InferredType = 'str' + lines.append(f'{MemberIndent}{attr_name}: {InferredType}') + else: + HasMembers = True + MemberIndent = IndentStr + ' ' + if SourceLines and hasattr(item, 'lineno'): + StartLine: int = item.lineno - 1 + EndLine: int = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(MemberIndent + SourceLines[i].lstrip()) + elif isinstance(item, ast.AnnAssign): + HasMembers = True + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + MemberIndent = IndentStr + ' ' + # __provides__/__requires__/__require_must__ 元数据:保留值以便跨模块加载 + if isinstance(item.target, ast.Name) and item.target.id in ('__provides__', '__requires__', '__require_must__'): + if item.value is not None: + try: + value_str: str = ast.unparse(item.value) + except Exception: + value_str = '' + lines.append(f'{MemberIndent}{item.target.id}: {TypeStr} = {value_str}') + else: + lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}') + continue + # 检查是否是宏变量(类型包含 t.CDefine) + if 't.CDefine' in TypeStr: + # 检查宏变量是否应该被排除 + if isinstance(item.target, ast.Name): + MacroName: str = item.target.id + if PythonToStubConverter._ShouldExcludeMacro(MacroName): + continue # 跳过该宏 + # 宏变量保持原样(包括赋值) + if SourceLines and hasattr(item, 'lineno'): + StartLine = item.lineno - 1 + EndLine = item.end_lineno if hasattr(item, 'end_lineno') and item.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(MemberIndent + SourceLines[i].lstrip()) + else: + lines.append(f'{MemberIndent}{item.target.id}: {TypeStr}') + else: + if isinstance(item.target, ast.Attribute): + MemberName: str = item.target.attr + else: + MemberName = item.target.id + FinalTypeStr: str = TypeStr + if IsListAnnotation(item.annotation): + slice_node: ast.AST = item.annotation.slice + has_size: bool = False + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + count_node: ast.AST = slice_node.elts[1] + if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int) and count_node.value > 0: + has_size = True + else: + try: + count_expr: str = ast.unparse(count_node) + except Exception: + count_expr = '' + if count_expr and count_expr != 'None': + has_size = True + elem_type_str: str = PythonToStubConverter._GetTypeString(slice_node.elts[0]) + FinalTypeStr = f't.CArray[{elem_type_str}, {count_expr}]' + if not has_size: + elem_type_str = PythonToStubConverter._GetTypeString(slice_node if not isinstance(slice_node, ast.Tuple) else slice_node.elts[0]) + init_len: int = 0 + if item.value is not None: + if isinstance(item.value, ast.List): + init_len = len(item.value.elts) + elif isinstance(item.value, ast.Constant) and isinstance(item.value.value, str): + init_len = len(item.value.value) + 1 + if init_len > 0: + FinalTypeStr = f't.CArray[{elem_type_str}, {init_len}]' + else: + FinalTypeStr = f't.CArray[{elem_type_str}, None]' + if item.value is not None and isinstance(item.value, ast.Constant): + try: + value_repr: str = ast.unparse(item.value) + lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr} = {value_repr}') + except Exception: + lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}') + else: + lines.append(f'{MemberIndent}{MemberName}: {FinalTypeStr}') + elif isinstance(item, ast.FunctionDef): + HasMembers = True + FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent+1, SourceLines=SourceLines, class_name=node.name) + lines.extend(FuncStub) + elif isinstance(item, ast.Expr): + # 处理宏调用,如 c.CUndef(...) / c.CPragma(...) + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + lines.append(f'{IndentStr} {ExprStr}') + elif isinstance(item, ast.If): + # 处理 cif 宏条件(`cif ...:` / `celif ...:` / `celse:`) + IfStr: str = PythonToStubConverter._get_if_macro_string(item, indent=indent+1) + if IfStr: + lines.append(IfStr) + elif isinstance(item, ast.ClassDef): + # 处理嵌套类 + HasMembers = True + NestedClassLines: list[str] = PythonToStubConverter._ConvertClass(item, SourceLines=SourceLines, PostdefVars=[], indent=indent+1) + # 移除嵌套类的宏守卫(因为已经在父类的宏守卫内) + FilteredLines: list[str] = [] + for line in NestedClassLines: + # 跳过宏守卫相关行 + if line.strip().startswith('c.CIfndef(') or line.strip().startswith('c.CEndif()'): + continue + if ': t.CDefine' in line and '__' in line: + continue + FilteredLines.append(line) + lines.extend(FilteredLines) + + if not HasMembers: + lines.append(f'{IndentStr} pass') + + # 添加 Postdefinition 变量 + if PostdefVars: + lines.append('') + for VarName, TypeStr, _ in PostdefVars: + lines.append(f'{VarName}: {TypeStr}') + + return lines + + @staticmethod + def _ConvertFunction(node: ast.FunctionDef, indent: int = 0, SourceLines: list[str] | None = None, class_name: str | None = None) -> list[str]: + """转换函数定义""" + lines: list[str] = [] + IndentStr: str = ' ' * indent + + # 检查是否是宏函数(返回类型包含 t.CDefine) + is_macro_func: bool = False + if node.returns: + ReturnType: str = PythonToStubConverter._GetTypeString(node.returns) + if 't.CDefine' in ReturnType: + is_macro_func = True + + # 如果是宏函数,检查是否应该被排除 + if is_macro_func: + if PythonToStubConverter._ShouldExcludeMacro(node.name): + return lines # 返回空列表,排除该宏 + + # 保持原样(包括代码块) + if SourceLines: + StartLine: int = node.lineno - 1 + EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 + Copied: list[str] = [SourceLines[i] + for i in range(StartLine, min(EndLine, len(SourceLines)))] + # 丢弃尾随空行:解析器的 end_lineno 会被紧随的空行 NL 顶到下一行 + # (vast Parser._ParseSuite 单行套件分支返回前 `_SkipNL()` 吞掉空行 NL, + # 调用方 _FixLoc 取 _Prev() 即该 NL)。若不丢弃,`define f(...): return x` + # 这类内联体宏末尾会多出空白行,使下方"末行是否已有内联体"判据落空, + # 凭空补一行 ` pass` → 存根缩进非法(hashlib.__sha512 第 29 行)。 + while Copied and not Copied[-1].strip(): + Copied.pop() + lines.extend(Copied) + if lines and lines[-1].strip() and not lines[-1].rstrip().endswith(':'): + pass + elif lines: + lines.append(f'{IndentStr} pass') + else: + # 源区间为空(理论不可达):产出合法占位,绝不落出 ` pass` 这类非法缩进 + lines.append(f'{IndentStr}pass') + return lines + + # 处理函数装饰器 + for decorator in node.decorator_list: + DecoratorStr: str = PythonToStubConverter._GetDecoratorString(decorator) + if DecoratorStr: + lines.append(f'{IndentStr}@{DecoratorStr}') + + # 构建参数列表 + # 治本修复:原实现只遍历 node.args.args,完全忽略 node.args.defaults, + # 导致带默认值的参数(如 category: str = "")在 .pyi 中丢失默认值。 + # 下游 DeclarationGenerator 据此生成 stub.ll 时,会因缺少默认参数而 + # 生成参数数量不足的 declare,进而造成调用方 ABI 不匹配崩溃。 + # 修复:按 Python AST 语义,defaults 从右向左对应 args 末尾的参数, + # 对有默认值的参数附加 '= '。 + params: list[str] = [] + NumDefaults: int = len(node.args.defaults) if node.args.defaults else 0 + DefaultsStart: int = len(node.args.args) - NumDefaults + for arg_idx, arg in enumerate(node.args.args): + ArgName: str = arg.arg + if arg.annotation: + TypeStr: str = PythonToStubConverter._GetTypeString(arg.annotation) + ParamPart: str = f'{ArgName}: {TypeStr}' + elif class_name and arg_idx == 0 and ArgName == 'self': + ParamPart = f'self: {class_name}' + else: + ParamPart = ArgName + # 附加默认值:defaults[i] 对应 args[DefaultsStart + i] + if NumDefaults > 0 and arg_idx >= DefaultsStart: + DefaultIdx: int = arg_idx - DefaultsStart + try: + DefaultStr: str = ast.unparse(node.args.defaults[DefaultIdx]) + ParamPart = f'{ParamPart} = {DefaultStr}' + except Exception: + # unparse 失败时保留无默认值形式,避免阻塞生成 + pass + params.append(ParamPart) + + # 处理 *args + if node.args.vararg: + ArgName = node.args.vararg.arg + if node.args.vararg.annotation: + TypeStr = PythonToStubConverter._GetTypeString(node.args.vararg.annotation) + params.append(f'*{ArgName}: {TypeStr}') + else: + params.append(f'*{ArgName}') + + # 处理 **kwargs + if node.args.kwarg: + ArgName = node.args.kwarg.arg + if node.args.kwarg.annotation: + TypeStr = PythonToStubConverter._GetTypeString(node.args.kwarg.annotation) + params.append(f'**{ArgName}: {TypeStr}') + else: + params.append(f'**{ArgName}') + + ParamStr: str = ', '.join(params) + + # 返回类型 - 保持原始类型(不添加t.State) + if node.returns: + ReturnType = PythonToStubConverter._GetTypeString(node.returns) + else: + ReturnType = 't.CInt' + + TypeParamStr: str = '' + if hasattr(node, 'type_params') and node.type_params: + param_names: list[str] = [tp.name for tp in node.type_params] + TypeParamStr = f'[{", ".join(param_names)}]' + + lines.append(f'{IndentStr}def {node.name}{TypeParamStr}({ParamStr}) -> {ReturnType}: pass') + return lines + + @staticmethod + def _ConvertVariable(node: ast.AnnAssign, SourceLines: list[str] | None = None) -> list[str]: + """转换变量定义""" + lines: list[str] = [] + if isinstance(node.target, ast.Name): + VarName: str = node.target.id + TypeStr: str = PythonToStubConverter._GetTypeString(node.annotation) + # 检查是否是宏变量(类型包含 t.CDefine) + if 't.CDefine' in TypeStr: + # 检查宏变量是否应该被排除 + if PythonToStubConverter._ShouldExcludeMacro(VarName): + return lines # 返回空列表,排除该宏 + # 宏变量保持原样(包括赋值) + if SourceLines and hasattr(node, 'lineno'): + StartLine: int = node.lineno - 1 + EndLine: int = node.end_lineno if hasattr(node, 'end_lineno') and node.end_lineno else StartLine + 1 + for i in range(StartLine, min(EndLine, len(SourceLines))): + lines.append(SourceLines[i]) + else: + lines.append(f'{VarName}: {TypeStr}') + return lines + # 非宏变量:不加 t.State + # 含有 typedef 的变量不加 extern,普通变量需要加 extern + has_typedef: bool = 't.CTypedef' in TypeStr or 'CTypedef' in TypeStr + if has_typedef and node.value is not None: + ValueTypeStr: str = PythonToStubConverter._GetTypeString(node.value) + lines.append(f'{VarName}: {TypeStr} = {ValueTypeStr}') + else: + if not has_typedef and 't.CExtern' not in TypeStr and 't.CStatic' not in TypeStr: + TypeStr = f't.CExtern | {TypeStr}' + lines.append(f'{VarName}: {TypeStr}') + return lines + + @staticmethod + def _ConvertGlobalAssign(node: ast.Assign) -> list[str]: + """转换模块级别的全局变量(无类型标注)""" + lines: list[str] = [] + if not node.targets or not isinstance(node.targets[0], ast.Name): + return lines + + VarName: str = node.targets[0].id + + # 跳过私有变量(以下划线开头) + if VarName.startswith('_'): + return lines + + # 从值推断类型 + inferred_type: str | None = PythonToStubConverter._InferTypeFromValue(node.value) + if inferred_type: + lines.append(f'{VarName}: {inferred_type}') + else: + lines.append(f'{VarName}: t.CInt') + + return lines + + @staticmethod + def _InferTypeFromValue(value: ast.AST) -> str | None: + """从值推断 C 类型""" + if isinstance(value, ast.Constant): + if isinstance(value.value, int): + return 't.CInt' + elif isinstance(value.value, float): + return 't.CDouble' + elif isinstance(value.value, str): + return 't.CCharPtr' + elif isinstance(value.value, bool): + return 't.CInt' + elif isinstance(value, ast.List): + return 't.CVoidPtr' + elif isinstance(value, ast.Dict): + return 't.CVoidPtr' + elif isinstance(value, ast.Name): + return 't.CInt' + elif isinstance(value, ast.BinOp): + left_type: str | None = PythonToStubConverter._InferTypeFromValue(value.left) + if left_type: + return left_type + return 't.CInt' + elif isinstance(value, ast.Call): + # 函数调用返回值,假设为指针类型 + if isinstance(value.func, ast.Name): + func_name: str = value.func.id + if func_name == 'malloc': + return 't.CVoidPtr' + elif func_name in ('ctypes.cast', 'cast'): + return 't.CVoidPtr' + return 't.CVoidPtr' + return None + + @staticmethod + def _GetTypeString(annotation: ast.AST) -> str: + """获取类型字符串 + + 支持集与 VPC(ViperC/App/lib/Stub/StubGen/Converter.vp 的 _PyiTypeStr) + 等价:Name/Attribute/Subscript/BinOp/UnaryOp/Compare/BoolOp/Constant/ + List/Call(VPC 另支持裸 Tuple,本侧仅在 Subscript.slice 位置接受 Tuple)。 + 未支持的节点种类与运算符在此 fail-fast(不自造 't.CVoid' 回落)。 + """ + # 无注解(None):调用方未提供注解,沿用原语义返回 't.CVoid'; + # 这不是"未知节点"——未知节点走函数尾 fail-fast。 + if annotation is None: + return 't.CVoid' + if isinstance(annotation, ast.Name): + return annotation.id + elif isinstance(annotation, ast.Attribute): + if isinstance(annotation.value, ast.Name): + return f'{annotation.value.id}.{annotation.attr}' + else: + # 处理嵌套属性访问,如 t.attr.packed + ParentStr: str = PythonToStubConverter._GetTypeString(annotation.value) + return f'{ParentStr}.{annotation.attr}' + elif isinstance(annotation, ast.Subscript): + base: str = PythonToStubConverter._GetTypeString(annotation.value) + if isinstance(annotation.slice, ast.Tuple): + slice_strs: list[str] = [PythonToStubConverter._GetTypeString(e) for e in annotation.slice.elts] + SliceStr: str = ', '.join(slice_strs) + else: + SliceStr = PythonToStubConverter._GetTypeString(annotation.slice) + return f'{base}[{SliceStr}]' + elif isinstance(annotation, ast.BinOp): + # 处理 BinOp,如 t.CExtern | fd_table.__set_default__(...) + left: ast.AST = annotation.left + right: ast.AST = annotation.right + + # 值引用参数标记:联合形态 `T | t.CRef`(ref)/ `T | t.CTempRef`(temp) + # 还原为源码写法 `&T` / `&temp T`,使跨模块 .vpi 往返保留取址模式。 + if isinstance(annotation.op, ast.BitOr): + _ref_kind: str | None = None + _type_side: ast.AST | None = None + if isinstance(right, ast.Attribute) and right.attr in ('CRef', 'CTempRef'): + _ref_kind = right.attr + _type_side = left + elif isinstance(left, ast.Attribute) and left.attr in ('CRef', 'CTempRef'): + _ref_kind = left.attr + _type_side = right + if _ref_kind is not None and _type_side is not None: + _inner: str = PythonToStubConverter._GetTypeString(_type_side) + return f'&temp {_inner}' if _ref_kind == 'CTempRef' else f'&{_inner}' + + # 检查右侧是否是 __set_default__ 调用 + if isinstance(right, ast.Call) and isinstance(right.func, ast.Attribute) and right.func.attr in ('__set_default__', '__set_default'): + # 去除 __set_default__,保留左侧,右侧替换为函数名部分 + LeftStr: str = PythonToStubConverter._GetTypeString(left) + RightStr: str = PythonToStubConverter._GetTypeString(right.func.value) + if isinstance(annotation.op, ast.BitOr): + return f'{LeftStr} | {RightStr}' + + LeftStr = PythonToStubConverter._GetTypeString(left) + RightStr = PythonToStubConverter._GetTypeString(right) + if isinstance(annotation.op, ast.BitOr): + return f'{LeftStr} | {RightStr}' + elif isinstance(annotation.op, ast.Add): + return f'{LeftStr} + {RightStr}' + elif isinstance(annotation.op, ast.Sub): + return f'{LeftStr} - {RightStr}' + elif isinstance(annotation.op, ast.Mult): + return f'{LeftStr} * {RightStr}' + elif isinstance(annotation.op, ast.Div): + return f'{LeftStr} / {RightStr}' + elif isinstance(annotation.op, ast.Mod): + return f'{LeftStr} % {RightStr}' + # 位与/右移:条件编译实参(如 cif MASK_BYTE & 0x0F、 + # cif (MAX_SIZE >> SHIFT_BITS) > 0)。两运算符必须与 VPC 的 + # _PyiBinOpOpText 保持一致,否则同一份源码两侧写出的 vpi 文本不同。 + elif isinstance(annotation.op, ast.BitAnd): + return f'{LeftStr} & {RightStr}' + elif isinstance(annotation.op, ast.RShift): + return f'{LeftStr} >> {RightStr}' + # 其余二元运算符(LShift/BitXor/FloorDiv/Pow/MatMult 等)不支持: + # 落到函数尾 fail-fast(与 VPC 同语义) + elif isinstance(annotation, ast.UnaryOp): + if isinstance(annotation.op, ast.Not): + operand: str = PythonToStubConverter._GetTypeString(annotation.operand) + return f'not {operand}' + elif isinstance(annotation.op, ast.Invert): + operand = PythonToStubConverter._GetTypeString(annotation.operand) + return f'~{operand}' + elif isinstance(annotation.op, ast.USub): + operand = PythonToStubConverter._GetTypeString(annotation.operand) + return f'-{operand}' + elif isinstance(annotation, ast.Compare): + # 处理比较操作,如 FF_MAX_SS != FF_MIN_SS + left: str = PythonToStubConverter._GetTypeString(annotation.left) + comparators: list[str] = [PythonToStubConverter._GetTypeString(c) for c in annotation.comparators] + ops: list[str] = [] + for op in annotation.ops: + if isinstance(op, ast.Eq): + ops.append('==') + elif isinstance(op, ast.NotEq): + ops.append('!=') + elif isinstance(op, ast.Lt): + ops.append('<') + elif isinstance(op, ast.LtE): + ops.append('<=') + elif isinstance(op, ast.Gt): + ops.append('>') + elif isinstance(op, ast.GtE): + ops.append('>=') + else: + ops.append('?') + result: str = left + for i, op in enumerate(ops): + result += f' {op} {comparators[i]}' + return result + elif isinstance(annotation, ast.BoolOp): + # 布尔运算:条件编译实参(如 cif ENABLED and MAX_SIZE > 0)。 + # 文本与 VPC 的 BoolOp 分支一致:and → ' and '、or → ' or '。 + Joiner: str = ' or ' if isinstance(annotation.op, ast.Or) else ' and ' + return Joiner.join([PythonToStubConverter._GetTypeString(v) for v in annotation.values]) + elif isinstance(annotation, ast.Constant): + return repr(annotation.value) + elif isinstance(annotation, ast.Index): + return PythonToStubConverter._GetTypeString(annotation.value) + elif isinstance(annotation, ast.List): + # 处理列表,如 [MAX_FILE_DESCRIPTORS] + elements: list[str] = [PythonToStubConverter._GetTypeString(elem) for elem in annotation.elts] + return f'[{", ".join(elements)}]' + elif isinstance(annotation, ast.Call): + # 处理函数调用形式的类型注解,如 t.Postdefinition(xxa) 或 callable(t.CVoid, app_id = t.CInt) + # 如果是 __set_default__ 调用,只返回函数名部分 + if isinstance(annotation.func, ast.Attribute) and annotation.func.attr in ('__set_default__', '__set_default'): + return PythonToStubConverter._GetTypeString(annotation.func.value) + + FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func) + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in annotation.args]) + # 处理关键字参数 + keywords_str: str = ', '.join([f'{kw.arg} = {PythonToStubConverter._GetTypeString(kw.value)}' for kw in annotation.keywords]) + # 合并位置参数和关键字参数 + if ArgsStr and keywords_str: + return f'{FuncStr}({ArgsStr}, {keywords_str})' + elif keywords_str: + return f'{FuncStr}({keywords_str})' + else: + return f'{FuncStr}({ArgsStr})' + # 未知/未支持的注解节点 fail-fast(与 VPC StubGen 的 _PyiTypeStr 同语义、 + # 同文案、同类型):静默回落 't.CVoid' 会把错误类型写进 stub 签名——实例: + # 源里的 t.Callable[[], int] 曾写成 t.Callable[int, int](见 + # ViperApps/ViperOS/VKernel/__vpcbuild__/temp/0b/0bb176432facf634.vpi:23), + # 下游按错误签名解析且无任何提示。 + # 归因信息:节点 kind + 行号;文件归因由调用方在日志里带上 + # (Generator.GenerateStub → "Failed to process <文件>", + # Phase1Generator → "处理 include 文件失败 <相对路径>")。 + KindName: str = type(annotation).__name__ + LineNo: int = getattr(annotation, 'lineno', 0) + raise TypeError(f'vpi 类型注解节点类型未知: {KindName} (line {LineNo})') + + @staticmethod + def _ExtractPostdefClass(annotation: ast.AST) -> str | None: + """从 Postdefinition 类型注解中提取类名 + + 例如:t.Postdefinition(__buddy_system) -> '__buddy_system' + t.Postdefinition(xxx) | t.CTypedef -> 'xxx' + """ + # 处理 BinOp (如: t.Postdefinition(xxx) | t.CTypedef) + if isinstance(annotation, ast.BinOp): + # 递归检查左操作数 + left_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.left) + if left_result: + return left_result + # 递归检查右操作数 + right_result: str | None = PythonToStubConverter._ExtractPostdefClass(annotation.right) + if right_result: + return right_result + return None + + # 处理函数调用 (如: t.Postdefinition(__buddy_system)) + if isinstance(annotation, ast.Call): + FuncStr: str = PythonToStubConverter._GetTypeString(annotation.func) + if FuncStr == 't.Postdefinition' and annotation.args: + # 提取第一个参数 + first_arg: ast.AST = annotation.args[0] + if isinstance(first_arg, ast.Name): + return first_arg.id + elif isinstance(first_arg, ast.Attribute): + return f'{first_arg.value.id}.{first_arg.attr}' + + return None + + @staticmethod + def _GetImportString(node: ast.Import, SourceLines: list[str] | None = None) -> str: + """获取导入语句字符串""" + parts: list[str] = [] + IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap + + for alias in node.names: + ModuleName: str = alias.name + # 检查是否是 __include.xxx 格式的导入 + if ModuleName.startswith('__include.'): + # 提取别名(xxx 部分) + SubModule: str = ModuleName[len('__include.'):] + if SubModule in IncludeAliasMap: + # 替换为实际模块路径 + RealModule: str = IncludeAliasMap[SubModule] + if alias.asname: + parts.append(f'{RealModule} as {alias.asname}') + else: + parts.append(f'{RealModule} as {SubModule}') + else: + # 不在映射表中,保持原样 + if alias.asname: + parts.append(f'{ModuleName} as {alias.asname}') + else: + parts.append(ModuleName) + else: + if alias.asname: + parts.append(f'{alias.name} as {alias.asname}') + else: + parts.append(alias.name) + + result: str = f'import {", ".join(parts)}' + + # 提取并保留注释 + if SourceLines and hasattr(node, 'lineno'): + LineIdx: int = node.lineno - 1 + if 0 <= LineIdx < len(SourceLines): + line: str = SourceLines[LineIdx] + CommentMatch: re.Match | None = _RE_LINE_COMMENT.search(line) + if CommentMatch: + result += ' ' + CommentMatch.group(0) + + return result + + @staticmethod + def _GetImportFromString(node: ast.ImportFrom, SourceLines: list[str] | None = None) -> str: + """获取 from ... import ... 语句字符串""" + module: str = node.module or '' + parts: list[str] = [] + IncludeAliasMap: dict[str, str] = PythonToStubConverter.IncludeAliasMap + + # 处理相对导入 (from . import xxx 或 from ..package import xxx) + if node.level > 0: + if module: + if node.level == 1: + full_module: str = f'.{module}' + else: + full_module = '.' * node.level + module + else: + full_module = '.' * node.level + for alias in node.names: + if alias.asname: + parts.append(f'{alias.name} as {alias.asname}') + else: + parts.append(alias.name) + return f'from {full_module} import {", ".join(parts)}' + + # 提取注释(在生成结果前获取) + comment: str = '' + if SourceLines and hasattr(node, 'lineno'): + LineIdx = node.lineno - 1 + if 0 <= LineIdx < len(SourceLines): + line = SourceLines[LineIdx] + CommentMatch: re.Match | None = _RE_LINE_COMMENT.search(line) + if CommentMatch: + comment = ' ' + CommentMatch.group(0) + + # 检查是否是 from __include import xxx 格式 + if module == '__include': + for alias in node.names: + name: str = alias.name + if name in IncludeAliasMap: + # 替换为实际模块路径 + RealModule: str = IncludeAliasMap[name] + if alias.asname: + parts.append(f'import {RealModule} as {alias.asname}{comment}') + else: + parts.append(f'import {RealModule} as {name}{comment}') + else: + # 不在映射表中,保持原样 + if alias.asname: + parts.append(f'from {module} import {name} as {alias.asname}{comment}') + else: + parts.append(f'from {module} import {name}{comment}') + return '\n'.join(parts) + + # 普通 from ... import ... 语句 + for alias in node.names: + if alias.asname: + parts.append(f'{alias.name} as {alias.asname}') + else: + parts.append(alias.name) + return f'from {module} import {", ".join(parts)}{comment}' + + @staticmethod + def _GetDecoratorString(decorator: ast.AST) -> str: + """获取装饰器字符串""" + if isinstance(decorator, ast.Name): + return decorator.id + elif isinstance(decorator, ast.Attribute): + if isinstance(decorator.value, ast.Name): + return f'{decorator.value.id}.{decorator.attr}' + return decorator.attr + elif isinstance(decorator, ast.Call): + # 处理带参数的装饰器,如 @c.Attribute(t.attr.packed) + FuncStr: str = PythonToStubConverter._GetDecoratorString(decorator.func) + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in decorator.args]) + return f'{FuncStr}({ArgsStr})' + return '' + + @staticmethod + def _GetExprString(expr: ast.AST) -> str: + """获取表达式字符串(用于宏调用)""" + if isinstance(expr, ast.Call): + # 处理函数调用,如 c.CUndef(...), c.CPragma(...) + # (旧的 c.CIf/c.CElif/c.CElse 已删除:cif/celif/celse 由 _ProcessIfMacro 渲染) + FuncStr: str = PythonToStubConverter._GetDecoratorString(expr.func) + if FuncStr and ('CEndif' in FuncStr or 'CUndef' in FuncStr or 'CPragma' in FuncStr): + ArgsStr: str = ', '.join([PythonToStubConverter._GetTypeString(arg) for arg in expr.args]) + return f'{FuncStr}({ArgsStr})' if ArgsStr else f'{FuncStr}()' + return '' + + @staticmethod + def _RestoreDefinedMarkers(node: ast.AST) -> ast.AST: + """把内部标记 c.@CIfdef(N) 递归还原为 defined(N) 调用(供 stub 文本渲染)。 + + cif/celif 条件的 desugar 会把 defined(NAME) 变成 c.@CIfdef(NAME) 标记调用; + 生成 .vpi 文本时必须还原为用户可写的 defined(NAME),否则存根不可再解析。 + """ + if isinstance(node, ast.Call): + F: str = PythonToStubConverter._GetDecoratorString(node.func) + short: str = F.rsplit('.', 1)[-1] if F else '' + if short in ('@CIfdef', '@CIfndef') and len(node.args) == 1: + inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(node.args[0]) + call: ast.Call = ast.Call( + func=ast.Name(id='defined', ctx=ast.Load()), + args=[inner], keywords=[]) + if short == '@CIfndef': + return ast.UnaryOp(op=ast.Not(), operand=call) + return call + for field, value in ast.iter_fields(node): + if isinstance(value, list): + for i, item in enumerate(value): + if isinstance(item, ast.AST): + value[i] = PythonToStubConverter._RestoreDefinedMarkers(item) + elif isinstance(value, ast.AST): + setattr(node, field, PythonToStubConverter._RestoreDefinedMarkers(value)) + return node + + @staticmethod + def _CifConditionText(test: ast.AST) -> str | None: + """cif/celif 条件文本:内部标记 c.@CIf(EXPR) → EXPR;c.@CIfdef(N) → defined(N)。 + + 返回 None 表示 test 不是 cif 族标记(普通 if 的运行时条件)。 + """ + if isinstance(test, ast.Call): + F: str = PythonToStubConverter._GetDecoratorString(test.func) + short: str = F.rsplit('.', 1)[-1] if F else '' + if short == '@CIf' and len(test.args) == 1: + inner: ast.AST = PythonToStubConverter._RestoreDefinedMarkers(test.args[0]) + return PythonToStubConverter._GetTypeString(inner) + if short in ('@CIfdef', '@CIfndef') and len(test.args) == 1: + return PythonToStubConverter._GetTypeString( + PythonToStubConverter._RestoreDefinedMarkers(test)) + return None + + @staticmethod + def _get_if_macro_string(node: ast.If, indent: int = 1) -> str: + """获取类内部的 cif 宏字符串(如 `cif :`,含 celif/celse 链)。""" + IndentStr: str = ' ' * indent + InnerIndent: str = ' ' * (indent + 1) + cond: str | None = PythonToStubConverter._CifConditionText(node.test) + if cond is None: + return '' + + def _body_lines(body: list, ind: str) -> list[str]: + out: list[str] = [] + for item in body: + if isinstance(item, ast.AnnAssign): + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + out.append(f'{ind}{item.target.id}: {TypeStr}') + elif isinstance(item, ast.Expr): + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + out.append(f'{ind}{ExprStr}') + return out + + result: list[str] = [f'{IndentStr}cif {cond}:'] + result.extend(_body_lines(node.body, InnerIndent)) + current: ast.If = node + while current.orelse: + if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): + elif_node: ast.If = current.orelse[0] + econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test) + if econd is None: + break + result.append(f'{IndentStr}celif {econd}:') + result.extend(_body_lines(elif_node.body, InnerIndent)) + current = elif_node + continue + result.append(f'{IndentStr}celse:') + result.extend(_body_lines(current.orelse, InnerIndent)) + break + return '\n'.join(result) + + @staticmethod + def _GetModuleIfMacroString(node: ast.If) -> str: + """获取模块级别的 cif 宏字符串(如 `cif FF_MULTI_PARTITION:`)。""" + return PythonToStubConverter._ProcessIfMacro(node, indent=0) + + @staticmethod + def _ProcessIfMacro(node: ast.If, indent: int = 0) -> str: + """处理 cif 宏节点,支持 celif / celse 链。""" + IndentStr: str = ' ' * indent + cond: str | None = PythonToStubConverter._CifConditionText(node.test) + if cond is None: + return '' + result: list[str] = [f'{IndentStr}cif {cond}:'] + for item in node.body: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + + current: ast.If = node + while current.orelse: + if len(current.orelse) == 1 and isinstance(current.orelse[0], ast.If): + # celif 情况 + elif_node: ast.If = current.orelse[0] + econd: str | None = PythonToStubConverter._CifConditionText(elif_node.test) + if econd is None: + break + result.append(f'{IndentStr}celif {econd}:') + for item in elif_node.body: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + current = elif_node + continue + # celse 情况 + result.append(f'{IndentStr}celse:') + for item in current.orelse: + result.extend(PythonToStubConverter._ProcessMacroBodyItem(item, indent + 1)) + break + return '\n'.join(result) + + @staticmethod + def _ProcessMacroBodyItem(item: ast.AST, indent: int = 0) -> list[str]: + """处理宏体内的单个语句""" + IndentStr: str = ' ' * indent + result: list[str] = [] + + if isinstance(item, ast.ClassDef): + class_stub: list[str] = PythonToStubConverter._ConvertClass(item) + for line in class_stub: + if line.strip(): + result.append(f'{IndentStr}{line}') + else: + result.append(line) + elif isinstance(item, ast.FunctionDef): + FuncStub: list[str] = PythonToStubConverter._ConvertFunction(item, indent=indent) + result.extend(FuncStub) + elif isinstance(item, ast.AnnAssign): + TypeStr: str = PythonToStubConverter._GetTypeString(item.annotation) + result.append(f'{IndentStr}{item.target.id}: {TypeStr}') + elif isinstance(item, ast.Expr): + ExprStr: str = PythonToStubConverter._GetExprString(item.value) + if ExprStr: + result.append(f'{IndentStr}{ExprStr}') + elif isinstance(item, ast.If): + # 处理嵌套的 if 宏 + NestedIf: str = PythonToStubConverter._ProcessIfMacro(item, indent=indent) + if NestedIf: + result.append(NestedIf) + return result \ No newline at end of file diff --git a/lib/core/ConstEvaluator.py b/lib/core/ConstEvaluator.py index 602f07a..07a0009 100644 --- a/lib/core/ConstEvaluator.py +++ b/lib/core/ConstEvaluator.py @@ -1,570 +1,570 @@ -"""统一常量表达式求值器 - -整合了原先分散在 5 处的常量求值逻辑: - - SymbolTable._eval_const_expr - - CTypeInfo.TryEvalConstExpr - - ConstEvalMixin._try_eval_const_expr - - HandlesAssign._eval_const_expr - - HandlesIf._eval_const_expr - -ConstEvaluator 提供三个级别的求值: - 1. eval_basic(node) — 纯 AST 常量 + 算术(无外部依赖) - 2. eval_with_symtab(node) — + SymbolTable 中的 define 查找 - 3. eval_full(node, ctx) — + Gen._define_constants / _all_define_constants / 平台宏 -""" -from __future__ import annotations - -import vast as ast -from typing import Any, Callable, Optional, TYPE_CHECKING - -import llvmlite.ir as ir - -from lib.core.Handles.HandlesBase import CTypeInfo -# 治本修复:导入 t 模块和 CType 基类,用于类型构造调用求值(如 t.CUnsignedLong(-11)) -from lib.includes import t as _t_module -from lib.includes.t import CType as _CType_base - -if TYPE_CHECKING: - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator - - -class ConstEvaluator: - """统一常量表达式求值器""" - - # ================================================================== - # Level 2: + SymbolTable define 查找 - # ================================================================== - - @staticmethod - def eval_with_symtab(node: ast.AST, symtab: Any) -> Optional[Any]: - """求值常量表达式,支持从 SymbolTable 查找 define 值。 - - 支持: Constant, BinOp, UnaryOp, Name(define), Attribute(define), Call(类型构造) - """ - if isinstance(node, ast.Constant): - return node.value - if isinstance(node, ast.Name): - if hasattr(symtab, '__contains__') and node.id in symtab: - info: CTypeInfo = symtab[node.id] - if info.IsDefine and isinstance(info.DefineValue, int): - return info.DefineValue - return None - if isinstance(node, ast.Attribute): - # 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性) - size_val = ConstEvaluator._eval_ctype_size_attr(node) - if size_val is not None: - return size_val - return ConstEvaluator._eval_attribute_define(node, symtab) - # 治本修复:支持 t.CUnsignedLong(-11) 等类型构造调用 - if isinstance(node, ast.Call): - return ConstEvaluator._eval_ctype_ctor_call(node, symtab) - return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_with_symtab(n, symtab)) - - @staticmethod - def _eval_ctype_ctor_call(node: ast.Call, symtab: Any) -> Optional[Any]: - """求值类型构造调用,如 t.CUnsignedLong(-11)、CInt(42)。 - - 在无 ctx(EvalContext)的场景下,通过 symtab 和 t 模块直接求值。 - 按类型的 Size 和 IsSigned 应用位掩码。 - """ - func: ast.expr = node.func - func_name: str | None = None - module_name: str | None = None - if isinstance(func, ast.Attribute): - if isinstance(func.value, ast.Name): - module_name = func.value.id - func_name = func.attr - elif isinstance(func, ast.Name): - func_name = func.id - - ctype_cls: Any = None - if module_name == 't' and func_name: - ctype_cls = getattr(_t_module, func_name, None) - elif func_name and not module_name: - ctype_cls = getattr(_t_module, func_name, None) - if ctype_cls is None and symtab: - t_type_syms = getattr(symtab, '_t_type_symbols', {}) - cls_candidate = t_type_syms.get(func_name) - if cls_candidate is not None: - ctype_cls = cls_candidate - - if ctype_cls is None or not (isinstance(ctype_cls, type) and issubclass(ctype_cls, _CType_base)): - return None - if not node.args: - return None - - arg_val: Any = ConstEvaluator.eval_with_symtab(node.args[0], symtab) - if arg_val is None: - return None - - size: int | None = None - is_signed: bool | None = None - try: - tmp_inst = ctype_cls() - size = getattr(tmp_inst, 'Size', None) - is_signed = getattr(tmp_inst, 'IsSigned', None) - except Exception: - pass - - if isinstance(arg_val, int) and size and size > 0: - mask: int = (1 << size) - 1 - if is_signed: - masked: int = arg_val & mask - if masked >= (1 << (size - 1)): - masked -= (1 << size) - return masked - else: - return arg_val & mask - return arg_val - - # ================================================================== - # Level 3: + Gen._define_constants / _all_define_constants / 平台宏 - # ================================================================== - - @staticmethod - def eval_full(node: ast.AST, ctx: 'EvalContext') -> Optional[Any]: - """完整求值,支持 define_constants、平台宏、BoolOp、Compare。 - - ctx: EvalContext 实例,封装了 Gen、SymbolTable 等上下文。 - """ - # BoolOp (HandlesIf 逻辑) - if isinstance(node, ast.BoolOp): - if isinstance(node.op, ast.And): - for val in node.values: - result: Any = ConstEvaluator.eval_full(val, ctx) - if result is None: - return None - if not result: - return 0 - return 1 - elif isinstance(node.op, ast.Or): - for val in node.values: - result: Any = ConstEvaluator.eval_full(val, ctx) - if result is None: - return None - if result: - return 1 - return 0 - return None - - # Compare (HandlesIf 逻辑) - if isinstance(node, ast.Compare): - left: Any = ConstEvaluator.eval_full(node.left, ctx) - if left is None: - return None - op: ast.cmpop - comparator: ast.AST - for op, comparator in zip(node.ops, node.comparators): - right: Any = ConstEvaluator.eval_full(comparator, ctx) - if right is None: - return None - result: bool | None = ConstEvaluator._eval_compare_op(op, left, right) - if result is None: - return None - if not result: - return 0 - left = right - return 1 - - # Constant — 直接返回原始值,bool→0/1 转换由 HandlesIf 自行处理 - if isinstance(node, ast.Constant): - return node.value - - # Name — 查 define_constants / SymbolTable / 平台宏 - if isinstance(node, ast.Name): - return ctx.lookup_name(node.id) - - # Attribute — 查 define_constants / SymbolTable / 平台宏 - if isinstance(node, ast.Attribute): - # 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性) - size_val = ConstEvaluator._eval_ctype_size_attr(node) - if size_val is not None: - return size_val - return ctx.lookup_attribute(node) - - # UnaryOp - if isinstance(node, ast.UnaryOp): - operand: Any = ConstEvaluator.eval_full(node.operand, ctx) - if operand is None: - return None - if isinstance(node.op, ast.Not): - return 1 if not operand else 0 - if isinstance(node.op, ast.USub): - return -operand - if isinstance(node.op, ast.UAdd): - return +operand - if isinstance(node.op, ast.Invert): - return ~operand - return None - - # Call — 编译时函数调用(如 ctraits.isptr(x)) - if isinstance(node, ast.Call): - return ConstEvaluator._eval_compile_time_call(node, ctx) - - # BinOp - return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_full(n, ctx)) - - # ================================================================== - # 内部辅助 - # ================================================================== - - @staticmethod - def _eval_compile_time_call(node: ast.Call, ctx: 'EvalContext') -> Optional[Any]: - """处理编译时函数调用,如 ctraits.isptr(x)、t.CUnsignedLong(-11)""" - func: ast.expr = node.func - func_name: str | None = None - module_name: str | None = None - - if isinstance(func, ast.Attribute): - if isinstance(func.value, ast.Name): - module_name = func.value.id - func_name = func.attr - elif isinstance(func, ast.Name): - func_name = func.id - - # c.@CIfdef(NAME) / c.@CIfndef(NAME):defined(NAME) 的解析期 desugar 标记,编译期返回 0/1。 - # 仅出现在 cif/celif 条件内(解析器保证),故此处只做宏定义判定。 - if func_name == '@CIfdef': - if node.args: - macro_name: str | None = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) - if macro_name is not None: - return 1 if ctx.is_macro_defined(macro_name) else 0 - return 0 - if func_name == '@CIfndef': - if node.args: - macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) - if macro_name is not None: - return 0 if ctx.is_macro_defined(macro_name) else 1 - return 1 - # defined(NAME) 谓词(未 desugar 形态 / TPV 口径):编译期返回 0/1。 - if func_name == 'defined' and module_name is None: - if node.args: - macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) - if macro_name is not None: - return 1 if ctx.is_macro_defined(macro_name) else 0 - return 0 - - # ctraits.isptr(x) — 带模块前缀 - # isptr(x) — 通过 from ctraits import isptr 导入 - is_isptr: bool = False - if module_name == 'ctraits' and func_name == 'isptr': - is_isptr = True - elif func_name == 'isptr' and not module_name: - # 检查是否通过 from ctraits import isptr 导入 - t_c_imported = getattr(ctx.translator, '_t_c_imported_names', {}) if ctx.translator else {} - if func_name in t_c_imported: - src_module, _ = t_c_imported[func_name] - if src_module == 'ctraits': - is_isptr = True - # 也检查符号表中是否有 ctraits.isptr - if not is_isptr and ctx.symtab: - sym = ctx.symtab.lookup('ctraits.isptr') if hasattr(ctx.symtab, 'lookup') else None - if sym: - is_isptr = True - - if is_isptr and node.args and ctx.translator: - try: - arg_node: ast.expr = node.args[0] - # 局部变量:优先通过 LLVM 类型判断(GetCTypeInfo 无法解析局部变量名) - if ctx.Gen and isinstance(arg_node, ast.Name): - var_name: str = arg_node.id - variables: dict = getattr(ctx.Gen, 'variables', {}) - if var_name in variables: - var_val: Any = variables[var_name] - if hasattr(var_val, 'type') and isinstance(var_val.type, ir.PointerType): - pointee: ir.Type = var_val.type.pointee - if isinstance(pointee, ir.PointerType): - return 1 - return 0 - # 类型名 / typedef:通过 TypeMergeHandler 获取 CTypeInfo - type_info = ctx.translator.TypeMergeHandler.GetCTypeInfo(arg_node) - if type_info and (type_info.IsPtr or type_info.PtrCount > 0): - return 1 - if type_info and not (type_info.IsPtr or type_info.PtrCount > 0): - return 0 - return None - except Exception: - return None - - # 治本修复:支持 t.CUnsignedLong(-11) 等类型构造调用 - # 当 t.CUnsignedLong(value) / CUnsignedInt(value) / CInt(value) 等被用作常量表达式时, - # 求值参数并返回(按类型的无符号/有符号语义应用位掩码)。 - # 这修复了 stdint.py 中 INFINITE = t.CUnsignedLong(-1) 等常量无法被求值的问题。 - is_ctype_ctor: bool = False - ctype_cls: Any = None - if module_name == 't' and func_name: - ctype_cls = getattr(_t_module, func_name, None) - if ctype_cls is not None and isinstance(ctype_cls, type) and issubclass(ctype_cls, _CType_base): - is_ctype_ctor = True - elif func_name and not module_name: - # from t import CUnsignedLong 形式 - ctype_cls = getattr(_t_module, func_name, None) - if ctype_cls is not None and isinstance(ctype_cls, type) and issubclass(ctype_cls, _CType_base): - is_ctype_ctor = True - # 也检查 _t_type_symbols(符号表中的 t 模块类型符号) - if not is_ctype_ctor and ctx.symtab: - t_type_syms = getattr(ctx.symtab, '_t_type_symbols', {}) - cls_candidate = t_type_syms.get(func_name) - if cls_candidate is not None and isinstance(cls_candidate, type) and issubclass(cls_candidate, _CType_base): - ctype_cls = cls_candidate - is_ctype_ctor = True - - if is_ctype_ctor and node.args: - try: - arg_node: ast.expr = node.args[0] - arg_val: Any = ConstEvaluator.eval_full(arg_node, ctx) - if arg_val is None: - return None - # 按类型的 Size 和 IsSigned 应用位掩码 - size: int | None = getattr(ctype_cls, '_Size', None) - is_signed: bool | None = getattr(ctype_cls, '_IsSigned', None) - # 尝试创建临时实例获取 Size/IsSigned - try: - tmp_inst = ctype_cls() - size = getattr(tmp_inst, 'Size', size) - is_signed = getattr(tmp_inst, 'IsSigned', is_signed) - except Exception: - pass - if isinstance(arg_val, int) and size and size > 0: - mask: int = (1 << size) - 1 - if is_signed: - # 有符号类型:解释为补码 - masked: int = arg_val & mask - if masked >= (1 << (size - 1)): - masked -= (1 << size) - return masked - else: - # 无符号类型:直接掩码 - return arg_val & mask - return arg_val - except Exception: - return None - return None - - @staticmethod - def _eval_arith(node: ast.AST, recurse: Callable[[ast.AST], Any]) -> Optional[Any]: - """通用算术求值:BinOp + UnaryOp""" - if isinstance(node, ast.BinOp): - left: Any = recurse(node.left) - right: Any = recurse(node.right) - if left is None or right is None: - return None - return ConstEvaluator._apply_binop(node.op, left, right) - if isinstance(node, ast.UnaryOp): - operand: Any = recurse(node.operand) - if operand is None: - return None - if isinstance(node.op, ast.USub): - return -operand - if isinstance(node.op, ast.UAdd): - return +operand - if isinstance(node.op, ast.Invert): - return ~operand - return None - return None - - @staticmethod - def _apply_binop(op: ast.operator, left: Any, right: Any) -> Optional[Any]: - """应用二元运算符""" - try: - if isinstance(op, ast.Add): - return left + right - elif isinstance(op, ast.Sub): - return left - right - elif isinstance(op, ast.Mult): - return left * right - elif isinstance(op, ast.Div): - return left // right if isinstance(left, int) and isinstance(right, int) else left / right - elif isinstance(op, ast.FloorDiv): - if isinstance(right, (int, float)) and right == 0: - return None - return left // right - elif isinstance(op, ast.Mod): - if isinstance(right, (int, float)) and right == 0: - return None - return left % right - elif isinstance(op, ast.Pow): - return left ** right - elif isinstance(op, ast.LShift): - return left << right - elif isinstance(op, ast.RShift): - return left >> right - elif isinstance(op, ast.BitOr): - return left | right - elif isinstance(op, ast.BitXor): - return left ^ right - elif isinstance(op, ast.BitAnd): - return left & right - except Exception: - return None - return None - - @staticmethod - def _eval_compare_op(op: ast.cmpop, left: Any, right: Any) -> Optional[bool]: - """应用比较运算符""" - if isinstance(op, ast.Eq): - return left == right - elif isinstance(op, ast.NotEq): - return left != right - elif isinstance(op, ast.Lt): - return left < right - elif isinstance(op, ast.LtE): - return left <= right - elif isinstance(op, ast.Gt): - return left > right - elif isinstance(op, ast.GtE): - return left >= right - return None - - @staticmethod - def _eval_ctype_size_attr(node: ast.Attribute) -> Optional[int]: - """求值 t.CSizeT().Size 等类型构造调用 + .Size 属性模式。 - - 匹配 AST 形态: Attribute(attr='Size', value=Call(func=Attribute(value=Name(id='t'), attr='CSizeT'))) - 或直接 Call(func=Name(id='CSizeT'))(from t import CSizeT 后的使用方式)。 - 返回类型大小(位数),如 CSizeT().Size 返回 64(64 位平台)。 - """ - if node.attr != 'Size': - return None - if not isinstance(node.value, ast.Call): - return None - call_node: ast.Call = node.value - if call_node.args or call_node.keywords: - return None - func: ast.expr = call_node.func - type_name: str | None = None - if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == 't': - type_name = func.attr - elif isinstance(func, ast.Name): - type_name = func.id - if not type_name: - return None - # 延迟导入避免循环依赖 - try: - from lib.includes.t import CTypeRegistry - except Exception: - return None - cls = CTypeRegistry.GetClassByName(type_name) - if cls is None: - return None - try: - instance = cls() - size_val = getattr(instance, 'Size', None) - if isinstance(size_val, int): - return size_val - except Exception: - pass - return None - - @staticmethod - def _resolve_macro_ref(node: ast.AST, ctx: 'EvalContext') -> Optional[str]: - """从 defined(...) 实参节点解析宏名:Name→id;Constant(str)→值;Attribute→点分全名。""" - if isinstance(node, ast.Name): - return node.id - if isinstance(node, ast.Constant) and isinstance(node.value, str): - return node.value - if isinstance(node, ast.Attribute): - return EvalContext._get_attr_full_name(node) - return None - - @staticmethod - def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]: - """从 SymbolTable 查找 Attribute 形式的 define 值""" - parts: list[str] = [] - current: ast.AST = node - while isinstance(current, ast.Attribute): - parts.append(current.attr) - current = current.value - if isinstance(current, ast.Name): - parts.append(current.id) - parts.reverse() - attr_name: str = parts[-1] - possible_keys: list[str] = [attr_name, '.'.join(parts)] - for key in possible_keys: - if hasattr(symtab, '__contains__') and key in symtab: - info: CTypeInfo = symtab[key] - if info.IsDefine and isinstance(info.DefineValue, int): - return info.DefineValue - # 模糊匹配 - if len(parts) >= 2: - mod_key: str - mod_info: CTypeInfo - for mod_key, mod_info in (symtab.items() if hasattr(symtab, 'items') else []): - if mod_info.IsDefine and isinstance(mod_info.DefineValue, int): - if mod_key.endswith('.' + attr_name) or mod_key == attr_name: - return mod_info.DefineValue - return None - - @staticmethod - def _intify(val: Any) -> Any: - """float → int 转换(仅当 float 值恰好是整数时)""" - if isinstance(val, float) and val == int(val): - return int(val) - return val - - -class EvalContext: - """常量求值上下文,封装 Gen / SymbolTable / 平台宏查找逻辑。 - - 由调用方构造并传入 ConstEvaluator.eval_full()。 - """ - - def __init__(self, Gen: LlvmCodeGenerator | None = None, symtab: Any = None, platform_macros: dict | None = None, translator: Any = None): - self.Gen: LlvmCodeGenerator | None = Gen - self.symtab: Any = symtab - self._platform_macros: dict | None = platform_macros - self.translator: Any = translator - - def lookup_name(self, name: str) -> Optional[Any]: - """查找 Name 节点的值:define_constants → SymbolTable → 平台宏 - - 治本修复:不再把 module.globals 的 initializer 当常量返回。 - CDefine 不创建 LLVM global(不在 module.globals),仅可变全局变量在 - module.globals 中;把可变全局的 initializer 当编译期常量会让 - range(_sha1_store_count) 等读取处求值成初始值 0 而不生成 load。 - CDefine 通过下方 _define_constants 查询返回常量,双点 bug 不复发。 - """ - # 1. Gen._define_constants - if self.Gen and name in self.Gen._define_constants: - val: Any = self.Gen._define_constants[name] - # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过 - # 共享 _define_constants 覆盖本模块常量 - # (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug) - _gvar: Any = None - try: - if hasattr(self.Gen, 'module') and name in self.Gen.module.globals: - _gvar = self.Gen.module.globals[name] - except Exception: - _gvar = None - if _gvar is not None and getattr(_gvar, 'initializer', None) is not None: - _init: Any = _gvar.initializer - if hasattr(_init, 'constant') and isinstance(_init.constant, (int, float)): - val = _init.constant - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - - # 2. Gen._DefineConstants (大写版本) - if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants: - val: Any = self.Gen._DefineConstants[name] - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - - # 3. SymbolTable - if self.symtab and name in self.symtab: - info: CTypeInfo = self.symtab[name] - val: Any = info.value - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - if info.IsDefine and isinstance(info.DefineValue, (int, float)): - return ConstEvaluator._intify(info.DefineValue) - - # 4. 平台宏 - macros: dict = self._get_platform_macros() - if name in macros: - return macros[name] - - return None - +"""统一常量表达式求值器 + +整合了原先分散在 5 处的常量求值逻辑: + - SymbolTable._eval_const_expr + - CTypeInfo.TryEvalConstExpr + - ConstEvalMixin._try_eval_const_expr + - HandlesAssign._eval_const_expr + - HandlesIf._eval_const_expr + +ConstEvaluator 提供三个级别的求值: + 1. eval_basic(node) — 纯 AST 常量 + 算术(无外部依赖) + 2. eval_with_symtab(node) — + SymbolTable 中的 define 查找 + 3. eval_full(node, ctx) — + Gen._define_constants / _all_define_constants / 平台宏 +""" +from __future__ import annotations + +import vast as ast +from typing import Any, Callable, Optional, TYPE_CHECKING + +import llvmlite.ir as ir + +from lib.core.Handles.HandlesBase import CTypeInfo +# 治本修复:导入 t 模块和 CType 基类,用于类型构造调用求值(如 t.CUnsignedLong(-11)) +from lib.includes import t as _t_module +from lib.includes.t import CType as _CType_base + +if TYPE_CHECKING: + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + + +class ConstEvaluator: + """统一常量表达式求值器""" + + # ================================================================== + # Level 2: + SymbolTable define 查找 + # ================================================================== + + @staticmethod + def eval_with_symtab(node: ast.AST, symtab: Any) -> Optional[Any]: + """求值常量表达式,支持从 SymbolTable 查找 define 值。 + + 支持: Constant, BinOp, UnaryOp, Name(define), Attribute(define), Call(类型构造) + """ + if isinstance(node, ast.Constant): + return node.value + if isinstance(node, ast.Name): + if hasattr(symtab, '__contains__') and node.id in symtab: + info: CTypeInfo = symtab[node.id] + if info.IsDefine and isinstance(info.DefineValue, int): + return info.DefineValue + return None + if isinstance(node, ast.Attribute): + # 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性) + size_val = ConstEvaluator._eval_ctype_size_attr(node) + if size_val is not None: + return size_val + return ConstEvaluator._eval_attribute_define(node, symtab) + # 治本修复:支持 t.CUnsignedLong(-11) 等类型构造调用 + if isinstance(node, ast.Call): + return ConstEvaluator._eval_ctype_ctor_call(node, symtab) + return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_with_symtab(n, symtab)) + + @staticmethod + def _eval_ctype_ctor_call(node: ast.Call, symtab: Any) -> Optional[Any]: + """求值类型构造调用,如 t.CUnsignedLong(-11)、CInt(42)。 + + 在无 ctx(EvalContext)的场景下,通过 symtab 和 t 模块直接求值。 + 按类型的 Size 和 IsSigned 应用位掩码。 + """ + func: ast.expr = node.func + func_name: str | None = None + module_name: str | None = None + if isinstance(func, ast.Attribute): + if isinstance(func.value, ast.Name): + module_name = func.value.id + func_name = func.attr + elif isinstance(func, ast.Name): + func_name = func.id + + ctype_cls: Any = None + if module_name == 't' and func_name: + ctype_cls = getattr(_t_module, func_name, None) + elif func_name and not module_name: + ctype_cls = getattr(_t_module, func_name, None) + if ctype_cls is None and symtab: + t_type_syms = getattr(symtab, '_t_type_symbols', {}) + cls_candidate = t_type_syms.get(func_name) + if cls_candidate is not None: + ctype_cls = cls_candidate + + if ctype_cls is None or not (isinstance(ctype_cls, type) and issubclass(ctype_cls, _CType_base)): + return None + if not node.args: + return None + + arg_val: Any = ConstEvaluator.eval_with_symtab(node.args[0], symtab) + if arg_val is None: + return None + + size: int | None = None + is_signed: bool | None = None + try: + tmp_inst = ctype_cls() + size = getattr(tmp_inst, 'Size', None) + is_signed = getattr(tmp_inst, 'IsSigned', None) + except Exception: + pass + + if isinstance(arg_val, int) and size and size > 0: + mask: int = (1 << size) - 1 + if is_signed: + masked: int = arg_val & mask + if masked >= (1 << (size - 1)): + masked -= (1 << size) + return masked + else: + return arg_val & mask + return arg_val + + # ================================================================== + # Level 3: + Gen._define_constants / _all_define_constants / 平台宏 + # ================================================================== + + @staticmethod + def eval_full(node: ast.AST, ctx: 'EvalContext') -> Optional[Any]: + """完整求值,支持 define_constants、平台宏、BoolOp、Compare。 + + ctx: EvalContext 实例,封装了 Gen、SymbolTable 等上下文。 + """ + # BoolOp (HandlesIf 逻辑) + if isinstance(node, ast.BoolOp): + if isinstance(node.op, ast.And): + for val in node.values: + result: Any = ConstEvaluator.eval_full(val, ctx) + if result is None: + return None + if not result: + return 0 + return 1 + elif isinstance(node.op, ast.Or): + for val in node.values: + result: Any = ConstEvaluator.eval_full(val, ctx) + if result is None: + return None + if result: + return 1 + return 0 + return None + + # Compare (HandlesIf 逻辑) + if isinstance(node, ast.Compare): + left: Any = ConstEvaluator.eval_full(node.left, ctx) + if left is None: + return None + op: ast.cmpop + comparator: ast.AST + for op, comparator in zip(node.ops, node.comparators): + right: Any = ConstEvaluator.eval_full(comparator, ctx) + if right is None: + return None + result: bool | None = ConstEvaluator._eval_compare_op(op, left, right) + if result is None: + return None + if not result: + return 0 + left = right + return 1 + + # Constant — 直接返回原始值,bool→0/1 转换由 HandlesIf 自行处理 + if isinstance(node, ast.Constant): + return node.value + + # Name — 查 define_constants / SymbolTable / 平台宏 + if isinstance(node, ast.Name): + return ctx.lookup_name(node.id) + + # Attribute — 查 define_constants / SymbolTable / 平台宏 + if isinstance(node, ast.Attribute): + # 先尝试 t.CSizeT().Size 模式(类型构造调用 + .Size 属性) + size_val = ConstEvaluator._eval_ctype_size_attr(node) + if size_val is not None: + return size_val + return ctx.lookup_attribute(node) + + # UnaryOp + if isinstance(node, ast.UnaryOp): + operand: Any = ConstEvaluator.eval_full(node.operand, ctx) + if operand is None: + return None + if isinstance(node.op, ast.Not): + return 1 if not operand else 0 + if isinstance(node.op, ast.USub): + return -operand + if isinstance(node.op, ast.UAdd): + return +operand + if isinstance(node.op, ast.Invert): + return ~operand + return None + + # Call — 编译时函数调用(如 ctraits.isptr(x)) + if isinstance(node, ast.Call): + return ConstEvaluator._eval_compile_time_call(node, ctx) + + # BinOp + return ConstEvaluator._eval_arith(node, lambda n: ConstEvaluator.eval_full(n, ctx)) + + # ================================================================== + # 内部辅助 + # ================================================================== + + @staticmethod + def _eval_compile_time_call(node: ast.Call, ctx: 'EvalContext') -> Optional[Any]: + """处理编译时函数调用,如 ctraits.isptr(x)、t.CUnsignedLong(-11)""" + func: ast.expr = node.func + func_name: str | None = None + module_name: str | None = None + + if isinstance(func, ast.Attribute): + if isinstance(func.value, ast.Name): + module_name = func.value.id + func_name = func.attr + elif isinstance(func, ast.Name): + func_name = func.id + + # c.@CIfdef(NAME) / c.@CIfndef(NAME):defined(NAME) 的解析期 desugar 标记,编译期返回 0/1。 + # 仅出现在 cif/celif 条件内(解析器保证),故此处只做宏定义判定。 + if func_name == '@CIfdef': + if node.args: + macro_name: str | None = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) + if macro_name is not None: + return 1 if ctx.is_macro_defined(macro_name) else 0 + return 0 + if func_name == '@CIfndef': + if node.args: + macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) + if macro_name is not None: + return 0 if ctx.is_macro_defined(macro_name) else 1 + return 1 + # defined(NAME) 谓词(未 desugar 形态 / VPC 口径):编译期返回 0/1。 + if func_name == 'defined' and module_name is None: + if node.args: + macro_name = ConstEvaluator._resolve_macro_ref(node.args[0], ctx) + if macro_name is not None: + return 1 if ctx.is_macro_defined(macro_name) else 0 + return 0 + + # ctraits.isptr(x) — 带模块前缀 + # isptr(x) — 通过 from ctraits import isptr 导入 + is_isptr: bool = False + if module_name == 'ctraits' and func_name == 'isptr': + is_isptr = True + elif func_name == 'isptr' and not module_name: + # 检查是否通过 from ctraits import isptr 导入 + t_c_imported = getattr(ctx.translator, '_t_c_imported_names', {}) if ctx.translator else {} + if func_name in t_c_imported: + src_module, _ = t_c_imported[func_name] + if src_module == 'ctraits': + is_isptr = True + # 也检查符号表中是否有 ctraits.isptr + if not is_isptr and ctx.symtab: + sym = ctx.symtab.lookup('ctraits.isptr') if hasattr(ctx.symtab, 'lookup') else None + if sym: + is_isptr = True + + if is_isptr and node.args and ctx.translator: + try: + arg_node: ast.expr = node.args[0] + # 局部变量:优先通过 LLVM 类型判断(GetCTypeInfo 无法解析局部变量名) + if ctx.Gen and isinstance(arg_node, ast.Name): + var_name: str = arg_node.id + variables: dict = getattr(ctx.Gen, 'variables', {}) + if var_name in variables: + var_val: Any = variables[var_name] + if hasattr(var_val, 'type') and isinstance(var_val.type, ir.PointerType): + pointee: ir.Type = var_val.type.pointee + if isinstance(pointee, ir.PointerType): + return 1 + return 0 + # 类型名 / typedef:通过 TypeMergeHandler 获取 CTypeInfo + type_info = ctx.translator.TypeMergeHandler.GetCTypeInfo(arg_node) + if type_info and (type_info.IsPtr or type_info.PtrCount > 0): + return 1 + if type_info and not (type_info.IsPtr or type_info.PtrCount > 0): + return 0 + return None + except Exception: + return None + + # 治本修复:支持 t.CUnsignedLong(-11) 等类型构造调用 + # 当 t.CUnsignedLong(value) / CUnsignedInt(value) / CInt(value) 等被用作常量表达式时, + # 求值参数并返回(按类型的无符号/有符号语义应用位掩码)。 + # 这修复了 stdint.py 中 INFINITE = t.CUnsignedLong(-1) 等常量无法被求值的问题。 + is_ctype_ctor: bool = False + ctype_cls: Any = None + if module_name == 't' and func_name: + ctype_cls = getattr(_t_module, func_name, None) + if ctype_cls is not None and isinstance(ctype_cls, type) and issubclass(ctype_cls, _CType_base): + is_ctype_ctor = True + elif func_name and not module_name: + # from t import CUnsignedLong 形式 + ctype_cls = getattr(_t_module, func_name, None) + if ctype_cls is not None and isinstance(ctype_cls, type) and issubclass(ctype_cls, _CType_base): + is_ctype_ctor = True + # 也检查 _t_type_symbols(符号表中的 t 模块类型符号) + if not is_ctype_ctor and ctx.symtab: + t_type_syms = getattr(ctx.symtab, '_t_type_symbols', {}) + cls_candidate = t_type_syms.get(func_name) + if cls_candidate is not None and isinstance(cls_candidate, type) and issubclass(cls_candidate, _CType_base): + ctype_cls = cls_candidate + is_ctype_ctor = True + + if is_ctype_ctor and node.args: + try: + arg_node: ast.expr = node.args[0] + arg_val: Any = ConstEvaluator.eval_full(arg_node, ctx) + if arg_val is None: + return None + # 按类型的 Size 和 IsSigned 应用位掩码 + size: int | None = getattr(ctype_cls, '_Size', None) + is_signed: bool | None = getattr(ctype_cls, '_IsSigned', None) + # 尝试创建临时实例获取 Size/IsSigned + try: + tmp_inst = ctype_cls() + size = getattr(tmp_inst, 'Size', size) + is_signed = getattr(tmp_inst, 'IsSigned', is_signed) + except Exception: + pass + if isinstance(arg_val, int) and size and size > 0: + mask: int = (1 << size) - 1 + if is_signed: + # 有符号类型:解释为补码 + masked: int = arg_val & mask + if masked >= (1 << (size - 1)): + masked -= (1 << size) + return masked + else: + # 无符号类型:直接掩码 + return arg_val & mask + return arg_val + except Exception: + return None + return None + + @staticmethod + def _eval_arith(node: ast.AST, recurse: Callable[[ast.AST], Any]) -> Optional[Any]: + """通用算术求值:BinOp + UnaryOp""" + if isinstance(node, ast.BinOp): + left: Any = recurse(node.left) + right: Any = recurse(node.right) + if left is None or right is None: + return None + return ConstEvaluator._apply_binop(node.op, left, right) + if isinstance(node, ast.UnaryOp): + operand: Any = recurse(node.operand) + if operand is None: + return None + if isinstance(node.op, ast.USub): + return -operand + if isinstance(node.op, ast.UAdd): + return +operand + if isinstance(node.op, ast.Invert): + return ~operand + return None + return None + + @staticmethod + def _apply_binop(op: ast.operator, left: Any, right: Any) -> Optional[Any]: + """应用二元运算符""" + try: + if isinstance(op, ast.Add): + return left + right + elif isinstance(op, ast.Sub): + return left - right + elif isinstance(op, ast.Mult): + return left * right + elif isinstance(op, ast.Div): + return left // right if isinstance(left, int) and isinstance(right, int) else left / right + elif isinstance(op, ast.FloorDiv): + if isinstance(right, (int, float)) and right == 0: + return None + return left // right + elif isinstance(op, ast.Mod): + if isinstance(right, (int, float)) and right == 0: + return None + return left % right + elif isinstance(op, ast.Pow): + return left ** right + elif isinstance(op, ast.LShift): + return left << right + elif isinstance(op, ast.RShift): + return left >> right + elif isinstance(op, ast.BitOr): + return left | right + elif isinstance(op, ast.BitXor): + return left ^ right + elif isinstance(op, ast.BitAnd): + return left & right + except Exception: + return None + return None + + @staticmethod + def _eval_compare_op(op: ast.cmpop, left: Any, right: Any) -> Optional[bool]: + """应用比较运算符""" + if isinstance(op, ast.Eq): + return left == right + elif isinstance(op, ast.NotEq): + return left != right + elif isinstance(op, ast.Lt): + return left < right + elif isinstance(op, ast.LtE): + return left <= right + elif isinstance(op, ast.Gt): + return left > right + elif isinstance(op, ast.GtE): + return left >= right + return None + + @staticmethod + def _eval_ctype_size_attr(node: ast.Attribute) -> Optional[int]: + """求值 t.CSizeT().Size 等类型构造调用 + .Size 属性模式。 + + 匹配 AST 形态: Attribute(attr='Size', value=Call(func=Attribute(value=Name(id='t'), attr='CSizeT'))) + 或直接 Call(func=Name(id='CSizeT'))(from t import CSizeT 后的使用方式)。 + 返回类型大小(位数),如 CSizeT().Size 返回 64(64 位平台)。 + """ + if node.attr != 'Size': + return None + if not isinstance(node.value, ast.Call): + return None + call_node: ast.Call = node.value + if call_node.args or call_node.keywords: + return None + func: ast.expr = call_node.func + type_name: str | None = None + if isinstance(func, ast.Attribute) and isinstance(func.value, ast.Name) and func.value.id == 't': + type_name = func.attr + elif isinstance(func, ast.Name): + type_name = func.id + if not type_name: + return None + # 延迟导入避免循环依赖 + try: + from lib.includes.t import CTypeRegistry + except Exception: + return None + cls = CTypeRegistry.GetClassByName(type_name) + if cls is None: + return None + try: + instance = cls() + size_val = getattr(instance, 'Size', None) + if isinstance(size_val, int): + return size_val + except Exception: + pass + return None + + @staticmethod + def _resolve_macro_ref(node: ast.AST, ctx: 'EvalContext') -> Optional[str]: + """从 defined(...) 实参节点解析宏名:Name→id;Constant(str)→值;Attribute→点分全名。""" + if isinstance(node, ast.Name): + return node.id + if isinstance(node, ast.Constant) and isinstance(node.value, str): + return node.value + if isinstance(node, ast.Attribute): + return EvalContext._get_attr_full_name(node) + return None + + @staticmethod + def _eval_attribute_define(node: ast.Attribute, symtab: Any) -> Optional[Any]: + """从 SymbolTable 查找 Attribute 形式的 define 值""" + parts: list[str] = [] + current: ast.AST = node + while isinstance(current, ast.Attribute): + parts.append(current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.append(current.id) + parts.reverse() + attr_name: str = parts[-1] + possible_keys: list[str] = [attr_name, '.'.join(parts)] + for key in possible_keys: + if hasattr(symtab, '__contains__') and key in symtab: + info: CTypeInfo = symtab[key] + if info.IsDefine and isinstance(info.DefineValue, int): + return info.DefineValue + # 模糊匹配 + if len(parts) >= 2: + mod_key: str + mod_info: CTypeInfo + for mod_key, mod_info in (symtab.items() if hasattr(symtab, 'items') else []): + if mod_info.IsDefine and isinstance(mod_info.DefineValue, int): + if mod_key.endswith('.' + attr_name) or mod_key == attr_name: + return mod_info.DefineValue + return None + + @staticmethod + def _intify(val: Any) -> Any: + """float → int 转换(仅当 float 值恰好是整数时)""" + if isinstance(val, float) and val == int(val): + return int(val) + return val + + +class EvalContext: + """常量求值上下文,封装 Gen / SymbolTable / 平台宏查找逻辑。 + + 由调用方构造并传入 ConstEvaluator.eval_full()。 + """ + + def __init__(self, Gen: LlvmCodeGenerator | None = None, symtab: Any = None, platform_macros: dict | None = None, translator: Any = None): + self.Gen: LlvmCodeGenerator | None = Gen + self.symtab: Any = symtab + self._platform_macros: dict | None = platform_macros + self.translator: Any = translator + + def lookup_name(self, name: str) -> Optional[Any]: + """查找 Name 节点的值:define_constants → SymbolTable → 平台宏 + + 治本修复:不再把 module.globals 的 initializer 当常量返回。 + CDefine 不创建 LLVM global(不在 module.globals),仅可变全局变量在 + module.globals 中;把可变全局的 initializer 当编译期常量会让 + range(_sha1_store_count) 等读取处求值成初始值 0 而不生成 load。 + CDefine 通过下方 _define_constants 查询返回常量,双点 bug 不复发。 + """ + # 1. Gen._define_constants + if self.Gen and name in self.Gen._define_constants: + val: Any = self.Gen._define_constants[name] + # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 通过 + # 共享 _define_constants 覆盖本模块常量 + # (VpiReader.SHA1_LEN=16 被 Phase1.SHA1_LEN=17 覆盖导致双点符号 bug) + _gvar: Any = None + try: + if hasattr(self.Gen, 'module') and name in self.Gen.module.globals: + _gvar = self.Gen.module.globals[name] + except Exception: + _gvar = None + if _gvar is not None and getattr(_gvar, 'initializer', None) is not None: + _init: Any = _gvar.initializer + if hasattr(_init, 'constant') and isinstance(_init.constant, (int, float)): + val = _init.constant + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 2. Gen._DefineConstants (大写版本) + if self.Gen and hasattr(self.Gen, '_DefineConstants') and name in self.Gen._DefineConstants: + val: Any = self.Gen._DefineConstants[name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 3. SymbolTable + if self.symtab and name in self.symtab: + info: CTypeInfo = self.symtab[name] + val: Any = info.value + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + if info.IsDefine and isinstance(info.DefineValue, (int, float)): + return ConstEvaluator._intify(info.DefineValue) + + # 4. 平台宏 + macros: dict = self._get_platform_macros() + if name in macros: + return macros[name] + + return None + def is_macro_defined(self, name: str) -> bool: """判定宏 NAME 是否已定义(供 defined(NAME) / c.@CIfdef 标记使用)。 @@ -598,120 +598,120 @@ class EvalContext: return False def lookup_attribute(self, node: ast.Attribute) -> Optional[Any]: - """查找 Attribute 节点的值""" - module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None - attr_name: str = node.attr - - if module_name and attr_name: - combined_key: str = f"{module_name}.{attr_name}" - - # 1. Gen._define_constants - if self.Gen and combined_key in self.Gen._define_constants: - val: Any = self.Gen._define_constants[combined_key] - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - - # 2. SymbolTable (精确匹配 + 后缀匹配) - if self.symtab: - key: str - info: CTypeInfo - for key, info in self.symtab.items(): - if key.endswith(f".{combined_key}") or key == combined_key: - if info.IsDefine and isinstance(info.DefineValue, (int, float)): - return ConstEvaluator._intify(info.DefineValue) - - # 3. _all_define_constants - all_dc: dict = self._get_all_define_constants() - if combined_key in all_dc: - val: Any = all_dc[combined_key] - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - if attr_name in all_dc: - val: Any = all_dc[attr_name] - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - dc_key: str - dc_val: Any - for dc_key, dc_val in all_dc.items(): - if dc_key == attr_name or dc_key.endswith(f".{attr_name}"): - if isinstance(dc_val, (int, float)): - return ConstEvaluator._intify(dc_val) - - # 4. SymbolTable 短名查找 - if self.symtab and attr_name in self.symtab: - info: CTypeInfo = self.symtab[attr_name] - if info.IsDefine and isinstance(info.DefineValue, (int, float)): - return ConstEvaluator._intify(info.DefineValue) - - # 5. Gen._define_constants 短名 - if self.Gen and attr_name in self.Gen._define_constants: - val: Any = self.Gen._define_constants[attr_name] - if isinstance(val, (int, float)): - return ConstEvaluator._intify(val) - - # 6. 平台宏 - macros: dict = self._get_platform_macros() - full_key: str | None = self._get_attr_full_name(node) - if full_key: - if full_key in macros: - return macros[full_key] - short_name: str = full_key.split('.')[-1] if '.' in full_key else full_key - if short_name in macros: - return macros[short_name] - - return None - - def _get_all_define_constants(self) -> dict: - """获取 _all_define_constants""" - if self.Gen and hasattr(self.Gen, '_all_define_constants'): - return self.Gen._all_define_constants - return {} - - def _get_platform_macros(self) -> dict: - """获取平台宏""" - if self._platform_macros is not None: - return self._platform_macros - if not self.Gen: - return {} - macros: dict[str, int] = {} - pi: dict = self.Gen._platform_info - ptr_size: int = self.Gen.ptr_size - if pi.get('is_windows'): - macros['_WIN32'] = 1 - if not pi.get('is_32bit'): - macros['_WIN64'] = 1 - macros['WIN64'] = 1 - macros['WIN32'] = 1 - if pi.get('is_linux'): - macros['__linux__'] = 1 - if pi.get('is_macos'): - macros['__APPLE__'] = 1 - macros['__MACH__'] = 1 - if pi.get('is_x86_64'): - macros['__x86_64__'] = 1 - macros['__x86_64'] = 1 - macros['_M_X64'] = 1 - elif pi.get('is_arm64'): - macros['__aarch64__'] = 1 - macros['_M_ARM64'] = 1 - if not pi.get('is_32bit') and pi.get('is_linux'): - macros['__LP64__'] = 1 - elif pi.get('is_32bit'): - macros['_ILP32'] = 1 - macros['SIZEOF_VOID_P'] = ptr_size - macros['__SIZEOF_POINTER__'] = ptr_size - macros['NDEBUG'] = 0 - return macros - - @staticmethod - def _get_attr_full_name(node: ast.Attribute) -> Optional[str]: - """从 Attribute 节点提取完整名称 (e.g. 'module.name')""" - parts: list[str] = [] - current: ast.AST = node - while isinstance(current, ast.Attribute): - parts.append(current.attr) - current = current.value - if isinstance(current, ast.Name): - parts.append(current.id) - parts.reverse() - return '.'.join(parts) if parts else None + """查找 Attribute 节点的值""" + module_name: str | None = node.value.id if isinstance(node.value, ast.Name) else None + attr_name: str = node.attr + + if module_name and attr_name: + combined_key: str = f"{module_name}.{attr_name}" + + # 1. Gen._define_constants + if self.Gen and combined_key in self.Gen._define_constants: + val: Any = self.Gen._define_constants[combined_key] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 2. SymbolTable (精确匹配 + 后缀匹配) + if self.symtab: + key: str + info: CTypeInfo + for key, info in self.symtab.items(): + if key.endswith(f".{combined_key}") or key == combined_key: + if info.IsDefine and isinstance(info.DefineValue, (int, float)): + return ConstEvaluator._intify(info.DefineValue) + + # 3. _all_define_constants + all_dc: dict = self._get_all_define_constants() + if combined_key in all_dc: + val: Any = all_dc[combined_key] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + if attr_name in all_dc: + val: Any = all_dc[attr_name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + dc_key: str + dc_val: Any + for dc_key, dc_val in all_dc.items(): + if dc_key == attr_name or dc_key.endswith(f".{attr_name}"): + if isinstance(dc_val, (int, float)): + return ConstEvaluator._intify(dc_val) + + # 4. SymbolTable 短名查找 + if self.symtab and attr_name in self.symtab: + info: CTypeInfo = self.symtab[attr_name] + if info.IsDefine and isinstance(info.DefineValue, (int, float)): + return ConstEvaluator._intify(info.DefineValue) + + # 5. Gen._define_constants 短名 + if self.Gen and attr_name in self.Gen._define_constants: + val: Any = self.Gen._define_constants[attr_name] + if isinstance(val, (int, float)): + return ConstEvaluator._intify(val) + + # 6. 平台宏 + macros: dict = self._get_platform_macros() + full_key: str | None = self._get_attr_full_name(node) + if full_key: + if full_key in macros: + return macros[full_key] + short_name: str = full_key.split('.')[-1] if '.' in full_key else full_key + if short_name in macros: + return macros[short_name] + + return None + + def _get_all_define_constants(self) -> dict: + """获取 _all_define_constants""" + if self.Gen and hasattr(self.Gen, '_all_define_constants'): + return self.Gen._all_define_constants + return {} + + def _get_platform_macros(self) -> dict: + """获取平台宏""" + if self._platform_macros is not None: + return self._platform_macros + if not self.Gen: + return {} + macros: dict[str, int] = {} + pi: dict = self.Gen._platform_info + ptr_size: int = self.Gen.ptr_size + if pi.get('is_windows'): + macros['_WIN32'] = 1 + if not pi.get('is_32bit'): + macros['_WIN64'] = 1 + macros['WIN64'] = 1 + macros['WIN32'] = 1 + if pi.get('is_linux'): + macros['__linux__'] = 1 + if pi.get('is_macos'): + macros['__APPLE__'] = 1 + macros['__MACH__'] = 1 + if pi.get('is_x86_64'): + macros['__x86_64__'] = 1 + macros['__x86_64'] = 1 + macros['_M_X64'] = 1 + elif pi.get('is_arm64'): + macros['__aarch64__'] = 1 + macros['_M_ARM64'] = 1 + if not pi.get('is_32bit') and pi.get('is_linux'): + macros['__LP64__'] = 1 + elif pi.get('is_32bit'): + macros['_ILP32'] = 1 + macros['SIZEOF_VOID_P'] = ptr_size + macros['__SIZEOF_POINTER__'] = ptr_size + macros['NDEBUG'] = 0 + return macros + + @staticmethod + def _get_attr_full_name(node: ast.Attribute) -> Optional[str]: + """从 Attribute 节点提取完整名称 (e.g. 'module.name')""" + parts: list[str] = [] + current: ast.AST = node + while isinstance(current, ast.Attribute): + parts.append(current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.append(current.id) + parts.reverse() + return '.'.join(parts) if parts else None diff --git a/lib/core/Handles/HandlesAnnAssign.py b/lib/core/Handles/HandlesAnnAssign.py index 801d911..a4ede0f 100644 --- a/lib/core/Handles/HandlesAnnAssign.py +++ b/lib/core/Handles/HandlesAnnAssign.py @@ -1,330 +1,330 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -import vast as ast -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.includes import t +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +import vast as ast +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes import t from lib.core.SymbolUtils import ( IsListAnnotation, ParseListAnnotation, FindStructNameInAnnotationWithContext, AnnotationContainsName, IsCPtrNode, RequireDefineConstStorable, FindAutoAddrMode, -) - - -def _IsGenericTpName(name: str) -> bool: - """判断名称是否为泛型类型参数(单字母大写,如 T/U/V) - - 用于 _is_struct_ptr_union 判定:elem_ptr: T | t.CPtr = t.CPtr(...) - (_list.vp 泛型方法体,T 未解析)时按 8 字节指针槽处理,避免 - var_struct_class 误注册 llvmlite.Value 后 elem_ptr[0] cast 到 Value* - 与函数声明 i8* 不匹配(8fbba llc 报错根因)。标量 T(如 int)退化为 - Ptr(i32) 不命中 _HandleSubscriptLlvm 的 i8 分支,不受此影响。 - """ - if not name: - return False - return len(name) == 1 and name.isalpha() and name.isupper() - - -# 指针/未知**占位**类名(非真实类):联合注解 rawptr[T] / X | t.CPtr 折叠后, -# var_struct_class 里可能落进这些占位名(它们可能恰好也在 Gen.structs 里,如 -# 'rawptr')。占位名不表态具体类 ⇒ 不得登记为「注解声明类」的权威值,否则会挡住 -# "无注解赋值从右值推断类名"的正常路径(如 `p: t.CPtr = Point()` 之后 `p.x` 需要 -# Point)。形态三种(见本文件 L753 的同类判据):裸 'rawptr'、带模块前缀 -# 'sha1.rawptr'、下标式 'rawptr[X]';CPtr 一族的裸名/限定名同样计入。 -_PTR_MARKER_CLASS_NAMES: frozenset[str] = frozenset({ - 'rawptr', 't.rawptr', 'c.rawptr', - 'CPtr', 't.CPtr', 'c.CPtr', - 'CAutoPtr', 't.CAutoPtr', - 'CRef', 't.CRef', 'CTempRef', 't.CTempRef', -}) - - -def _IsPtrMarkerClassName(Name: str | None) -> bool: - """类名是否为指针/未知占位名(判据见 _PTR_MARKER_CLASS_NAMES)""" - if not Name: - return False - if Name in _PTR_MARKER_CLASS_NAMES: - return True - # 带模块前缀('sha1.rawptr')与下标式('rawptr[X]')两种形态 - if '.rawptr' in Name or Name.startswith('rawptr['): - return True - if '.CPtr' in Name or Name.startswith('CPtr['): - return True - return False - - -class AnnAssignHandle(BaseHandle): - def _StoreOrStrCopy(self, VarName: str, VarPtr: ir.Value, Value: ir.Value) -> None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - DestPtr: ir.Value = Gen._load(VarPtr, name=VarName) - if isinstance(DestPtr.type, ir.PointerType) and isinstance(DestPtr.type.pointee, ir.IntType) and DestPtr.type.pointee.width == 8: - if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.IntType) and Value.type.pointee.width == 8: - if 'llvm.memcpy' not in Gen.functions: - memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(32), ir.IntType(1)]) - Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type) - if isinstance(DestPtr.type.pointee, ir.IntType) and not isinstance(DestPtr.type.pointee, ir.IntType(8)): - DestPtr = Gen.builder.bitcast(DestPtr, ir.PointerType(ir.IntType(8)), name=f"strcpy_dst_cast_{VarName}") - if isinstance(Value.type.pointee, ir.IntType) and not isinstance(Value.type.pointee, ir.IntType(8)): - Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"strcpy_src_cast_{VarName}") - size: ir.Constant = ir.Constant(ir.IntType(64), 8) - align: ir.Constant = ir.Constant(ir.IntType(32), 1) - isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0) - Gen.builder.call(Gen.functions['llvm.memcpy'], [DestPtr, Value, size, align, isvolatile], name=f"strcpy_call_{VarName}") - return - try: - CastedVal: ir.Value = Gen.builder.bitcast(Value, VarPtr.type.pointee, name=f"cast_{VarName}") - Gen._store(CastedVal, VarPtr) - except Exception: # 回退:bitcast 失败时 alloca 新变量 - NewVar: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) - Gen._store(Value, NewVar) - Gen.variables[VarName] = NewVar - - @staticmethod - def _RhsEndsWithVoidCall(Gen: LlvmCodeGenerator, BlockBefore: ir.Block | None, InsnStart: int) -> bool: - """判定刚求值的带注解赋值 RHS 是否"按类型即无值"(以返回 void 的调用收尾)。 - - 判据完全取自**实际发射的指令**(不依赖 RHS 的 AST 形态白名单):仅当求值 - 发生在同一基本块、且新增指令的最后一条是返回 void 的 call 时,才认为该 - 表达式语义上本就无值(如返回 void 的内建/库调用),此时"无值可存"合法。 - 其余情况——无任何指令产出(类型下标被当乘法 ⇒ 整条 RHS 未生成任何东西) - 或只产出 load/gep 等非收尾指令后求值失败(属性链解析失败,实测同块新增 - 1 条 load)——都属编译期求值失败,必须 fail-fast。 - """ - BlockAfter: ir.Block | None = Gen.builder.block if Gen.builder is not None else None - if InsnStart < 0 or BlockAfter is None or BlockAfter is not BlockBefore: - return False - NewInstrs: list[ir.Instruction] = BlockAfter.instructions[InsnStart:] - if not NewInstrs: - return False - Last: ir.Instruction = NewInstrs[-1] - return isinstance(Last, ir.CallInstr) and isinstance(Last.type, ir.VoidType) - - def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - AttrName: str = Target.attr - if isinstance(Target.value, ast.Name) and Target.value.id == 'self': - ClassName: str | None = self._CurrentCpythonObjectClass - if not ClassName: - self_var: ir.Value | None = Gen.variables.get('self') - if self_var: - var_type: ir.Type = self_var.type - if isinstance(var_type, ir.PointerType) and isinstance(var_type.pointee, ir.PointerType): - var_type = var_type.pointee - if isinstance(var_type, ir.PointerType): - pointee: ir.Type = var_type.pointee - for cn, st in Gen.structs.items(): - if st == pointee: - ClassName = cn - break - if ClassName and ClassName in Gen.structs: - SelfVar: ir.Value | None = Gen._get_var_ptr('self') - if SelfVar: - SelfPtr: ir.Value = Gen._load(SelfVar, name="self") - # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):self 的 pointee - # 可能是**另一份 opaque 同名实例**(函数签名那份),typed pointer 下 - # GEP 必须编译期算出元素类型 ⇒ 先补 body / 取同短名孪生体再 GEP。 - _SP: Any - SelfPtr, _SP = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, SelfPtr, ClassName, f"cast_self_{ClassName}") - offset: int = Gen._get_member_offset(AttrName, ClassName, _SP) - if offset is not None: - MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - elif isinstance(Target.value, ast.Name): - VarName: str = Target.value.id - - # 模块全局变量写入(治本修复 2026-08-27,与 HandlesAssign 同款): - # X.attr = ... 且 X 是 import 模块(含别名/后缀匹配 _ImportedModules) - # 时按"模块全局变量"直接 store,优先 {sha1}.{Attr} 前缀名,回退裸名。 - # 必须放在 ClassName heuristic(startswith(CN))之前,否则模块名与 - # 类名同名(如 CompilerFactory)会被误判为类字段 → fail-fast。 - _mod_imported: list[str] | None = getattr(self.Trans, '_ImportedModules', None) - _resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) - _is_mod_write: bool = False - if _mod_imported: - _is_mod_write = (VarName in _mod_imported or _resolved_mod in _mod_imported) - if not _is_mod_write: - for _mn in _mod_imported: - if (_mn == VarName or _mn.endswith(f".{VarName}") - or _mn == _resolved_mod or _mn.endswith(f".{_resolved_mod}")): - _is_mod_write = True - break - if _is_mod_write: - _mod_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) - _cands: list[str] = [VarName, _resolved_mod] - for _mn in _mod_imported: - if _mn.endswith(f".{VarName}") or _mn == VarName: - if _mn not in _cands: - _cands.append(_mn) - for _cand in _cands: - _sha1: str | None = _mod_sha1_map.get(_cand) - if _sha1: - _prefixed: str = f"{_sha1}.{AttrName}" - if _prefixed in Gen.module.globals: - _pgv: ir.GlobalVariable = Gen.module.globals[_prefixed] - if isinstance(_pgv, ir.Function): - return - self._StoreWithCoerce(Value, _pgv) - return - if AttrName in Gen.module.globals: - _gv: ir.GlobalVariable = Gen.module.globals[AttrName] - if isinstance(_gv, ir.Function): - return - self._StoreWithCoerce(Value, _gv) - return - - # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) - # 直接通过 a.__field__ = value 访问影子结构体字段 (语法糖) - _fd_meta_name: str = f"{VarName}__meta__" - if _fd_meta_name in Gen.variables and '_str' in Gen.structs: - _fd_offset = Gen._get_member_offset(AttrName, '_str') - if _fd_offset is not None: - _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] - _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") - self._StoreWithCoerce(Value, _fd_field_ptr) - return - - ClassName: str | None = Gen.var_struct_class.get(VarName) - if not ClassName: - for CN in Gen.structs: - if VarName == CN.lower() or VarName.startswith(CN): - ClassName = CN - break - if not ClassName and VarName in Gen.variables: - VarPtr: ir.Value = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType): - pointee: ir.Type = VarPtr.type.pointee - if isinstance(pointee, ir.PointerType): - pointee = pointee.pointee - for CN, ST in Gen.structs.items(): - if pointee == ST: - ClassName = CN - break - IsUnion: bool = False - if ClassName: - ClassTypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName) - if ClassTypeInfo: - if ClassTypeInfo.IsUnion: - IsUnion = True - if ClassName and ClassName in Gen.structs: - ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - if ObjVal: - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IntType) and ObjVal.type.pointee.width == 8: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}") - # opaque 泛型结构体 → 特化版本 bitcast - # 当 ObjVal 的 pointee 是 opaque 基础泛型结构体(如 GSList,来自跨模块 stub), - # 而 ClassName 是特化版本(如 GSList[BasicBlock])时,两者是不同的 - # IdentifiedStructType,直接比较会失败。这里 bitcast 到特化版本, - # 使后续 GEP 能正确访问字段。 - if (isinstance(ObjVal.type, ir.PointerType) - and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) - and ObjVal.type.pointee is not Gen.structs[ClassName] - and '[' in ClassName): - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_spec_{ClassName}") - if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]: - # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):类键那份可能 - # 仍是 opaque(body 未 set_body / 另一份实例已具化)⇒ 先补 body / - # 取同短名孪生体,偏移与 GEP 同源。 - _STa: Any - ObjVal, _STa = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, ObjVal, ClassName, f"cast_{ClassName}") - if IsUnion: - NestedStructName: str = f"{ClassName}_{AttrName}" - if NestedStructName in Gen.structs: - NestedStructType: ir.Type = Gen.structs[NestedStructName] - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") - offset: int = Gen._get_member_offset(AttrName, NestedStructName) - if offset is not None and offset < len(NestedStructType.elements): - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - return - offset: int = Gen._get_member_offset(AttrName, ClassName, _STa) - if offset is not None: - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - elif isinstance(Target.value, ast.Attribute): - ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) - if ObjVal and isinstance(ObjVal.type, ir.PointerType): - pointee: ir.Type = ObjVal.type.pointee - if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase) - _STb: Any - ObjVal, _STb = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, ObjVal, CN, f"cast_{CN}") - offset: int = Gen._get_member_offset(AttrName, CN, _STb) - if offset is not None: - MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - self._StoreWithCoerce(Value, MemberPtr) - break - - def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if isinstance(Ptr.type, ir.PointerType): - TargetType: ir.Type = Ptr.type.pointee - if Value.type != TargetType: - # None(空值常量)允许隐式转换为任意字段类型:指针→空指针,其余→零值。 - if Gen._IsNoneValue(Value, Node): - Value = (ir.Constant(TargetType, None) if isinstance(TargetType, ir.PointerType) - else Gen._ZeroConst(TargetType)) - elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.IntType): - if TargetType.width < Value.type.width: - # 目标 i1:真值归一化 `x != 0`(与 bool(x)/if 条件同一判据), - # 不是 trunc 取最低位(运行时值 2 会得 false,与 `if 2:` 相反) - if TargetType.width == 1: - Value = Gen._CoerceIntToI1(Value, name="bool_norm") - # 隐式窄化**禁止**(与 MemoryOps._coerce_value 同口径,规则 2026-10-10): - # 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。 - # 唯一豁免:源是**编译期常量且值落进目标位宽**(无信息损失)。 - elif (isinstance(Value, ir.Constant) and isinstance(Value.constant, int) - and -(1 << (TargetType.width - 1)) <= Value.constant <= (1 << TargetType.width) - 1): - Value = Gen.builder.trunc(Value, TargetType, name="trunc_const") - else: - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"隐式窄化禁止:{Value.type.width} 位整数不能隐式赋给 {TargetType.width} 位类型" - f"(会丢高位);请显式转换(如 int32(x) / t.CInt32T(x) )或把目标类型加宽" - f"{Gen._DescribeValueNode(Node)}" - ) - else: - Value = Gen.builder.zext(Value, TargetType, name="zext") - elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType): - Allowed: bool - Reason: str - Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType) - if not Allowed: - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转换已禁止({Reason}):不能把指针类型 {Value.type} 隐式转为 {TargetType};" - f"如需转换请显式 cast(如 t.CPtr(...))" - f"{Gen._DescribeValueNode(Node)}" - ) - Value = Gen.builder.bitcast(Value, TargetType, name="ptr_bitcast") - elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType): - Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr") - elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType): - # 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径) - # (None 已在上方短路为零值,不会落到这里)。 - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转整数已禁止:不能把指针类型 {Value.type} 赋给整数类型 {TargetType};" - f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" - f"{Gen._DescribeValueNode(Node)}" - ) - Gen._store(Value, Ptr) - +) + + +def _IsGenericTpName(name: str) -> bool: + """判断名称是否为泛型类型参数(单字母大写,如 T/U/V) + + 用于 _is_struct_ptr_union 判定:elem_ptr: T | t.CPtr = t.CPtr(...) + (_list.vp 泛型方法体,T 未解析)时按 8 字节指针槽处理,避免 + var_struct_class 误注册 llvmlite.Value 后 elem_ptr[0] cast 到 Value* + 与函数声明 i8* 不匹配(8fbba llc 报错根因)。标量 T(如 int)退化为 + Ptr(i32) 不命中 _HandleSubscriptLlvm 的 i8 分支,不受此影响。 + """ + if not name: + return False + return len(name) == 1 and name.isalpha() and name.isupper() + + +# 指针/未知**占位**类名(非真实类):联合注解 rawptr[T] / X | t.CPtr 折叠后, +# var_struct_class 里可能落进这些占位名(它们可能恰好也在 Gen.structs 里,如 +# 'rawptr')。占位名不表态具体类 ⇒ 不得登记为「注解声明类」的权威值,否则会挡住 +# "无注解赋值从右值推断类名"的正常路径(如 `p: t.CPtr = Point()` 之后 `p.x` 需要 +# Point)。形态三种(见本文件 L753 的同类判据):裸 'rawptr'、带模块前缀 +# 'sha1.rawptr'、下标式 'rawptr[X]';CPtr 一族的裸名/限定名同样计入。 +_PTR_MARKER_CLASS_NAMES: frozenset[str] = frozenset({ + 'rawptr', 't.rawptr', 'c.rawptr', + 'CPtr', 't.CPtr', 'c.CPtr', + 'CAutoPtr', 't.CAutoPtr', + 'CRef', 't.CRef', 'CTempRef', 't.CTempRef', +}) + + +def _IsPtrMarkerClassName(Name: str | None) -> bool: + """类名是否为指针/未知占位名(判据见 _PTR_MARKER_CLASS_NAMES)""" + if not Name: + return False + if Name in _PTR_MARKER_CLASS_NAMES: + return True + # 带模块前缀('sha1.rawptr')与下标式('rawptr[X]')两种形态 + if '.rawptr' in Name or Name.startswith('rawptr['): + return True + if '.CPtr' in Name or Name.startswith('CPtr['): + return True + return False + + +class AnnAssignHandle(BaseHandle): + def _StoreOrStrCopy(self, VarName: str, VarPtr: ir.Value, Value: ir.Value) -> None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + DestPtr: ir.Value = Gen._load(VarPtr, name=VarName) + if isinstance(DestPtr.type, ir.PointerType) and isinstance(DestPtr.type.pointee, ir.IntType) and DestPtr.type.pointee.width == 8: + if isinstance(Value.type, ir.PointerType) and isinstance(Value.type.pointee, ir.IntType) and Value.type.pointee.width == 8: + if 'llvm.memcpy' not in Gen.functions: + memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(32), ir.IntType(1)]) + Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type) + if isinstance(DestPtr.type.pointee, ir.IntType) and not isinstance(DestPtr.type.pointee, ir.IntType(8)): + DestPtr = Gen.builder.bitcast(DestPtr, ir.PointerType(ir.IntType(8)), name=f"strcpy_dst_cast_{VarName}") + if isinstance(Value.type.pointee, ir.IntType) and not isinstance(Value.type.pointee, ir.IntType(8)): + Value = Gen.builder.bitcast(Value, ir.PointerType(ir.IntType(8)), name=f"strcpy_src_cast_{VarName}") + size: ir.Constant = ir.Constant(ir.IntType(64), 8) + align: ir.Constant = ir.Constant(ir.IntType(32), 1) + isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0) + Gen.builder.call(Gen.functions['llvm.memcpy'], [DestPtr, Value, size, align, isvolatile], name=f"strcpy_call_{VarName}") + return + try: + CastedVal: ir.Value = Gen.builder.bitcast(Value, VarPtr.type.pointee, name=f"cast_{VarName}") + Gen._store(CastedVal, VarPtr) + except Exception: # 回退:bitcast 失败时 alloca 新变量 + NewVar: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) + Gen._store(Value, NewVar) + Gen.variables[VarName] = NewVar + + @staticmethod + def _RhsEndsWithVoidCall(Gen: LlvmCodeGenerator, BlockBefore: ir.Block | None, InsnStart: int) -> bool: + """判定刚求值的带注解赋值 RHS 是否"按类型即无值"(以返回 void 的调用收尾)。 + + 判据完全取自**实际发射的指令**(不依赖 RHS 的 AST 形态白名单):仅当求值 + 发生在同一基本块、且新增指令的最后一条是返回 void 的 call 时,才认为该 + 表达式语义上本就无值(如返回 void 的内建/库调用),此时"无值可存"合法。 + 其余情况——无任何指令产出(类型下标被当乘法 ⇒ 整条 RHS 未生成任何东西) + 或只产出 load/gep 等非收尾指令后求值失败(属性链解析失败,实测同块新增 + 1 条 load)——都属编译期求值失败,必须 fail-fast。 + """ + BlockAfter: ir.Block | None = Gen.builder.block if Gen.builder is not None else None + if InsnStart < 0 or BlockAfter is None or BlockAfter is not BlockBefore: + return False + NewInstrs: list[ir.Instruction] = BlockAfter.instructions[InsnStart:] + if not NewInstrs: + return False + Last: ir.Instruction = NewInstrs[-1] + return isinstance(Last, ir.CallInstr) and isinstance(Last.type, ir.VoidType) + + def _HandleAttributeStoreLlvm(self, Target: ast.Attribute, Value: ir.Value) -> None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + AttrName: str = Target.attr + if isinstance(Target.value, ast.Name) and Target.value.id == 'self': + ClassName: str | None = self._CurrentCpythonObjectClass + if not ClassName: + self_var: ir.Value | None = Gen.variables.get('self') + if self_var: + var_type: ir.Type = self_var.type + if isinstance(var_type, ir.PointerType) and isinstance(var_type.pointee, ir.PointerType): + var_type = var_type.pointee + if isinstance(var_type, ir.PointerType): + pointee: ir.Type = var_type.pointee + for cn, st in Gen.structs.items(): + if st == pointee: + ClassName = cn + break + if ClassName and ClassName in Gen.structs: + SelfVar: ir.Value | None = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: ir.Value = Gen._load(SelfVar, name="self") + # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):self 的 pointee + # 可能是**另一份 opaque 同名实例**(函数签名那份),typed pointer 下 + # GEP 必须编译期算出元素类型 ⇒ 先补 body / 取同短名孪生体再 GEP。 + _SP: Any + SelfPtr, _SP = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, SelfPtr, ClassName, f"cast_self_{ClassName}") + offset: int = Gen._get_member_offset(AttrName, ClassName, _SP) + if offset is not None: + MemberPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + elif isinstance(Target.value, ast.Name): + VarName: str = Target.value.id + + # 模块全局变量写入(治本修复 2026-08-27,与 HandlesAssign 同款): + # X.attr = ... 且 X 是 import 模块(含别名/后缀匹配 _ImportedModules) + # 时按"模块全局变量"直接 store,优先 {sha1}.{Attr} 前缀名,回退裸名。 + # 必须放在 ClassName heuristic(startswith(CN))之前,否则模块名与 + # 类名同名(如 CompilerFactory)会被误判为类字段 → fail-fast。 + _mod_imported: list[str] | None = getattr(self.Trans, '_ImportedModules', None) + _resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + _is_mod_write: bool = False + if _mod_imported: + _is_mod_write = (VarName in _mod_imported or _resolved_mod in _mod_imported) + if not _is_mod_write: + for _mn in _mod_imported: + if (_mn == VarName or _mn.endswith(f".{VarName}") + or _mn == _resolved_mod or _mn.endswith(f".{_resolved_mod}")): + _is_mod_write = True + break + if _is_mod_write: + _mod_sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) + _cands: list[str] = [VarName, _resolved_mod] + for _mn in _mod_imported: + if _mn.endswith(f".{VarName}") or _mn == VarName: + if _mn not in _cands: + _cands.append(_mn) + for _cand in _cands: + _sha1: str | None = _mod_sha1_map.get(_cand) + if _sha1: + _prefixed: str = f"{_sha1}.{AttrName}" + if _prefixed in Gen.module.globals: + _pgv: ir.GlobalVariable = Gen.module.globals[_prefixed] + if isinstance(_pgv, ir.Function): + return + self._StoreWithCoerce(Value, _pgv) + return + if AttrName in Gen.module.globals: + _gv: ir.GlobalVariable = Gen.module.globals[AttrName] + if isinstance(_gv, ir.Function): + return + self._StoreWithCoerce(Value, _gv) + return + + # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) + # 直接通过 a.__field__ = value 访问影子结构体字段 (语法糖) + _fd_meta_name: str = f"{VarName}__meta__" + if _fd_meta_name in Gen.variables and '_str' in Gen.structs: + _fd_offset = Gen._get_member_offset(AttrName, '_str') + if _fd_offset is not None: + _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] + _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") + self._StoreWithCoerce(Value, _fd_field_ptr) + return + + ClassName: str | None = Gen.var_struct_class.get(VarName) + if not ClassName: + for CN in Gen.structs: + if VarName == CN.lower() or VarName.startswith(CN): + ClassName = CN + break + if not ClassName and VarName in Gen.variables: + VarPtr: ir.Value = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType): + pointee: ir.Type = VarPtr.type.pointee + if isinstance(pointee, ir.PointerType): + pointee = pointee.pointee + for CN, ST in Gen.structs.items(): + if pointee == ST: + ClassName = CN + break + IsUnion: bool = False + if ClassName: + ClassTypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName) + if ClassTypeInfo: + if ClassTypeInfo.IsUnion: + IsUnion = True + if ClassName and ClassName in Gen.structs: + ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.IntType) and ObjVal.type.pointee.width == 8: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{ClassName}") + # opaque 泛型结构体 → 特化版本 bitcast + # 当 ObjVal 的 pointee 是 opaque 基础泛型结构体(如 GSList,来自跨模块 stub), + # 而 ClassName 是特化版本(如 GSList[BasicBlock])时,两者是不同的 + # IdentifiedStructType,直接比较会失败。这里 bitcast 到特化版本, + # 使后续 GEP 能正确访问字段。 + if (isinstance(ObjVal.type, ir.PointerType) + and isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) + and ObjVal.type.pointee is not Gen.structs[ClassName] + and '[' in ClassName): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_spec_{ClassName}") + if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]: + # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):类键那份可能 + # 仍是 opaque(body 未 set_body / 另一份实例已具化)⇒ 先补 body / + # 取同短名孪生体,偏移与 GEP 同源。 + _STa: Any + ObjVal, _STa = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, ObjVal, ClassName, f"cast_{ClassName}") + if IsUnion: + NestedStructName: str = f"{ClassName}_{AttrName}" + if NestedStructName in Gen.structs: + NestedStructType: ir.Type = Gen.structs[NestedStructName] + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") + offset: int = Gen._get_member_offset(AttrName, NestedStructName) + if offset is not None and offset < len(NestedStructType.elements): + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + return + offset: int = Gen._get_member_offset(AttrName, ClassName, _STa) + if offset is not None: + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + elif isinstance(Target.value, ast.Attribute): + ObjVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Target.value) + if ObjVal and isinstance(ObjVal.type, ir.PointerType): + pointee: ir.Type = ObjVal.type.pointee + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase) + _STb: Any + ObjVal, _STb = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, ObjVal, CN, f"cast_{CN}") + offset: int = Gen._get_member_offset(AttrName, CN, _STb) + if offset is not None: + MemberPtr: ir.Value = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + self._StoreWithCoerce(Value, MemberPtr) + break + + def _StoreWithCoerce(self, Value: ir.Value, Ptr: ir.Value, Node: ast.AST | None = None) -> None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(Ptr.type, ir.PointerType): + TargetType: ir.Type = Ptr.type.pointee + if Value.type != TargetType: + # None(空值常量)允许隐式转换为任意字段类型:指针→空指针,其余→零值。 + if Gen._IsNoneValue(Value, Node): + Value = (ir.Constant(TargetType, None) if isinstance(TargetType, ir.PointerType) + else Gen._ZeroConst(TargetType)) + elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.IntType): + if TargetType.width < Value.type.width: + # 目标 i1:真值归一化 `x != 0`(与 bool(x)/if 条件同一判据), + # 不是 trunc 取最低位(运行时值 2 会得 false,与 `if 2:` 相反) + if TargetType.width == 1: + Value = Gen._CoerceIntToI1(Value, name="bool_norm") + # 隐式窄化**禁止**(与 MemoryOps._coerce_value 同口径,规则 2026-10-10): + # 位宽高→低会丢高位,必须显式 cast 或加宽目标类型。 + # 唯一豁免:源是**编译期常量且值落进目标位宽**(无信息损失)。 + elif (isinstance(Value, ir.Constant) and isinstance(Value.constant, int) + and -(1 << (TargetType.width - 1)) <= Value.constant <= (1 << TargetType.width) - 1): + Value = Gen.builder.trunc(Value, TargetType, name="trunc_const") + else: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"隐式窄化禁止:{Value.type.width} 位整数不能隐式赋给 {TargetType.width} 位类型" + f"(会丢高位);请显式转换(如 int32(x) / t.CInt32T(x) )或把目标类型加宽" + f"{Gen._DescribeValueNode(Node)}" + ) + else: + Value = Gen.builder.zext(Value, TargetType, name="zext") + elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.PointerType): + Allowed: bool + Reason: str + Allowed, Reason = Gen._ClassifyPtrImplicitConvert(Value.type, TargetType) + if not Allowed: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转换已禁止({Reason}):不能把指针类型 {Value.type} 隐式转为 {TargetType};" + f"如需转换请显式 cast(如 t.CPtr(...))" + f"{Gen._DescribeValueNode(Node)}" + ) + Value = Gen.builder.bitcast(Value, TargetType, name="ptr_bitcast") + elif isinstance(TargetType, ir.PointerType) and isinstance(Value.type, ir.IntType): + Value = Gen.builder.inttoptr(Value, TargetType, name="int_to_ptr") + elif isinstance(TargetType, ir.IntType) and isinstance(Value.type, ir.PointerType): + # 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径) + # (None 已在上方短路为零值,不会落到这里)。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转整数已禁止:不能把指针类型 {Value.type} 赋给整数类型 {TargetType};" + f"如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" + f"{Gen._DescribeValueNode(Node)}" + ) + Gen._store(Value, Ptr) + def _EvalRhsWithTargetHint(self, Node: ast.AnnAssign, VarType: Any, Gen: Any) -> Any: """求值注解赋值的 RHS,并把**左值注解的靶型类**作为提示传给属性链解析。 - 与 TPV `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:注解是 + 与 VPC `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:注解是 源码级权威意图(RHS 必须能赋给它)。两个类的成员表都有同名字段时,属性链读错 类名不会当场报错,只会**静默**给出错类型(现场 CallGenDispatch.vp:787 `len_ap: llvmlite.LLVMType* = len_val.Ty.Pointee`:`.Pointee` 属 LLVMType 的 REnum @@ -349,331 +349,331 @@ class AnnAssignHandle(BaseHandle): finally: Gen._TargetStructHint = _Saved - def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None: - """处理 list[T] = [...] → 构造 list[T] 对象 + append 序列。 - - 将 list 字面量翻译为: - 1. 特化 list[T] 类 - 2. alloca + __before_init__ + __new__(pool) + __before_init__ + __init__(pool) - 3. 对每个元素调用 append(elem) - 4. 将堆指针存入变量 - list_sub 是从注解中提取的 list[...] Subscript 节点(支持 list[T] 和 list[T] | t.CPtr) - """ - # 1. 解析元素类型 - slice_node: ast.expr = list_sub.slice - result: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) - if result is None: - return - type_arg: str = result[0] - type_name: str = result[1] - - # 2. 特化 list[T] 类 - spec_name: str | None = self.Trans.ClassHandler._specialize_generic_class( - 'list', [type_arg], Gen, type_names=[type_name]) - if spec_name is None: - return - - # 3. 获取结构体类型 - StructType: ir.Type | None = Gen.structs.get(spec_name) - if StructType is None: - return - if isinstance(StructType, ir.PointerType): - StructType = StructType.pointee - - # 4. 查找 pool 变量(pool / _mbuddy / mbuddy / mb) - pool_val: ir.Value | None = None - for pool_name in ('pool', '_mbuddy', 'mbuddy', 'mb'): - if pool_name in Gen.variables: - pool_var: ir.Value = Gen.variables[pool_name] - if isinstance(pool_var.type, ir.PointerType): - pointee: ir.Type = pool_var.type.pointee - if isinstance(pointee, ir.PointerType): - # alloca of pointer → load - pool_val = Gen._load(pool_var, name=f"pool_load_{VarName}") - else: - pool_val = pool_var - else: - pool_val = pool_var - break - if pool_val is None: - return - - # 5. alloca 临时结构体 - alloca_ptr: ir.AllocaInstr = Gen._allocaEntry(StructType, name=f"{VarName}_alloca") - - # 6. 调用 __before_init__(alloca_ptr) - before_init_name: str = f'{spec_name}.__before_init__' - before_init_func: ir.Function | None = None - if Gen._has_function(before_init_name): - before_init_func = Gen._get_function(before_init_name) - Gen.builder.call(before_init_func, [alloca_ptr], name=f"before_init_{VarName}") - - # 7. 调用 __new__(alloca_ptr, pool_val) → heap_ptr - new_method_name: str = f'{spec_name}.__new__' - heap_ptr: ir.Value = alloca_ptr - has_new: bool = Gen._find_function(new_method_name) is not None - if has_new: - new_func: ir.Function | None = Gen._find_function(new_method_name) - if new_func: - new_args: list[ir.Value] = [alloca_ptr, pool_val] - self.Trans.ExprCallHandle._fill_default_args(new_args, new_func, new_method_name) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_new: list[ir.Value] = Gen._adjust_args(new_args, new_func) - heap_ptr = Gen.builder.call(new_func, adjusted_new, name=f"new_{VarName}") - # 转换返回指针类型 - target_ptr_type: ir.PointerType = ir.PointerType(StructType) - if isinstance(heap_ptr.type, ir.PointerType) and heap_ptr.type != target_ptr_type: - heap_ptr = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{VarName}_cast") - elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type: - if heap_ptr.type.width < 64: - i64_val: ir.Value = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{VarName}_i64") - heap_ptr = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{VarName}_ptr") - else: - heap_ptr = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{VarName}_ptr") - # 对堆指针重新调用 __before_init__ - if before_init_func: - Gen.builder.call(before_init_func, [heap_ptr], name=f"before_init_{VarName}_heap") - - # 8. 调用 __init__(heap_ptr, pool_val, elem_size=0) - init_name: str = f'{spec_name}.__init__' - if Gen._has_function(init_name): - init_func: ir.Function | None = Gen._get_function(init_name) - if init_func: - elem_size_val: ir.Constant = ir.Constant(ir.IntType(64), 0) - init_args: list[ir.Value] = [heap_ptr, pool_val, elem_size_val] - self.Trans.ExprCallHandle._fill_default_args(init_args, init_func, init_name) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_init: list[ir.Value] = Gen._adjust_args(init_args, init_func) - Gen.builder.call(init_func, adjusted_init, name=f"init_{VarName}") - - # 9. 对每个元素调用 append(heap_ptr, elem_val) - append_name: str = f'{spec_name}.append' - append_func: ir.Function | None = Gen._find_function(append_name) - if append_func and Node.value and isinstance(Node.value, ast.List): - elts: list[ast.expr] = Node.value.elts - for i, elem_node in enumerate(elts): - elem_val: ir.Value | None = self.HandleExprLlvm(elem_node) - if elem_val: - append_args: list[ir.Value] = [heap_ptr, elem_val] - self.Trans.ExprCallHandle._fill_default_args(append_args, append_func, append_name) - # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 - adjusted_append: list[ir.Value] = Gen._adjust_args(append_args, append_func) - Gen.builder.call(append_func, adjusted_append, name=f"append_{VarName}_{i}") - - # 10. 创建变量并存储堆指针 - # 判断变量类型:list[T] | t.CPtr → i8*;list[T] → Ptr(StructType) - is_cptr_union: bool = isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr) - if is_cptr_union: - # list[T] | t.CPtr → 变量类型为 i8* - i8_ptr_type: ir.PointerType = ir.PointerType(ir.IntType(8)) - var: ir.AllocaInstr = Gen._allocaEntry(i8_ptr_type, name=VarName) - # bitcast heap_ptr (Ptr(StructType)) → i8* - if isinstance(heap_ptr.type, ir.PointerType) and heap_ptr.type != i8_ptr_type: - stored_val: ir.Value = Gen.builder.bitcast(heap_ptr, i8_ptr_type, name=f"store_cast_{VarName}") - else: - stored_val = heap_ptr - Gen._store(stored_val, var) - else: - # list[T] → 变量类型为 Ptr(StructType) - var = Gen._allocaEntry(ir.PointerType(StructType), name=VarName) - Gen._store(heap_ptr, var) - Gen.variables[VarName] = var - Gen.var_struct_class[VarName] = spec_name - Gen.global_struct_class[VarName] = spec_name - Gen._record_var_signedness(VarName, False) - - def _HandleAnnAssignLlvm(self, Node: ast.AnnAssign) -> None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - self.Trans._current_assign_node = Node - try: - self._HandleAnnAssignLlvmInner(Node) - finally: - self.Trans._current_assign_node = None - # 【注解声明类台账(2026-09-30)】带注解声明过的变量,其类名由**声明**决定, - # 是权威类型:后续赋值只能把右值**转换进**该类型,不能反过来改掉它。 - # 放在 Inner **返回之后**:Inner 内部多处(BinOp 联合短名回退、rawptr[X] 恢复等) - # 会按注解写 var_struct_class,此处取的是**终值**。 - # 反例(现场 includes/ast/parser.vp:1899 与 :1919): - # `node: rawptr[AST] = Name(ps.pool, name_str, ASTCtx.Load)` —— 本行本身合法(上转) - # 但若让右值推出的类 `Name` 覆盖声明的 `AST`,则下一处 - # `node = _parse_fstring(...)`(返回 rawptr[AST])就变成被禁止的**向下转型**: - # TPV 报 "指针隐式转指针已禁止:源类型 %AST* → 目标类型 %Name*"。 - if isinstance(getattr(Node, 'target', None), ast.Name) and getattr(Node, 'annotation', None) is not None: - _VarName: str = Node.target.id - _DeclCls: str | None = Gen.var_struct_class.get(_VarName) - # 只把**真实类名**记为权威:指针/未知标记名(rawptr/CPtr/... 这些是联合 - # 注解 `rawptr[T]`/`X | t.CPtr` 折叠后的占位名,可能恰好在 Gen.structs 里) - # 不表态具体类,若一并记为权威会挡住"无注解赋值从右值推断类名"的正常路径 - # (如 `p: t.CPtr = Point()` 之后 `p.x` 需要 Point)。 - if _DeclCls and not _IsPtrMarkerClassName(_DeclCls): - _M: dict = getattr(Gen, 'var_annotated_class', None) - if _M is None: - _M = {} - Gen.var_annotated_class = _M - _M[_VarName] = _DeclCls - - def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - # 值引用标记(`&T` / `&temp T`)仅用于函数参数位:局部/全局变量注解出现即 fail-fast。 - _ref_mode: str | None = FindAutoAddrMode(Node.annotation) - if _ref_mode is not None: - from lib.core.LLVMCG.FuncGen import FailFastError - _ann_text: str = ast.unparse(Node.annotation) - raise FailFastError( - f"变量注解不支持值引用(`&T` / `&temp T`):'{_ann_text}'。" - f"值引用仅用于函数参数位;局部变量请改用显式指针类型(如 `T*`)。") - # 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列): - # 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。 - self._CheckFixedArrayKeyword(Node.annotation) - # M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要) - if isinstance(Node.target, ast.Name) and Node.value is not None: - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): - callee: str = Node.value.func.id - if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(callee): - if not hasattr(Gen, '_coro_var_async'): - Gen._coro_var_async = {} - Gen._coro_var_async[Node.target.id] = callee - if isinstance(Node.target, ast.Name): - VarName: str = Node.target.id - TypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) - if TypeInfo is None: - TypeInfo = CTypeInfo() - TypeInfo.BaseType = t.CInt() - IsPtr: bool = TypeInfo.IsPtr - if TypeInfo.IsDefine: - if Node.value and isinstance(Node.value, ast.Constant): - # fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储 - # (自动推导形态)。唯一判据见 SymbolUtils.RequireDefineIntStorable。 - RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType, - Node.annotation, Node.value.value, VarName) - if not hasattr(Gen, '_define_constants'): - Gen._define_constants = {} - Gen._define_constants[VarName] = Node.value.value - info: CTypeInfo = CTypeInfo() - info.IsDefine = True - info.DefineValue = Node.value.value - self.Trans.SymbolTable.insert(VarName, info) - Gen._record_var_signedness(VarName, TypeInfo.IsUInt) - elif Node.value: - # 非 Constant 表达式:尝试编译期求值(如 t.CSizeT().Size // 8) - from lib.core.ConstEvaluator import ConstEvaluator, EvalContext - ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans) - eval_val = ConstEvaluator.eval_full(Node.value, ctx) - if eval_val is not None and isinstance(eval_val, (int, float)): - RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType, - Node.annotation, eval_val, VarName) - if not hasattr(Gen, '_define_constants'): - Gen._define_constants = {} - Gen._define_constants[VarName] = eval_val - info: CTypeInfo = CTypeInfo() - info.IsDefine = True - info.DefineValue = eval_val - self.Trans.SymbolTable.insert(VarName, info) - Gen._record_var_signedness(VarName, TypeInfo.IsUInt) - return - if TypeInfo.IsStr or (isinstance(Node.annotation, ast.Name) and Node.annotation.id == 'str'): - # 仅指针层数 <= 1 才算裸 str/bytes(i8*),可以压平:`str | t.CPtr` / - # `str*` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 指针数组), - # 压平会让局部变量比模块级同名全局槽少一层指针 - # (全局走 HandlesAssign._EmitGlobalAnnAssignLlvm 的 FromNode,得到 i8**), - # 于是 includes/sys.vp `_argv = argv_arr` 被指针隐式转换 fail-fast 拦下。 - # 与 HandlesFunctions.py:599 / HandlesImports.py:1440 的 - # `IsStr and PtrCount <= 1` 同一条规则源。 - if TypeInfo.PtrCount <= 1: - TypeInfo = CTypeInfo() - TypeInfo.BaseType = t.CChar() - TypeInfo.PtrCount = 1 - IsPtr = True - # FakeDuck: 为 str 变量创建影子结构体 a__meta__ (栈上, per-instance) - # a 保持为 char* 用于 printf; a__meta__ 存储 __data__ 和 __mbuddy__ 字段 - # 仅对 str 类型创建,bytes 和 t.CChar|t.CPtr 不创建 - if '_str' in Gen.structs and isinstance(Node.annotation, ast.Name) and Node.annotation.id == 'str': - _meta_name: str = f"{VarName}__meta__" - _meta_var: ir.AllocaInstr = Gen._allocaEntry(Gen.structs['_str'], name=_meta_name) - Gen.variables[_meta_name] = _meta_var - # 标记指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) - # elem_ptr[0] 应加载/存储 8 字节 (指针), 而非 1 字节 (字符) - # 仅当注解包含 str 或 bytes 时设置(t.CChar | t.CPtr 不应设置,它是纯字符指针) - if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): - if AnnotationContainsName(Node.annotation, 'str') or AnnotationContainsName(Node.annotation, 'bytes'): - Gen.var_ptr_element[VarName] = True - # 同时记录到全局字典,使全局变量在跨函数访问时仍能用 8 字节指针步长 - Gen.global_var_ptr_element[VarName] = True - parse_result = ParseListAnnotation(Node.annotation) - if parse_result: - elem_type_node: ast.expr = parse_result.elem_type_node - ElemType, ElemTypeInfo = self.ResolveListElementType( - elem_type_node, Gen, - handle_str=True, void_fallback_width=32, - ) - if parse_result.is_pointer: - # 单参数 list[type] → 指针 - # 但如果有列表字面量初始化值 [a, b, c],则按数组 t.CArray[type, N] 处理 - if not (Node.value and isinstance(Node.value, ast.List)): - VarType: ir.Type = ir.PointerType(ElemType) - var: ir.AllocaInstr = Gen._allocaEntry(VarType, name=VarName) - Gen.variables[VarName] = var - Gen._record_var_signedness(VarName, False) - if self.Trans.VarScopes: - self.Trans.VarScopes[-1][VarName] = ElemTypeInfo - return - # 有列表字面量值,继续走数组路径(count_node=None 时从列表长度推断) - count_node: ast.expr | None = parse_result.count_node - ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='local') - VarType = ir.ArrayType(ElemType, ArrayCount) - var = Gen._allocaEntry(VarType, name=VarName) - Gen.variables[VarName] = var - Gen._record_var_signedness(VarName, False) - if Node.value and isinstance(Node.value, ast.List): - InitConstants: list[ir.Constant] | None = self.Trans._BuildArrayInitConstants(Node.value, ElemType, elem_type_node, ArrayCount, Gen, byte_order=ElemTypeInfo.ByteOrder if ElemTypeInfo else '') - if InitConstants: - try: - InitVal: ir.Constant = ir.Constant(VarType, InitConstants) - Gen.builder.store(InitVal, var) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str): - str_val: str = Node.value.value + '\x00' - str_bytes: bytearray = bytearray(str_val, 'utf-8') - if isinstance(VarType, ir.ArrayType) and len(str_bytes) < VarType.count: - str_bytes.extend(b'\x00' * (VarType.count - len(str_bytes))) - try: - InitVal: ir.Constant = ir.Constant(VarType, str_bytes[:VarType.count] if isinstance(VarType, ir.ArrayType) else str_bytes) - Gen.builder.store(InitVal, var) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - if self.Trans.VarScopes: - self.Trans.VarScopes[-1][VarName] = ElemTypeInfo - # 记录数组元素字节序 - if ElemTypeInfo and ElemTypeInfo.ByteOrder == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64): - Gen.local_var_byteorders[VarName] = 'big' - return - # list[T] 泛型类注解 + list 字面量值 → 构造 list[T] 对象 + append 序列 - # (list[str] = ["a", "b"] 不应走 C 数组路径,而应构造 list[str] 对象) - # 支持两种注解形式:list[T] 和 list[T] | t.CPtr - list_sub: ast.Subscript | None = None - if (isinstance(Node.annotation, ast.Subscript) - and isinstance(Node.annotation.value, ast.Name) - and Node.annotation.value.id == 'list'): - list_sub = Node.annotation - elif (isinstance(Node.annotation, ast.BinOp) - and isinstance(Node.annotation.op, ast.BitOr)): - for side in (Node.annotation.left, Node.annotation.right): - if (isinstance(side, ast.Subscript) - and isinstance(side.value, ast.Name) - and side.value.id == 'list'): - list_sub = side - break - if (list_sub is not None - and not isinstance(list_sub.slice, ast.Tuple) - and Node.value is not None - and isinstance(Node.value, ast.List) - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and 'list' in self.Trans.ClassHandler._generic_class_templates): - self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub) - return + def _HandleGenericListLiteralAssign(self, VarName: str, Node: ast.AnnAssign, Gen: LlvmCodeGenerator, list_sub: ast.Subscript) -> None: + """处理 list[T] = [...] → 构造 list[T] 对象 + append 序列。 + + 将 list 字面量翻译为: + 1. 特化 list[T] 类 + 2. alloca + __before_init__ + __new__(pool) + __before_init__ + __init__(pool) + 3. 对每个元素调用 append(elem) + 4. 将堆指针存入变量 + list_sub 是从注解中提取的 list[...] Subscript 节点(支持 list[T] 和 list[T] | t.CPtr) + """ + # 1. 解析元素类型 + slice_node: ast.expr = list_sub.slice + result: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) + if result is None: + return + type_arg: str = result[0] + type_name: str = result[1] + + # 2. 特化 list[T] 类 + spec_name: str | None = self.Trans.ClassHandler._specialize_generic_class( + 'list', [type_arg], Gen, type_names=[type_name]) + if spec_name is None: + return + + # 3. 获取结构体类型 + StructType: ir.Type | None = Gen.structs.get(spec_name) + if StructType is None: + return + if isinstance(StructType, ir.PointerType): + StructType = StructType.pointee + + # 4. 查找 pool 变量(pool / _mbuddy / mbuddy / mb) + pool_val: ir.Value | None = None + for pool_name in ('pool', '_mbuddy', 'mbuddy', 'mb'): + if pool_name in Gen.variables: + pool_var: ir.Value = Gen.variables[pool_name] + if isinstance(pool_var.type, ir.PointerType): + pointee: ir.Type = pool_var.type.pointee + if isinstance(pointee, ir.PointerType): + # alloca of pointer → load + pool_val = Gen._load(pool_var, name=f"pool_load_{VarName}") + else: + pool_val = pool_var + else: + pool_val = pool_var + break + if pool_val is None: + return + + # 5. alloca 临时结构体 + alloca_ptr: ir.AllocaInstr = Gen._allocaEntry(StructType, name=f"{VarName}_alloca") + + # 6. 调用 __before_init__(alloca_ptr) + before_init_name: str = f'{spec_name}.__before_init__' + before_init_func: ir.Function | None = None + if Gen._has_function(before_init_name): + before_init_func = Gen._get_function(before_init_name) + Gen.builder.call(before_init_func, [alloca_ptr], name=f"before_init_{VarName}") + + # 7. 调用 __new__(alloca_ptr, pool_val) → heap_ptr + new_method_name: str = f'{spec_name}.__new__' + heap_ptr: ir.Value = alloca_ptr + has_new: bool = Gen._find_function(new_method_name) is not None + if has_new: + new_func: ir.Function | None = Gen._find_function(new_method_name) + if new_func: + new_args: list[ir.Value] = [alloca_ptr, pool_val] + self.Trans.ExprCallHandle._fill_default_args(new_args, new_func, new_method_name) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_new: list[ir.Value] = Gen._adjust_args(new_args, new_func) + heap_ptr = Gen.builder.call(new_func, adjusted_new, name=f"new_{VarName}") + # 转换返回指针类型 + target_ptr_type: ir.PointerType = ir.PointerType(StructType) + if isinstance(heap_ptr.type, ir.PointerType) and heap_ptr.type != target_ptr_type: + heap_ptr = Gen.builder.bitcast(heap_ptr, target_ptr_type, name=f"new_{VarName}_cast") + elif isinstance(heap_ptr.type, ir.IntType) and target_ptr_type: + if heap_ptr.type.width < 64: + i64_val: ir.Value = Gen.builder.zext(heap_ptr, ir.IntType(64), name=f"new_{VarName}_i64") + heap_ptr = Gen.builder.inttoptr(i64_val, target_ptr_type, name=f"new_{VarName}_ptr") + else: + heap_ptr = Gen.builder.inttoptr(heap_ptr, target_ptr_type, name=f"new_{VarName}_ptr") + # 对堆指针重新调用 __before_init__ + if before_init_func: + Gen.builder.call(before_init_func, [heap_ptr], name=f"before_init_{VarName}_heap") + + # 8. 调用 __init__(heap_ptr, pool_val, elem_size=0) + init_name: str = f'{spec_name}.__init__' + if Gen._has_function(init_name): + init_func: ir.Function | None = Gen._get_function(init_name) + if init_func: + elem_size_val: ir.Constant = ir.Constant(ir.IntType(64), 0) + init_args: list[ir.Value] = [heap_ptr, pool_val, elem_size_val] + self.Trans.ExprCallHandle._fill_default_args(init_args, init_func, init_name) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_init: list[ir.Value] = Gen._adjust_args(init_args, init_func) + Gen.builder.call(init_func, adjusted_init, name=f"init_{VarName}") + + # 9. 对每个元素调用 append(heap_ptr, elem_val) + append_name: str = f'{spec_name}.append' + append_func: ir.Function | None = Gen._find_function(append_name) + if append_func and Node.value and isinstance(Node.value, ast.List): + elts: list[ast.expr] = Node.value.elts + for i, elem_node in enumerate(elts): + elem_val: ir.Value | None = self.HandleExprLlvm(elem_node) + if elem_val: + append_args: list[ir.Value] = [heap_ptr, elem_val] + self.Trans.ExprCallHandle._fill_default_args(append_args, append_func, append_name) + # 自动取址已收口到 Gen._adjust_args(唯一汇聚点,按 func.name 查表)。 + adjusted_append: list[ir.Value] = Gen._adjust_args(append_args, append_func) + Gen.builder.call(append_func, adjusted_append, name=f"append_{VarName}_{i}") + + # 10. 创建变量并存储堆指针 + # 判断变量类型:list[T] | t.CPtr → i8*;list[T] → Ptr(StructType) + is_cptr_union: bool = isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr) + if is_cptr_union: + # list[T] | t.CPtr → 变量类型为 i8* + i8_ptr_type: ir.PointerType = ir.PointerType(ir.IntType(8)) + var: ir.AllocaInstr = Gen._allocaEntry(i8_ptr_type, name=VarName) + # bitcast heap_ptr (Ptr(StructType)) → i8* + if isinstance(heap_ptr.type, ir.PointerType) and heap_ptr.type != i8_ptr_type: + stored_val: ir.Value = Gen.builder.bitcast(heap_ptr, i8_ptr_type, name=f"store_cast_{VarName}") + else: + stored_val = heap_ptr + Gen._store(stored_val, var) + else: + # list[T] → 变量类型为 Ptr(StructType) + var = Gen._allocaEntry(ir.PointerType(StructType), name=VarName) + Gen._store(heap_ptr, var) + Gen.variables[VarName] = var + Gen.var_struct_class[VarName] = spec_name + Gen.global_struct_class[VarName] = spec_name + Gen._record_var_signedness(VarName, False) + + def _HandleAnnAssignLlvm(self, Node: ast.AnnAssign) -> None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + self.Trans._current_assign_node = Node + try: + self._HandleAnnAssignLlvmInner(Node) + finally: + self.Trans._current_assign_node = None + # 【注解声明类台账(2026-09-30)】带注解声明过的变量,其类名由**声明**决定, + # 是权威类型:后续赋值只能把右值**转换进**该类型,不能反过来改掉它。 + # 放在 Inner **返回之后**:Inner 内部多处(BinOp 联合短名回退、rawptr[X] 恢复等) + # 会按注解写 var_struct_class,此处取的是**终值**。 + # 反例(现场 includes/ast/parser.vp:1899 与 :1919): + # `node: rawptr[AST] = Name(ps.pool, name_str, ASTCtx.Load)` —— 本行本身合法(上转) + # 但若让右值推出的类 `Name` 覆盖声明的 `AST`,则下一处 + # `node = _parse_fstring(...)`(返回 rawptr[AST])就变成被禁止的**向下转型**: + # VPC 报 "指针隐式转指针已禁止:源类型 %AST* → 目标类型 %Name*"。 + if isinstance(getattr(Node, 'target', None), ast.Name) and getattr(Node, 'annotation', None) is not None: + _VarName: str = Node.target.id + _DeclCls: str | None = Gen.var_struct_class.get(_VarName) + # 只把**真实类名**记为权威:指针/未知标记名(rawptr/CPtr/... 这些是联合 + # 注解 `rawptr[T]`/`X | t.CPtr` 折叠后的占位名,可能恰好在 Gen.structs 里) + # 不表态具体类,若一并记为权威会挡住"无注解赋值从右值推断类名"的正常路径 + # (如 `p: t.CPtr = Point()` 之后 `p.x` 需要 Point)。 + if _DeclCls and not _IsPtrMarkerClassName(_DeclCls): + _M: dict = getattr(Gen, 'var_annotated_class', None) + if _M is None: + _M = {} + Gen.var_annotated_class = _M + _M[_VarName] = _DeclCls + + def _HandleAnnAssignLlvmInner(self, Node: ast.AnnAssign) -> None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # 值引用标记(`&T` / `&temp T`)仅用于函数参数位:局部/全局变量注解出现即 fail-fast。 + _ref_mode: str | None = FindAutoAddrMode(Node.annotation) + if _ref_mode is not None: + from lib.core.LLVMCG.FuncGen import FailFastError + _ann_text: str = ast.unparse(Node.annotation) + raise FailFastError( + f"变量注解不支持值引用(`&T` / `&temp T`):'{_ann_text}'。" + f"值引用仅用于函数参数位;局部变量请改用显式指针类型(如 `T*`)。") + # 内建定长数组构造子 array 的注解校验(误用即 fail-fast,含文件:行:列): + # 局部变量/self 字段注解不经过 ResolveAnnotationType,故在此显式校验。 + self._CheckFixedArrayKeyword(Node.annotation) + # M1 协程:记录 async 函数调用赋值的变量名(coro.coro_run 内联翻译需要) + if isinstance(Node.target, ast.Name) and Node.value is not None: + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): + callee: str = Node.value.func.id + if hasattr(self.Trans, 'AsyncHandler') and self.Trans.AsyncHandler._IsAsyncFunc(callee): + if not hasattr(Gen, '_coro_var_async'): + Gen._coro_var_async = {} + Gen._coro_var_async[Node.target.id] = callee + if isinstance(Node.target, ast.Name): + VarName: str = Node.target.id + TypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) + if TypeInfo is None: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CInt() + IsPtr: bool = TypeInfo.IsPtr + if TypeInfo.IsDefine: + if Node.value and isinstance(Node.value, ast.Constant): + # fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储 + # (自动推导形态)。唯一判据见 SymbolUtils.RequireDefineIntStorable。 + RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType, + Node.annotation, Node.value.value, VarName) + if not hasattr(Gen, '_define_constants'): + Gen._define_constants = {} + Gen._define_constants[VarName] = Node.value.value + info: CTypeInfo = CTypeInfo() + info.IsDefine = True + info.DefineValue = Node.value.value + self.Trans.SymbolTable.insert(VarName, info) + Gen._record_var_signedness(VarName, TypeInfo.IsUInt) + elif Node.value: + # 非 Constant 表达式:尝试编译期求值(如 t.CSizeT().Size // 8) + from lib.core.ConstEvaluator import ConstEvaluator, EvalContext + ctx = EvalContext(Gen=Gen, symtab=self.Trans.SymbolTable, translator=self.Trans) + eval_val = ConstEvaluator.eval_full(Node.value, ctx) + if eval_val is not None and isinstance(eval_val, (int, float)): + RequireDefineConstStorable(Gen, self.Trans, Node, TypeInfo.BaseType, + Node.annotation, eval_val, VarName) + if not hasattr(Gen, '_define_constants'): + Gen._define_constants = {} + Gen._define_constants[VarName] = eval_val + info: CTypeInfo = CTypeInfo() + info.IsDefine = True + info.DefineValue = eval_val + self.Trans.SymbolTable.insert(VarName, info) + Gen._record_var_signedness(VarName, TypeInfo.IsUInt) + return + if TypeInfo.IsStr or (isinstance(Node.annotation, ast.Name) and Node.annotation.id == 'str'): + # 仅指针层数 <= 1 才算裸 str/bytes(i8*),可以压平:`str | t.CPtr` / + # `str*` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 指针数组), + # 压平会让局部变量比模块级同名全局槽少一层指针 + # (全局走 HandlesAssign._EmitGlobalAnnAssignLlvm 的 FromNode,得到 i8**), + # 于是 includes/sys.vp `_argv = argv_arr` 被指针隐式转换 fail-fast 拦下。 + # 与 HandlesFunctions.py:599 / HandlesImports.py:1440 的 + # `IsStr and PtrCount <= 1` 同一条规则源。 + if TypeInfo.PtrCount <= 1: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CChar() + TypeInfo.PtrCount = 1 + IsPtr = True + # FakeDuck: 为 str 变量创建影子结构体 a__meta__ (栈上, per-instance) + # a 保持为 char* 用于 printf; a__meta__ 存储 __data__ 和 __mbuddy__ 字段 + # 仅对 str 类型创建,bytes 和 t.CChar|t.CPtr 不创建 + if '_str' in Gen.structs and isinstance(Node.annotation, ast.Name) and Node.annotation.id == 'str': + _meta_name: str = f"{VarName}__meta__" + _meta_var: ir.AllocaInstr = Gen._allocaEntry(Gen.structs['_str'], name=_meta_name) + Gen.variables[_meta_name] = _meta_var + # 标记指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) + # elem_ptr[0] 应加载/存储 8 字节 (指针), 而非 1 字节 (字符) + # 仅当注解包含 str 或 bytes 时设置(t.CChar | t.CPtr 不应设置,它是纯字符指针) + if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): + if AnnotationContainsName(Node.annotation, 'str') or AnnotationContainsName(Node.annotation, 'bytes'): + Gen.var_ptr_element[VarName] = True + # 同时记录到全局字典,使全局变量在跨函数访问时仍能用 8 字节指针步长 + Gen.global_var_ptr_element[VarName] = True + parse_result = ParseListAnnotation(Node.annotation) + if parse_result: + elem_type_node: ast.expr = parse_result.elem_type_node + ElemType, ElemTypeInfo = self.ResolveListElementType( + elem_type_node, Gen, + handle_str=True, void_fallback_width=32, + ) + if parse_result.is_pointer: + # 单参数 list[type] → 指针 + # 但如果有列表字面量初始化值 [a, b, c],则按数组 t.CArray[type, N] 处理 + if not (Node.value and isinstance(Node.value, ast.List)): + VarType: ir.Type = ir.PointerType(ElemType) + var: ir.AllocaInstr = Gen._allocaEntry(VarType, name=VarName) + Gen.variables[VarName] = var + Gen._record_var_signedness(VarName, False) + if self.Trans.VarScopes: + self.Trans.VarScopes[-1][VarName] = ElemTypeInfo + return + # 有列表字面量值,继续走数组路径(count_node=None 时从列表长度推断) + count_node: ast.expr | None = parse_result.count_node + ArrayCount: int = self.ParseArrayCount(count_node, Gen, Node.value, mode='local') + VarType = ir.ArrayType(ElemType, ArrayCount) + var = Gen._allocaEntry(VarType, name=VarName) + Gen.variables[VarName] = var + Gen._record_var_signedness(VarName, False) + if Node.value and isinstance(Node.value, ast.List): + InitConstants: list[ir.Constant] | None = self.Trans._BuildArrayInitConstants(Node.value, ElemType, elem_type_node, ArrayCount, Gen, byte_order=ElemTypeInfo.ByteOrder if ElemTypeInfo else '') + if InitConstants: + try: + InitVal: ir.Constant = ir.Constant(VarType, InitConstants) + Gen.builder.store(InitVal, var) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + elif Node.value and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str): + str_val: str = Node.value.value + '\x00' + str_bytes: bytearray = bytearray(str_val, 'utf-8') + if isinstance(VarType, ir.ArrayType) and len(str_bytes) < VarType.count: + str_bytes.extend(b'\x00' * (VarType.count - len(str_bytes))) + try: + InitVal: ir.Constant = ir.Constant(VarType, str_bytes[:VarType.count] if isinstance(VarType, ir.ArrayType) else str_bytes) + Gen.builder.store(InitVal, var) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if self.Trans.VarScopes: + self.Trans.VarScopes[-1][VarName] = ElemTypeInfo + # 记录数组元素字节序 + if ElemTypeInfo and ElemTypeInfo.ByteOrder == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64): + Gen.local_var_byteorders[VarName] = 'big' + return + # list[T] 泛型类注解 + list 字面量值 → 构造 list[T] 对象 + append 序列 + # (list[str] = ["a", "b"] 不应走 C 数组路径,而应构造 list[str] 对象) + # 支持两种注解形式:list[T] 和 list[T] | t.CPtr + list_sub: ast.Subscript | None = None + if (isinstance(Node.annotation, ast.Subscript) + and isinstance(Node.annotation.value, ast.Name) + and Node.annotation.value.id == 'list'): + list_sub = Node.annotation + elif (isinstance(Node.annotation, ast.BinOp) + and isinstance(Node.annotation.op, ast.BitOr)): + for side in (Node.annotation.left, Node.annotation.right): + if (isinstance(side, ast.Subscript) + and isinstance(side.value, ast.Name) + and side.value.id == 'list'): + list_sub = side + break + if (list_sub is not None + and not isinstance(list_sub.slice, ast.Tuple) + and Node.value is not None + and isinstance(Node.value, ast.List) + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and 'list' in self.Trans.ClassHandler._generic_class_templates): + self._HandleGenericListLiteralAssign(VarName, Node, Gen, list_sub) + return # Only fall back to the left operand when the union neither resolved a base # type nor accumulated multi-level pointers. A pointer-marker based union # (`t.CPtr*` = `t.CPtr | t.CPtr` => CVoid/2) is already a fully resolved @@ -682,568 +682,568 @@ class AnnAssignHandle(BaseHandle): if (TypeInfo.IsVoid and TypeInfo.PtrCount <= 1 and isinstance(Node.annotation, ast.BinOp)): found: str | None = FindStructNameInAnnotationWithContext( - Node.annotation, self.Trans, Gen) - if found: - TypeInfo = CTypeInfo() - TypeInfo.BaseType = t.CStruct(name=found) - TypeInfo.PtrCount = 1 - IsPtr = True - elif isinstance(Node.annotation.left, ast.Attribute): - AttrTypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation.left) - if AttrTypeInfo and AttrTypeInfo.BaseType: - TypeInfo = AttrTypeInfo - TypeInfo.PtrCount = max(TypeInfo.PtrCount, 1) - IsPtr = True - # 对于局部变量声明 s: TestStruct(不带 | t.CPtr),应为栈上结构体而非指针 - # GetCTypeInfo 默认设置 PtrCount=1,但纯结构体名注解应为值类型 - if (isinstance(Node.annotation, ast.Name) - and Node.annotation.id in Gen.structs - and TypeInfo.IsStruct - and Node.annotation.id not in Gen.class_vtable): - TypeInfo.PtrCount = 0 - IsPtr = False - # const / volatile 局部变量登记(统一登记入口)。放在此处:TypeInfo 的 - # PtrCount/IsStruct 已定稿(上方 526-547),且早于本函数所有后续分支与 - # 提前 return(数组/list/_reg_values/已存在 alloca 等),保证「局部变量」 - # 这一登记点只有一处、无遗漏。 - Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=False) - VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) - if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name: - stripped: str = TypeInfo.Name - if stripped in Gen.class_vtable and stripped in Gen.structs: - VarType = ir.PointerType(Gen.structs[stripped]) - IsPtr = True - if isinstance(VarType, ir.IdentifiedStructType): - struct_name: str = getattr(VarType, 'name', '') - if struct_name and Gen.class_vtable: - for cls_name in Gen.class_vtable: - if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name: - VarType = ir.PointerType(VarType) - IsPtr = True - break - if isinstance(VarType, ir.VoidType): - VarType = ir.IntType(32) - IsUnsigned: bool = TypeInfo.IsUInt - # 修复 3: BinOp 注解(如 ast.Arguments | t.CPtr)的 var_struct_class 设置 - # TypeInfo.Name 由 _HandleSubscript 设置(特化名如 list[sha1.ASTptr])或 - # GetCTypeInfo 设置(普通结构体名),经 _MergeCTypeInfoInto 传播后保留。 - # 此处确保 BinOp 注解的变量也能被 _get_var_class 识别,避免方法调用分派 - # fallthrough 到 char pointer fallback 错误匹配其他类(如 AST.__len__)。 - # 使用 not in 条件,不覆盖后续 L337-339/L396-398 对 ast.Name 注解的精确设置。 - if TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: - if VarName not in Gen.var_struct_class: - Gen.var_struct_class[VarName] = TypeInfo.Name - Gen.global_struct_class[VarName] = TypeInfo.Name - # 联合指针注解(X | t.CPtr)短名回退:resolver 对模块限定 struct - # 段(如 llvmlite.Value)无法精确 in Gen.structs(键可能是 'Value' - # 或带 sha1 前缀),Name 常退化为指针别名 'rawptr'(被注册成 opaque - # struct)→ 变量 var_struct_class 登记为 rawptr → 字段访问 - # "属性 'Next' 在类 'rawptr' 中找不到"(FuncGenMixin.vp L307)。 - # FindStructNameInAnnotation 以 attr/短名命中,找回真实类名。 - if (isinstance(Node.annotation, ast.BinOp) - and isinstance(Node.annotation.op, ast.BitOr)): + Node.annotation, self.Trans, Gen) + if found: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CStruct(name=found) + TypeInfo.PtrCount = 1 + IsPtr = True + elif isinstance(Node.annotation.left, ast.Attribute): + AttrTypeInfo: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation.left) + if AttrTypeInfo and AttrTypeInfo.BaseType: + TypeInfo = AttrTypeInfo + TypeInfo.PtrCount = max(TypeInfo.PtrCount, 1) + IsPtr = True + # 对于局部变量声明 s: TestStruct(不带 | t.CPtr),应为栈上结构体而非指针 + # GetCTypeInfo 默认设置 PtrCount=1,但纯结构体名注解应为值类型 + if (isinstance(Node.annotation, ast.Name) + and Node.annotation.id in Gen.structs + and TypeInfo.IsStruct + and Node.annotation.id not in Gen.class_vtable): + TypeInfo.PtrCount = 0 + IsPtr = False + # const / volatile 局部变量登记(统一登记入口)。放在此处:TypeInfo 的 + # PtrCount/IsStruct 已定稿(上方 526-547),且早于本函数所有后续分支与 + # 提前 return(数组/list/_reg_values/已存在 alloca 等),保证「局部变量」 + # 这一登记点只有一处、无遗漏。 + Gen.RegisterVarQualifiers(VarName, TypeInfo, Node.annotation, IsGlobal=False) + VarType: ir.Type = Gen._ctype_to_llvm(TypeInfo) + if not IsPtr and TypeInfo.IsStruct and TypeInfo.Name: + stripped: str = TypeInfo.Name + if stripped in Gen.class_vtable and stripped in Gen.structs: + VarType = ir.PointerType(Gen.structs[stripped]) + IsPtr = True + if isinstance(VarType, ir.IdentifiedStructType): + struct_name: str = getattr(VarType, 'name', '') + if struct_name and Gen.class_vtable: + for cls_name in Gen.class_vtable: + if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name: + VarType = ir.PointerType(VarType) + IsPtr = True + break + if isinstance(VarType, ir.VoidType): + VarType = ir.IntType(32) + IsUnsigned: bool = TypeInfo.IsUInt + # 修复 3: BinOp 注解(如 ast.Arguments | t.CPtr)的 var_struct_class 设置 + # TypeInfo.Name 由 _HandleSubscript 设置(特化名如 list[sha1.ASTptr])或 + # GetCTypeInfo 设置(普通结构体名),经 _MergeCTypeInfoInto 传播后保留。 + # 此处确保 BinOp 注解的变量也能被 _get_var_class 识别,避免方法调用分派 + # fallthrough 到 char pointer fallback 错误匹配其他类(如 AST.__len__)。 + # 使用 not in 条件,不覆盖后续 L337-339/L396-398 对 ast.Name 注解的精确设置。 + if TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: + if VarName not in Gen.var_struct_class: + Gen.var_struct_class[VarName] = TypeInfo.Name + Gen.global_struct_class[VarName] = TypeInfo.Name + # 联合指针注解(X | t.CPtr)短名回退:resolver 对模块限定 struct + # 段(如 llvmlite.Value)无法精确 in Gen.structs(键可能是 'Value' + # 或带 sha1 前缀),Name 常退化为指针别名 'rawptr'(被注册成 opaque + # struct)→ 变量 var_struct_class 登记为 rawptr → 字段访问 + # "属性 'Next' 在类 'rawptr' 中找不到"(FuncGenMixin.vp L307)。 + # FindStructNameInAnnotation 以 attr/短名命中,找回真实类名。 + if (isinstance(Node.annotation, ast.BinOp) + and isinstance(Node.annotation.op, ast.BitOr)): _union_struct_name2: str | None = FindStructNameInAnnotationWithContext( - Node.annotation, self.Trans, Gen) - # resolver 对模块限定 struct 联合(llvmlite.Value | t.CPtr)常给出 - # 指针占位名(如 '{sha1}.rawptr')且该占位恰好注册在 Gen.structs, - # 使上方 TypeInfo.Name in Gen.structs 通过并登记错误类名 → 字段 - # 访问 "属性 'Next' 在类 'rawptr' 中找不到"。凡短名命中真实类 - # (attr 精确 in structs),一律覆盖为真实类名(Value)。 - if _union_struct_name2 and _union_struct_name2 in Gen.structs: - if Gen.var_struct_class.get(VarName) != _union_struct_name2: - Gen.var_struct_class[VarName] = _union_struct_name2 - if getattr(Gen, 'global_struct_class', None) is not None: - Gen.global_struct_class[VarName] = _union_struct_name2 - # Subscript rawptr[X] / t.CPtr[X] 注解的本地结构指针变量(如 - # includes/ast/parser.vp _fix_store_ctx 里 `tp: rawptr[Tuple] = - # (rawptr[Tuple])(node)`,随后 `tp_elts = tp.elts`):TypeMerge 的 - # builtins 分支把 rawptr[X] 折叠为 i8*(PtrCount=1/CVoid),既不登记 - # var_struct_class 也不保留内层 X —— 单级 Name 字段取值(tp.elts) - # 在属性访问层 ClassName 解析为 None → 求值 None → 赋值被 fail-fast - # 拦截(parser.vp L427 bootstrap 根因)。与上方 BinOp 联合的短名回退 - # 对称:把内层 X 解析为真实 struct 键(兼容短名 / 带 class_sha1 前缀 - # 的键)并登记;仅在缺失或指针占位(rawptr…)时覆盖,不碰已精确登记值。 - _rst_sub: ast.AST | None = None - if isinstance(Node.annotation, ast.Subscript): - _rst_v: ast.expr = Node.annotation.value - _rst_nm: str = '' - if isinstance(_rst_v, ast.Name): - _rst_nm = _rst_v.id - elif isinstance(_rst_v, ast.Attribute) and isinstance(_rst_v.value, ast.Name): - _rst_nm = f'{_rst_v.value.id}.{_rst_v.attr}' - if _rst_nm in ('rawptr', 't.CPtr'): - _rst_sub = Node.annotation - if _rst_sub is not None: - _rst_sl: ast.AST = _rst_sub.slice - if hasattr(ast, 'Index') and isinstance(_rst_sl, ast.Index): - _rst_sl = _rst_sl.value - _rst_tn: str = '' - if isinstance(_rst_sl, ast.Name): - _rst_tn = _rst_sl.id - elif isinstance(_rst_sl, ast.Attribute) and isinstance(_rst_sl.value, ast.Name): - _rst_tn = _rst_sl.attr - _rst_tk: str | None = None - if _rst_tn: - if _rst_tn in Gen.structs: - _rst_tk = _rst_tn - else: - _rst_sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_rst_tn) - if _rst_sha and f'{_rst_sha}.{_rst_tn}' in Gen.structs: - _rst_tk = f'{_rst_sha}.{_rst_tn}' - else: - for _rst_k3 in Gen.structs: - _rst_short3: str = _rst_k3.split('.')[-1] if '.' in _rst_k3 else _rst_k3 - if _rst_short3 == _rst_tn: - _rst_tk = _rst_k3 - break - if _rst_tk: - _rst_cur: str | None = Gen.var_struct_class.get(VarName) - _rst_need: bool = (not _rst_cur or _rst_cur == 'rawptr' or '.rawptr' in _rst_cur - or _rst_cur.startswith('rawptr[')) - if _rst_need and _rst_cur != _rst_tk: - Gen.var_struct_class[VarName] = _rst_tk - if getattr(Gen, 'global_struct_class', None) is not None: - Gen.global_struct_class[VarName] = _rst_tk - # 检测指针到指针缓冲区变量:注解为 StructName | t.CPtr,且赋值来自 t.CPtr(...) 调用。 - # 这种变量(如 list[T].__getitem__ 中的 elem_ptr: T | t.CPtr = t.CPtr(...)) - # 实际指向存储结构体指针的缓冲区(T=AST|t.CPtr 时特化为 AST*,缓冲区存 AST* 数组)。 - # [index] 应 bitcast 为 AST** 后 GEP 指针数组再 load,而非 GEP 结构体数组。 - # 泛型特化将 T|t.CPtr 降级为 StructName|t.CPtr(丢失一层指针),故通过赋值来源区分: - # elem_ptr 的值是 t.CPtr(buf + idx*elem_size)(原始指针运算,缓冲区内容未知), - # pts 的值是 (Point|t.CPtr)(...)(类型转换,明确指向 Point 结构体)。 - _is_cptr_assign: bool = ( - isinstance(Node.value, ast.Call) - and isinstance(Node.value.func, ast.Attribute) - and isinstance(Node.value.func.value, ast.Name) - and Node.value.func.value.id == 't' - and Node.value.func.attr == 'CPtr' - ) - _is_struct_ptr_union: bool = ( - isinstance(Node.annotation, ast.BinOp) - and isinstance(Node.annotation.op, ast.BitOr) - and ((IsCPtrNode(Node.annotation.left) - and isinstance(Node.annotation.right, ast.Name) - and (Node.annotation.right.id in Gen.structs - or _IsGenericTpName(Node.annotation.right.id))) - or (IsCPtrNode(Node.annotation.right) - and isinstance(Node.annotation.left, ast.Name) - and (Node.annotation.left.id in Gen.structs - or _IsGenericTpName(Node.annotation.left.id)))) - ) - if _is_cptr_assign and _is_struct_ptr_union: - Gen.var_ptr_element[VarName] = True - Gen.global_var_ptr_element[VarName] = True - if VarName in Gen._reg_values: - OldVal: ir.Value = Gen._reg_values[VarName] - if isinstance(OldVal.type, ir.PointerType): - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - del Gen._reg_values[VarName] - if Node.value: - InitValue: ir.Value | None = self._EvalRhsWithTargetHint(Node, VarType, Gen) - if InitValue: - self._StoreOrStrCopy(VarName, var, InitValue) - elif isinstance(Node.value, ast.Attribute): - # 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp - # 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。 - self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName) - Gen._record_var_signedness(VarName, IsUnsigned) - return - del Gen._reg_values[VarName] - InitValue: ir.Value | None = None - RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None - RhsInsnStart: int = len(RhsBlock.instructions) if RhsBlock is not None else -1 - if Node.value: - InitValue = self._EvalRhsWithTargetHint(Node, VarType, Gen) - # 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型 - if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): - CalledName: str = Node.value.func.id - if CalledName in Gen._closure_return_types: - Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName] - if InitValue is None and Node.value is not None: - # 防静默(通用判据,不再只认 ast.Attribute):带值带注解赋值 RHS - # 求值为 None 且非"按类型即无值"(未以返回 void 的调用收尾)时, - # 变量 alloca 已创建却无 store → 后续 load 读未初始化栈变量 - # (运行期随机崩溃;如类型下标被当乘法的 list[X*]*(...) 整条 RHS - # 不产任何指令)。判据取自实际发射的指令,见 _RhsEndsWithVoidCall。 - if not self._RhsEndsWithVoidCall(Gen, RhsBlock, RhsInsnStart): - self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName) - if VarName in Gen._reg_values: - del Gen._reg_values[VarName] - if VarName in Gen.variables and Gen.variables[VarName] is not None: - if InitValue and isinstance(InitValue.type, ir.PointerType): - pointee: ir.Type = InitValue.type.pointee - if isinstance(pointee, ir.IdentifiedStructType): - struct_name: str = getattr(pointee, 'name', '') - is_vtable_class: bool = False - for cls_name in Gen.class_vtable: - if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name: - is_vtable_class = True - break - if is_vtable_class: - var: ir.AllocaInstr = Gen._allocaEntry(InitValue.type, name=VarName) - Gen._store(InitValue, var) - Gen.variables[VarName] = var - if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs: - Gen.var_struct_class[VarName] = Node.annotation.id - Gen.global_struct_class[VarName] = Node.annotation.id - Gen._record_var_signedness(VarName, IsUnsigned) - return - if InitValue: - try: - VarPtr: ir.Value = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType): - TargetType: ir.Type = VarPtr.type.pointee - if InitValue.type != TargetType: - if isinstance(InitValue.type, ir.IntType) and isinstance(TargetType, ir.IntType): - if TargetType.width > InitValue.type.width: - InitValue = Gen.builder.zext(InitValue, TargetType, name=f"zext_{VarName}") - elif TargetType.width < InitValue.type.width: - InitValue = Gen.builder.trunc(InitValue, TargetType, name=f"trunc_{VarName}") - elif isinstance(InitValue.type, ir.PointerType) and isinstance(TargetType, ir.PointerType): - InitValue = Gen.builder.bitcast(InitValue, TargetType, name=f"cast_{VarName}") - Gen._store(InitValue, VarPtr) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - Gen._record_var_signedness(VarName, IsUnsigned) - return - if VarName in Gen.global_vars: - if VarName in Gen.module.globals: - GVar: ir.GlobalVariable = Gen.module.globals[VarName] - if InitValue: - Gen._store(InitValue, GVar) - Gen.variables[VarName] = GVar - Gen._record_var_signedness(VarName, IsUnsigned) - return - var: ir.AllocaInstr = Gen._allocaEntry(VarType, name=VarName) - if InitValue and isinstance(InitValue.type, ir.PointerType): - pointee: ir.Type = InitValue.type.pointee - # 当 InitValue 是 i8* (t.CPtr) 且注解是 Gen.structs 中的结构体名时, - # bitcast 为对应的结构体指针,以便正确存储和设置 var_struct_class - # 例如: sub: dict = sub_p (sub_p 是 t.CPtr) - if (isinstance(pointee, ir.IntType) and pointee.width == 8 - and isinstance(Node.annotation, ast.Name) - and Node.annotation.id in Gen.structs): - TargetStructType: ir.Type = Gen.structs[Node.annotation.id] - if isinstance(TargetStructType, ir.PointerType): - TargetStructType = TargetStructType.pointee - InitValue = Gen.builder.bitcast(InitValue, ir.PointerType(TargetStructType), name=f"cast_{VarName}_to_{Node.annotation.id}") - pointee = InitValue.type.pointee - if isinstance(pointee, ir.IdentifiedStructType): - struct_name: str = getattr(pointee, 'name', '') - is_vtable_class: bool = False - for cls_name in Gen.class_vtable: - if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name: - is_vtable_class = True - break - if is_vtable_class and not isinstance(VarType, ir.PointerType): - var = Gen._allocaEntry(InitValue.type, name=VarName) - VarType = InitValue.type - IsPtr = True - Gen._store(InitValue, var) - Gen.variables[VarName] = var - if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs: - Gen.var_struct_class[VarName] = Node.annotation.id - Gen.global_struct_class[VarName] = Node.annotation.id - Gen._record_var_signedness(VarName, IsUnsigned) - return - # 方向判定用:取注解一侧的泛型基名(`Base[...]` 或 `Base[...] | t.CPtr` - # 形态中的 Base)。此判据**不依赖泛型模板注册表**(list/dict 等内建 - # 容器可能未登记),只做结构性识别;仅 Name 形态可识别,属性链 - # (如 t.CPtr[...])不参与。 - _ann_gen_base: str | None = None - _ann_sub_side: ast.expr | None = None - if isinstance(Node.annotation, ast.Subscript): - _ann_sub_side = Node.annotation - elif (isinstance(Node.annotation, ast.BinOp) - and isinstance(Node.annotation.op, ast.BitOr)): - for _ann_side in (Node.annotation.left, Node.annotation.right): - if isinstance(_ann_side, ast.Subscript): - _ann_sub_side = _ann_side - break - if _ann_sub_side is not None: - _ann_base_v: ast.expr = _ann_sub_side.value - # 兼容嵌套下标链(Base[...][...]):逐层剥到最内层 Name - while isinstance(_ann_base_v, ast.Subscript): - _ann_base_v = _ann_base_v.value - if isinstance(_ann_base_v, ast.Name): - _ann_gen_base = _ann_base_v.id - # 「反向重建」方向判定(其二,见下方 _rb_is_upcast)所需的**注解侧 - # 被指类**:注解被 TypeMerge 折叠成 i8*(rawptr[X] / t.CPtr[X],见 - # 上方 635-684)时 VarType.pointee 不是结构体,此时用该处恢复进 - # var_struct_class 的 X —— 那正是注解写明的被指类。 - _ann_pointee: ir.Type | None = None - if isinstance(VarType, ir.PointerType): - _ap: ir.Type = VarType.pointee - if isinstance(_ap, ir.IdentifiedStructType): - _ann_pointee = _ap - else: - _ask: str | None = Gen.var_struct_class.get(VarName) - _ann_st: ir.Type | None = Gen.structs.get(_ask) if _ask else None - if isinstance(_ann_st, ir.PointerType): - _ann_st = _ann_st.pointee - _ann_pointee = _ann_st - for CN, ST in Gen.structs.items(): - # 泛型占位类型(T/U/V,如 GSList[T].Head 的元素类型)opaque 无 body, - # 不参与变量类型重建/var_struct_class 设置:跟随 T* 会污染注解声明的 - # 真实类型(如 BasicBlock*),导致后续字段访问/循环体翻译静默失败。 - # (属性访问层已特化返回真实元素类型,此处为防御性兜底。) - _cn_short: str = CN.split('.')[-1] if '.' in CN else CN - if _IsGenericTpName(_cn_short): - continue - # rawptr 占位 struct('rawptr' / '{sha1}.rawptr' / 'rawptr[...]')是 - # 模块限定联合 X | t.CPtr 被 resolver 退化的产物(无真实字段), - # 绝不能覆盖注解已解析出的真实类名(如 Value)→ env_size_val.Next - # 报 "属性在类 'rawptr' 中找不到"(FuncGenMixin.vp L307 根因)。 - if CN == 'rawptr' or CN.endswith('.rawptr') or CN.startswith('rawptr['): - continue - if pointee is ST or pointee == ST: - # 若注解是泛型特化(如 ndarray[t.CDouble] | t.CPtr), - # 构造正确的特化名(如 ndarray[double]),而非用 opaque 基础名 - resolved_cn: str = CN - if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr) and '[' not in CN: - for side in (Node.annotation.left, Node.annotation.right): - if isinstance(side, ast.Subscript): - bn: ast.expr = side.value - bnm: str | None = bn.id if isinstance(bn, ast.Name) else (bn.attr if isinstance(bn, ast.Attribute) else None) - # 归一化比较:CN 可能带 16 位 sha1 模块前缀 - # (如 'e94ef9e139461915.list'),注解侧裸名为 - # 'list',直接与 CN 比较恒不等 ⇒ 特化名恢复失败 - # ⇒ 写入裸泛型基名 ⇒ .get() 按错误元素类型 - # 分派崩溃。用 _cn_short(已去前缀)比较。 - if bnm and bnm == _cn_short: - sn2: ast.AST = side.slice - if hasattr(ast, 'Index') and isinstance(sn2, ast.Index): - sn2 = sn2.value - r2: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(sn2) - if r2: - resolved_cn = f'{CN}[{r2[0]}]' - break - if resolved_cn != Gen.var_struct_class.get(VarName): - Gen.var_struct_class[VarName] = resolved_cn - Gen.global_struct_class[VarName] = resolved_cn - if isinstance(VarType, ir.PointerType) and VarType.pointee is not ST and VarType.pointee != ST: - # 方向判定:本重建的本意是「注解退化成裸基类 / i8*, - # RHS 构造出精确特化 ⇒ 用 RHS 反向升级」(见 L765 注释)。 - # 但 RHS 的 pointee 若为**裸泛型基类**(丢类型实参,如 - # {sha1}.list)而注解一侧本身就是带类型实参的泛型 - # (`Base[...]` 或 `Base[...] | t.CPtr`),以 RHS 重建 - # alloca 会把注解已解析出的特化类型覆盖成裸基类 → 之后 - # .get() 按错误元素类型分派(list[int].get 返回 i32)→ - # inttoptr 截断指针 → 读垃圾 AST 节点崩溃 - # (MatchGenMixin._handle_renum_match 现场根因)。 - # 判据(_cn_short 无 '[' 且与注解泛型基名同名)只拦这一 - # 确定的方向错误,属**结构性识别**而非兜底:注解退化为 - # i8*/裸基类、RHS 精确特化的合法升级路径不受影响。 - _rb_is_bare_generic: bool = ( - _ann_gen_base is not None - and '[' not in _cn_short - and _cn_short == _ann_gen_base - ) - # 方向判定(其二):注解写明的被指类是 RHS 被指类的 - # **基类**时,注解就是合法的「基类指针」声明,RHS 只是 - # 继承上转 —— 以 RHS 重建 alloca 会把声明类型**窄化**成 - # 派生类,使同一变量随后赋基类/兄弟类的值被指针隐式 - # 转换 fail-fast 拦下(includes/ast/parser.vp - # `node: rawptr[AST] = Name(...)` 之后 - # `node = _new_const_str(...)` 现场根因)。 - # 判据只拦「上转被窄化」这一个确定的方向错误:注解退化 - # 成 i8*/裸占位(_ann_pointee 非其基类)时不受影响。 - _rb_is_upcast: bool = ( - _ann_pointee is not None - and _ann_pointee is not ST - and _ann_pointee != ST - and Gen._IsInheritanceUpcastPointee(pointee, _ann_pointee) - ) - if not _rb_is_bare_generic and not _rb_is_upcast: - var = Gen._allocaEntry(ir.PointerType(pointee), name=VarName) - VarType = ir.PointerType(pointee) - break - if InitValue: - try: - if InitValue.type != VarType: - if Gen._IsNoneValue(InitValue, Node.value): - # None(空值常量)允许隐式转换为任意变量类型: - # 指针→空指针,其余→零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。 - InitValue = (ir.Constant(VarType, None) if isinstance(VarType, ir.PointerType) - else Gen._ZeroConst(VarType)) - elif isinstance(InitValue.type, ir.IntType) and isinstance(VarType, ir.IntType): - if VarType.width > InitValue.type.width: - InitValue = Gen.builder.zext(InitValue, VarType, name=f"zext_{VarName}") - elif VarType.width < InitValue.type.width: - InitValue = Gen.builder.trunc(InitValue, VarType, name=f"trunc_{VarName}") - elif isinstance(InitValue.type, ir.PointerType) and isinstance(VarType, ir.PointerType): - Allowed: bool - Reason: str - Allowed, Reason = Gen._ClassifyPtrImplicitConvert(InitValue.type, VarType, Node.value) - if not Allowed: - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转换已禁止({Reason}):不能用指针类型 {InitValue.type} 初始化 " - f"{VarName}: {VarType};如需转换请显式 cast(如 t.CPtr(...))" - f"{Gen._DescribeValueNode(Node.value)}" - ) - InitValue = Gen.builder.bitcast(InitValue, VarType, name=f"cast_{VarName}") - elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType): - InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}") - elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType): - # 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径) - # (None 已在上方短路为零值,不会落到这里)。 - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针隐式转整数已禁止:不能用指针类型 {InitValue.type} 初始化整数变量 " - f"{VarName}: {VarType};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" - f"{Gen._DescribeValueNode(Node.value)}" - ) - # 大端局部变量:存储时 bswap - InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}") - Gen._store(InitValue, var, Node=Node.value) - except (TypeError, ValueError) as _e: - # 仅回退 llvmlite 自身的类型/值错误;FailFastError 等一律上抛(不吞) - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - Gen.variables[VarName] = var - Gen._record_var_signedness(VarName, IsUnsigned) - # FakeDuck: 初始化影子结构体 a__meta__ 的字段 - _meta_name_check: str = f"{VarName}__meta__" - if _meta_name_check in Gen.variables and '_str' in Gen.structs: - _meta_var_init: ir.Value = Gen.variables[_meta_name_check] - _str_st: ir.Type = Gen.structs['_str'] - # __data__ 字段 = 字符串指针 - _data_off = Gen._get_member_offset('__data__', '_str') - if _data_off is not None and InitValue: - _data_ptr = Gen.builder.gep(_meta_var_init, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _data_off)], name=f"{VarName}_meta_data") - Gen._store(InitValue, _data_ptr) - # __mbuddy__ 字段 = NULL - _mpool_off = Gen._get_member_offset('__mbuddy__', '_str') - if _mpool_off is not None: - _mpool_ptr = Gen.builder.gep(_meta_var_init, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _mpool_off)], name=f"{VarName}_meta_mbuddy") - Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), _mpool_ptr) - # FakeDuck: 从 with 上下文自动注入 requires 字段(如 __mbuddy__) - self.Trans.ExprCallHandle._InjectRequiresFields(_meta_var_init, '_str', Gen) - # 记录局部变量字节序 - if TypeInfo.ByteOrder == 'big': - if isinstance(VarType, ir.IntType) and VarType.width in (16, 32, 64): - Gen.local_var_byteorders[VarName] = 'big' - elif isinstance(VarType, ir.ArrayType) and isinstance(VarType.element, ir.IntType) and VarType.element.width in (16, 32, 64): - Gen.local_var_byteorders[VarName] = 'big' - - if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): + Node.annotation, self.Trans, Gen) + # resolver 对模块限定 struct 联合(llvmlite.Value | t.CPtr)常给出 + # 指针占位名(如 '{sha1}.rawptr')且该占位恰好注册在 Gen.structs, + # 使上方 TypeInfo.Name in Gen.structs 通过并登记错误类名 → 字段 + # 访问 "属性 'Next' 在类 'rawptr' 中找不到"。凡短名命中真实类 + # (attr 精确 in structs),一律覆盖为真实类名(Value)。 + if _union_struct_name2 and _union_struct_name2 in Gen.structs: + if Gen.var_struct_class.get(VarName) != _union_struct_name2: + Gen.var_struct_class[VarName] = _union_struct_name2 + if getattr(Gen, 'global_struct_class', None) is not None: + Gen.global_struct_class[VarName] = _union_struct_name2 + # Subscript rawptr[X] / t.CPtr[X] 注解的本地结构指针变量(如 + # includes/ast/parser.vp _fix_store_ctx 里 `tp: rawptr[Tuple] = + # (rawptr[Tuple])(node)`,随后 `tp_elts = tp.elts`):TypeMerge 的 + # builtins 分支把 rawptr[X] 折叠为 i8*(PtrCount=1/CVoid),既不登记 + # var_struct_class 也不保留内层 X —— 单级 Name 字段取值(tp.elts) + # 在属性访问层 ClassName 解析为 None → 求值 None → 赋值被 fail-fast + # 拦截(parser.vp L427 bootstrap 根因)。与上方 BinOp 联合的短名回退 + # 对称:把内层 X 解析为真实 struct 键(兼容短名 / 带 class_sha1 前缀 + # 的键)并登记;仅在缺失或指针占位(rawptr…)时覆盖,不碰已精确登记值。 + _rst_sub: ast.AST | None = None + if isinstance(Node.annotation, ast.Subscript): + _rst_v: ast.expr = Node.annotation.value + _rst_nm: str = '' + if isinstance(_rst_v, ast.Name): + _rst_nm = _rst_v.id + elif isinstance(_rst_v, ast.Attribute) and isinstance(_rst_v.value, ast.Name): + _rst_nm = f'{_rst_v.value.id}.{_rst_v.attr}' + if _rst_nm in ('rawptr', 't.CPtr'): + _rst_sub = Node.annotation + if _rst_sub is not None: + _rst_sl: ast.AST = _rst_sub.slice + if hasattr(ast, 'Index') and isinstance(_rst_sl, ast.Index): + _rst_sl = _rst_sl.value + _rst_tn: str = '' + if isinstance(_rst_sl, ast.Name): + _rst_tn = _rst_sl.id + elif isinstance(_rst_sl, ast.Attribute) and isinstance(_rst_sl.value, ast.Name): + _rst_tn = _rst_sl.attr + _rst_tk: str | None = None + if _rst_tn: + if _rst_tn in Gen.structs: + _rst_tk = _rst_tn + else: + _rst_sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_rst_tn) + if _rst_sha and f'{_rst_sha}.{_rst_tn}' in Gen.structs: + _rst_tk = f'{_rst_sha}.{_rst_tn}' + else: + for _rst_k3 in Gen.structs: + _rst_short3: str = _rst_k3.split('.')[-1] if '.' in _rst_k3 else _rst_k3 + if _rst_short3 == _rst_tn: + _rst_tk = _rst_k3 + break + if _rst_tk: + _rst_cur: str | None = Gen.var_struct_class.get(VarName) + _rst_need: bool = (not _rst_cur or _rst_cur == 'rawptr' or '.rawptr' in _rst_cur + or _rst_cur.startswith('rawptr[')) + if _rst_need and _rst_cur != _rst_tk: + Gen.var_struct_class[VarName] = _rst_tk + if getattr(Gen, 'global_struct_class', None) is not None: + Gen.global_struct_class[VarName] = _rst_tk + # 检测指针到指针缓冲区变量:注解为 StructName | t.CPtr,且赋值来自 t.CPtr(...) 调用。 + # 这种变量(如 list[T].__getitem__ 中的 elem_ptr: T | t.CPtr = t.CPtr(...)) + # 实际指向存储结构体指针的缓冲区(T=AST|t.CPtr 时特化为 AST*,缓冲区存 AST* 数组)。 + # [index] 应 bitcast 为 AST** 后 GEP 指针数组再 load,而非 GEP 结构体数组。 + # 泛型特化将 T|t.CPtr 降级为 StructName|t.CPtr(丢失一层指针),故通过赋值来源区分: + # elem_ptr 的值是 t.CPtr(buf + idx*elem_size)(原始指针运算,缓冲区内容未知), + # pts 的值是 (Point|t.CPtr)(...)(类型转换,明确指向 Point 结构体)。 + _is_cptr_assign: bool = ( + isinstance(Node.value, ast.Call) + and isinstance(Node.value.func, ast.Attribute) + and isinstance(Node.value.func.value, ast.Name) + and Node.value.func.value.id == 't' + and Node.value.func.attr == 'CPtr' + ) + _is_struct_ptr_union: bool = ( + isinstance(Node.annotation, ast.BinOp) + and isinstance(Node.annotation.op, ast.BitOr) + and ((IsCPtrNode(Node.annotation.left) + and isinstance(Node.annotation.right, ast.Name) + and (Node.annotation.right.id in Gen.structs + or _IsGenericTpName(Node.annotation.right.id))) + or (IsCPtrNode(Node.annotation.right) + and isinstance(Node.annotation.left, ast.Name) + and (Node.annotation.left.id in Gen.structs + or _IsGenericTpName(Node.annotation.left.id)))) + ) + if _is_cptr_assign and _is_struct_ptr_union: + Gen.var_ptr_element[VarName] = True + Gen.global_var_ptr_element[VarName] = True + if VarName in Gen._reg_values: + OldVal: ir.Value = Gen._reg_values[VarName] + if isinstance(OldVal.type, ir.PointerType): + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + del Gen._reg_values[VarName] + if Node.value: + InitValue: ir.Value | None = self._EvalRhsWithTargetHint(Node, VarType, Gen) + if InitValue: + self._StoreOrStrCopy(VarName, var, InitValue) + elif isinstance(Node.value, ast.Attribute): + # 防静默:带值带注解属性链求值失败禁止跳过(见 __function.vp + # 两级链 blk = func.Blocks.Head 编译产物 store 丢失根因)。 + self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName) + Gen._record_var_signedness(VarName, IsUnsigned) + return + del Gen._reg_values[VarName] + InitValue: ir.Value | None = None + RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None + RhsInsnStart: int = len(RhsBlock.instructions) if RhsBlock is not None else -1 + if Node.value: + InitValue = self._EvalRhsWithTargetHint(Node, VarType, Gen) + # 闭包变量追踪:当赋值右侧是返回闭包的函数调用时,记录变量为闭包类型 + if isinstance(Node.value, ast.Call) and isinstance(Node.value.func, ast.Name): + CalledName: str = Node.value.func.id + if CalledName in Gen._closure_return_types: + Gen._closure_var_types[VarName] = Gen._closure_return_types[CalledName] + if InitValue is None and Node.value is not None: + # 防静默(通用判据,不再只认 ast.Attribute):带值带注解赋值 RHS + # 求值为 None 且非"按类型即无值"(未以返回 void 的调用收尾)时, + # 变量 alloca 已创建却无 store → 后续 load 读未初始化栈变量 + # (运行期随机崩溃;如类型下标被当乘法的 list[X*]*(...) 整条 RHS + # 不产任何指令)。判据取自实际发射的指令,见 _RhsEndsWithVoidCall。 + if not self._RhsEndsWithVoidCall(Gen, RhsBlock, RhsInsnStart): + self._RaiseUnresolvedExpr("带注解赋值", Node.value, Gen, Node.annotation, VarName) + if VarName in Gen._reg_values: + del Gen._reg_values[VarName] + if VarName in Gen.variables and Gen.variables[VarName] is not None: + if InitValue and isinstance(InitValue.type, ir.PointerType): + pointee: ir.Type = InitValue.type.pointee + if isinstance(pointee, ir.IdentifiedStructType): + struct_name: str = getattr(pointee, 'name', '') + is_vtable_class: bool = False + for cls_name in Gen.class_vtable: + if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name: + is_vtable_class = True + break + if is_vtable_class: + var: ir.AllocaInstr = Gen._allocaEntry(InitValue.type, name=VarName) + Gen._store(InitValue, var) + Gen.variables[VarName] = var + if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs: + Gen.var_struct_class[VarName] = Node.annotation.id + Gen.global_struct_class[VarName] = Node.annotation.id + Gen._record_var_signedness(VarName, IsUnsigned) + return + if InitValue: + try: + VarPtr: ir.Value = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType): + TargetType: ir.Type = VarPtr.type.pointee + if InitValue.type != TargetType: + if isinstance(InitValue.type, ir.IntType) and isinstance(TargetType, ir.IntType): + if TargetType.width > InitValue.type.width: + InitValue = Gen.builder.zext(InitValue, TargetType, name=f"zext_{VarName}") + elif TargetType.width < InitValue.type.width: + InitValue = Gen.builder.trunc(InitValue, TargetType, name=f"trunc_{VarName}") + elif isinstance(InitValue.type, ir.PointerType) and isinstance(TargetType, ir.PointerType): + InitValue = Gen.builder.bitcast(InitValue, TargetType, name=f"cast_{VarName}") + Gen._store(InitValue, VarPtr) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + Gen._record_var_signedness(VarName, IsUnsigned) + return + if VarName in Gen.global_vars: + if VarName in Gen.module.globals: + GVar: ir.GlobalVariable = Gen.module.globals[VarName] + if InitValue: + Gen._store(InitValue, GVar) + Gen.variables[VarName] = GVar + Gen._record_var_signedness(VarName, IsUnsigned) + return + var: ir.AllocaInstr = Gen._allocaEntry(VarType, name=VarName) + if InitValue and isinstance(InitValue.type, ir.PointerType): + pointee: ir.Type = InitValue.type.pointee + # 当 InitValue 是 i8* (t.CPtr) 且注解是 Gen.structs 中的结构体名时, + # bitcast 为对应的结构体指针,以便正确存储和设置 var_struct_class + # 例如: sub: dict = sub_p (sub_p 是 t.CPtr) + if (isinstance(pointee, ir.IntType) and pointee.width == 8 + and isinstance(Node.annotation, ast.Name) + and Node.annotation.id in Gen.structs): + TargetStructType: ir.Type = Gen.structs[Node.annotation.id] + if isinstance(TargetStructType, ir.PointerType): + TargetStructType = TargetStructType.pointee + InitValue = Gen.builder.bitcast(InitValue, ir.PointerType(TargetStructType), name=f"cast_{VarName}_to_{Node.annotation.id}") + pointee = InitValue.type.pointee + if isinstance(pointee, ir.IdentifiedStructType): + struct_name: str = getattr(pointee, 'name', '') + is_vtable_class: bool = False + for cls_name in Gen.class_vtable: + if struct_name.endswith(f'.{cls_name}') or struct_name == cls_name: + is_vtable_class = True + break + if is_vtable_class and not isinstance(VarType, ir.PointerType): + var = Gen._allocaEntry(InitValue.type, name=VarName) + VarType = InitValue.type + IsPtr = True + Gen._store(InitValue, var) + Gen.variables[VarName] = var + if isinstance(Node.annotation, ast.Name) and Node.annotation.id in Gen.structs: + Gen.var_struct_class[VarName] = Node.annotation.id + Gen.global_struct_class[VarName] = Node.annotation.id + Gen._record_var_signedness(VarName, IsUnsigned) + return + # 方向判定用:取注解一侧的泛型基名(`Base[...]` 或 `Base[...] | t.CPtr` + # 形态中的 Base)。此判据**不依赖泛型模板注册表**(list/dict 等内建 + # 容器可能未登记),只做结构性识别;仅 Name 形态可识别,属性链 + # (如 t.CPtr[...])不参与。 + _ann_gen_base: str | None = None + _ann_sub_side: ast.expr | None = None + if isinstance(Node.annotation, ast.Subscript): + _ann_sub_side = Node.annotation + elif (isinstance(Node.annotation, ast.BinOp) + and isinstance(Node.annotation.op, ast.BitOr)): + for _ann_side in (Node.annotation.left, Node.annotation.right): + if isinstance(_ann_side, ast.Subscript): + _ann_sub_side = _ann_side + break + if _ann_sub_side is not None: + _ann_base_v: ast.expr = _ann_sub_side.value + # 兼容嵌套下标链(Base[...][...]):逐层剥到最内层 Name + while isinstance(_ann_base_v, ast.Subscript): + _ann_base_v = _ann_base_v.value + if isinstance(_ann_base_v, ast.Name): + _ann_gen_base = _ann_base_v.id + # 「反向重建」方向判定(其二,见下方 _rb_is_upcast)所需的**注解侧 + # 被指类**:注解被 TypeMerge 折叠成 i8*(rawptr[X] / t.CPtr[X],见 + # 上方 635-684)时 VarType.pointee 不是结构体,此时用该处恢复进 + # var_struct_class 的 X —— 那正是注解写明的被指类。 + _ann_pointee: ir.Type | None = None + if isinstance(VarType, ir.PointerType): + _ap: ir.Type = VarType.pointee + if isinstance(_ap, ir.IdentifiedStructType): + _ann_pointee = _ap + else: + _ask: str | None = Gen.var_struct_class.get(VarName) + _ann_st: ir.Type | None = Gen.structs.get(_ask) if _ask else None + if isinstance(_ann_st, ir.PointerType): + _ann_st = _ann_st.pointee + _ann_pointee = _ann_st + for CN, ST in Gen.structs.items(): + # 泛型占位类型(T/U/V,如 GSList[T].Head 的元素类型)opaque 无 body, + # 不参与变量类型重建/var_struct_class 设置:跟随 T* 会污染注解声明的 + # 真实类型(如 BasicBlock*),导致后续字段访问/循环体翻译静默失败。 + # (属性访问层已特化返回真实元素类型,此处为防御性兜底。) + _cn_short: str = CN.split('.')[-1] if '.' in CN else CN + if _IsGenericTpName(_cn_short): + continue + # rawptr 占位 struct('rawptr' / '{sha1}.rawptr' / 'rawptr[...]')是 + # 模块限定联合 X | t.CPtr 被 resolver 退化的产物(无真实字段), + # 绝不能覆盖注解已解析出的真实类名(如 Value)→ env_size_val.Next + # 报 "属性在类 'rawptr' 中找不到"(FuncGenMixin.vp L307 根因)。 + if CN == 'rawptr' or CN.endswith('.rawptr') or CN.startswith('rawptr['): + continue + if pointee is ST or pointee == ST: + # 若注解是泛型特化(如 ndarray[t.CDouble] | t.CPtr), + # 构造正确的特化名(如 ndarray[double]),而非用 opaque 基础名 + resolved_cn: str = CN + if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr) and '[' not in CN: + for side in (Node.annotation.left, Node.annotation.right): + if isinstance(side, ast.Subscript): + bn: ast.expr = side.value + bnm: str | None = bn.id if isinstance(bn, ast.Name) else (bn.attr if isinstance(bn, ast.Attribute) else None) + # 归一化比较:CN 可能带 16 位 sha1 模块前缀 + # (如 'e94ef9e139461915.list'),注解侧裸名为 + # 'list',直接与 CN 比较恒不等 ⇒ 特化名恢复失败 + # ⇒ 写入裸泛型基名 ⇒ .get() 按错误元素类型 + # 分派崩溃。用 _cn_short(已去前缀)比较。 + if bnm and bnm == _cn_short: + sn2: ast.AST = side.slice + if hasattr(ast, 'Index') and isinstance(sn2, ast.Index): + sn2 = sn2.value + r2: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(sn2) + if r2: + resolved_cn = f'{CN}[{r2[0]}]' + break + if resolved_cn != Gen.var_struct_class.get(VarName): + Gen.var_struct_class[VarName] = resolved_cn + Gen.global_struct_class[VarName] = resolved_cn + if isinstance(VarType, ir.PointerType) and VarType.pointee is not ST and VarType.pointee != ST: + # 方向判定:本重建的本意是「注解退化成裸基类 / i8*, + # RHS 构造出精确特化 ⇒ 用 RHS 反向升级」(见 L765 注释)。 + # 但 RHS 的 pointee 若为**裸泛型基类**(丢类型实参,如 + # {sha1}.list)而注解一侧本身就是带类型实参的泛型 + # (`Base[...]` 或 `Base[...] | t.CPtr`),以 RHS 重建 + # alloca 会把注解已解析出的特化类型覆盖成裸基类 → 之后 + # .get() 按错误元素类型分派(list[int].get 返回 i32)→ + # inttoptr 截断指针 → 读垃圾 AST 节点崩溃 + # (MatchGenMixin._handle_renum_match 现场根因)。 + # 判据(_cn_short 无 '[' 且与注解泛型基名同名)只拦这一 + # 确定的方向错误,属**结构性识别**而非兜底:注解退化为 + # i8*/裸基类、RHS 精确特化的合法升级路径不受影响。 + _rb_is_bare_generic: bool = ( + _ann_gen_base is not None + and '[' not in _cn_short + and _cn_short == _ann_gen_base + ) + # 方向判定(其二):注解写明的被指类是 RHS 被指类的 + # **基类**时,注解就是合法的「基类指针」声明,RHS 只是 + # 继承上转 —— 以 RHS 重建 alloca 会把声明类型**窄化**成 + # 派生类,使同一变量随后赋基类/兄弟类的值被指针隐式 + # 转换 fail-fast 拦下(includes/ast/parser.vp + # `node: rawptr[AST] = Name(...)` 之后 + # `node = _new_const_str(...)` 现场根因)。 + # 判据只拦「上转被窄化」这一个确定的方向错误:注解退化 + # 成 i8*/裸占位(_ann_pointee 非其基类)时不受影响。 + _rb_is_upcast: bool = ( + _ann_pointee is not None + and _ann_pointee is not ST + and _ann_pointee != ST + and Gen._IsInheritanceUpcastPointee(pointee, _ann_pointee) + ) + if not _rb_is_bare_generic and not _rb_is_upcast: + var = Gen._allocaEntry(ir.PointerType(pointee), name=VarName) + VarType = ir.PointerType(pointee) + break + if InitValue: + try: + if InitValue.type != VarType: + if Gen._IsNoneValue(InitValue, Node.value): + # None(空值常量)允许隐式转换为任意变量类型: + # 指针→空指针,其余→零值(整数 0 / 浮点 0.0 / 聚合 zeroinitializer)。 + InitValue = (ir.Constant(VarType, None) if isinstance(VarType, ir.PointerType) + else Gen._ZeroConst(VarType)) + elif isinstance(InitValue.type, ir.IntType) and isinstance(VarType, ir.IntType): + if VarType.width > InitValue.type.width: + InitValue = Gen.builder.zext(InitValue, VarType, name=f"zext_{VarName}") + elif VarType.width < InitValue.type.width: + InitValue = Gen.builder.trunc(InitValue, VarType, name=f"trunc_{VarName}") + elif isinstance(InitValue.type, ir.PointerType) and isinstance(VarType, ir.PointerType): + Allowed: bool + Reason: str + Allowed, Reason = Gen._ClassifyPtrImplicitConvert(InitValue.type, VarType, Node.value) + if not Allowed: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转换已禁止({Reason}):不能用指针类型 {InitValue.type} 初始化 " + f"{VarName}: {VarType};如需转换请显式 cast(如 t.CPtr(...))" + f"{Gen._DescribeValueNode(Node.value)}" + ) + InitValue = Gen.builder.bitcast(InitValue, VarType, name=f"cast_{VarName}") + elif isinstance(VarType, ir.PointerType) and isinstance(InitValue.type, ir.IntType): + InitValue = Gen.builder.inttoptr(InitValue, VarType, name=f"int2ptr_{VarName}") + elif isinstance(VarType, ir.IntType) and isinstance(InitValue.type, ir.PointerType): + # 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径) + # (None 已在上方短路为零值,不会落到这里)。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针隐式转整数已禁止:不能用指针类型 {InitValue.type} 初始化整数变量 " + f"{VarName}: {VarType};如需转换请显式 cast(如 t.CUInt64T(p) / t.CInt(p))" + f"{Gen._DescribeValueNode(Node.value)}" + ) + # 大端局部变量:存储时 bswap + InitValue = Gen._apply_bswap_if_big(InitValue, TypeInfo.ByteOrder, f"bswap_store_{VarName}") + Gen._store(InitValue, var, Node=Node.value) + except (TypeError, ValueError) as _e: + # 仅回退 llvmlite 自身的类型/值错误;FailFastError 等一律上抛(不吞) + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + Gen.variables[VarName] = var + Gen._record_var_signedness(VarName, IsUnsigned) + # FakeDuck: 初始化影子结构体 a__meta__ 的字段 + _meta_name_check: str = f"{VarName}__meta__" + if _meta_name_check in Gen.variables and '_str' in Gen.structs: + _meta_var_init: ir.Value = Gen.variables[_meta_name_check] + _str_st: ir.Type = Gen.structs['_str'] + # __data__ 字段 = 字符串指针 + _data_off = Gen._get_member_offset('__data__', '_str') + if _data_off is not None and InitValue: + _data_ptr = Gen.builder.gep(_meta_var_init, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _data_off)], name=f"{VarName}_meta_data") + Gen._store(InitValue, _data_ptr) + # __mbuddy__ 字段 = NULL + _mpool_off = Gen._get_member_offset('__mbuddy__', '_str') + if _mpool_off is not None: + _mpool_ptr = Gen.builder.gep(_meta_var_init, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _mpool_off)], name=f"{VarName}_meta_mbuddy") + Gen._store(ir.Constant(ir.PointerType(ir.IntType(8)), None), _mpool_ptr) + # FakeDuck: 从 with 上下文自动注入 requires 字段(如 __mbuddy__) + self.Trans.ExprCallHandle._InjectRequiresFields(_meta_var_init, '_str', Gen) + # 记录局部变量字节序 + if TypeInfo.ByteOrder == 'big': + if isinstance(VarType, ir.IntType) and VarType.width in (16, 32, 64): + Gen.local_var_byteorders[VarName] = 'big' + elif isinstance(VarType, ir.ArrayType) and isinstance(VarType.element, ir.IntType) and VarType.element.width in (16, 32, 64): + Gen.local_var_byteorders[VarName] = 'big' + + if isinstance(Node.annotation, ast.BinOp) and isinstance(Node.annotation.op, ast.BitOr): found_struct: str | None = FindStructNameInAnnotationWithContext( - Node.annotation, self.Trans, Gen) - # 若 FindStructNameInAnnotation 仅返回裸模板名(如 'ndarray'),但注解中 - # 含泛型特化(如 numpy.ndarray[t.CDouble]),用 LLVM 类型名构造特化名 - # (如 'ndarray[double]')。main.py 的 Gen.structs 可能只有 opaque 'ndarray', - # 但 numpy 模块已特化 ndarray[double],方法调用通过 sha1 前缀查找。 - if found_struct and '[' not in found_struct: - for side in (Node.annotation.left, Node.annotation.right): - if isinstance(side, ast.Subscript): - base_node: ast.expr = side.value - base_nm: str | None = None - if isinstance(base_node, ast.Name): - base_nm = base_node.id - elif isinstance(base_node, ast.Attribute): - base_nm = base_node.attr - if base_nm and base_nm == found_struct: - slice_node: ast.AST = side.slice - if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): - slice_node = slice_node.value - result: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) - if result: - found_struct = f'{base_nm}[{result[0]}]' - break - if found_struct: - Gen.var_struct_class[VarName] = found_struct - Gen.global_struct_class[VarName] = found_struct - - if InitValue and isinstance(InitValue.type, ir.PointerType): - pointee: ir.Type = InitValue.type.pointee - if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - # rawptr 占位 struct 不参与覆盖(见 L706-711 说明) - if CN == 'rawptr' or CN.endswith('.rawptr') or CN.startswith('rawptr['): - break - Gen._var_to_heap_ptr[VarName] = InitValue - # 不覆盖已设置的特化名(含 '['):opaque 基础类型(如 ndarray) - # 会匹配到裸模板名,但注解已提供更精确的特化名(如 ndarray[double]) - existing_cn: str | None = Gen.var_struct_class.get(VarName) - if not (existing_cn and '[' in existing_cn) and CN != existing_cn: - Gen.var_struct_class[VarName] = CN - Gen.global_struct_class[VarName] = CN - break - - if self.Trans.VarScopes: - if TypeInfo: - self.Trans.VarScopes[-1][VarName] = TypeInfo - else: - DefaultInfo: CTypeInfo = CTypeInfo() - DefaultInfo.BaseType = t.CInt - self.Trans.VarScopes[-1][VarName] = DefaultInfo - elif isinstance(Node.target, ast.Attribute): - if isinstance(Node.target.value, ast.Name) and Node.target.value.id == 'self': - if Node.value: - AttrVarType: Any = None - try: - if IsListAnnotation(Node.annotation): - slice_node: ast.AST = Node.annotation.slice - if isinstance(slice_node, ast.Name): - AttrVarType = f't.CArray[{slice_node.id}]' - elif isinstance(slice_node, ast.Tuple): - elts: str = ', '.join(getattr(e, 'id', str(e)) for e in slice_node.elts) - AttrVarType = f't.CArray[{elts}]' - elif isinstance(slice_node, ast.Attribute): - # 还原 t.xxx 形式(如 t.CInt、t.CArray) - if isinstance(slice_node.value, ast.Name): - AttrVarType = f't.CArray[{slice_node.value.id}.{slice_node.attr}]' - else: - AttrVarType = f't.CArray[{ast.unparse(slice_node)}]' - if AttrVarType is None: - AttrTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) - if AttrTypeInfo: - AttrVarType = Gen._ctype_to_llvm(AttrTypeInfo) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型失败: {_e}", "Exception") - _RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None - _RhsInsnStart: int = len(_RhsBlock.instructions) if _RhsBlock is not None else -1 - Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType) - if Value: - self._HandleAttributeStoreLlvm(Node.target, Value) - elif not self._RhsEndsWithVoidCall(Gen, _RhsBlock, _RhsInsnStart): - # 防静默(通用判据,不再只认 ast.Attribute):self 字段带注解 - # 赋值右侧求值失败(None)必须报错,否则字段写被静默跳过。 - self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen, Node.annotation, Node.target.attr) - if Value: - AttrName: str = Node.target.attr - len_name: str = f'{AttrName}__len' - ClassName: str | None = self._CurrentCpythonObjectClass - if not ClassName: - self_var: ir.Value | None = Gen.variables.get('self') - if self_var: - var_type: ir.Type = self_var.type - if isinstance(var_type, ir.PointerType) and isinstance(var_type.pointee, ir.PointerType): - var_type = var_type.pointee - if isinstance(var_type, ir.PointerType): - pointee: ir.Type = var_type.pointee - for cn, st in Gen.structs.items(): - if st == pointee: - ClassName = cn - break - if ClassName and ClassName in Gen.class_members: - for m_name, m_type in Gen.class_members[ClassName]: - if m_name == len_name: - list_len: ir.Constant | None = None - if isinstance(Node.value, ast.List): - list_len = ir.Constant(ir.IntType(64), len(Node.value.elts)) - if list_len is not None: - SelfVar: ir.Value | None = Gen._get_var_ptr('self') - if SelfVar: - SelfPtr: ir.Value = Gen._load(SelfVar, name="self") - offset: int = Gen._get_member_offset(len_name, ClassName) - LenPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=len_name) - Gen._store(list_len, LenPtr) + Node.annotation, self.Trans, Gen) + # 若 FindStructNameInAnnotation 仅返回裸模板名(如 'ndarray'),但注解中 + # 含泛型特化(如 numpy.ndarray[t.CDouble]),用 LLVM 类型名构造特化名 + # (如 'ndarray[double]')。main.py 的 Gen.structs 可能只有 opaque 'ndarray', + # 但 numpy 模块已特化 ndarray[double],方法调用通过 sha1 前缀查找。 + if found_struct and '[' not in found_struct: + for side in (Node.annotation.left, Node.annotation.right): + if isinstance(side, ast.Subscript): + base_node: ast.expr = side.value + base_nm: str | None = None + if isinstance(base_node, ast.Name): + base_nm = base_node.id + elif isinstance(base_node, ast.Attribute): + base_nm = base_node.attr + if base_nm and base_nm == found_struct: + slice_node: ast.AST = side.slice + if hasattr(ast, 'Index') and isinstance(slice_node, ast.Index): + slice_node = slice_node.value + result: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) + if result: + found_struct = f'{base_nm}[{result[0]}]' + break + if found_struct: + Gen.var_struct_class[VarName] = found_struct + Gen.global_struct_class[VarName] = found_struct + + if InitValue and isinstance(InitValue.type, ir.PointerType): + pointee: ir.Type = InitValue.type.pointee + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + # rawptr 占位 struct 不参与覆盖(见 L706-711 说明) + if CN == 'rawptr' or CN.endswith('.rawptr') or CN.startswith('rawptr['): + break + Gen._var_to_heap_ptr[VarName] = InitValue + # 不覆盖已设置的特化名(含 '['):opaque 基础类型(如 ndarray) + # 会匹配到裸模板名,但注解已提供更精确的特化名(如 ndarray[double]) + existing_cn: str | None = Gen.var_struct_class.get(VarName) + if not (existing_cn and '[' in existing_cn) and CN != existing_cn: + Gen.var_struct_class[VarName] = CN + Gen.global_struct_class[VarName] = CN + break + + if self.Trans.VarScopes: + if TypeInfo: + self.Trans.VarScopes[-1][VarName] = TypeInfo + else: + DefaultInfo: CTypeInfo = CTypeInfo() + DefaultInfo.BaseType = t.CInt + self.Trans.VarScopes[-1][VarName] = DefaultInfo + elif isinstance(Node.target, ast.Attribute): + if isinstance(Node.target.value, ast.Name) and Node.target.value.id == 'self': + if Node.value: + AttrVarType: Any = None + try: + if IsListAnnotation(Node.annotation): + slice_node: ast.AST = Node.annotation.slice + if isinstance(slice_node, ast.Name): + AttrVarType = f't.CArray[{slice_node.id}]' + elif isinstance(slice_node, ast.Tuple): + elts: str = ', '.join(getattr(e, 'id', str(e)) for e in slice_node.elts) + AttrVarType = f't.CArray[{elts}]' + elif isinstance(slice_node, ast.Attribute): + # 还原 t.xxx 形式(如 t.CInt、t.CArray) + if isinstance(slice_node.value, ast.Name): + AttrVarType = f't.CArray[{slice_node.value.id}.{slice_node.attr}]' + else: + AttrVarType = f't.CArray[{ast.unparse(slice_node)}]' + if AttrVarType is None: + AttrTypeInfo: CTypeInfo | None = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) + if AttrTypeInfo: + AttrVarType = Gen._ctype_to_llvm(AttrTypeInfo) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + _RhsBlock: ir.Block | None = Gen.builder.block if Gen.builder is not None else None + _RhsInsnStart: int = len(_RhsBlock.instructions) if _RhsBlock is not None else -1 + Value: ir.Value | None = self.HandleExprLlvm(Node.value, VarType=AttrVarType) + if Value: + self._HandleAttributeStoreLlvm(Node.target, Value) + elif not self._RhsEndsWithVoidCall(Gen, _RhsBlock, _RhsInsnStart): + # 防静默(通用判据,不再只认 ast.Attribute):self 字段带注解 + # 赋值右侧求值失败(None)必须报错,否则字段写被静默跳过。 + self._RaiseUnresolvedExpr("self 字段带注解赋值", Node.value, Gen, Node.annotation, Node.target.attr) + if Value: + AttrName: str = Node.target.attr + len_name: str = f'{AttrName}__len' + ClassName: str | None = self._CurrentCpythonObjectClass + if not ClassName: + self_var: ir.Value | None = Gen.variables.get('self') + if self_var: + var_type: ir.Type = self_var.type + if isinstance(var_type, ir.PointerType) and isinstance(var_type.pointee, ir.PointerType): + var_type = var_type.pointee + if isinstance(var_type, ir.PointerType): + pointee: ir.Type = var_type.pointee + for cn, st in Gen.structs.items(): + if st == pointee: + ClassName = cn + break + if ClassName and ClassName in Gen.class_members: + for m_name, m_type in Gen.class_members[ClassName]: + if m_name == len_name: + list_len: ir.Constant | None = None + if isinstance(Node.value, ast.List): + list_len = ir.Constant(ir.IntType(64), len(Node.value.elts)) + if list_len is not None: + SelfVar: ir.Value | None = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: ir.Value = Gen._load(SelfVar, name="self") + offset: int = Gen._get_member_offset(len_name, ClassName) + LenPtr: ir.Value = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=len_name) + Gen._store(list_len, LenPtr) break \ No newline at end of file diff --git a/lib/core/Handles/HandlesAugAssign.py b/lib/core/Handles/HandlesAugAssign.py index bae4e6f..d4f9637 100644 --- a/lib/core/Handles/HandlesAugAssign.py +++ b/lib/core/Handles/HandlesAugAssign.py @@ -1,300 +1,300 @@ -from __future__ import annotations -from typing import TYPE_CHECKING -import vast as ast -import sys -import llvmlite.ir as ir -if TYPE_CHECKING: - from lib.core.translator import Translator -from lib.core.VLogger import get_logger as _vlog -from lib.core.Handles.HandlesBase import BaseHandle - - -class AugAssignHandle(BaseHandle): - _AUGOP_MAP: dict[type, str] = { - ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__', - ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__', - ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__', - ast.LShift: '__ilshift__', ast.RShift: '__irshift__', - } - _AUGOP_FALLBACK: dict[type, str] = { - ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__', - ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__', - ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__', - ast.LShift: '__lshift__', ast.RShift: '__rshift__', - } - - def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type: - """解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。 - - 问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为 - `_g_alloc_count: t.CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量 - 类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配, - 产生 "Operands must be the same type, got (i64, i32)" 类错误。 - 模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局, - 但注解无初始值(如 `_g_alloc_max: t.CSizeT = 0`)时 initializer 为空、 - Gen.module.globals 中无该符号,故需在此兜底解析注解类型。 - """ - module_ann: dict = getattr(Gen, '_module_ann_types', None) - if module_ann and VarName in module_ann: - ann_type: ir.Type = module_ann[VarName] - if ann_type is not None: - return ann_type - if isinstance(fallback, ir.IntType): - return fallback - return ir.IntType(64) - - def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - if isinstance(Node.target, ast.Name): - if Gen._check_node_unsigned(Node.target): - return True - elif isinstance(Node.target, ast.Attribute): - if Gen._check_node_unsigned(Node.target): - return True - elif isinstance(Node.target, ast.Subscript): - if Gen._check_node_unsigned(Node.target): - return True - if Node.value is not None and Gen._check_node_unsigned(Node.value): - return True - return False - - def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None: - Gen: "Translator.LlvmGen" = self.Trans.LlvmGen - if isinstance(Node.target, ast.Name): - VarName: str = Node.target.id - if (VarName not in Gen.variables - and VarName not in Gen.global_vars - and VarName not in Gen._reg_values - and VarName not in Gen._direct_values): - src_info: str = Gen._get_node_info() - _vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}") - sys.exit(1) - if Gen._IsVarConst(VarName): - _vlog().error(Gen._ConstAssignMessage("增量赋值", VarName)) - sys.exit(1) - ClassName: str | None = Gen.var_struct_class.get(VarName) - iop_name: str | None = self._AUGOP_MAP.get(type(Node.op)) - if ClassName and iop_name: - iFuncName: str = f'{ClassName}.{iop_name}' - FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}' - if iFuncName in Gen.functions or FuncName in Gen.functions: - OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load())) - Value: ir.Value | None = self.HandleExprLlvm(Node.value) - if OldVal and Value: - result: ir.Value | None - if iFuncName in Gen.functions: - result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value) - else: - result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value) - if result is not None: - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: ir.Value = Gen.variables[VarName] - if VarPtr.type.pointee == result.type: - Gen._store(result, VarPtr) - elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType): - CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}") - Gen._store(CastedVal, VarPtr) - else: - NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName) - Gen._store(result, NewVar) - Gen.variables[VarName] = NewVar - else: - Gen._reg_values[VarName] = result - Gen.variables[VarName] = None - return - Value: ir.Value | None = self.HandleExprLlvm(Node.value) - if not Value: - return - is_unsigned: bool = self._is_aug_unsigned(Node, None, Value) - if isinstance(Node.target, ast.Name): - VarName: str = Node.target.id - Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) - if VarName in Gen.global_vars: - # 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局 - # (注解无初始值的全局变量),此处自动创建零初始化全局变量 - GVar: ir.GlobalVariable - if VarName not in Gen.module.globals: - _gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type) - GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName) - GVar.initializer = ir.Constant(_gv_type, 0) - else: - GVar = Gen.module.globals[VarName] - Gen.variables[VarName] = GVar - OldVal: ir.Value = Gen._load(GVar, name=VarName) - NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) - Gen._store(NewVal, GVar) - return - if VarName in Gen._reg_values: - OldVal: ir.Value = Gen._reg_values[VarName] - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - del Gen._reg_values[VarName] - if VarName in Gen._direct_values: - OldVal: ir.Value = Gen._direct_values.pop(VarName) - var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) - Gen._store(OldVal, var) - Gen.variables[VarName] = var - if Gen.builder.block.is_terminated: - return - if VarName in Gen.variables and Gen.variables[VarName] is not None: - OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName) - store_ptr: ir.Value = Gen.variables[VarName] - if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType): - if Value.type.width != 64: - Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset") - NewVal: ir.Value - if Op == '-' or Op == '+': - if Op == '-': - NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset") - NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub") - else: - NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add") - else: - ptr_val: ir.Value = OldVal - OldVal = Gen._load(OldVal, name="deref_ptr_for_op") - orig_type: ir.Type = OldVal.type - if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): - if OldVal.type.width < Value.type.width: - OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left") - elif OldVal.type.width > Value.type.width: - Value = Gen.builder.zext(Value, OldVal.type, name="zext_right") - NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) - if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType): - if NewVal.type.width > orig_type.width: - NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result") - store_ptr = ptr_val - else: - if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): - if OldVal.type.width < Value.type.width: - OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left") - elif OldVal.type.width > Value.type.width: - Value = Gen.builder.zext(Value, OldVal.type, name="zext_right") - elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType): - if Op in ('+', '-'): - if Value.type.width < 64: - Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug") - NewVal: ir.Value - if Op == '-': - NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug") - NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug") - else: - NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug") - Gen._store(NewVal, store_ptr) - return - NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) - if isinstance(store_ptr.type, ir.PointerType): - TargetType: ir.Type = store_ptr.type.pointee - if NewVal.type != TargetType: - Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType) - if Coerced is not None: - NewVal = Coerced - Gen._store(NewVal, store_ptr) - elif VarName == 'pos': - var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) - Gen.variables[VarName] = var - OldVal: ir.Value = Gen._load(var, name=VarName) - NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) - Gen._store(NewVal, var) - elif isinstance(Node.target, ast.Attribute): - AttrName: str = Node.target.attr - OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target) - if OldVal: - def _deref_if_ptr(val: ir.Value) -> ir.Value: - if isinstance(val.type, ir.PointerType): - pointee: ir.Type = val.type.pointee - if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)): - return Gen._load(val, name="deref_aug") - return val - OldVal = _deref_if_ptr(OldVal) - Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) - if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): - if OldVal.type.width < Value.type.width: - OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr") - elif OldVal.type.width > Value.type.width: - Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr") - NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) - self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal) - elif isinstance(Node.target, ast.Subscript): - SubTarget: ast.Subscript = Node.target - ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget) - if ElemPtr: - if isinstance(ElemPtr.type, ir.PointerType): - OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val") - Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) - if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): - if OldVal.type.width < Value.type.width: - OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub") - elif OldVal.type.width > Value.type.width: - Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub") - elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType): - if Op in ('+', '-'): - if Value.type.width < 64: - Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub") - NewVal: ir.Value - if Op == '-': - NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub") - NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub") - else: - NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub") - Gen._store(NewVal, ElemPtr) - return - NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) - if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType): - if NewVal.type.width > ElemPtr.type.pointee.width: - NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result") - Gen._store(NewVal, ElemPtr) - elif isinstance(Node.target, ast.UnaryOp): - # 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref) - # (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。 - # 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。 - if not isinstance(Node.target.op, ast.Deref): - BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op) - raise Exception( - f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)" - f"{Gen._get_node_info()}" - ) - DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand) - if DerefPtr is None: - raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}") - if not isinstance(DerefPtr.type, ir.PointerType): - raise Exception( - f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})" - f"{Gen._get_node_info()}" - ) - DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) - DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val") - if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType): - if DerefOld.type.width < Value.type.width: - DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref") - elif DerefOld.type.width > Value.type.width: - Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref") - elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType): - if DerefOp in ('+', '-'): - if Value.type.width < 64: - Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref") - OffVal: ir.Value - if DerefOp == '-': - OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref") - else: - OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref") - Gen._store(OffVal, DerefPtr) - return - DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True) - if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType): - if DerefNew.type.width > DerefPtr.type.pointee.width: - DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result") - Gen._store(DerefNew, DerefPtr) - else: - # fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 TransPyV - # translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、 - # 变量静默不变"。 - TargetOp: str | None = None - if isinstance(Node.target, ast.UnaryOp): - TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op) - raise Exception( - f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用" - f"(当前 {type(Node.target).__name__}" - + (f" op='{TargetOp}'" if TargetOp else "") - + f"){Gen._get_node_info()}" +from __future__ import annotations +from typing import TYPE_CHECKING +import vast as ast +import sys +import llvmlite.ir as ir +if TYPE_CHECKING: + from lib.core.translator import Translator +from lib.core.VLogger import get_logger as _vlog +from lib.core.Handles.HandlesBase import BaseHandle + + +class AugAssignHandle(BaseHandle): + _AUGOP_MAP: dict[type, str] = { + ast.Add: '__iadd__', ast.Sub: '__isub__', ast.Mult: '__imul__', + ast.Div: '__itruediv__', ast.FloorDiv: '__ifloordiv__', ast.Mod: '__imod__', ast.Pow: '__ipow__', + ast.BitAnd: '__iand__', ast.BitOr: '__ior__', ast.BitXor: '__ixor__', + ast.LShift: '__ilshift__', ast.RShift: '__irshift__', + } + _AUGOP_FALLBACK: dict[type, str] = { + ast.Add: '__add__', ast.Sub: '__sub__', ast.Mult: '__mul__', + ast.Div: '__truediv__', ast.FloorDiv: '__floordiv__', ast.Mod: '__mod__', ast.Pow: '__pow__', + ast.BitAnd: '__and__', ast.BitOr: '__or__', ast.BitXor: '__xor__', + ast.LShift: '__lshift__', ast.RShift: '__rshift__', + } + + def _resolve_global_var_type(self, Gen: "Translator.LlvmGen", VarName: str, fallback: ir.Type) -> ir.Type: + """解析 global 变量的 LLVM 类型:优先用模块级 AnnAssign 的注解类型。 + + 问题场景:函数内 `_g_alloc_count += 1`(global 声明),模块级注解为 + `_g_alloc_count: t.CSizeT`(i64)。若不查模块级注解类型,此处会用 RHS 字面量 + 类型(i32)自动建 i32 全局,与模块内其他读取/写入(i64)类型不匹配, + 产生 "Operands must be the same type, got (i64, i32)" 类错误。 + 模块级 AnnAssign 已通过 _EmitGlobalAnnAssignLlvm 创建 LLVM 全局, + 但注解无初始值(如 `_g_alloc_max: t.CSizeT = 0`)时 initializer 为空、 + Gen.module.globals 中无该符号,故需在此兜底解析注解类型。 + """ + module_ann: dict = getattr(Gen, '_module_ann_types', None) + if module_ann and VarName in module_ann: + ann_type: ir.Type = module_ann[VarName] + if ann_type is not None: + return ann_type + if isinstance(fallback, ir.IntType): + return fallback + return ir.IntType(64) + + def _is_aug_unsigned(self, Node: ast.AugAssign, OldVal: ir.Value | None, Value: ir.Value) -> bool: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if isinstance(Node.target, ast.Name): + if Gen._check_node_unsigned(Node.target): + return True + elif isinstance(Node.target, ast.Attribute): + if Gen._check_node_unsigned(Node.target): + return True + elif isinstance(Node.target, ast.Subscript): + if Gen._check_node_unsigned(Node.target): + return True + if Node.value is not None and Gen._check_node_unsigned(Node.value): + return True + return False + + def _HandleAugAssignLlvm(self, Node: ast.AugAssign) -> None: + Gen: "Translator.LlvmGen" = self.Trans.LlvmGen + if isinstance(Node.target, ast.Name): + VarName: str = Node.target.id + if (VarName not in Gen.variables + and VarName not in Gen.global_vars + and VarName not in Gen._reg_values + and VarName not in Gen._direct_values): + src_info: str = Gen._get_node_info() + _vlog().error(f"变量 '{VarName}' 未声明,不能用于增量赋值(需用 global 声明或先声明局部变量){src_info}") + sys.exit(1) + if Gen._IsVarConst(VarName): + _vlog().error(Gen._ConstAssignMessage("增量赋值", VarName)) + sys.exit(1) + ClassName: str | None = Gen.var_struct_class.get(VarName) + iop_name: str | None = self._AUGOP_MAP.get(type(Node.op)) + if ClassName and iop_name: + iFuncName: str = f'{ClassName}.{iop_name}' + FuncName: str = f'{ClassName}.{self._AUGOP_FALLBACK.get(type(Node.op), "")}' + if iFuncName in Gen.functions or FuncName in Gen.functions: + OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(ast.Name(id=VarName, ctx=ast.Load())) + Value: ir.Value | None = self.HandleExprLlvm(Node.value) + if OldVal and Value: + result: ir.Value | None + if iFuncName in Gen.functions: + result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, iop_name, OldVal, Gen, Value) + else: + result = self.Trans.ExprHandler._try_operator_overLoad(ClassName, self._AUGOP_FALLBACK[type(Node.op)], OldVal, Gen, Value) + if result is not None: + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: ir.Value = Gen.variables[VarName] + if VarPtr.type.pointee == result.type: + Gen._store(result, VarPtr) + elif isinstance(VarPtr.type.pointee, ir.PointerType) and isinstance(result.type, ir.PointerType): + CastedVal: ir.Value = Gen.builder.bitcast(result, VarPtr.type.pointee, name=f"cast_{VarName}") + Gen._store(CastedVal, VarPtr) + else: + NewVar: ir.AllocaInstr = Gen._allocaEntry(result.type, name=VarName) + Gen._store(result, NewVar) + Gen.variables[VarName] = NewVar + else: + Gen._reg_values[VarName] = result + Gen.variables[VarName] = None + return + Value: ir.Value | None = self.HandleExprLlvm(Node.value) + if not Value: + return + is_unsigned: bool = self._is_aug_unsigned(Node, None, Value) + if isinstance(Node.target, ast.Name): + VarName: str = Node.target.id + Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) + if VarName in Gen.global_vars: + # 治本修复:global 声明的变量若未被模块级 AnnAssign 创建为 LLVM 全局 + # (注解无初始值的全局变量),此处自动创建零初始化全局变量 + GVar: ir.GlobalVariable + if VarName not in Gen.module.globals: + _gv_type: ir.Type = self._resolve_global_var_type(Gen, VarName, Value.type) + GVar = ir.GlobalVariable(Gen.module, _gv_type, name=VarName) + GVar.initializer = ir.Constant(_gv_type, 0) + else: + GVar = Gen.module.globals[VarName] + Gen.variables[VarName] = GVar + OldVal: ir.Value = Gen._load(GVar, name=VarName) + NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) + Gen._store(NewVal, GVar) + return + if VarName in Gen._reg_values: + OldVal: ir.Value = Gen._reg_values[VarName] + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + del Gen._reg_values[VarName] + if VarName in Gen._direct_values: + OldVal: ir.Value = Gen._direct_values.pop(VarName) + var: ir.AllocaInstr = Gen._allocaEntry(OldVal.type, name=VarName) + Gen._store(OldVal, var) + Gen.variables[VarName] = var + if Gen.builder.block.is_terminated: + return + if VarName in Gen.variables and Gen.variables[VarName] is not None: + OldVal: ir.Value = Gen._load(Gen.variables[VarName], name=VarName) + store_ptr: ir.Value = Gen.variables[VarName] + if isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType): + if Value.type.width != 64: + Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset") + NewVal: ir.Value + if Op == '-' or Op == '+': + if Op == '-': + NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset") + NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub") + else: + NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add") + else: + ptr_val: ir.Value = OldVal + OldVal = Gen._load(OldVal, name="deref_ptr_for_op") + orig_type: ir.Type = OldVal.type + if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): + if OldVal.type.width < Value.type.width: + OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left") + elif OldVal.type.width > Value.type.width: + Value = Gen.builder.zext(Value, OldVal.type, name="zext_right") + NewVal = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) + if isinstance(NewVal.type, ir.IntType) and isinstance(orig_type, ir.IntType): + if NewVal.type.width > orig_type.width: + NewVal = Gen.builder.trunc(NewVal, orig_type, name="trunc_result") + store_ptr = ptr_val + else: + if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): + if OldVal.type.width < Value.type.width: + OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left") + elif OldVal.type.width > Value.type.width: + Value = Gen.builder.zext(Value, OldVal.type, name="zext_right") + elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType): + if Op in ('+', '-'): + if Value.type.width < 64: + Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_aug") + NewVal: ir.Value + if Op == '-': + NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_aug") + NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_aug") + else: + NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_aug") + Gen._store(NewVal, store_ptr) + return + NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) + if isinstance(store_ptr.type, ir.PointerType): + TargetType: ir.Type = store_ptr.type.pointee + if NewVal.type != TargetType: + Coerced: ir.Value | None = Gen._coerce_value(NewVal, TargetType) + if Coerced is not None: + NewVal = Coerced + Gen._store(NewVal, store_ptr) + elif VarName == 'pos': + var: ir.AllocaInstr = Gen._allocaEntry(Value.type, name=VarName) + Gen.variables[VarName] = var + OldVal: ir.Value = Gen._load(var, name=VarName) + NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) + Gen._store(NewVal, var) + elif isinstance(Node.target, ast.Attribute): + AttrName: str = Node.target.attr + OldVal: ir.Value | None = self.Trans.ExprHandler.HandleExprLlvm(Node.target) + if OldVal: + def _deref_if_ptr(val: ir.Value) -> ir.Value: + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, (ir.IntType, ir.FloatType, ir.DoubleType)): + return Gen._load(val, name="deref_aug") + return val + OldVal = _deref_if_ptr(OldVal) + Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) + if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): + if OldVal.type.width < Value.type.width: + OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_attr") + elif OldVal.type.width > Value.type.width: + Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_attr") + NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) + self.Trans.AssignHandler._HandleAttributeStoreLlvm(Node.target, NewVal) + elif isinstance(Node.target, ast.Subscript): + SubTarget: ast.Subscript = Node.target + ElemPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(SubTarget) + if ElemPtr: + if isinstance(ElemPtr.type, ir.PointerType): + OldVal: ir.Value = Gen._load(ElemPtr, name="old_subscript_val") + Op: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) + if isinstance(OldVal.type, ir.IntType) and isinstance(Value.type, ir.IntType): + if OldVal.type.width < Value.type.width: + OldVal = Gen.builder.zext(OldVal, Value.type, name="zext_left_sub") + elif OldVal.type.width > Value.type.width: + Value = Gen.builder.zext(Value, OldVal.type, name="zext_right_sub") + elif isinstance(OldVal.type, ir.PointerType) and isinstance(Value.type, ir.IntType): + if Op in ('+', '-'): + if Value.type.width < 64: + Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_sub") + NewVal: ir.Value + if Op == '-': + NegValue: ir.Value = Gen.builder.neg(Value, name="neg_ptr_offset_sub") + NewVal = Gen.builder.gep(OldVal, [NegValue], name="ptr_sub_sub") + else: + NewVal = Gen.builder.gep(OldVal, [Value], name="ptr_add_sub") + Gen._store(NewVal, ElemPtr) + return + NewVal: ir.Value = Gen.emit_binary_op(Op, OldVal, Value, is_unsigned=is_unsigned, is_aug=True) + if isinstance(NewVal.type, ir.IntType) and isinstance(ElemPtr.type.pointee, ir.IntType): + if NewVal.type.width > ElemPtr.type.pointee.width: + NewVal = Gen.builder.trunc(NewVal, ElemPtr.type.pointee, name="trunc_sub_result") + Gen._store(NewVal, ElemPtr) + elif isinstance(Node.target, ast.UnaryOp): + # 解引用增强赋值 `*p += v`:目标节点是 UnaryOp(Deref) + # (SetStoreContext 不改 UnaryOp 的 ctx),与 `*p = v` 同一形态。 + # 流程:操作数求值取指针 → load 当前值 → 二元运算 → store 回同址。 + if not isinstance(Node.target.op, ast.Deref): + BadOp: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op) + raise Exception( + f"AugAssign 目标只支持单目 '*' 解引用('{BadOp}' 不是合法左值)" + f"{Gen._get_node_info()}" + ) + DerefPtr: ir.Value | None = self.HandleExprLlvm(Node.target.operand) + if DerefPtr is None: + raise Exception(f"解引用增量赋值 '*' 的操作数无法求值{Gen._get_node_info()}") + if not isinstance(DerefPtr.type, ir.PointerType): + raise Exception( + f"解引用增量赋值 '*' 的操作数必须是指针类型(实际 {DerefPtr.type})" + f"{Gen._get_node_info()}" + ) + DerefOp: str = self.Trans.ExprHandler.GetOpSymbol(Node.op) + DerefOld: ir.Value = Gen._load(DerefPtr, name="old_deref_val") + if isinstance(DerefOld.type, ir.IntType) and isinstance(Value.type, ir.IntType): + if DerefOld.type.width < Value.type.width: + DerefOld = Gen.builder.zext(DerefOld, Value.type, name="zext_left_deref") + elif DerefOld.type.width > Value.type.width: + Value = Gen.builder.zext(Value, DerefOld.type, name="zext_right_deref") + elif isinstance(DerefOld.type, ir.PointerType) and isinstance(Value.type, ir.IntType): + if DerefOp in ('+', '-'): + if Value.type.width < 64: + Value = Gen.builder.zext(Value, ir.IntType(64), name="zext_ptr_offset_deref") + OffVal: ir.Value + if DerefOp == '-': + OffVal = Gen.builder.gep(DerefOld, [Gen.builder.neg(Value, name="neg_ptr_offset_deref")], name="ptr_sub_deref") + else: + OffVal = Gen.builder.gep(DerefOld, [Value], name="ptr_add_deref") + Gen._store(OffVal, DerefPtr) + return + DerefNew: ir.Value = Gen.emit_binary_op(DerefOp, DerefOld, Value, is_unsigned=is_unsigned, is_aug=True) + if isinstance(DerefNew.type, ir.IntType) and isinstance(DerefPtr.type.pointee, ir.IntType): + if DerefNew.type.width > DerefPtr.type.pointee.width: + DerefNew = Gen.builder.trunc(DerefNew, DerefPtr.type.pointee, name="trunc_deref_result") + Gen._store(DerefNew, DerefPtr) + else: + # fail-fast:目标形态未覆盖时绝不静默丢弃(对齐 ViperC + # translate_augassign 的同名 fail-fast),避免"语句被整条丢掉、 + # 变量静默不变"。 + TargetOp: str | None = None + if isinstance(Node.target, ast.UnaryOp): + TargetOp = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.target.op) + raise Exception( + f"AugAssign 目标类型不支持,仅支持 Name/Attribute/Subscript/*解引用" + f"(当前 {type(Node.target).__name__}" + + (f" op='{TargetOp}'" if TargetOp else "") + + f"){Gen._get_node_info()}" ) \ No newline at end of file diff --git a/lib/core/Handles/HandlesBase.py b/lib/core/Handles/HandlesBase.py index b219f78..cb7f754 100644 --- a/lib/core/Handles/HandlesBase.py +++ b/lib/core/Handles/HandlesBase.py @@ -1,594 +1,594 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, List, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator - -import vast as ast -from enum import IntFlag, auto - -from llvmlite import ir as _ir - -from lib.constants import config as _config -from lib.includes import t -from lib.includes.t import CTypeRegistry -from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES +from __future__ import annotations +from typing import TYPE_CHECKING, List, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + +import vast as ast +from enum import IntFlag, auto + +from llvmlite import ir as _ir + +from lib.constants import config as _config +from lib.includes import t +from lib.includes.t import CTypeRegistry +from lib.core.TypeSpec import TypeSpec, SymbolMeta, FIELD_ROUTES from lib.core.SymbolUtils import ( IsPtrSubscriptNode, IsListAnnotation, IsCPtrMarkerNode, FlattenUnionOperands, IsI8DoublePtrType, IsClassRefPtrAnnotation, - FixedArrayKeywordAnnotationError, FindAutoAddrMode) -from lib.core.VLogger import get_logger as _vlog - - -EXCEPTION_CODE_MAP: dict[str, int] = { - 'ValueError': 1, 'TypeError': 2, 'RuntimeError': 3, - 'ZeroDivisionError': 4, 'IndexError': 5, 'KeyError': 6, - 'IOError': 7, 'OSError': 8, 'AssertionError': 9, 'Exception': 99, -} - - -class FuncMeta(IntFlag): - NONE = 0 - STATIC_METHOD = auto() - PROPERTY_GETTER = auto() - PROPERTY_SETTER = auto() - PROPERTY_DELETER = auto() - CLASS_METHOD = auto() - ABSTRACT = auto() - - -class _Delegated: - """CTypeInfo 旧字段名委托描述符 — 静态委托到 _ts/_sm 的字段 - - 替代旧的 FIELD_ROUTES + __setattr__/__getattr__ 动态路由: - 描述符在类创建时绑定,访问时直接走 __get__/__set__, - 无 dict 查找和字符串比较的运行时开销。 - """ - __slots__ = ('_target_attr', '_field_name') - - def __init__(self, target_attr: str, field_name: str) -> None: - self._target_attr: str = target_attr # '_ts' or '_sm' - self._field_name: str = field_name - - def __get__(self, obj: Any, objtype: type | None = None) -> Any: - if obj is None: - return self - target: Any = object.__getattribute__(obj, self._target_attr) - return getattr(target, self._field_name) - - def __set__(self, obj: Any, value: Any) -> None: - target: Any = object.__getattribute__(obj, self._target_attr) - setattr(target, self._field_name, value) - - -class CTypeInfo: - """ - C 类型信息对象 - 统一符号表条目和类型计算 - - 设计原则: - - BaseType 是 CType 实例(来自 t 模块),永远不使用硬编码字符串 - - 描述符分为两种: - - VarConst/VarVolatile: 变量本身(指针)的限定符,如 int * const - - DataConst/DataVolatile: 指向数据的限定符,如 const int * - - 属性(类型计算): - - BaseType: t.CType - 基础类型实例 (如 t.CInt(), t.CVoid()) - - PtrCount: int - 指针层数 - - VarConst: bool - 变量本身(指针)是否 const,如 int * const - - VarVolatile: bool - 变量本身(指针)是否 volatile - - DataConst: bool - 指向的数据是否 const,如 const int * - - DataVolatile: bool - 指向的数据是否 volatile - - ArrayDims: List[str] - 数组维度 - - Storage: t.CType - 存储类 (static, extern 等) - - IsFuncPtr: bool - 是否函数指针 - - FuncPtrParams: List[Tuple[str, CTypeInfo]] - 函数指针参数列表,每个元素是 (参数名, 参数类型) - - FuncPtrReturn: CTypeInfo - 函数指针返回类型 - - IsBitField: bool - 是否位域 - - BitWidth: int - 位域宽度 - - IsTypedef/IsStruct/IsEnum/IsUnion: bool - 类型种类 - - 属性(符号表元数据): - - Name: str - 名称(struct/enum/union/typedef 名) - - Members: Dict[str, CTypeInfo] - 成员字典(用于 struct/union) - - OriginalType: str - typedef 的原始类型 - - DeclaredType: str - 声明类型 - - CreturnTypes: list - C 返回类型列表 - - Lineno: int - 定义行号 - - IsAnonymous: bool - 是否匿名类型 - - IsCpythonObject: bool - 是否 CPython 对象 - - IsEnumMember: bool - 是否枚举成员 - """ - - def __init__(self) -> None: - object.__setattr__(self, '_ts', TypeSpec()) - sm: SymbolMeta = SymbolMeta() - sm.meta_list = FuncMeta.NONE - object.__setattr__(self, '_sm', sm) - - # --- 新公共 API:供新代码直接访问 TypeSpec / SymbolMeta / SymbolKind --- - @property - def ts(self) -> TypeSpec: - """TypeSpec — 纯类型描述(只读)""" - return self._ts - - @property - def sm(self) -> SymbolMeta: - """SymbolMeta — 符号表元数据(只读)""" - return self._sm - - @property - def kind(self) -> 'SymbolKind': - """SymbolKind — 符号类型种类枚举""" - return self._sm.kind - - # 注:旧字段名(PtrCount/IsStruct/Name 等)的委托通过 _Delegated 描述符实现, - # 在模块末尾从 FIELD_ROUTES 静态绑定,无需 __setattr__/__getattr__ 动态路由 - - - - @property - def BaseType(self) -> t.CType | tuple | None: - return self._ts._base_type - - @BaseType.setter - def BaseType(self, value: t.CType | type | tuple | List | None) -> None: - """设置 BaseType,接受以下类型: - - - None: 清空 - - t.CType 实例: 单个类型,自动设置 IsStruct/IsEnum/IsUnion 标志 - - type (CType 子类): 如 t.CStruct,自动实例化 - - tuple/list of t.CType: 多个组合类型 - """ - ts: TypeSpec = self._ts - sm: SymbolMeta = self._sm - - if value is None: - ts._base_type = None - sm.is_struct = False - sm.is_enum = False - sm.is_union = False - sm.is_renum = False - return - - if isinstance(value, (tuple, list)): - ts._base_type = tuple(value) - for v in value: - if isinstance(v, t.CStruct): - sm.is_struct = True - elif isinstance(v, t.CEnum): - sm.is_enum = True - elif isinstance(v, t.CUnion): - sm.is_union = True - elif isinstance(v, t.REnum): - sm.is_renum = True - sm.is_enum = True - return - - if isinstance(value, type) and issubclass(value, t.CType): - ts._base_type = value() - return - - if isinstance(value, t.CType): - ts._base_type = value - if isinstance(value, t.CStruct): - sm.is_struct = True - sm.is_enum = False - sm.is_union = False - sm.is_renum = False - elif isinstance(value, t.REnum): - sm.is_renum = True - sm.is_enum = True - sm.is_struct = False - sm.is_union = False - elif isinstance(value, t.CEnum): - sm.is_enum = True - sm.is_struct = False - sm.is_union = False - sm.is_renum = False - elif isinstance(value, t.CUnion): - sm.is_union = True - sm.is_struct = False - sm.is_enum = False - sm.is_renum = False - return - - raise TypeError(f"BaseType must be t.CType instance, type subclass, or tuple of t.CType, got {type(value)}") - - @property - def TypeCls(self) -> type | None: - """获取 CType 类(从 BaseType 推断)""" - bt: t.CType | tuple | None = self._ts._base_type - if bt: - return type(bt) - sm: SymbolMeta = self._sm - if sm.is_struct: - return t.CStruct - if sm.is_enum: - return t.CEnum - if sm.is_union: - return t.CUnion - if sm.is_typedef: - return t._CTypedef - return None - - @property - def IsPtr(self) -> bool: - return self._ts.ptr_count > 0 - - @property - def IsVoid(self) -> bool: - return isinstance(self._ts._base_type, t.CVoid) - - @property - def IsStr(self) -> bool: - return isinstance(self._ts._base_type, t.CChar) and self._ts.ptr_count > 0 - - @property - def IsInt(self) -> bool: - bt: t.CType | tuple | None = self._ts._base_type - return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is not None and bt.IsSigned is True and not isinstance(bt, t.CVoid) - - @property - def IsUInt(self) -> bool: - bt: t.CType | tuple | None = self._ts._base_type - return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is False - - @property - def IsFloat(self) -> bool: - bt: t.CType | tuple | None = self._ts._base_type - return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is None and getattr(bt, 'Size', 0) in (32, 64, 128) and not isinstance(bt, t.CVoid) - - def ToString(self) -> str: - ts: TypeSpec = self._ts - sm: SymbolMeta = self._sm - bt: t.CType | tuple | None = ts._base_type - - if ts.is_func_ptr: - return 'Callable' - parts: list[str] = [] - - if ts.storage and not isinstance(ts.storage, t.CExport): - parts.append(type(ts.storage).__name__) - - if ts.data_const or ts.data_volatile: - qualifiers: list[str] = [] - if ts.data_const: - qualifiers.append("const") - if ts.data_volatile: - qualifiers.append("volatile") - parts.append("_".join(qualifiers)) - - if sm.is_typedef and sm.name: - return sm.name - elif sm.is_renum and sm.name: - base_name: str = sm.name - elif bt: - if isinstance(bt, str): - base_name = bt - elif isinstance(bt, type) and issubclass(bt, t.CType): - base_name = bt.__name__ - elif isinstance(bt, t.CType): - # 对命名的 CType(CStruct/CEnum/CUnion/REnum),优先使用其 name 属性 - # 否则回退到类名(如 'CInt'、'CChar') - _named: str | None = getattr(bt, 'name', None) - base_name = _named if _named else type(bt).__name__ - else: - base_name = str(bt) - else: - base_name = "void" - - if ts.is_array_ptr: - ptr_str: str = "" - if ts.ptr_count > 0: - ptr_str = "*" * ts.ptr_count - if ts.array_ptr: - inner: str = ts.array_ptr.ToString() - if ptr_str: - parts.append(f"{base_name} {ptr_str}({inner})") - else: - parts.append(f"{base_name} ({inner})") - else: - if ptr_str: - parts.append(f"{base_name} {ptr_str}()") - else: - parts.append(f"{base_name} ()") - elif ts.array_ptr: - inner = ts.array_ptr.ToString() - if ts.ptr_count > 0: - parts.append(f"{base_name} {'*' * ts.ptr_count}({inner})") - else: - parts.append(f"{base_name} ({inner})") - else: - parts.append(base_name) - if ts.ptr_count > 0: - parts.append("*" * ts.ptr_count) - if ts.var_const: - parts.append("const") - if ts.var_volatile: - parts.append("volatile") - - for dim in ts.array_dims: - if dim: - parts.append(f"[{dim}]") - else: - parts.append("[]") - - return " ".join(parts) if parts else "void" - - @staticmethod - def CreateFromTypeName(TypeName: str) -> "CTypeInfo": - """从类型名字符串创建 CTypeInfo(兼容旧 API)""" - info: CTypeInfo = CTypeInfo() - if TypeName.startswith('struct '): - info.BaseType = t.CStruct(name=TypeName[7:].strip()) if TypeName[7:].strip() else t.CStruct() - elif TypeName.startswith('enum '): - info.BaseType = t.CEnum(name=TypeName[5:].strip()) if TypeName[5:].strip() else t.CEnum() - elif TypeName.startswith('union '): - info.BaseType = t.CUnion(name=TypeName[6:].strip()) if TypeName[6:].strip() else t.CUnion() - else: - info.BaseType = t.CStruct(name=TypeName) - return info - - # LLVM primitive type name → (CTypeClass, PtrCount) mapping - # Used when generic type inference produces LLVM type names like 'i64', 'double', etc. - _LLVM_PRIMITIVE_MAP: dict[str, tuple[type, int]] | None = None - - @classmethod - def _get_llvm_primitive_map(cls) -> dict[str, tuple[type, int]]: - if cls._LLVM_PRIMITIVE_MAP is not None: - return cls._LLVM_PRIMITIVE_MAP - cls._LLVM_PRIMITIVE_MAP = { - 'void': (t.CVoid, 0), - 'i1': (t.CInt, 0), - 'i8': (t.CChar, 0), - 'i16': (t.CShort, 0), - 'i32': (t.CInt, 0), - 'i64': (t.CLong, 0), - 'i128': (t.CInt, 0), - 'float': (t.CFloat, 0), - 'double': (t.CDouble, 0), - 'fp128': (t.CFloat128T, 0), - } - return cls._LLVM_PRIMITIVE_MAP - - @staticmethod - def FromTypeName(TypeName: str) -> "CTypeInfo": - """从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)""" - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver - return TypeAnnotationResolver.from_type_name(TypeName) - - # FromStr 已移除 - 类型解析不再经过中间字符串格式 - # typedef 展开直接走 CTypeInfo 对象,不经过字符串解析 - - @staticmethod - def TryEvalConstExpr(node: ast.AST, SymbolTable: Any) -> Any: - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver - return TypeAnnotationResolver.try_eval_const_expr(node, SymbolTable) - - @classmethod - def FromNode(cls, Node: ast.AST, SymbolTable: Any) -> "CTypeInfo": - """从 AST 节点解析 CTypeInfo(类方法) - - Args: - Node: AST 节点(如 ast.Name, ast.Attribute 等) - SymbolTable: 符号表字典 - """ - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver - return TypeAnnotationResolver.from_node(Node, SymbolTable) - - - def Copy(self) -> "CTypeInfo": - NewInfo: CTypeInfo = CTypeInfo() - NewInfo._ts = self._ts.copy() - NewInfo._sm = self._sm.copy() - # CTypeInfo 引用字段需要深拷贝 - if isinstance(NewInfo._ts.func_ptr_return, CTypeInfo): - NewInfo._ts.func_ptr_return = NewInfo._ts.func_ptr_return.Copy() - if isinstance(NewInfo._ts.original_type, CTypeInfo): - NewInfo._ts.original_type = NewInfo._ts.original_type.Copy() - if NewInfo._ts.array_ptr: - NewInfo._ts.array_ptr = NewInfo._ts.array_ptr.Copy() - return NewInfo - - def get(self, key: str, default: Any = None) -> Any: - """获取额外属性""" - return self._sm._extra.get(key, default) - - def set(self, key: str, value: Any) -> None: - """设置额外属性""" - self._sm._extra[key] = value - - @staticmethod - def VoidTypeInfo() -> "CTypeInfo": - info: CTypeInfo = CTypeInfo() - info.BaseType = t.CVoid() - return info - - def ToLLVM(self, Gen: LlvmCodeGenerator) -> _ir.Type: - if Gen is None: - return _ir.IntType(32) - return Gen._ctype_to_llvm(self) - - def __str__(self) -> str: - return self.ToString() - - def __repr__(self) -> str: - ts: TypeSpec = self._ts - sm: SymbolMeta = self._sm - return f"CTypeInfo(BaseType={ts._base_type}, PtrCount={ts.ptr_count}, IsTypedef={sm.is_typedef}, OriginalType={ts.original_type!r}, VarConst={ts.var_const}, DataConst={ts.data_const})" - - -# 从 FIELD_ROUTES 静态绑定 _Delegated 描述符到 CTypeInfo 类 -# 这是一次性配置(类创建期执行),替代运行时 __setattr__/__getattr__ 动态路由 -for _old_name, (_target, _field) in FIELD_ROUTES.items(): - setattr(CTypeInfo, _old_name, _Delegated('_' + _target, _field)) -del _old_name, _target, _field - - -class CTypeHelper: - - @staticmethod - def GetTModuleCType(TypeName: str) -> type | None: - result: type | None = CTypeRegistry.GetClassByName(TypeName) - if result is not None: - return result - TypeClass: type | None = getattr(t, TypeName, None) - if TypeClass and isinstance(TypeClass, type) and issubclass(TypeClass, t.CType): - return TypeClass - FallbackClass: type | None = getattr(t, f'_{TypeName}', None) - if FallbackClass and isinstance(FallbackClass, type) and issubclass(FallbackClass, t.CType): - return FallbackClass - return None - - @staticmethod - def GetCName(type_or_name: str | type) -> str: - if isinstance(type_or_name, str): - cls: type | None = CTypeRegistry.GetClassByName(type_or_name) - if cls is None: - cls = CTypeHelper.GetTModuleCType(type_or_name) - if cls: - return cls.__name__ - return type_or_name - elif isinstance(type_or_name, type) and issubclass(type_or_name, t.CType): - return type_or_name.__name__ - return '' - - -class BuiltinTypeMap: - """ - 内置类型映射表 - 字符串到 (CType类, PtrCount) 的映射 - - 用法:BuiltinTypeMap.Get('int') -> (t.CInt, 0) - BuiltinTypeMap.Get('BYTEPTR') -> (t.CUnsignedChar, 1) - """ - - _map: dict[str, tuple[type, int]] | None = None - - @classmethod - def _build_map(cls) -> dict[str, tuple[type, int]]: - if cls._map is not None: - return cls._map - CTypeRegistry._build() - cls._map = {} - # 仅使用 Python 类名 → (CType 类, ptr_level) 映射。 - # C 类型名(如 'int'、'int32_t')不再通过 _cname_to_class 提供。 - # C 头文件 stub 生成器使用独立的硬编码表(CTypeMapper._CNAME_TO_PY)。 - for pyname, ctype_cls in CTypeRegistry._name_to_class.items(): - pos: frozenset = getattr(ctype_cls, 'position', frozenset()) - if t.CType.POINTER in pos and t.CType.BASE not in pos: - cls._map[pyname] = (ctype_cls, 1) - else: - cls._map[pyname] = (ctype_cls, 0) - # 保留一些常用的字符串速记(这些不是 CName,而是硬编码的便捷别名)。 - # 包含 Python 内置类型名(int/float/bool)和 C 类型名速记(char/short/long 等)。 - _SPECIAL: dict[str, tuple[type, int]] = { - # Python 内置类型名 - 'str': (t.CChar, 1), 'bytes': (t.CChar, 1), - 'int': (t.CInt, 0), 'float': (t.CFloat, 0), 'bool': (t.CBool, 0), - # C 类型名速记 - 'char': (t.CChar, 0), 'short': (t.CShort, 0), - 'long': (t.CLong, 0), 'long long': (t.CLongLong, 0), - 'double': (t.CDouble, 0), - 'unsigned': (t.CUnsigned, 0), - 'signed': (t.CInt, 0), 'signed int': (t.CInt, 0), - 'signed char': (t.CSignedChar, 0), - 'void': (t.CVoid, 0), 'Void': (t.CVoid, 0), - } - for k, v in _SPECIAL.items(): - if k not in cls._map: - cls._map[k] = v - _FLOAT_ALIASES: dict[str, tuple[type, int]] = { - 'FLOAT8': (t.CFloat8T, 0), 'FLOAT16': (t.CFloat16T, 0), - 'FLOAT32': (t.CFloat32T, 0), 'FLOAT64': (t.CFloat64T, 0), - 'FLOAT128': (t.CFloat128T, 0), - } - for k, v in _FLOAT_ALIASES.items(): - if k not in cls._map: - cls._map[k] = v - return cls._map - - @classmethod - def Get(cls, type_name: str) -> tuple[type, int] | None: - """获取类型类和指针层级""" - return cls._build_map().get(type_name) - - -class BaseHandle: - def __init__(self, translator: "Translator") -> None: - self.Trans: Translator = translator - self._CurrentCpythonObjectClass: str | None = None - - @staticmethod - def _is_char_pointer(val: _ir.Value) -> bool: - """检查值是否为 char* (i8*) 指针""" - if not isinstance(val.type, _ir.PointerType): - return False - pointee: _ir.Type = val.type.pointee - return isinstance(pointee, _ir.IntType) and pointee.width == 8 - - def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None, - ConstWidthHint: int | None = None) -> _ir.Value | None: - return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint) - - def _CoerceCondToBool(self, Gen: "LlvmCodeGenerator", Node: Any, Cond: Any) -> Any: - """把条件表达式归一到 i1(条件真值)。 - - 与 TPV 对齐的调度顺序: - 1. 已是 i1 → 原样返回; - 2. 条件是对象(类实例)且类内定义了 `__bool__` → 调用 `__bool__`,结果 `!= 0`; - 3. 浮点 → `!= 0.0`; - 4. 指针 → `!= null`; - 5. 整数 → `!= 0`。 - - 语义唯一来源:`if` / `while` / `and` / `or` / 三元条件全部汇聚于此, - 避免各处各自演化出「struct 恒真」这类不一致判据。 - """ - if isinstance(Cond.type, _ir.IntType) and Cond.type.width == 1: - return Cond - try: - ClassName: str | None = self.Trans.ExprUtils._get_var_class(Node, Gen) - except Exception: - ClassName = None - if ClassName and Gen._has_function(f'{ClassName}.__bool__'): - Res: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, '__bool__', Cond, Gen) - if Res is not None: - if isinstance(Res.type, _ir.IntType): - if Res.type.width == 1: - return Res - return Gen.builder.icmp_signed('!=', Res, Gen._ZeroConst(Res.type), name="bool_result") - if isinstance(Res.type, (_ir.FloatType, _ir.DoubleType)): - return Gen.builder.fcmp_ordered('!=', Res, _ir.Constant(Res.type, 0.0), name="bool_result") - if isinstance(Cond.type, (_ir.FloatType, _ir.DoubleType)): - return Gen.builder.fcmp_ordered('!=', Cond, _ir.Constant(Cond.type, 0.0), name="cond_bool") - if isinstance(Cond.type, _ir.PointerType): - return Gen.builder.icmp_signed('!=', Cond, _ir.Constant(Cond.type, None), name="cond_bool") - return Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="cond_bool") - - @staticmethod - def _ExprShape(Node: ast.AST, Depth: int = 0) -> str: - """表达式节点的**形态摘要**(类名 + 关键子节点),供 fail-fast 报文取证。 - - 与 `_ExprText` 的分工:`_ExprText` 给"源码文本"(属性链只到 Name 为止,遇 - Call/Subscript 基座会退化成裸 attr 名,如 `(ast.Expr*(node)).value` → `'value'`), - 本方法给"AST 形态",用于判定强转调用是否真被 parser 产成 `Call(func=BinOp(BitOr))`。 - """ - if Depth >= 4: - return '…' - if isinstance(Node, ast.Attribute): - return f"Attribute(.{Node.attr}←{BaseHandle._ExprShape(Node.value, Depth + 1)})" - if isinstance(Node, ast.Call): - return (f"Call(func={BaseHandle._ExprShape(Node.func, Depth + 1)}" - f",args={len(Node.args)})") - if isinstance(Node, ast.BinOp): - return (f"BinOp({type(Node.op).__name__}, " - f"{BaseHandle._ExprShape(Node.left, Depth + 1)}, " - f"{BaseHandle._ExprShape(Node.right, Depth + 1)})") - if isinstance(Node, ast.Name): - return f"Name({Node.id})" - if isinstance(Node, ast.Subscript): - return f"Subscript({BaseHandle._ExprShape(Node.value, Depth + 1)})" - return type(Node).__name__ - + FixedArrayKeywordAnnotationError, FindAutoAddrMode) +from lib.core.VLogger import get_logger as _vlog + + +EXCEPTION_CODE_MAP: dict[str, int] = { + 'ValueError': 1, 'TypeError': 2, 'RuntimeError': 3, + 'ZeroDivisionError': 4, 'IndexError': 5, 'KeyError': 6, + 'IOError': 7, 'OSError': 8, 'AssertionError': 9, 'Exception': 99, +} + + +class FuncMeta(IntFlag): + NONE = 0 + STATIC_METHOD = auto() + PROPERTY_GETTER = auto() + PROPERTY_SETTER = auto() + PROPERTY_DELETER = auto() + CLASS_METHOD = auto() + ABSTRACT = auto() + + +class _Delegated: + """CTypeInfo 旧字段名委托描述符 — 静态委托到 _ts/_sm 的字段 + + 替代旧的 FIELD_ROUTES + __setattr__/__getattr__ 动态路由: + 描述符在类创建时绑定,访问时直接走 __get__/__set__, + 无 dict 查找和字符串比较的运行时开销。 + """ + __slots__ = ('_target_attr', '_field_name') + + def __init__(self, target_attr: str, field_name: str) -> None: + self._target_attr: str = target_attr # '_ts' or '_sm' + self._field_name: str = field_name + + def __get__(self, obj: Any, objtype: type | None = None) -> Any: + if obj is None: + return self + target: Any = object.__getattribute__(obj, self._target_attr) + return getattr(target, self._field_name) + + def __set__(self, obj: Any, value: Any) -> None: + target: Any = object.__getattribute__(obj, self._target_attr) + setattr(target, self._field_name, value) + + +class CTypeInfo: + """ + C 类型信息对象 - 统一符号表条目和类型计算 + + 设计原则: + - BaseType 是 CType 实例(来自 t 模块),永远不使用硬编码字符串 + - 描述符分为两种: + - VarConst/VarVolatile: 变量本身(指针)的限定符,如 int * const + - DataConst/DataVolatile: 指向数据的限定符,如 const int * + + 属性(类型计算): + - BaseType: t.CType - 基础类型实例 (如 t.CInt(), t.CVoid()) + - PtrCount: int - 指针层数 + - VarConst: bool - 变量本身(指针)是否 const,如 int * const + - VarVolatile: bool - 变量本身(指针)是否 volatile + - DataConst: bool - 指向的数据是否 const,如 const int * + - DataVolatile: bool - 指向的数据是否 volatile + - ArrayDims: List[str] - 数组维度 + - Storage: t.CType - 存储类 (static, extern 等) + - IsFuncPtr: bool - 是否函数指针 + - FuncPtrParams: List[Tuple[str, CTypeInfo]] - 函数指针参数列表,每个元素是 (参数名, 参数类型) + - FuncPtrReturn: CTypeInfo - 函数指针返回类型 + - IsBitField: bool - 是否位域 + - BitWidth: int - 位域宽度 + - IsTypedef/IsStruct/IsEnum/IsUnion: bool - 类型种类 + + 属性(符号表元数据): + - Name: str - 名称(struct/enum/union/typedef 名) + - Members: Dict[str, CTypeInfo] - 成员字典(用于 struct/union) + - OriginalType: str - typedef 的原始类型 + - DeclaredType: str - 声明类型 + - CreturnTypes: list - C 返回类型列表 + - Lineno: int - 定义行号 + - IsAnonymous: bool - 是否匿名类型 + - IsCpythonObject: bool - 是否 CPython 对象 + - IsEnumMember: bool - 是否枚举成员 + """ + + def __init__(self) -> None: + object.__setattr__(self, '_ts', TypeSpec()) + sm: SymbolMeta = SymbolMeta() + sm.meta_list = FuncMeta.NONE + object.__setattr__(self, '_sm', sm) + + # --- 新公共 API:供新代码直接访问 TypeSpec / SymbolMeta / SymbolKind --- + @property + def ts(self) -> TypeSpec: + """TypeSpec — 纯类型描述(只读)""" + return self._ts + + @property + def sm(self) -> SymbolMeta: + """SymbolMeta — 符号表元数据(只读)""" + return self._sm + + @property + def kind(self) -> 'SymbolKind': + """SymbolKind — 符号类型种类枚举""" + return self._sm.kind + + # 注:旧字段名(PtrCount/IsStruct/Name 等)的委托通过 _Delegated 描述符实现, + # 在模块末尾从 FIELD_ROUTES 静态绑定,无需 __setattr__/__getattr__ 动态路由 + + + + @property + def BaseType(self) -> t.CType | tuple | None: + return self._ts._base_type + + @BaseType.setter + def BaseType(self, value: t.CType | type | tuple | List | None) -> None: + """设置 BaseType,接受以下类型: + + - None: 清空 + - t.CType 实例: 单个类型,自动设置 IsStruct/IsEnum/IsUnion 标志 + - type (CType 子类): 如 t.CStruct,自动实例化 + - tuple/list of t.CType: 多个组合类型 + """ + ts: TypeSpec = self._ts + sm: SymbolMeta = self._sm + + if value is None: + ts._base_type = None + sm.is_struct = False + sm.is_enum = False + sm.is_union = False + sm.is_renum = False + return + + if isinstance(value, (tuple, list)): + ts._base_type = tuple(value) + for v in value: + if isinstance(v, t.CStruct): + sm.is_struct = True + elif isinstance(v, t.CEnum): + sm.is_enum = True + elif isinstance(v, t.CUnion): + sm.is_union = True + elif isinstance(v, t.REnum): + sm.is_renum = True + sm.is_enum = True + return + + if isinstance(value, type) and issubclass(value, t.CType): + ts._base_type = value() + return + + if isinstance(value, t.CType): + ts._base_type = value + if isinstance(value, t.CStruct): + sm.is_struct = True + sm.is_enum = False + sm.is_union = False + sm.is_renum = False + elif isinstance(value, t.REnum): + sm.is_renum = True + sm.is_enum = True + sm.is_struct = False + sm.is_union = False + elif isinstance(value, t.CEnum): + sm.is_enum = True + sm.is_struct = False + sm.is_union = False + sm.is_renum = False + elif isinstance(value, t.CUnion): + sm.is_union = True + sm.is_struct = False + sm.is_enum = False + sm.is_renum = False + return + + raise TypeError(f"BaseType must be t.CType instance, type subclass, or tuple of t.CType, got {type(value)}") + + @property + def TypeCls(self) -> type | None: + """获取 CType 类(从 BaseType 推断)""" + bt: t.CType | tuple | None = self._ts._base_type + if bt: + return type(bt) + sm: SymbolMeta = self._sm + if sm.is_struct: + return t.CStruct + if sm.is_enum: + return t.CEnum + if sm.is_union: + return t.CUnion + if sm.is_typedef: + return t._CTypedef + return None + + @property + def IsPtr(self) -> bool: + return self._ts.ptr_count > 0 + + @property + def IsVoid(self) -> bool: + return isinstance(self._ts._base_type, t.CVoid) + + @property + def IsStr(self) -> bool: + return isinstance(self._ts._base_type, t.CChar) and self._ts.ptr_count > 0 + + @property + def IsInt(self) -> bool: + bt: t.CType | tuple | None = self._ts._base_type + return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is not None and bt.IsSigned is True and not isinstance(bt, t.CVoid) + + @property + def IsUInt(self) -> bool: + bt: t.CType | tuple | None = self._ts._base_type + return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is False + + @property + def IsFloat(self) -> bool: + bt: t.CType | tuple | None = self._ts._base_type + return isinstance(bt, t.CType) and getattr(bt, 'IsSigned', None) is None and getattr(bt, 'Size', 0) in (32, 64, 128) and not isinstance(bt, t.CVoid) + + def ToString(self) -> str: + ts: TypeSpec = self._ts + sm: SymbolMeta = self._sm + bt: t.CType | tuple | None = ts._base_type + + if ts.is_func_ptr: + return 'Callable' + parts: list[str] = [] + + if ts.storage and not isinstance(ts.storage, t.CExport): + parts.append(type(ts.storage).__name__) + + if ts.data_const or ts.data_volatile: + qualifiers: list[str] = [] + if ts.data_const: + qualifiers.append("const") + if ts.data_volatile: + qualifiers.append("volatile") + parts.append("_".join(qualifiers)) + + if sm.is_typedef and sm.name: + return sm.name + elif sm.is_renum and sm.name: + base_name: str = sm.name + elif bt: + if isinstance(bt, str): + base_name = bt + elif isinstance(bt, type) and issubclass(bt, t.CType): + base_name = bt.__name__ + elif isinstance(bt, t.CType): + # 对命名的 CType(CStruct/CEnum/CUnion/REnum),优先使用其 name 属性 + # 否则回退到类名(如 'CInt'、'CChar') + _named: str | None = getattr(bt, 'name', None) + base_name = _named if _named else type(bt).__name__ + else: + base_name = str(bt) + else: + base_name = "void" + + if ts.is_array_ptr: + ptr_str: str = "" + if ts.ptr_count > 0: + ptr_str = "*" * ts.ptr_count + if ts.array_ptr: + inner: str = ts.array_ptr.ToString() + if ptr_str: + parts.append(f"{base_name} {ptr_str}({inner})") + else: + parts.append(f"{base_name} ({inner})") + else: + if ptr_str: + parts.append(f"{base_name} {ptr_str}()") + else: + parts.append(f"{base_name} ()") + elif ts.array_ptr: + inner = ts.array_ptr.ToString() + if ts.ptr_count > 0: + parts.append(f"{base_name} {'*' * ts.ptr_count}({inner})") + else: + parts.append(f"{base_name} ({inner})") + else: + parts.append(base_name) + if ts.ptr_count > 0: + parts.append("*" * ts.ptr_count) + if ts.var_const: + parts.append("const") + if ts.var_volatile: + parts.append("volatile") + + for dim in ts.array_dims: + if dim: + parts.append(f"[{dim}]") + else: + parts.append("[]") + + return " ".join(parts) if parts else "void" + + @staticmethod + def CreateFromTypeName(TypeName: str) -> "CTypeInfo": + """从类型名字符串创建 CTypeInfo(兼容旧 API)""" + info: CTypeInfo = CTypeInfo() + if TypeName.startswith('struct '): + info.BaseType = t.CStruct(name=TypeName[7:].strip()) if TypeName[7:].strip() else t.CStruct() + elif TypeName.startswith('enum '): + info.BaseType = t.CEnum(name=TypeName[5:].strip()) if TypeName[5:].strip() else t.CEnum() + elif TypeName.startswith('union '): + info.BaseType = t.CUnion(name=TypeName[6:].strip()) if TypeName[6:].strip() else t.CUnion() + else: + info.BaseType = t.CStruct(name=TypeName) + return info + + # LLVM primitive type name → (CTypeClass, PtrCount) mapping + # Used when generic type inference produces LLVM type names like 'i64', 'double', etc. + _LLVM_PRIMITIVE_MAP: dict[str, tuple[type, int]] | None = None + + @classmethod + def _get_llvm_primitive_map(cls) -> dict[str, tuple[type, int]]: + if cls._LLVM_PRIMITIVE_MAP is not None: + return cls._LLVM_PRIMITIVE_MAP + cls._LLVM_PRIMITIVE_MAP = { + 'void': (t.CVoid, 0), + 'i1': (t.CInt, 0), + 'i8': (t.CChar, 0), + 'i16': (t.CShort, 0), + 'i32': (t.CInt, 0), + 'i64': (t.CLong, 0), + 'i128': (t.CInt, 0), + 'float': (t.CFloat, 0), + 'double': (t.CDouble, 0), + 'fp128': (t.CFloat128T, 0), + } + return cls._LLVM_PRIMITIVE_MAP + + @staticmethod + def FromTypeName(TypeName: str) -> "CTypeInfo": + """从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)""" + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver + return TypeAnnotationResolver.from_type_name(TypeName) + + # FromStr 已移除 - 类型解析不再经过中间字符串格式 + # typedef 展开直接走 CTypeInfo 对象,不经过字符串解析 + + @staticmethod + def TryEvalConstExpr(node: ast.AST, SymbolTable: Any) -> Any: + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver + return TypeAnnotationResolver.try_eval_const_expr(node, SymbolTable) + + @classmethod + def FromNode(cls, Node: ast.AST, SymbolTable: Any) -> "CTypeInfo": + """从 AST 节点解析 CTypeInfo(类方法) + + Args: + Node: AST 节点(如 ast.Name, ast.Attribute 等) + SymbolTable: 符号表字典 + """ + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver + return TypeAnnotationResolver.from_node(Node, SymbolTable) + + + def Copy(self) -> "CTypeInfo": + NewInfo: CTypeInfo = CTypeInfo() + NewInfo._ts = self._ts.copy() + NewInfo._sm = self._sm.copy() + # CTypeInfo 引用字段需要深拷贝 + if isinstance(NewInfo._ts.func_ptr_return, CTypeInfo): + NewInfo._ts.func_ptr_return = NewInfo._ts.func_ptr_return.Copy() + if isinstance(NewInfo._ts.original_type, CTypeInfo): + NewInfo._ts.original_type = NewInfo._ts.original_type.Copy() + if NewInfo._ts.array_ptr: + NewInfo._ts.array_ptr = NewInfo._ts.array_ptr.Copy() + return NewInfo + + def get(self, key: str, default: Any = None) -> Any: + """获取额外属性""" + return self._sm._extra.get(key, default) + + def set(self, key: str, value: Any) -> None: + """设置额外属性""" + self._sm._extra[key] = value + + @staticmethod + def VoidTypeInfo() -> "CTypeInfo": + info: CTypeInfo = CTypeInfo() + info.BaseType = t.CVoid() + return info + + def ToLLVM(self, Gen: LlvmCodeGenerator) -> _ir.Type: + if Gen is None: + return _ir.IntType(32) + return Gen._ctype_to_llvm(self) + + def __str__(self) -> str: + return self.ToString() + + def __repr__(self) -> str: + ts: TypeSpec = self._ts + sm: SymbolMeta = self._sm + return f"CTypeInfo(BaseType={ts._base_type}, PtrCount={ts.ptr_count}, IsTypedef={sm.is_typedef}, OriginalType={ts.original_type!r}, VarConst={ts.var_const}, DataConst={ts.data_const})" + + +# 从 FIELD_ROUTES 静态绑定 _Delegated 描述符到 CTypeInfo 类 +# 这是一次性配置(类创建期执行),替代运行时 __setattr__/__getattr__ 动态路由 +for _old_name, (_target, _field) in FIELD_ROUTES.items(): + setattr(CTypeInfo, _old_name, _Delegated('_' + _target, _field)) +del _old_name, _target, _field + + +class CTypeHelper: + + @staticmethod + def GetTModuleCType(TypeName: str) -> type | None: + result: type | None = CTypeRegistry.GetClassByName(TypeName) + if result is not None: + return result + TypeClass: type | None = getattr(t, TypeName, None) + if TypeClass and isinstance(TypeClass, type) and issubclass(TypeClass, t.CType): + return TypeClass + FallbackClass: type | None = getattr(t, f'_{TypeName}', None) + if FallbackClass and isinstance(FallbackClass, type) and issubclass(FallbackClass, t.CType): + return FallbackClass + return None + + @staticmethod + def GetCName(type_or_name: str | type) -> str: + if isinstance(type_or_name, str): + cls: type | None = CTypeRegistry.GetClassByName(type_or_name) + if cls is None: + cls = CTypeHelper.GetTModuleCType(type_or_name) + if cls: + return cls.__name__ + return type_or_name + elif isinstance(type_or_name, type) and issubclass(type_or_name, t.CType): + return type_or_name.__name__ + return '' + + +class BuiltinTypeMap: + """ + 内置类型映射表 - 字符串到 (CType类, PtrCount) 的映射 + + 用法:BuiltinTypeMap.Get('int') -> (t.CInt, 0) + BuiltinTypeMap.Get('BYTEPTR') -> (t.CUnsignedChar, 1) + """ + + _map: dict[str, tuple[type, int]] | None = None + + @classmethod + def _build_map(cls) -> dict[str, tuple[type, int]]: + if cls._map is not None: + return cls._map + CTypeRegistry._build() + cls._map = {} + # 仅使用 Python 类名 → (CType 类, ptr_level) 映射。 + # C 类型名(如 'int'、'int32_t')不再通过 _cname_to_class 提供。 + # C 头文件 stub 生成器使用独立的硬编码表(CTypeMapper._CNAME_TO_PY)。 + for pyname, ctype_cls in CTypeRegistry._name_to_class.items(): + pos: frozenset = getattr(ctype_cls, 'position', frozenset()) + if t.CType.POINTER in pos and t.CType.BASE not in pos: + cls._map[pyname] = (ctype_cls, 1) + else: + cls._map[pyname] = (ctype_cls, 0) + # 保留一些常用的字符串速记(这些不是 CName,而是硬编码的便捷别名)。 + # 包含 Python 内置类型名(int/float/bool)和 C 类型名速记(char/short/long 等)。 + _SPECIAL: dict[str, tuple[type, int]] = { + # Python 内置类型名 + 'str': (t.CChar, 1), 'bytes': (t.CChar, 1), + 'int': (t.CInt, 0), 'float': (t.CFloat, 0), 'bool': (t.CBool, 0), + # C 类型名速记 + 'char': (t.CChar, 0), 'short': (t.CShort, 0), + 'long': (t.CLong, 0), 'long long': (t.CLongLong, 0), + 'double': (t.CDouble, 0), + 'unsigned': (t.CUnsigned, 0), + 'signed': (t.CInt, 0), 'signed int': (t.CInt, 0), + 'signed char': (t.CSignedChar, 0), + 'void': (t.CVoid, 0), 'Void': (t.CVoid, 0), + } + for k, v in _SPECIAL.items(): + if k not in cls._map: + cls._map[k] = v + _FLOAT_ALIASES: dict[str, tuple[type, int]] = { + 'FLOAT8': (t.CFloat8T, 0), 'FLOAT16': (t.CFloat16T, 0), + 'FLOAT32': (t.CFloat32T, 0), 'FLOAT64': (t.CFloat64T, 0), + 'FLOAT128': (t.CFloat128T, 0), + } + for k, v in _FLOAT_ALIASES.items(): + if k not in cls._map: + cls._map[k] = v + return cls._map + + @classmethod + def Get(cls, type_name: str) -> tuple[type, int] | None: + """获取类型类和指针层级""" + return cls._build_map().get(type_name) + + +class BaseHandle: + def __init__(self, translator: "Translator") -> None: + self.Trans: Translator = translator + self._CurrentCpythonObjectClass: str | None = None + + @staticmethod + def _is_char_pointer(val: _ir.Value) -> bool: + """检查值是否为 char* (i8*) 指针""" + if not isinstance(val.type, _ir.PointerType): + return False + pointee: _ir.Type = val.type.pointee + return isinstance(pointee, _ir.IntType) and pointee.width == 8 + + def HandleExprLlvm(self, Node: ast.AST, VarType: _ir.Type | str | None = None, + ConstWidthHint: int | None = None) -> _ir.Value | None: + return self.Trans.ExprHandler.HandleExprLlvm(Node, VarType, ConstWidthHint) + + def _CoerceCondToBool(self, Gen: "LlvmCodeGenerator", Node: Any, Cond: Any) -> Any: + """把条件表达式归一到 i1(条件真值)。 + + 与 VPC 对齐的调度顺序: + 1. 已是 i1 → 原样返回; + 2. 条件是对象(类实例)且类内定义了 `__bool__` → 调用 `__bool__`,结果 `!= 0`; + 3. 浮点 → `!= 0.0`; + 4. 指针 → `!= null`; + 5. 整数 → `!= 0`。 + + 语义唯一来源:`if` / `while` / `and` / `or` / 三元条件全部汇聚于此, + 避免各处各自演化出「struct 恒真」这类不一致判据。 + """ + if isinstance(Cond.type, _ir.IntType) and Cond.type.width == 1: + return Cond + try: + ClassName: str | None = self.Trans.ExprUtils._get_var_class(Node, Gen) + except Exception: + ClassName = None + if ClassName and Gen._has_function(f'{ClassName}.__bool__'): + Res: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, '__bool__', Cond, Gen) + if Res is not None: + if isinstance(Res.type, _ir.IntType): + if Res.type.width == 1: + return Res + return Gen.builder.icmp_signed('!=', Res, Gen._ZeroConst(Res.type), name="bool_result") + if isinstance(Res.type, (_ir.FloatType, _ir.DoubleType)): + return Gen.builder.fcmp_ordered('!=', Res, _ir.Constant(Res.type, 0.0), name="bool_result") + if isinstance(Cond.type, (_ir.FloatType, _ir.DoubleType)): + return Gen.builder.fcmp_ordered('!=', Cond, _ir.Constant(Cond.type, 0.0), name="cond_bool") + if isinstance(Cond.type, _ir.PointerType): + return Gen.builder.icmp_signed('!=', Cond, _ir.Constant(Cond.type, None), name="cond_bool") + return Gen.builder.icmp_signed('!=', Cond, Gen._ZeroConst(Cond.type), name="cond_bool") + + @staticmethod + def _ExprShape(Node: ast.AST, Depth: int = 0) -> str: + """表达式节点的**形态摘要**(类名 + 关键子节点),供 fail-fast 报文取证。 + + 与 `_ExprText` 的分工:`_ExprText` 给"源码文本"(属性链只到 Name 为止,遇 + Call/Subscript 基座会退化成裸 attr 名,如 `(ast.Expr*(node)).value` → `'value'`), + 本方法给"AST 形态",用于判定强转调用是否真被 parser 产成 `Call(func=BinOp(BitOr))`。 + """ + if Depth >= 4: + return '…' + if isinstance(Node, ast.Attribute): + return f"Attribute(.{Node.attr}←{BaseHandle._ExprShape(Node.value, Depth + 1)})" + if isinstance(Node, ast.Call): + return (f"Call(func={BaseHandle._ExprShape(Node.func, Depth + 1)}" + f",args={len(Node.args)})") + if isinstance(Node, ast.BinOp): + return (f"BinOp({type(Node.op).__name__}, " + f"{BaseHandle._ExprShape(Node.left, Depth + 1)}, " + f"{BaseHandle._ExprShape(Node.right, Depth + 1)})") + if isinstance(Node, ast.Name): + return f"Name({Node.id})" + if isinstance(Node, ast.Subscript): + return f"Subscript({BaseHandle._ExprShape(Node.value, Depth + 1)})" + return type(Node).__name__ + def _NoteAttrNone(self, Gen: Any, ClassName: str | None, AttrName: str, Reason: str) -> None: """记录「属性读取返回 None」的**确切原因**(供 fail-fast 报文取证,纯记账)。 @@ -604,592 +604,592 @@ class BaseHandle: except Exception: # noqa: BLE001 pass - def _DescribeAttrBase(self, Gen: Any, Node: ast.Attribute) -> str: - """属性读返回 None 时的**基址事实**(供 fail-fast 报文取证,不改变任何判定)。 - - 两类根因:① 类名登记退化(var_struct_class 存 'rawptr' / 'rawptr[X]' / 折叠态, - 或压根没登记);② 登记名与 Gen.structs / class_members 的键不同源(短名 vs - sha1 全名)。另有一条**必须验伪**的可能:基座是强转调用 `(T*(x)).attr` - (parser 统一产成 Call(func=BinOp(BitOr)))时,HandlesExprAttr 的 - `_handle_attr_call_result` 自带 `_FailFastAttrCallBase` 详细 fail-fast - (FailFastError 全仓无 except,必上抛)——若仍走到本诊断,即说明那条 - fail-fast 未被走到,属**另一处缺口**,故显式标注该事实。 - """ - _base: ast.AST = Node.value - _parts: list[str] = [f"基座形态={BaseHandle._ExprShape(_base)}"] - _vc_head: Any = None - if isinstance(_base, ast.Name): - _bn: str = _base.id - _vc: str | None = None - _alloc: Any = None - try: - _vc = (getattr(Gen, 'var_struct_class', None) or {}).get(_bn) - if not _vc: - _vc = (getattr(Gen, 'global_struct_class', None) or {}).get(_bn) - _alloc = (getattr(Gen, 'variables', None) or {}).get(_bn) - except Exception: # noqa: BLE001 - pass + def _DescribeAttrBase(self, Gen: Any, Node: ast.Attribute) -> str: + """属性读返回 None 时的**基址事实**(供 fail-fast 报文取证,不改变任何判定)。 + + 两类根因:① 类名登记退化(var_struct_class 存 'rawptr' / 'rawptr[X]' / 折叠态, + 或压根没登记);② 登记名与 Gen.structs / class_members 的键不同源(短名 vs + sha1 全名)。另有一条**必须验伪**的可能:基座是强转调用 `(T*(x)).attr` + (parser 统一产成 Call(func=BinOp(BitOr)))时,HandlesExprAttr 的 + `_handle_attr_call_result` 自带 `_FailFastAttrCallBase` 详细 fail-fast + (FailFastError 全仓无 except,必上抛)——若仍走到本诊断,即说明那条 + fail-fast 未被走到,属**另一处缺口**,故显式标注该事实。 + """ + _base: ast.AST = Node.value + _parts: list[str] = [f"基座形态={BaseHandle._ExprShape(_base)}"] + _vc_head: Any = None + if isinstance(_base, ast.Name): + _bn: str = _base.id + _vc: str | None = None + _alloc: Any = None + try: + _vc = (getattr(Gen, 'var_struct_class', None) or {}).get(_bn) + if not _vc: + _vc = (getattr(Gen, 'global_struct_class', None) or {}).get(_bn) + _alloc = (getattr(Gen, 'variables', None) or {}).get(_bn) + except Exception: # noqa: BLE001 + pass _parts.append(f"'{_bn}' 的 var_struct_class={_vc!r}") - _vc_head = _vc - _parts.append(f"alloca 类型={getattr(_alloc, 'type', None)}") - _parts.append("该名在 Gen.structs=" - + ('是' if _vc and _vc in Gen.structs else ('否' if _vc else '—'))) - _parts.append("class_members=" - + (f"{len(Gen.class_members.get(_vc) or [])} 个字段" - if _vc and _vc in Gen.class_members else '无该键')) - if _vc and _vc in Gen.class_members: - _parts.append(f"字段名单={[m[0] for m in Gen.class_members[_vc]][:12]}") - # struct body 元素数 与 该字段的**计算偏移**:字段名在成员表里能找到、 - # 却没读到值,只可能是"偏移 ≥ body 元素数"(`_gep_and_Load_member` 的 - # 越界 return None)。这两个数字是判定该情况的唯一依据,必须一并报出。 - _st_attr: Any = Gen.structs.get(_vc) if _vc else None - if _st_attr is not None: - _nelem: Any = (len(_st_attr.elements) - if getattr(_st_attr, 'elements', None) is not None else 'opaque') - _parts.append(f"struct body 元素数={_nelem}") - if _vc: - try: - _off: Any = Gen._get_member_offset(Node.attr, _vc) - except Exception as _e: # noqa: BLE001 - _off = f"<取偏移抛 {type(_e).__name__}>" - _parts.append(f"字段 '{Node.attr}' 计算偏移={_off}") - elif (isinstance(_base, ast.Call) and isinstance(_base.func, ast.BinOp) - and isinstance(_base.func.op, ast.BitOr)): - _cls: str | None = None - try: - _cls = self.Trans.ExprAttrHandle._extract_union_base_class(_base.func) - except Exception: # noqa: BLE001 - _cls = None + _vc_head = _vc + _parts.append(f"alloca 类型={getattr(_alloc, 'type', None)}") + _parts.append("该名在 Gen.structs=" + + ('是' if _vc and _vc in Gen.structs else ('否' if _vc else '—'))) + _parts.append("class_members=" + + (f"{len(Gen.class_members.get(_vc) or [])} 个字段" + if _vc and _vc in Gen.class_members else '无该键')) + if _vc and _vc in Gen.class_members: + _parts.append(f"字段名单={[m[0] for m in Gen.class_members[_vc]][:12]}") + # struct body 元素数 与 该字段的**计算偏移**:字段名在成员表里能找到、 + # 却没读到值,只可能是"偏移 ≥ body 元素数"(`_gep_and_Load_member` 的 + # 越界 return None)。这两个数字是判定该情况的唯一依据,必须一并报出。 + _st_attr: Any = Gen.structs.get(_vc) if _vc else None + if _st_attr is not None: + _nelem: Any = (len(_st_attr.elements) + if getattr(_st_attr, 'elements', None) is not None else 'opaque') + _parts.append(f"struct body 元素数={_nelem}") + if _vc: + try: + _off: Any = Gen._get_member_offset(Node.attr, _vc) + except Exception as _e: # noqa: BLE001 + _off = f"<取偏移抛 {type(_e).__name__}>" + _parts.append(f"字段 '{Node.attr}' 计算偏移={_off}") + elif (isinstance(_base, ast.Call) and isinstance(_base.func, ast.BinOp) + and isinstance(_base.func.op, ast.BitOr)): + _cls: str | None = None + try: + _cls = self.Trans.ExprAttrHandle._extract_union_base_class(_base.func) + except Exception: # noqa: BLE001 + _cls = None _parts.append(f"联合基类名={_cls!r}") - _vc_head = _cls - _parts.append(f"短名 in structs={'是' if _cls and _cls in Gen.structs else '否'}") - _parts.append("class_members=" - + (f"{len(Gen.class_members.get(_cls) or [])} 个字段" - if _cls in Gen.class_members else '无该键')) - _parts.append("注: 强转基座形态本应走 _FailFastAttrCallBase 详细报文," - "此处仍是本条 ⇒ 那条 fail-fast 未触发") - # 回显读取侧的**确切失败原因**(`ExprAttrHandle` 各 return None 点登记)。 - # 仅在 (类名, 字段名) 与被报节点一致时输出:嵌套读取留下的旧记录不得冒充本节点。 - try: - _rec: Any = getattr(Gen, '_LastAttrNone', None) - except Exception: # noqa: BLE001 - _rec = None - if isinstance(_rec, tuple) and len(_rec) >= 3: - _rec_cls, _rec_attr, _rec_reason = _rec[0], _rec[1], _rec[2] - _short = lambda s: (s.split('.')[-1] if s else s) # noqa: E731 - _same: bool = bool(_rec_attr) and _rec_attr == Node.attr and ( - not _rec_cls or not _vc_head or _rec_cls == _vc_head - or _short(_rec_cls) == _short(_vc_head)) - if _same: - _parts.append(f"读取侧原因={_rec_reason}") - return ";".join(_parts) - - @staticmethod - def _ExprText(Node: ast.AST) -> str: - """把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用""" - if isinstance(Node, ast.Attribute): - parts: list[str] = [] - cur: ast.AST = Node - while isinstance(cur, ast.Attribute): - parts.append(cur.attr) - cur = cur.value - if isinstance(cur, ast.Name): - parts.append(cur.id) - if parts: - return '.'.join(reversed(parts)) - if isinstance(Node, ast.Name): - return Node.id - try: - rendered: str = ast.unparse(Node) - if rendered: - return rendered - except Exception: - pass - return getattr(Node, 'attr', '') or '' - - def _FailFastFixedArrayKeyword(self, Node: ast.AST, Msg: str) -> None: - """内建定长数组构造子 array 误用的 fail-fast 出口。 - - 报错含:文件:行:列 + 源码片段 + 期望形态(array[E, N])+ 正确示例 - (消息文本由 SymbolUtils.FixedArrayKeywordAnnotationError 给出)。 - """ - import sys - Gen: Any = getattr(self.Trans, 'LlvmGen', None) - raw_lineno: int = getattr(Node, 'lineno', 0) or 0 - lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 - col: int = getattr(Node, 'col_offset', -1) - src_file: str = getattr(Gen, '_current_source_file', '') or '' - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - code_line: str = '' - if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'): - try: - code_line = (Gen._get_source_line(lineno) or '').strip() - except Exception: - code_line = '' - loc: str = src_file - if lineno > 0: - loc += f":{lineno}" - if col >= 0: - loc += f":{col}" - full: str = f"{Msg}。位置 {loc}" + (f",源码: {code_line}" if code_line else '') - _vlog().compile_error(full, src_file, lineno, code_line) - sys.exit(1) - - def _CheckFixedArrayKeyword(self, Annotation: ast.AST) -> None: - """注解入口统一校验内建定长数组构造子 array(误用即 fail-fast)。 - - 只校验带位置的注解入口(本类的 ResolveAnnotationType / CollectAnnAssignMember - + HandlesAnnAssign 局部 / HandlesAssign 全局显式调用),**不**在通用表达式 - 解析路径上调用——以保证 array 在非类型位置仍是普通标识符(软关键字)。 - """ - if Annotation is None: - return - Msg: str | None = FixedArrayKeywordAnnotationError(Annotation, self.Trans.SymbolTable) - if Msg: - self._FailFastFixedArrayKeyword(Annotation, Msg) - - def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any, AnnNode: ast.AST | None = None, - VarName: str | None = None) -> None: - """fail-fast:带值表达式在编译期求值失败(返回 None 且无值可存)时终止。 - - 属性链(含两级自动解引用链,如 blk = func.Blocks.Head)解析不出目标 - 类型时 _handle_attr_nested 会返回 None;类型下标被当乘法的畸形写法 - (如 list[X*]*(...))会让整条 RHS 不产任何指令而求值 None。赋值调用方 - 若静默跳过 store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机 - 崩溃)。此处用 VLogger 输出 目标变量 + 注解文本 + 表达式源码与形态 + - 文件名:行:列 后退出(严格模式语义)。 - """ - import sys - ann_text: str = '' - if AnnNode is not None: - try: - ann_text = ast.unparse(AnnNode) - except Exception: - ann_text = '' - # RHS 形态 = AST 节点类名(Name/Subscript/BinOp/Call/Attribute/…), - # 便于报错可直接按形态分类,不必再从表达式文本反推。 - rhs_kind: str = type(Node).__name__ - raw_lineno: int = getattr(Node, 'lineno', 0) or 0 - lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 - col: int = getattr(Node, 'col_offset', -1) - src_file: str = getattr(Gen, '_current_source_file', '') or '' - expr_text: str = self._ExprText(Node) - # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, - # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator)。 - _line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - code_line: str = '' - if lineno > 0 and src_file: - # 行号基准修正(vast AST 行号 vs 磁盘原文件行号偏差): - # Phase2Translator._inject_auto_imports 会在解析前向源码头部注入 - # 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件 - # 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、 - # FuncGenMixin.vp 真实 514 报 515)。上面已按 _line_offset 精确还原; - # 此处再以表达式文本为锚(从原始行号向上至多 8 行)回溯校正 - # 属性链(其 expr_text 逐字出现在磁盘行中)可能残留的偏差。 - if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'): - _src_lines: Any = getattr(Gen, '_current_source_lines', None) - if isinstance(_src_lines, list) and _src_lines: - for cand in range(lineno, max(0, lineno - 8), -1): - if 0 < cand <= len(_src_lines) and expr_text in _src_lines[cand - 1]: - lineno = cand - break - try: - code_line = (Gen._get_source_line(lineno) or '').strip() - except Exception: - code_line = '' - loc: str = src_file - if lineno > 0: - loc += f":{lineno}" - if col >= 0: - loc += f":{col}" - msg: str = (f"{What}表达式 '{expr_text}'({rhs_kind} 形态)编译期求值为空(" - f"表达式未解析出目标、无值可存;且其求值未以返回 void 的调用收尾)," - f"赋值将被跳过、变量保持未初始化" - f"{(',目标变量 ' + repr(VarName)) if VarName else ''}" - f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}" - f"{(',源码: ' + code_line) if code_line else ''}") - # 【基址诊断】属性读返回 None 时报出**基座真实形态 + 类名登记事实**,不再从 - # "求值为空"反推(同一份事实也供 _RequireArgValue 的实参求值失败路径使用, - # 见 HandlesExprCall._RequireArgValue)。 - if isinstance(Node, ast.Attribute): - msg += "\n│ 基址诊断: " + self._DescribeAttrBase(Gen, Node) - elif isinstance(Node, ast.Call) and isinstance(Node.func, ast.Attribute): - # 被调者形如 `接收者链.方法(...)`:整条 Call 求值为空的**唯一来源**是接收者链 - # 先求值为空(HandlesExprCall._HandleMethodCallLlvm 里 - # `ObjVal = HandleExprLlvm(Node.func.value)` 为假即 `return None`; - # 方法声明缺失会在同函数内 raise "Undefined method …",不会走到这里)。 - # 行号只能指到整条调用,故把**接收者**的基址事实与最近一次属性读取失败原因 - # 一并回显;Call 形态下无法用 (类, 字段) 与节点比对,该记录无条件回显。 - try: - msg += "\n│ 被调者接收者诊断: " + self._DescribeAttrBase(Gen, Node.func.value) - except Exception as _e_d: # noqa: BLE001 - msg += f"\n│ 被调者接收者诊断生成失败: {type(_e_d).__name__}: {_e_d}" - try: - _recC: Any = getattr(Gen, '_LastAttrNone', None) - if isinstance(_recC, tuple) and len(_recC) >= 3 and _recC[2]: - msg += (f"\n│ 最近一次属性读取失败: 类={_recC[0]!r} 字段={_recC[1]!r}" - f" 原因={_recC[2]}") - except Exception: # noqa: BLE001 - pass - _mnC: str = getattr(Node.func, 'attr', '') or '' - _declC: list[str] = [] - try: - for _fk in (getattr(Gen, 'functions', {}) or {}): - if _mnC and _fk.endswith(f'.{_mnC}'): - _declC.append(_fk) - except Exception: # noqa: BLE001 - _declC = [] - msg += (f"\n│ 被调方法={_mnC!r};已声明同名方法键(示前 4 / 共 {len(_declC)}): " - f"{_declC[:4]}") - _vlog().compile_error(msg, src_file, lineno, code_line) - sys.exit(1) - - def HandleBodyLlvm(self, Body: ast.AST | list[ast.stmt]) -> Any: - return self.Trans.BodyHandler.HandleBodyLlvm(Body) - - def _get_int_ptr(self, Node: ast.AST) -> _ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if isinstance(Node, ast.Name): - if Node.id in Gen.variables: - return Gen.variables[Node.id] - elif isinstance(Node, ast.Attribute): - obj_val: Any = self.HandleExprLlvm(Node.value) - if not obj_val: - return None - if isinstance(obj_val.type, _ir.PointerType): - pointee: _ir.Type = obj_val.type.pointee - if isinstance(pointee, (_ir.IdentifiedStructType, _ir.LiteralStructType)): - StructName: str | None = None - if isinstance(pointee, _ir.IdentifiedStructType): - StructName = pointee.name - if not StructName: - for ClassName, struct_type in Gen.structs.items(): - if struct_type == pointee: - StructName = ClassName - break - if StructName and StructName in Gen.structs: - # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):pointee 可能是 - # **另一份 opaque 同名实例**,typed pointer 下 GEP 必须编译期算出 - # 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。 - obj_val, _STk = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, obj_val, StructName, f"cast_{StructName}") - offset: int = Gen._get_member_offset(Node.attr, StructName, _STk) - member_ptr: Any = Gen.builder.gep(obj_val, [_ir.Constant(_ir.IntType(32), 0), _ir.Constant(_ir.IntType(32), offset)], name=f"{Node.attr}_ptr") - return member_ptr - return None - - def _IsTModuleType(self, TypeName: str) -> bool: - if TypeName in ('t', 'State', 'Bit', 'Anonymous', 'Postdefinition'): - return True - TypeClass: type | None = getattr(t, TypeName, None) - if TypeClass and isinstance(TypeClass, type): - if issubclass(TypeClass, t.CType): - return True - FallbackClass: type | None = getattr(t, f'_{TypeName}', None) - if FallbackClass and isinstance(FallbackClass, type): - if issubclass(FallbackClass, t.CType): - return True - return False - - def ResolveListElementType( - self, - elem_type_node: ast.expr, - Gen: Any, - *, - check_func_ptr: bool = False, - handle_str: bool = False, - void_fallback_width: int = 32, - fallback_to_cint: bool = False, - ) -> tuple[Any, CTypeInfo | None]: - """解析 list 元素类型,返回 (LLVM类型, CTypeInfo|None)。 - - 统一处理结构体、str、函数指针、VoidType 回退等变体。 - - Args: - check_func_ptr: 是否检查函数指针(全局变量路径需要) - handle_str: 是否处理 str → i8*(局部变量路径需要) - void_fallback_width: VoidType 回退宽度 (32 或 8) - fallback_to_cint: 无类型信息时回退到 CInt - """ - elem_type_info: CTypeInfo | None = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable) - - # 结构体类型 - if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: - if check_func_ptr: - sym_entry: CTypeInfo | None = self.Trans.SymbolTable.lookup(elem_type_node.id) - if sym_entry and isinstance(sym_entry, CTypeInfo) and sym_entry.IsFuncPtr: - return _ir.IntType(8).as_pointer(), elem_type_info - return Gen.structs[elem_type_node.id], elem_type_info - - # str → i8* - if handle_str and isinstance(elem_type_node, ast.Name) and elem_type_node.id == 'str': - return _ir.PointerType(_ir.IntType(8)), elem_type_info - - # 函数指针检查 - if check_func_ptr and elem_type_info and elem_type_info.IsFuncPtr: - return _ir.IntType(8).as_pointer(), elem_type_info - - # 回退到 CInt - if elem_type_info is None and fallback_to_cint: - elem_type_info = CTypeInfo() - elem_type_info.BaseType = t.CInt() - - elem_type: Any = Gen._ctype_to_llvm(elem_type_info) - if isinstance(elem_type, _ir.VoidType): - elem_type = _ir.IntType(void_fallback_width) - - return elem_type, elem_type_info - - def ParseArrayCount( - self, - count_node: ast.expr | None, - Gen: Any, - value_node: ast.expr | None = None, - *, - mode: str = 'global', - ) -> int: - """解析数组计数,返回计数(默认 1)。 - - 统一处理 Constant/Name/Attribute/BinOp/None 等形式。 - None 或 Constant(None) 时从 value_node 推断(List 长度或 str 长度+1)。 - - Args: - count_node: 计数 AST 节点 - value_node: 赋值右侧值节点(用于 count_node 为 None 时推断) - mode: 'global' 使用 _eval_global_count + SymbolTable + _define_constants; - 'local' 使用 TryEvalConstExpr - """ - # None 或 Constant(None) → 从 value_node 推断 - if count_node is None or (isinstance(count_node, ast.Constant) and count_node.value is None): - if value_node and isinstance(value_node, ast.List): - return len(value_node.elts) - if value_node and isinstance(value_node, ast.Constant) and isinstance(value_node.value, str): - return len(value_node.value) + 1 - return 1 - - # Constant(int) - if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int): - return count_node.value - - if mode == 'global': - # Name → SymbolTable + _define_constants - if isinstance(count_node, ast.Name): - ArrayCount: int = 1 - if self.Trans.SymbolTable.has(count_node.id): - sym_info: Any = self.Trans.SymbolTable[count_node.id] - if isinstance(sym_info.value, int): - ArrayCount = sym_info.value - if ArrayCount == 1 and count_node.id in Gen._define_constants: - def_val: Any = Gen._define_constants[count_node.id] - # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 覆盖 - try: - if hasattr(Gen, 'module') and count_node.id in Gen.module.globals: - _gv: Any = Gen.module.globals[count_node.id] - if getattr(_gv, 'initializer', None) is not None: - _iv: Any = _gv.initializer - if hasattr(_iv, 'constant') and isinstance(_iv.constant, int): - def_val = _iv.constant - except Exception: - pass - if isinstance(def_val, int): - ArrayCount = def_val - return ArrayCount - # Attribute / BinOp → _eval_global_count - if isinstance(count_node, (ast.Attribute, ast.BinOp)): - eval_fn: Any = getattr(self, '_eval_global_count', None) - if eval_fn is not None: - ev: int | None = eval_fn(count_node, Gen) - if ev is not None: - return ev - return 1 - else: - # local mode → TryEvalConstExpr - eval_count: Any = CTypeInfo.TryEvalConstExpr(count_node, self.Trans.SymbolTable) - if eval_count is not None and isinstance(eval_count, int) and eval_count > 0: - return eval_count - return 1 - - return 1 - - def ResolveAnnotationType(self, annotation: ast.expr) -> CTypeInfo | None: - """解析注解类型,BinOp(BitOr) 优先使用 MergeTypes,其他使用 FromNode。 - - 统一处理函数返回类型和参数类型的 BinOp 联合类型解析。 - 指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] / 裸名 CPtr[T],三者等价 - `T | t.CPtr`)同样交给 TypeMerge 解析:内层 T 走通用类型解析(含跨模块 - 类经 Gen.structs 反查 SHA1 前缀),指针层数与源码 `T | t.CPtr` 一致; - 若交给 CTypeInfo.FromNode(TypeAnnotationResolver)解析, - rawptr[ast.AST] 会退化成 CVoid/1(i8*)、t.CPtr[ast.AST] 亦退化成 - CVoid/1(其 Subscript-CPtr 分支不带 Gen.structs 反查),与 declare 端 - %".AST"* 分裂。 - 命中泛型模板的泛型容器下标(list[X] / rawptr[list[X]] 等)同样交 - TypeMerge:字段(embedded)位置与参数/返回注解**同一解析**,否则 - CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端 - 的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。 - """ - # 内建定长数组构造子 array 的注解校验(误用即 fail-fast): - # 本方法覆盖函数参数/返回注解与类字段(CollectAnnAssignMember 亦经此)。 - self._CheckFixedArrayKeyword(annotation) - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) - if merger: - info: CTypeInfo | None = merger.MergeTypes(annotation) - if info is not None and isinstance(info, CTypeInfo): - return info - if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable): - merger = getattr(self.Trans, 'TypeMergeHandler', None) - if merger: - info = merger.GetCTypeInfo(annotation) - if isinstance(info, CTypeInfo) and info.BaseType is not None: - return info - if (isinstance(annotation, ast.Subscript) - and self._SubscriptHitsGenericTemplate(annotation) - and getattr(self.Trans, 'TypeMergeHandler', None) is not None): - gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation) - if isinstance(gci, CTypeInfo) and gci.BaseType is not None: - return gci - if isinstance(annotation, ast.Attribute): - return self._ResolveAttributeAnnotationType(annotation) - Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) - return self._ResolveStackArrayElementFallback(annotation, Fallback) - - def _ResolveStackArrayElementFallback( - self, annotation: ast.expr, Fallback: CTypeInfo | None) -> CTypeInfo | None: - """固定数组注解(`t.CArray[T, N]` / `[N]T`)元素类型兜底解析。 - - CTypeInfo.FromNode(TypeAnnotationResolver 的 IsListAnnotation 分支)只 - 会用自身解析元素类型:元素命中泛型模板时(list[X] / list[rawptr[X]])解 - 析为空 → 整个注解 BaseType=None → 调用方(CollectAnnAssignMember)把 - VoidType 换成 i8*,字段即塌成 i8*,与声明端 - DeclarationGenerator._get_type_str 的 ValueIsCArray 分支给出的 - `[N x <元素>]` 布局不一致(规则 4:能不变 i8* 的就不变 i8*)。 - 此处仅在该元素解析为空时兜底:用**与参数注解同一解析** - (ResolveAnnotationType 递归,泛型容器因此走 TypeMerge 特化,跨模块类 - 经 Gen.structs 反查 SHA1)解析元素,再套上数组维度(维度序与 - TypeAnnotationResolver 的 `insert(0, count)` 一致)。 - 元素可解析的形态(t.CArray[t.CInt, 8] / t.CArray[Node, 4] / 嵌套 CArray) - 保持 FromNode 结果原样,避免改动既有布局。 - """ - if isinstance(Fallback, CTypeInfo) and Fallback.BaseType is not None: - return Fallback - if not (isinstance(annotation, ast.Subscript) and IsListAnnotation(annotation)): - return Fallback - SliceNode: ast.AST = annotation.slice - if not (isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2): - return Fallback - ElemInfo: CTypeInfo | None = self.ResolveAnnotationType(SliceNode.elts[0]) - if not isinstance(ElemInfo, CTypeInfo) or ElemInfo.BaseType is None: - return Fallback - CountVal: int | None = CTypeInfo.TryEvalConstExpr(SliceNode.elts[1], self.Trans.SymbolTable) - if not isinstance(CountVal, int) or CountVal <= 0: - return Fallback - Result: CTypeInfo = ElemInfo.Copy() - Result.ArrayDims = [str(CountVal)] + list(ElemInfo.ArrayDims) - return Result - - def _ResolveAttributeAnnotationType(self, annotation: ast.Attribute) -> CTypeInfo | None: - """Attribute 注解(`t.CInt` / `ast.AST` / `.CLS`)解析。 - - 先走 CTypeInfo.FromNode(TypeAnnotationResolver,覆盖 t/c 内建与已在本模块 - SymbolTable 注册的类):其 BaseType 已解析时语义不变。 - **仅在 FromNode 解析不出来时**(BaseType 为 None,即该类未随本模块 - import/预扫描注册)按类名查 `Gen.structs` / `Gen._struct_sha1_map` 后交 - TypeMerge(HandlesTypeMerge 的 Attribute 分支,与声明端 - DeclarationGenerator._get_type_str 的 `attr_name in struct_names`、名称域 - SymbolUtils._ResolveAttributeArgStr 同一条"类名 → SHA1"规则)。 - - 不修此路时跨模块类 Attribute(`rawptr[.CLS]` 脱糖后的 - `.CLS | t.CPtr`、泛型 T 替换出的裸 `.CLS`、`list[ast.AST]` - 特化的 `append(item: T)`)在定义端判为"无法识别的参数类型注解"抛 - SyntaxError,被上层 broad except 吞掉后退化为 i32/i8*,而声明端给 - `%".CLS"*`,字段/签名两侧不一致。 - """ - fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) - if fallback is not None and fallback.BaseType is not None: - return fallback - attr_name: str = getattr(annotation, 'attr', '') or '' - gen: Any = getattr(self.Trans, 'LlvmGen', None) - if not attr_name or gen is None: - return fallback - if (attr_name not in getattr(gen, 'structs', {}) - and attr_name not in getattr(gen, '_struct_sha1_map', {})): - return fallback - merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) - if merger is not None: - info: CTypeInfo | None = merger.GetCTypeInfo(annotation) - if isinstance(info, CTypeInfo) and info.BaseType is not None: - return info - return fallback - - def ResolveParamAnnotation(self, annotation: ast.expr) -> CTypeInfo | None: - """函数参数注解解析(阻塞 B 同根第三处修复)。 - - 局部变量注解走 TypeMergeHandler.GetCTypeInfo(_HandleSubscript 泛型 - 分支),函数参数却走 ResolveAnnotationType → Subscript 非 BinOp 直接 - CTypeInfo.FromNode → TypeAnnotationResolver 不识别泛型模板,容器 - (list[X] / rawptr[list[X]] / GSList[X] / rawptr[GSList[X]])被折叠成 - i8* → 参数无 var_struct_class → 方法调用 FakeDuck 兜底错绑兄弟类 - (如 parser.vp _append_str_value 的 values.append 被编成 AST.append - 虚调用,this=list* 读 vtable 崩溃)。此处对命中泛型模板的 Subscript - 改走 TypeMerge 泛型识别路径,其余保持 ResolveAnnotationType 语义。 - """ - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) - if merger: - info: CTypeInfo | None = merger.MergeTypes(annotation) - if info is not None and isinstance(info, CTypeInfo): - return info - # 指针下标参数注解(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],含 - # rawptr[list[X]] 等容器:_SubscriptHitsGenericTemplate 也命中,但统一交 - # ResolveAnnotationType 处理更简单——它已把指针下标路由到 TypeMerge)。 - if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable): - info = self.ResolveAnnotationType(annotation) - if isinstance(info, CTypeInfo) and info.BaseType is not None: - return info - if isinstance(annotation, ast.Subscript) and getattr(self.Trans, 'TypeMergeHandler', None): - if self._SubscriptHitsGenericTemplate(annotation): - try: - _gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation) - if _gci is not None and isinstance(_gci, CTypeInfo) and _gci.BaseType is not None: - return _gci - except Exception: - pass - # Attribute 参数注解(`.CLS` 裸引用 / 泛型 T 替换出的 `.CLS`) - # 与 ResolveAnnotationType 共用同一条规则(_ResolveAttributeAnnotationType → - # TypeMergeHandler.GetCTypeInfo 的 Attribute 分支查 Gen.structs / - # Gen._struct_sha1_map),不在此复制第二套查表;否则 list[X] 特化方法 - # `append(item: T)` 的参数 T(= 裸 `.CLS`)解析为空 → VoidType → - # 抛"无法识别的参数类型注解" → 特化模板方法未注册。 - if isinstance(annotation, ast.Attribute): - return self.ResolveAnnotationType(annotation) - Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) - return self._ResolveStackArrayElementFallback(annotation, Fallback) - - def _SubscriptHitsGenericTemplate(self, annotation: ast.Subscript) -> bool: - """判断 Subscript 是否命中泛型模板:裸模板名(list[X]/GSList[X]), - 或 rawptr/t.CPtr 裸指针 typedef 内嵌模板名(rawptr[list[X]] 等)。""" - tpls: Any = getattr(getattr(self.Trans, 'ClassHandler', None), '_generic_class_templates', None) - if not tpls: - return False - base: ast.AST = annotation.value - if isinstance(base, ast.Name): - if base.id in tpls: - return True - if isinstance(annotation.slice, ast.Subscript): - inner: ast.AST = annotation.slice.value - if isinstance(inner, ast.Name) and inner.id in tpls: - return True - elif isinstance(base, ast.Attribute) and base.attr == 'CPtr': - if isinstance(annotation.slice, ast.Subscript): - inner = annotation.slice.value - if isinstance(inner, ast.Name) and inner.id in tpls: - return True - return False - + _vc_head = _cls + _parts.append(f"短名 in structs={'是' if _cls and _cls in Gen.structs else '否'}") + _parts.append("class_members=" + + (f"{len(Gen.class_members.get(_cls) or [])} 个字段" + if _cls in Gen.class_members else '无该键')) + _parts.append("注: 强转基座形态本应走 _FailFastAttrCallBase 详细报文," + "此处仍是本条 ⇒ 那条 fail-fast 未触发") + # 回显读取侧的**确切失败原因**(`ExprAttrHandle` 各 return None 点登记)。 + # 仅在 (类名, 字段名) 与被报节点一致时输出:嵌套读取留下的旧记录不得冒充本节点。 + try: + _rec: Any = getattr(Gen, '_LastAttrNone', None) + except Exception: # noqa: BLE001 + _rec = None + if isinstance(_rec, tuple) and len(_rec) >= 3: + _rec_cls, _rec_attr, _rec_reason = _rec[0], _rec[1], _rec[2] + _short = lambda s: (s.split('.')[-1] if s else s) # noqa: E731 + _same: bool = bool(_rec_attr) and _rec_attr == Node.attr and ( + not _rec_cls or not _vc_head or _rec_cls == _vc_head + or _short(_rec_cls) == _short(_vc_head)) + if _same: + _parts.append(f"读取侧原因={_rec_reason}") + return ";".join(_parts) + + @staticmethod + def _ExprText(Node: ast.AST) -> str: + """把表达式节点渲染成源码文本(如 func.Blocks.Head、list[X*] * uint64(v)),供错误信息使用""" + if isinstance(Node, ast.Attribute): + parts: list[str] = [] + cur: ast.AST = Node + while isinstance(cur, ast.Attribute): + parts.append(cur.attr) + cur = cur.value + if isinstance(cur, ast.Name): + parts.append(cur.id) + if parts: + return '.'.join(reversed(parts)) + if isinstance(Node, ast.Name): + return Node.id + try: + rendered: str = ast.unparse(Node) + if rendered: + return rendered + except Exception: + pass + return getattr(Node, 'attr', '') or '' + + def _FailFastFixedArrayKeyword(self, Node: ast.AST, Msg: str) -> None: + """内建定长数组构造子 array 误用的 fail-fast 出口。 + + 报错含:文件:行:列 + 源码片段 + 期望形态(array[E, N])+ 正确示例 + (消息文本由 SymbolUtils.FixedArrayKeywordAnnotationError 给出)。 + """ + import sys + Gen: Any = getattr(self.Trans, 'LlvmGen', None) + raw_lineno: int = getattr(Node, 'lineno', 0) or 0 + lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 + col: int = getattr(Node, 'col_offset', -1) + src_file: str = getattr(Gen, '_current_source_file', '') or '' + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + code_line: str = '' + if lineno > 0 and Gen is not None and hasattr(Gen, '_get_source_line'): + try: + code_line = (Gen._get_source_line(lineno) or '').strip() + except Exception: + code_line = '' + loc: str = src_file + if lineno > 0: + loc += f":{lineno}" + if col >= 0: + loc += f":{col}" + full: str = f"{Msg}。位置 {loc}" + (f",源码: {code_line}" if code_line else '') + _vlog().compile_error(full, src_file, lineno, code_line) + sys.exit(1) + + def _CheckFixedArrayKeyword(self, Annotation: ast.AST) -> None: + """注解入口统一校验内建定长数组构造子 array(误用即 fail-fast)。 + + 只校验带位置的注解入口(本类的 ResolveAnnotationType / CollectAnnAssignMember + + HandlesAnnAssign 局部 / HandlesAssign 全局显式调用),**不**在通用表达式 + 解析路径上调用——以保证 array 在非类型位置仍是普通标识符(软关键字)。 + """ + if Annotation is None: + return + Msg: str | None = FixedArrayKeywordAnnotationError(Annotation, self.Trans.SymbolTable) + if Msg: + self._FailFastFixedArrayKeyword(Annotation, Msg) + + def _RaiseUnresolvedExpr(self, What: str, Node: ast.AST, Gen: Any, AnnNode: ast.AST | None = None, + VarName: str | None = None) -> None: + """fail-fast:带值表达式在编译期求值失败(返回 None 且无值可存)时终止。 + + 属性链(含两级自动解引用链,如 blk = func.Blocks.Head)解析不出目标 + 类型时 _handle_attr_nested 会返回 None;类型下标被当乘法的畸形写法 + (如 list[X*]*(...))会让整条 RHS 不产任何指令而求值 None。赋值调用方 + 若静默跳过 store,目标 alloca 永不初始化 → 运行期读未初始化栈变量(随机 + 崩溃)。此处用 VLogger 输出 目标变量 + 注解文本 + 表达式源码与形态 + + 文件名:行:列 后退出(严格模式语义)。 + """ + import sys + ann_text: str = '' + if AnnNode is not None: + try: + ann_text = ast.unparse(AnnNode) + except Exception: + ann_text = '' + # RHS 形态 = AST 节点类名(Name/Subscript/BinOp/Call/Attribute/…), + # 便于报错可直接按形态分类,不必再从表达式文本反推。 + rhs_kind: str = type(Node).__name__ + raw_lineno: int = getattr(Node, 'lineno', 0) or 0 + lineno: int = raw_lineno or getattr(Gen, '_current_lineno', 0) or 0 + col: int = getattr(Node, 'col_offset', -1) + src_file: str = getattr(Gen, '_current_source_file', '') or '' + expr_text: str = self._ExprText(Node) + # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, + # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator)。 + _line_offset: int = int(getattr(getattr(self, 'Trans', None), '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + code_line: str = '' + if lineno > 0 and src_file: + # 行号基准修正(vast AST 行号 vs 磁盘原文件行号偏差): + # Phase2Translator._inject_auto_imports 会在解析前向源码头部注入 + # 1~3 行 "import _list/_dict/json",使 AST 节点 lineno 比磁盘原文件 + # 真实行号大 N(实测 CallGenCBuiltin.vp 真实 1409 报 1410、 + # FuncGenMixin.vp 真实 514 报 515)。上面已按 _line_offset 精确还原; + # 此处再以表达式文本为锚(从原始行号向上至多 8 行)回溯校正 + # 属性链(其 expr_text 逐字出现在磁盘行中)可能残留的偏差。 + if expr_text and '.' in expr_text and hasattr(Gen, '_get_source_line'): + _src_lines: Any = getattr(Gen, '_current_source_lines', None) + if isinstance(_src_lines, list) and _src_lines: + for cand in range(lineno, max(0, lineno - 8), -1): + if 0 < cand <= len(_src_lines) and expr_text in _src_lines[cand - 1]: + lineno = cand + break + try: + code_line = (Gen._get_source_line(lineno) or '').strip() + except Exception: + code_line = '' + loc: str = src_file + if lineno > 0: + loc += f":{lineno}" + if col >= 0: + loc += f":{col}" + msg: str = (f"{What}表达式 '{expr_text}'({rhs_kind} 形态)编译期求值为空(" + f"表达式未解析出目标、无值可存;且其求值未以返回 void 的调用收尾)," + f"赋值将被跳过、变量保持未初始化" + f"{(',目标变量 ' + repr(VarName)) if VarName else ''}" + f"{(',注解 ' + ann_text) if ann_text else ''}。位置 {loc}" + f"{(',源码: ' + code_line) if code_line else ''}") + # 【基址诊断】属性读返回 None 时报出**基座真实形态 + 类名登记事实**,不再从 + # "求值为空"反推(同一份事实也供 _RequireArgValue 的实参求值失败路径使用, + # 见 HandlesExprCall._RequireArgValue)。 + if isinstance(Node, ast.Attribute): + msg += "\n│ 基址诊断: " + self._DescribeAttrBase(Gen, Node) + elif isinstance(Node, ast.Call) and isinstance(Node.func, ast.Attribute): + # 被调者形如 `接收者链.方法(...)`:整条 Call 求值为空的**唯一来源**是接收者链 + # 先求值为空(HandlesExprCall._HandleMethodCallLlvm 里 + # `ObjVal = HandleExprLlvm(Node.func.value)` 为假即 `return None`; + # 方法声明缺失会在同函数内 raise "Undefined method …",不会走到这里)。 + # 行号只能指到整条调用,故把**接收者**的基址事实与最近一次属性读取失败原因 + # 一并回显;Call 形态下无法用 (类, 字段) 与节点比对,该记录无条件回显。 + try: + msg += "\n│ 被调者接收者诊断: " + self._DescribeAttrBase(Gen, Node.func.value) + except Exception as _e_d: # noqa: BLE001 + msg += f"\n│ 被调者接收者诊断生成失败: {type(_e_d).__name__}: {_e_d}" + try: + _recC: Any = getattr(Gen, '_LastAttrNone', None) + if isinstance(_recC, tuple) and len(_recC) >= 3 and _recC[2]: + msg += (f"\n│ 最近一次属性读取失败: 类={_recC[0]!r} 字段={_recC[1]!r}" + f" 原因={_recC[2]}") + except Exception: # noqa: BLE001 + pass + _mnC: str = getattr(Node.func, 'attr', '') or '' + _declC: list[str] = [] + try: + for _fk in (getattr(Gen, 'functions', {}) or {}): + if _mnC and _fk.endswith(f'.{_mnC}'): + _declC.append(_fk) + except Exception: # noqa: BLE001 + _declC = [] + msg += (f"\n│ 被调方法={_mnC!r};已声明同名方法键(示前 4 / 共 {len(_declC)}): " + f"{_declC[:4]}") + _vlog().compile_error(msg, src_file, lineno, code_line) + sys.exit(1) + + def HandleBodyLlvm(self, Body: ast.AST | list[ast.stmt]) -> Any: + return self.Trans.BodyHandler.HandleBodyLlvm(Body) + + def _get_int_ptr(self, Node: ast.AST) -> _ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(Node, ast.Name): + if Node.id in Gen.variables: + return Gen.variables[Node.id] + elif isinstance(Node, ast.Attribute): + obj_val: Any = self.HandleExprLlvm(Node.value) + if not obj_val: + return None + if isinstance(obj_val.type, _ir.PointerType): + pointee: _ir.Type = obj_val.type.pointee + if isinstance(pointee, (_ir.IdentifiedStructType, _ir.LiteralStructType)): + StructName: str | None = None + if isinstance(pointee, _ir.IdentifiedStructType): + StructName = pointee.name + if not StructName: + for ClassName, struct_type in Gen.structs.items(): + if struct_type == pointee: + StructName = ClassName + break + if StructName and StructName in Gen.structs: + # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):pointee 可能是 + # **另一份 opaque 同名实例**,typed pointer 下 GEP 必须编译期算出 + # 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。 + obj_val, _STk = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, obj_val, StructName, f"cast_{StructName}") + offset: int = Gen._get_member_offset(Node.attr, StructName, _STk) + member_ptr: Any = Gen.builder.gep(obj_val, [_ir.Constant(_ir.IntType(32), 0), _ir.Constant(_ir.IntType(32), offset)], name=f"{Node.attr}_ptr") + return member_ptr + return None + + def _IsTModuleType(self, TypeName: str) -> bool: + if TypeName in ('t', 'State', 'Bit', 'Anonymous', 'Postdefinition'): + return True + TypeClass: type | None = getattr(t, TypeName, None) + if TypeClass and isinstance(TypeClass, type): + if issubclass(TypeClass, t.CType): + return True + FallbackClass: type | None = getattr(t, f'_{TypeName}', None) + if FallbackClass and isinstance(FallbackClass, type): + if issubclass(FallbackClass, t.CType): + return True + return False + + def ResolveListElementType( + self, + elem_type_node: ast.expr, + Gen: Any, + *, + check_func_ptr: bool = False, + handle_str: bool = False, + void_fallback_width: int = 32, + fallback_to_cint: bool = False, + ) -> tuple[Any, CTypeInfo | None]: + """解析 list 元素类型,返回 (LLVM类型, CTypeInfo|None)。 + + 统一处理结构体、str、函数指针、VoidType 回退等变体。 + + Args: + check_func_ptr: 是否检查函数指针(全局变量路径需要) + handle_str: 是否处理 str → i8*(局部变量路径需要) + void_fallback_width: VoidType 回退宽度 (32 或 8) + fallback_to_cint: 无类型信息时回退到 CInt + """ + elem_type_info: CTypeInfo | None = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable) + + # 结构体类型 + if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: + if check_func_ptr: + sym_entry: CTypeInfo | None = self.Trans.SymbolTable.lookup(elem_type_node.id) + if sym_entry and isinstance(sym_entry, CTypeInfo) and sym_entry.IsFuncPtr: + return _ir.IntType(8).as_pointer(), elem_type_info + return Gen.structs[elem_type_node.id], elem_type_info + + # str → i8* + if handle_str and isinstance(elem_type_node, ast.Name) and elem_type_node.id == 'str': + return _ir.PointerType(_ir.IntType(8)), elem_type_info + + # 函数指针检查 + if check_func_ptr and elem_type_info and elem_type_info.IsFuncPtr: + return _ir.IntType(8).as_pointer(), elem_type_info + + # 回退到 CInt + if elem_type_info is None and fallback_to_cint: + elem_type_info = CTypeInfo() + elem_type_info.BaseType = t.CInt() + + elem_type: Any = Gen._ctype_to_llvm(elem_type_info) + if isinstance(elem_type, _ir.VoidType): + elem_type = _ir.IntType(void_fallback_width) + + return elem_type, elem_type_info + + def ParseArrayCount( + self, + count_node: ast.expr | None, + Gen: Any, + value_node: ast.expr | None = None, + *, + mode: str = 'global', + ) -> int: + """解析数组计数,返回计数(默认 1)。 + + 统一处理 Constant/Name/Attribute/BinOp/None 等形式。 + None 或 Constant(None) 时从 value_node 推断(List 长度或 str 长度+1)。 + + Args: + count_node: 计数 AST 节点 + value_node: 赋值右侧值节点(用于 count_node 为 None 时推断) + mode: 'global' 使用 _eval_global_count + SymbolTable + _define_constants; + 'local' 使用 TryEvalConstExpr + """ + # None 或 Constant(None) → 从 value_node 推断 + if count_node is None or (isinstance(count_node, ast.Constant) and count_node.value is None): + if value_node and isinstance(value_node, ast.List): + return len(value_node.elts) + if value_node and isinstance(value_node, ast.Constant) and isinstance(value_node.value, str): + return len(value_node.value) + 1 + return 1 + + # Constant(int) + if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int): + return count_node.value + + if mode == 'global': + # Name → SymbolTable + _define_constants + if isinstance(count_node, ast.Name): + ArrayCount: int = 1 + if self.Trans.SymbolTable.has(count_node.id): + sym_info: Any = self.Trans.SymbolTable[count_node.id] + if isinstance(sym_info.value, int): + ArrayCount = sym_info.value + if ArrayCount == 1 and count_node.id in Gen._define_constants: + def_val: Any = Gen._define_constants[count_node.id] + # 治本修复:本模块全局变量初始化值优先,避免跨模块同名 CDefine 覆盖 + try: + if hasattr(Gen, 'module') and count_node.id in Gen.module.globals: + _gv: Any = Gen.module.globals[count_node.id] + if getattr(_gv, 'initializer', None) is not None: + _iv: Any = _gv.initializer + if hasattr(_iv, 'constant') and isinstance(_iv.constant, int): + def_val = _iv.constant + except Exception: + pass + if isinstance(def_val, int): + ArrayCount = def_val + return ArrayCount + # Attribute / BinOp → _eval_global_count + if isinstance(count_node, (ast.Attribute, ast.BinOp)): + eval_fn: Any = getattr(self, '_eval_global_count', None) + if eval_fn is not None: + ev: int | None = eval_fn(count_node, Gen) + if ev is not None: + return ev + return 1 + else: + # local mode → TryEvalConstExpr + eval_count: Any = CTypeInfo.TryEvalConstExpr(count_node, self.Trans.SymbolTable) + if eval_count is not None and isinstance(eval_count, int) and eval_count > 0: + return eval_count + return 1 + + return 1 + + def ResolveAnnotationType(self, annotation: ast.expr) -> CTypeInfo | None: + """解析注解类型,BinOp(BitOr) 优先使用 MergeTypes,其他使用 FromNode。 + + 统一处理函数返回类型和参数类型的 BinOp 联合类型解析。 + 指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] / 裸名 CPtr[T],三者等价 + `T | t.CPtr`)同样交给 TypeMerge 解析:内层 T 走通用类型解析(含跨模块 + 类经 Gen.structs 反查 SHA1 前缀),指针层数与源码 `T | t.CPtr` 一致; + 若交给 CTypeInfo.FromNode(TypeAnnotationResolver)解析, + rawptr[ast.AST] 会退化成 CVoid/1(i8*)、t.CPtr[ast.AST] 亦退化成 + CVoid/1(其 Subscript-CPtr 分支不带 Gen.structs 反查),与 declare 端 + %".AST"* 分裂。 + 命中泛型模板的泛型容器下标(list[X] / rawptr[list[X]] 等)同样交 + TypeMerge:字段(embedded)位置与参数/返回注解**同一解析**,否则 + CTypeInfo.FromNode 把容器折叠成 i8*,字段类型与 declare/stub 端 + 的特化名指针不一致(同一结构体在定义端/声明端布局描述不同)。 + """ + # 内建定长数组构造子 array 的注解校验(误用即 fail-fast): + # 本方法覆盖函数参数/返回注解与类字段(CollectAnnAssignMember 亦经此)。 + self._CheckFixedArrayKeyword(annotation) + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) + if merger: + info: CTypeInfo | None = merger.MergeTypes(annotation) + if info is not None and isinstance(info, CTypeInfo): + return info + if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable): + merger = getattr(self.Trans, 'TypeMergeHandler', None) + if merger: + info = merger.GetCTypeInfo(annotation) + if isinstance(info, CTypeInfo) and info.BaseType is not None: + return info + if (isinstance(annotation, ast.Subscript) + and self._SubscriptHitsGenericTemplate(annotation) + and getattr(self.Trans, 'TypeMergeHandler', None) is not None): + gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation) + if isinstance(gci, CTypeInfo) and gci.BaseType is not None: + return gci + if isinstance(annotation, ast.Attribute): + return self._ResolveAttributeAnnotationType(annotation) + Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + return self._ResolveStackArrayElementFallback(annotation, Fallback) + + def _ResolveStackArrayElementFallback( + self, annotation: ast.expr, Fallback: CTypeInfo | None) -> CTypeInfo | None: + """固定数组注解(`t.CArray[T, N]` / `[N]T`)元素类型兜底解析。 + + CTypeInfo.FromNode(TypeAnnotationResolver 的 IsListAnnotation 分支)只 + 会用自身解析元素类型:元素命中泛型模板时(list[X] / list[rawptr[X]])解 + 析为空 → 整个注解 BaseType=None → 调用方(CollectAnnAssignMember)把 + VoidType 换成 i8*,字段即塌成 i8*,与声明端 + DeclarationGenerator._get_type_str 的 ValueIsCArray 分支给出的 + `[N x <元素>]` 布局不一致(规则 4:能不变 i8* 的就不变 i8*)。 + 此处仅在该元素解析为空时兜底:用**与参数注解同一解析** + (ResolveAnnotationType 递归,泛型容器因此走 TypeMerge 特化,跨模块类 + 经 Gen.structs 反查 SHA1)解析元素,再套上数组维度(维度序与 + TypeAnnotationResolver 的 `insert(0, count)` 一致)。 + 元素可解析的形态(t.CArray[t.CInt, 8] / t.CArray[Node, 4] / 嵌套 CArray) + 保持 FromNode 结果原样,避免改动既有布局。 + """ + if isinstance(Fallback, CTypeInfo) and Fallback.BaseType is not None: + return Fallback + if not (isinstance(annotation, ast.Subscript) and IsListAnnotation(annotation)): + return Fallback + SliceNode: ast.AST = annotation.slice + if not (isinstance(SliceNode, ast.Tuple) and len(SliceNode.elts) == 2): + return Fallback + ElemInfo: CTypeInfo | None = self.ResolveAnnotationType(SliceNode.elts[0]) + if not isinstance(ElemInfo, CTypeInfo) or ElemInfo.BaseType is None: + return Fallback + CountVal: int | None = CTypeInfo.TryEvalConstExpr(SliceNode.elts[1], self.Trans.SymbolTable) + if not isinstance(CountVal, int) or CountVal <= 0: + return Fallback + Result: CTypeInfo = ElemInfo.Copy() + Result.ArrayDims = [str(CountVal)] + list(ElemInfo.ArrayDims) + return Result + + def _ResolveAttributeAnnotationType(self, annotation: ast.Attribute) -> CTypeInfo | None: + """Attribute 注解(`t.CInt` / `ast.AST` / `.CLS`)解析。 + + 先走 CTypeInfo.FromNode(TypeAnnotationResolver,覆盖 t/c 内建与已在本模块 + SymbolTable 注册的类):其 BaseType 已解析时语义不变。 + **仅在 FromNode 解析不出来时**(BaseType 为 None,即该类未随本模块 + import/预扫描注册)按类名查 `Gen.structs` / `Gen._struct_sha1_map` 后交 + TypeMerge(HandlesTypeMerge 的 Attribute 分支,与声明端 + DeclarationGenerator._get_type_str 的 `attr_name in struct_names`、名称域 + SymbolUtils._ResolveAttributeArgStr 同一条"类名 → SHA1"规则)。 + + 不修此路时跨模块类 Attribute(`rawptr[.CLS]` 脱糖后的 + `.CLS | t.CPtr`、泛型 T 替换出的裸 `.CLS`、`list[ast.AST]` + 特化的 `append(item: T)`)在定义端判为"无法识别的参数类型注解"抛 + SyntaxError,被上层 broad except 吞掉后退化为 i32/i8*,而声明端给 + `%".CLS"*`,字段/签名两侧不一致。 + """ + fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + if fallback is not None and fallback.BaseType is not None: + return fallback + attr_name: str = getattr(annotation, 'attr', '') or '' + gen: Any = getattr(self.Trans, 'LlvmGen', None) + if not attr_name or gen is None: + return fallback + if (attr_name not in getattr(gen, 'structs', {}) + and attr_name not in getattr(gen, '_struct_sha1_map', {})): + return fallback + merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) + if merger is not None: + info: CTypeInfo | None = merger.GetCTypeInfo(annotation) + if isinstance(info, CTypeInfo) and info.BaseType is not None: + return info + return fallback + + def ResolveParamAnnotation(self, annotation: ast.expr) -> CTypeInfo | None: + """函数参数注解解析(阻塞 B 同根第三处修复)。 + + 局部变量注解走 TypeMergeHandler.GetCTypeInfo(_HandleSubscript 泛型 + 分支),函数参数却走 ResolveAnnotationType → Subscript 非 BinOp 直接 + CTypeInfo.FromNode → TypeAnnotationResolver 不识别泛型模板,容器 + (list[X] / rawptr[list[X]] / GSList[X] / rawptr[GSList[X]])被折叠成 + i8* → 参数无 var_struct_class → 方法调用 FakeDuck 兜底错绑兄弟类 + (如 parser.vp _append_str_value 的 values.append 被编成 AST.append + 虚调用,this=list* 读 vtable 崩溃)。此处对命中泛型模板的 Subscript + 改走 TypeMerge 泛型识别路径,其余保持 ResolveAnnotationType 语义。 + """ + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + merger: Any = getattr(self.Trans, 'TypeMergeHandler', None) + if merger: + info: CTypeInfo | None = merger.MergeTypes(annotation) + if info is not None and isinstance(info, CTypeInfo): + return info + # 指针下标参数注解(rawptr[T] / t.CPtr[T] / 裸名 CPtr[T],含 + # rawptr[list[X]] 等容器:_SubscriptHitsGenericTemplate 也命中,但统一交 + # ResolveAnnotationType 处理更简单——它已把指针下标路由到 TypeMerge)。 + if IsPtrSubscriptNode(annotation, self.Trans.SymbolTable): + info = self.ResolveAnnotationType(annotation) + if isinstance(info, CTypeInfo) and info.BaseType is not None: + return info + if isinstance(annotation, ast.Subscript) and getattr(self.Trans, 'TypeMergeHandler', None): + if self._SubscriptHitsGenericTemplate(annotation): + try: + _gci: CTypeInfo | None = self.Trans.TypeMergeHandler.GetCTypeInfo(annotation) + if _gci is not None and isinstance(_gci, CTypeInfo) and _gci.BaseType is not None: + return _gci + except Exception: + pass + # Attribute 参数注解(`.CLS` 裸引用 / 泛型 T 替换出的 `.CLS`) + # 与 ResolveAnnotationType 共用同一条规则(_ResolveAttributeAnnotationType → + # TypeMergeHandler.GetCTypeInfo 的 Attribute 分支查 Gen.structs / + # Gen._struct_sha1_map),不在此复制第二套查表;否则 list[X] 特化方法 + # `append(item: T)` 的参数 T(= 裸 `.CLS`)解析为空 → VoidType → + # 抛"无法识别的参数类型注解" → 特化模板方法未注册。 + if isinstance(annotation, ast.Attribute): + return self.ResolveAnnotationType(annotation) + Fallback: CTypeInfo | None = CTypeInfo.FromNode(annotation, self.Trans.SymbolTable) + return self._ResolveStackArrayElementFallback(annotation, Fallback) + + def _SubscriptHitsGenericTemplate(self, annotation: ast.Subscript) -> bool: + """判断 Subscript 是否命中泛型模板:裸模板名(list[X]/GSList[X]), + 或 rawptr/t.CPtr 裸指针 typedef 内嵌模板名(rawptr[list[X]] 等)。""" + tpls: Any = getattr(getattr(self.Trans, 'ClassHandler', None), '_generic_class_templates', None) + if not tpls: + return False + base: ast.AST = annotation.value + if isinstance(base, ast.Name): + if base.id in tpls: + return True + if isinstance(annotation.slice, ast.Subscript): + inner: ast.AST = annotation.slice.value + if isinstance(inner, ast.Name) and inner.id in tpls: + return True + elif isinstance(base, ast.Attribute) and base.attr == 'CPtr': + if isinstance(annotation.slice, ast.Subscript): + inner = annotation.slice.value + if isinstance(inner, ast.Name) and inner.id in tpls: + return True + return False + def _EnsureRenumVariantStruct(self, Gen: Any, RenumName: str, VariantName: str) -> str: """确保 REnum 变体嵌套结构体 `_` 及其 class_members 就绪。 @@ -1229,322 +1229,322 @@ class BaseHandle: pass return NestedStructName - def CollectAnnAssignMember( - self, item: ast.AST, Gen: Any - ) -> tuple[str, _ir.Type, CTypeInfo | None] | None: - """从 ast.AnnAssign 节点解析成员信息。 - - 返回 (VarName, MemberType, TypeInfo)。 - - **不再静默兜底**(原实现:注解解析返回 None → i32;_ctype_to_llvm 得 void → i8*; - 任何异常 → i32 + TypeInfo=None)。这些兜底会把"注解没解析出来"伪装成一个 - 具体类型,下游于是报出与真因无关的错——实测:`free_lists: uint64*`(脱糖为 - `uint64 | t.CPtr`)在解析异常时被当成 **i32**,`self.free_lists = base`(i8*) - 便误报"指针隐式转整数已禁止"。类型信息缺失一律响亮失败。 - 非 AnnAssign/Name 目标时返回 None。 - """ - if not isinstance(item, ast.AnnAssign) or not isinstance(item.target, ast.Name): - return None - VarName: str = item.target.id - AnnotationText: str = ast.unparse(item.annotation) - # 值引用标记(`&T` / `&temp T`)仅用于函数参数位:结构体字段/成员注解出现即 fail-fast。 - if FindAutoAddrMode(item.annotation) is not None: - raise ValueError( - f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}':结构体字段/成员不支持" - f"值引用(`&T` / `&temp T`)——值引用仅用于函数参数位,请改用显式指针类型(如 `T*`)") - try: - TypeInfo: CTypeInfo | None = self.ResolveAnnotationType(item.annotation) - except RecursionError: - # RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的 - # 真缺陷信号。此前被下方 `except Exception` 吞成一行 - # `WARN: HandlesBase: 忽略异常 maximum recursion depth exceeded` 并静默 - # 退化为 i32 ⇒ 真正的环(如跨模块类声明发射重入 - # _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub ↔ _TryLoadClassMembersFromPyi) - # 被掩盖,下游只看到类型解析静默退化。此处 fail-fast 上抛(保留原始栈), - # 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。 - raise - except Exception as e: - raise ValueError( - f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析失败:" - f"{type(e).__name__}: {e} —— 拒绝静默退化为 i32" - ) from e - if TypeInfo is None: - raise ValueError( - f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析不出类型" - f"(ResolveAnnotationType 返回 None)—— 拒绝静默退化为 i32" - ) - MemberType: _ir.Type = Gen._ctype_to_llvm(TypeInfo) - if isinstance(MemberType, _ir.VoidType): - raise ValueError( - f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析为 void" - f"(BaseType={getattr(TypeInfo, 'BaseType', None)} " - f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} " - f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} " - f"Name={getattr(TypeInfo, 'Name', None)})—— 拒绝静默退化为 i8*" - ) - # 「内层是类引用却解析成通用指针」fail-fast(降级源头定位): - # `rawptr[Mod.Class]` / `Mod.Class | t.CPtr` 的语义是"指向该类",内层若确实是 - # 已定义类,解析结果必须带该类 BaseType;退化成 void 基座(i8* / 多一层 i8**) - # 就是类型信息被丢掉 —— 该值一旦进 class_members 就会被冻进 struct body - # (llvmlite set_body 只能设一次),后期只修表不修体(现场:Generator 第 3/11 - # 槽 i8**)。此处响亮失败并附判据,使降级**当场暴露**而非留到赋值时报"层数不同"。 - self._CheckPtrAnnotationKeepsClass(item.annotation, TypeInfo, VarName, AnnotationText) - # 形状判据(更硬的一条):注解形态是「**指针包装的类引用**」而解析结果正是 - # `i8**`(= void 基座两层,T 的类型被丢掉)⇒ 必属降级,直接响亮失败并把两侧 - # 证据打全。放在字段类型的**唯一漏斗**里,覆盖全部"按注解推导成员类型"的写入链 - # (本地类 / 跨模块 .vpi / 嵌套类)。 - if (IsClassRefPtrAnnotation(item.annotation, self.Trans.SymbolTable) - and IsI8DoublePtrType(MemberType)): - _inner_name: str = '' - _inner_node: Any = (item.annotation.slice - if isinstance(item.annotation, ast.Subscript) else None) - if isinstance(_inner_node, ast.Attribute): - _inner_name = _inner_node.attr - elif isinstance(_inner_node, ast.Name): - _inner_name = _inner_node.id - _sym: Any = None - try: - _sym = self.Trans.SymbolTable.lookup(_inner_name) if _inner_name else None - except Exception: - _sym = None - _gen: Any = getattr(self.Trans, 'LlvmGen', None) - _structs_hit: str = ( - '有' if (_gen is not None and _inner_name in getattr(_gen, 'structs', {})) else '无') - _sha1_hit: Any = ( - getattr(_gen, '_struct_sha1_map', {}).get(_inner_name) if _gen is not None else None) - raise ValueError( - f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 是**类引用指针**," - f"却被解析成 {MemberType}(void 基座两层 = 类型信息丢失)—— 拒绝降级:" - f"CTypeInfo(BaseType={getattr(TypeInfo, 'BaseType', None)} " - f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} " - f"IsStruct={getattr(TypeInfo, 'IsStruct', None)} " - f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} " - f"IsEnum={getattr(TypeInfo, 'IsEnum', None)} " - f"Name={getattr(TypeInfo, 'Name', None)});内层名='{_inner_name}' " - f"符号表[该名] => IsTypedef={getattr(_sym, 'IsTypedef', None)} " - f"BaseType={getattr(_sym, 'BaseType', None)} " - f"PtrCount={getattr(_sym, 'PtrCount', None)} " - f"IsStruct={getattr(_sym, 'IsStruct', None)} " - f"IsRenum={getattr(_sym, 'IsRenum', None)} " - f"IsModuleAlias={getattr(_sym, 'IsModuleAlias', None)};" - f"Gen.structs 命中={_structs_hit} _struct_sha1_map 命中={_sha1_hit!r}" - ) - return VarName, MemberType, TypeInfo - - def _CheckPtrAnnotationKeepsClass(self, Annotation: ast.AST, Info: CTypeInfo | None, - MemberName: str, AnnotationText: str) -> None: - """指针包装注解的内层是**已定义类**时,解析结果不得退化成 void 基座(fail-fast)。 - - 只拦"必属类型信息丢失"的组合:内层节点(Name / Attribute)的名字在 - `Gen.structs`(本模块已建)或 `Gen._struct_sha1_map`(预扫描的类→模块映射) - 中有类定义,而解析出的 `BaseType` 是 `t.CVoid` **且没有任何类身份** - (IsStruct / IsRenum / IsEnum / Name 全空)。其余一律放行 - (`rawptr[rawptr]` 这类本就 void 基座的多层指针、模块名、泛型特化实参等)。 - """ - if Info is None or Info.BaseType is None: - return - if not (Info.BaseType is t.CVoid or isinstance(Info.BaseType, t.CVoid)): - return - # 只要还带着任何"类身份"(IsStruct / IsRenum / IsEnum / Name),就不是信息丢失: - # REnum 类型条目本身 BaseType=None,`T | t.CPtr` 合并后会被默认填成 CVoid,但 - # IsRenum/IsStruct/Name 齐备,_ctype_to_llvm 仍能解析出 %.T* —— 误判会 - # 把合法注解(如 REnum 变体载荷 `Pointee: rawptr[LLVMType]`)打成致命错误。 - if (getattr(Info, 'IsStruct', False) or getattr(Info, 'IsRenum', False) - or getattr(Info, 'IsEnum', False) or getattr(Info, 'Name', None)): - return - Inner: ast.AST | None = None - if isinstance(Annotation, ast.Subscript) and IsPtrSubscriptNode(Annotation, self.Trans.SymbolTable): - Inner = Annotation.slice - elif isinstance(Annotation, ast.BinOp) and isinstance(Annotation.op, ast.BitOr): - for Side in FlattenUnionOperands(Annotation): - if not IsCPtrMarkerNode(Side, self.Trans.SymbolTable): - Inner = Side - break - if Inner is None: - return - InnerName: str | None = None - if isinstance(Inner, ast.Name): - InnerName = Inner.id - elif isinstance(Inner, ast.Attribute): - InnerName = Inner.attr - if not InnerName or InnerName in ('rawptr', 'CPtr'): - return - Gen: Any = getattr(self.Trans, 'LlvmGen', None) - if Gen is None: - return - if (InnerName not in getattr(Gen, 'structs', {}) - and InnerName not in getattr(Gen, '_struct_sha1_map', {})): - return - _e: Any = None - try: - _e = self.Trans.SymbolTable.lookup(InnerName) - except Exception: - _e = None - raise ValueError( - f"[FATAL] 成员 '{MemberName}' 的注解 '{AnnotationText}' 内层 '{InnerName}' " - f"是**已定义类**,却被解析成通用指针(类型信息丢失)—— 拒绝静默降级:" - f"解析结果 BaseType={Info.BaseType} PtrCount={getattr(Info, 'PtrCount', None)} " - f"IsStruct={getattr(Info, 'IsStruct', None)} IsTypedef={getattr(Info, 'IsTypedef', None)} " - f"Name={getattr(Info, 'Name', None)};" - f"符号表['{InnerName}'] => IsTypedef={getattr(_e, 'IsTypedef', None)} " - f"BaseType={getattr(_e, 'BaseType', None)} PtrCount={getattr(_e, 'PtrCount', None)} " - f"IsStruct={getattr(_e, 'IsStruct', None)} IsRenum={getattr(_e, 'IsRenum', None)} " - f"IsModuleAlias={getattr(_e, 'IsModuleAlias', None)} " - f"IsEnumMember={getattr(_e, 'IsEnumMember', None)};" - f"Gen.structs 命中={'有' if InnerName in getattr(Gen, 'structs', {}) else '无'} " - f"_struct_sha1_map 命中={getattr(Gen, '_struct_sha1_map', {}).get(InnerName)!r}" - ) - - def LookupFunctionSymbol(self, name: str, *, module_path: str | None = None) -> CTypeInfo | None: - """查找函数符号,先尝试 module_path.name,再回退到 name。 - - 统一处理 HandlesExprCall / HandlesAssign 中重复的二级查找模式。 - 返回 CTypeInfo(不限定 IsFunction),由调用方检查标志位。 - """ - st = self.Trans.SymbolTable - if module_path: - sym: CTypeInfo | None = st.lookup(f'{module_path}.{name}') - if sym: - return sym - return st.lookup(name) - - def BuildLLVMFuncTypeFromSig( - self, - sym_info: CTypeInfo, - Gen: Any, - *, - mangled_name: str | None = None, - use_infer_fallback: bool = False, - ) -> tuple[Any, list[Any], bool] | None: - """从 CTypeInfo 函数签名构建 LLVM 函数类型。 - - 返回 (ret_type, llvm_param_types, is_variadic),非函数返回 None。 - 统一处理 FuncPtrReturn/FuncPtrParams → ToLLVM 转换、VoidType 回退。 - - Args: - mangled_name: 用于 _infer_return_type + stub 查找回退 - use_infer_fallback: 为 True 时启用 _infer_return_type + stub 查找回退链 - """ - if not sym_info.IsFunction: - return None - is_variadic: bool = sym_info.IsVariadic - ret_type_info = sym_info.FuncPtrReturn - if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType: - ret_type: Any = ret_type_info.ToLLVM(Gen) - elif use_infer_fallback and mangled_name: - inferred: Any = Gen._infer_return_type(mangled_name) - if isinstance(inferred, _ir.IntType) and inferred.width == 32: - try: - stub_ft: Any = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) - if stub_ft and hasattr(stub_ft, 'return_type'): - inferred = stub_ft.return_type - except Exception: - pass - ret_type = inferred - else: - ret_type = Gen._CType2LLVM('i32', False) - param_type_infos = list(sym_info.FuncPtrParams or []) - if isinstance(ret_type, _ir.VoidType): - ret_type = _ir.IntType(32) - llvm_param_types: list[Any] = [] - for ParamIdx, (ParamName, pt) in enumerate(param_type_infos): - if isinstance(pt, CTypeInfo) and pt.BaseType: - lp: Any = pt.ToLLVM(Gen) - elif isinstance(pt, str): - lp = Gen._CType2LLVM(pt, '*' in pt) - else: - # fast-fail:形参类型未解析出 BaseType 时不再硬编码 'i32' 造形参。 - # 旧回退会把"类型解析失败"伪装成 llvmlite 的 - # Type of #{N} arg mismatch: {expected} != {actual} - # (形参被静默写成 i32 ⇒ 报错方向与真因相反,且不含符号/位置信息, - # 如 "Type of #2 arg mismatch: i32 != %".SymbolTable"*"), - # 这里直接抛出带上下文的编译错误暴露真实缺失。 - if isinstance(pt, CTypeInfo): - _PtDiag: str = ( - f"CTypeInfo(BaseType=None, Name={pt.Name!r}, PtrCount={pt.PtrCount}," - f" IsTypedef={pt.IsTypedef}, IsFuncPtr={pt.IsFuncPtr}, IsState={pt.IsState}," - f" OriginalType={pt.OriginalType!r})") - else: - _PtDiag = f"{type(pt).__name__}({pt!r})" - raise TypeError( - f"[fast-fail] 函数 '{sym_info.Name}' 第 {ParamIdx + 1} 个形参 '{ParamName}' 的" - f"类型未解析出 BaseType,无法构建 LLVM 形参类型({_PtDiag});" - f"定义位置={getattr(sym_info, 'file', None)!r}:{sym_info.Lineno}," - f" IsVariadic={is_variadic}, mangled_name={mangled_name!r}" - ) - if isinstance(lp, _ir.VoidType): - lp = _ir.IntType(8).as_pointer() - llvm_param_types.append(lp) - return ret_type, llvm_param_types, is_variadic - - def GetOrCreateFuncDecl( - self, - name: str, - ret_type: Any, - param_types: list[Any], - Gen: Any, - *, - is_variadic: bool = False, - replace_name: str | None = None, - ) -> Any: - """创建或替换 LLVM 函数声明。 - - 若 name 已存在且类型不匹配,通过 _replace_function_decl 替换。 - replace_name 传递给 _replace_function_decl(通常为未 mangle 的 func_name)。 - """ - if name not in Gen.functions: - # Gen.functions 未登记、但 module.globals 已有同名函数(本模块其它调用点 - # 或依赖 stub 先建过声明)→ 复用并登记,绝不重建:llvmlite 的 NameScope - # 按名字去重(GlobalValue.deduplicate_name=False),重复 ir.Function 必抛 - # DuplicatedNameError。 - g_existing: Any = Gen.module.globals.get(name) - if g_existing is not None: - Gen.functions[name] = g_existing - return g_existing - func_type = _ir.FunctionType(ret_type, param_types, var_arg=is_variadic) - func_decl = Gen._CreateOrReuseFuncDecl(name, func_type) - Gen.functions[name] = func_decl - else: - existing = Gen.functions[name] - existing_type = getattr(existing, 'ftype', None) - new_type = _ir.FunctionType(ret_type, param_types, var_arg=is_variadic) - if existing_type and existing_type != new_type: - replaced = self.Trans.FunctionHandler._replace_function_decl(Gen, replace_name or name, new_type) - if replaced and replaced != existing: - Gen.functions[name] = replaced - return Gen.functions[name] - - def GetOrCreateStubFuncDecl( - self, - decl_name: str, - stub_func_type: Any, - Gen: Any, - *, - alias: str | None = None, - check_existing: bool = True, - ) -> Any: - """创建 stub 函数声明,可选注册别名。 - - check_existing=False 时跳过 Gen.functions 存在性检查(用于确定不存在的场景), - 但**仍复用 module.globals 里的同名声明**——llvmlite 的 NameScope 按名字去重 - (GlobalValue.deduplicate_name=False),"强制新建同名 global"在 llvmlite 下 - 不成立、必抛 DuplicatedNameError,故此路必须幂等。 - 真实事故:同一模块两处 `self.Gen.ExprGen.translate_value(...)`(VarGenMixin.vp) - 都走 check_existing=False,第一处建声明成功、第二处抛 DuplicatedNameError。 - """ - if check_existing and decl_name in Gen.functions: - func_decl = Gen.functions[decl_name] - else: - g_ex_decl: Any = Gen.module.globals.get(decl_name) - if g_ex_decl is not None: - func_decl = g_ex_decl - else: - func_decl = Gen._CreateOrReuseFuncDecl(decl_name, stub_func_type) - Gen.functions[decl_name] = func_decl - if alias and alias != decl_name: - if not check_existing or alias not in Gen.functions: - Gen.functions[alias] = func_decl - return func_decl - - + def CollectAnnAssignMember( + self, item: ast.AST, Gen: Any + ) -> tuple[str, _ir.Type, CTypeInfo | None] | None: + """从 ast.AnnAssign 节点解析成员信息。 + + 返回 (VarName, MemberType, TypeInfo)。 + + **不再静默兜底**(原实现:注解解析返回 None → i32;_ctype_to_llvm 得 void → i8*; + 任何异常 → i32 + TypeInfo=None)。这些兜底会把"注解没解析出来"伪装成一个 + 具体类型,下游于是报出与真因无关的错——实测:`free_lists: uint64*`(脱糖为 + `uint64 | t.CPtr`)在解析异常时被当成 **i32**,`self.free_lists = base`(i8*) + 便误报"指针隐式转整数已禁止"。类型信息缺失一律响亮失败。 + 非 AnnAssign/Name 目标时返回 None。 + """ + if not isinstance(item, ast.AnnAssign) or not isinstance(item.target, ast.Name): + return None + VarName: str = item.target.id + AnnotationText: str = ast.unparse(item.annotation) + # 值引用标记(`&T` / `&temp T`)仅用于函数参数位:结构体字段/成员注解出现即 fail-fast。 + if FindAutoAddrMode(item.annotation) is not None: + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}':结构体字段/成员不支持" + f"值引用(`&T` / `&temp T`)——值引用仅用于函数参数位,请改用显式指针类型(如 `T*`)") + try: + TypeInfo: CTypeInfo | None = self.ResolveAnnotationType(item.annotation) + except RecursionError: + # RecursionError 不是"可忽略异常":它是类型解析/声明发射链**成环**的 + # 真缺陷信号。此前被下方 `except Exception` 吞成一行 + # `WARN: HandlesBase: 忽略异常 maximum recursion depth exceeded` 并静默 + # 退化为 i32 ⇒ 真正的环(如跨模块类声明发射重入 + # _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub ↔ _TryLoadClassMembersFromPyi) + # 被掩盖,下游只看到类型解析静默退化。此处 fail-fast 上抛(保留原始栈), + # 让环路根因暴露在错误栈顶,不再被 WARN 掩埋。 + raise + except Exception as e: + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析失败:" + f"{type(e).__name__}: {e} —— 拒绝静默退化为 i32" + ) from e + if TypeInfo is None: + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析不出类型" + f"(ResolveAnnotationType 返回 None)—— 拒绝静默退化为 i32" + ) + MemberType: _ir.Type = Gen._ctype_to_llvm(TypeInfo) + if isinstance(MemberType, _ir.VoidType): + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 解析为 void" + f"(BaseType={getattr(TypeInfo, 'BaseType', None)} " + f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} " + f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} " + f"Name={getattr(TypeInfo, 'Name', None)})—— 拒绝静默退化为 i8*" + ) + # 「内层是类引用却解析成通用指针」fail-fast(降级源头定位): + # `rawptr[Mod.Class]` / `Mod.Class | t.CPtr` 的语义是"指向该类",内层若确实是 + # 已定义类,解析结果必须带该类 BaseType;退化成 void 基座(i8* / 多一层 i8**) + # 就是类型信息被丢掉 —— 该值一旦进 class_members 就会被冻进 struct body + # (llvmlite set_body 只能设一次),后期只修表不修体(现场:Generator 第 3/11 + # 槽 i8**)。此处响亮失败并附判据,使降级**当场暴露**而非留到赋值时报"层数不同"。 + self._CheckPtrAnnotationKeepsClass(item.annotation, TypeInfo, VarName, AnnotationText) + # 形状判据(更硬的一条):注解形态是「**指针包装的类引用**」而解析结果正是 + # `i8**`(= void 基座两层,T 的类型被丢掉)⇒ 必属降级,直接响亮失败并把两侧 + # 证据打全。放在字段类型的**唯一漏斗**里,覆盖全部"按注解推导成员类型"的写入链 + # (本地类 / 跨模块 .vpi / 嵌套类)。 + if (IsClassRefPtrAnnotation(item.annotation, self.Trans.SymbolTable) + and IsI8DoublePtrType(MemberType)): + _inner_name: str = '' + _inner_node: Any = (item.annotation.slice + if isinstance(item.annotation, ast.Subscript) else None) + if isinstance(_inner_node, ast.Attribute): + _inner_name = _inner_node.attr + elif isinstance(_inner_node, ast.Name): + _inner_name = _inner_node.id + _sym: Any = None + try: + _sym = self.Trans.SymbolTable.lookup(_inner_name) if _inner_name else None + except Exception: + _sym = None + _gen: Any = getattr(self.Trans, 'LlvmGen', None) + _structs_hit: str = ( + '有' if (_gen is not None and _inner_name in getattr(_gen, 'structs', {})) else '无') + _sha1_hit: Any = ( + getattr(_gen, '_struct_sha1_map', {}).get(_inner_name) if _gen is not None else None) + raise ValueError( + f"[FATAL] 成员 '{VarName}' 的注解 '{AnnotationText}' 是**类引用指针**," + f"却被解析成 {MemberType}(void 基座两层 = 类型信息丢失)—— 拒绝降级:" + f"CTypeInfo(BaseType={getattr(TypeInfo, 'BaseType', None)} " + f"PtrCount={getattr(TypeInfo, 'PtrCount', None)} " + f"IsStruct={getattr(TypeInfo, 'IsStruct', None)} " + f"IsTypedef={getattr(TypeInfo, 'IsTypedef', None)} " + f"IsEnum={getattr(TypeInfo, 'IsEnum', None)} " + f"Name={getattr(TypeInfo, 'Name', None)});内层名='{_inner_name}' " + f"符号表[该名] => IsTypedef={getattr(_sym, 'IsTypedef', None)} " + f"BaseType={getattr(_sym, 'BaseType', None)} " + f"PtrCount={getattr(_sym, 'PtrCount', None)} " + f"IsStruct={getattr(_sym, 'IsStruct', None)} " + f"IsRenum={getattr(_sym, 'IsRenum', None)} " + f"IsModuleAlias={getattr(_sym, 'IsModuleAlias', None)};" + f"Gen.structs 命中={_structs_hit} _struct_sha1_map 命中={_sha1_hit!r}" + ) + return VarName, MemberType, TypeInfo + + def _CheckPtrAnnotationKeepsClass(self, Annotation: ast.AST, Info: CTypeInfo | None, + MemberName: str, AnnotationText: str) -> None: + """指针包装注解的内层是**已定义类**时,解析结果不得退化成 void 基座(fail-fast)。 + + 只拦"必属类型信息丢失"的组合:内层节点(Name / Attribute)的名字在 + `Gen.structs`(本模块已建)或 `Gen._struct_sha1_map`(预扫描的类→模块映射) + 中有类定义,而解析出的 `BaseType` 是 `t.CVoid` **且没有任何类身份** + (IsStruct / IsRenum / IsEnum / Name 全空)。其余一律放行 + (`rawptr[rawptr]` 这类本就 void 基座的多层指针、模块名、泛型特化实参等)。 + """ + if Info is None or Info.BaseType is None: + return + if not (Info.BaseType is t.CVoid or isinstance(Info.BaseType, t.CVoid)): + return + # 只要还带着任何"类身份"(IsStruct / IsRenum / IsEnum / Name),就不是信息丢失: + # REnum 类型条目本身 BaseType=None,`T | t.CPtr` 合并后会被默认填成 CVoid,但 + # IsRenum/IsStruct/Name 齐备,_ctype_to_llvm 仍能解析出 %.T* —— 误判会 + # 把合法注解(如 REnum 变体载荷 `Pointee: rawptr[LLVMType]`)打成致命错误。 + if (getattr(Info, 'IsStruct', False) or getattr(Info, 'IsRenum', False) + or getattr(Info, 'IsEnum', False) or getattr(Info, 'Name', None)): + return + Inner: ast.AST | None = None + if isinstance(Annotation, ast.Subscript) and IsPtrSubscriptNode(Annotation, self.Trans.SymbolTable): + Inner = Annotation.slice + elif isinstance(Annotation, ast.BinOp) and isinstance(Annotation.op, ast.BitOr): + for Side in FlattenUnionOperands(Annotation): + if not IsCPtrMarkerNode(Side, self.Trans.SymbolTable): + Inner = Side + break + if Inner is None: + return + InnerName: str | None = None + if isinstance(Inner, ast.Name): + InnerName = Inner.id + elif isinstance(Inner, ast.Attribute): + InnerName = Inner.attr + if not InnerName or InnerName in ('rawptr', 'CPtr'): + return + Gen: Any = getattr(self.Trans, 'LlvmGen', None) + if Gen is None: + return + if (InnerName not in getattr(Gen, 'structs', {}) + and InnerName not in getattr(Gen, '_struct_sha1_map', {})): + return + _e: Any = None + try: + _e = self.Trans.SymbolTable.lookup(InnerName) + except Exception: + _e = None + raise ValueError( + f"[FATAL] 成员 '{MemberName}' 的注解 '{AnnotationText}' 内层 '{InnerName}' " + f"是**已定义类**,却被解析成通用指针(类型信息丢失)—— 拒绝静默降级:" + f"解析结果 BaseType={Info.BaseType} PtrCount={getattr(Info, 'PtrCount', None)} " + f"IsStruct={getattr(Info, 'IsStruct', None)} IsTypedef={getattr(Info, 'IsTypedef', None)} " + f"Name={getattr(Info, 'Name', None)};" + f"符号表['{InnerName}'] => IsTypedef={getattr(_e, 'IsTypedef', None)} " + f"BaseType={getattr(_e, 'BaseType', None)} PtrCount={getattr(_e, 'PtrCount', None)} " + f"IsStruct={getattr(_e, 'IsStruct', None)} IsRenum={getattr(_e, 'IsRenum', None)} " + f"IsModuleAlias={getattr(_e, 'IsModuleAlias', None)} " + f"IsEnumMember={getattr(_e, 'IsEnumMember', None)};" + f"Gen.structs 命中={'有' if InnerName in getattr(Gen, 'structs', {}) else '无'} " + f"_struct_sha1_map 命中={getattr(Gen, '_struct_sha1_map', {}).get(InnerName)!r}" + ) + + def LookupFunctionSymbol(self, name: str, *, module_path: str | None = None) -> CTypeInfo | None: + """查找函数符号,先尝试 module_path.name,再回退到 name。 + + 统一处理 HandlesExprCall / HandlesAssign 中重复的二级查找模式。 + 返回 CTypeInfo(不限定 IsFunction),由调用方检查标志位。 + """ + st = self.Trans.SymbolTable + if module_path: + sym: CTypeInfo | None = st.lookup(f'{module_path}.{name}') + if sym: + return sym + return st.lookup(name) + + def BuildLLVMFuncTypeFromSig( + self, + sym_info: CTypeInfo, + Gen: Any, + *, + mangled_name: str | None = None, + use_infer_fallback: bool = False, + ) -> tuple[Any, list[Any], bool] | None: + """从 CTypeInfo 函数签名构建 LLVM 函数类型。 + + 返回 (ret_type, llvm_param_types, is_variadic),非函数返回 None。 + 统一处理 FuncPtrReturn/FuncPtrParams → ToLLVM 转换、VoidType 回退。 + + Args: + mangled_name: 用于 _infer_return_type + stub 查找回退 + use_infer_fallback: 为 True 时启用 _infer_return_type + stub 查找回退链 + """ + if not sym_info.IsFunction: + return None + is_variadic: bool = sym_info.IsVariadic + ret_type_info = sym_info.FuncPtrReturn + if isinstance(ret_type_info, CTypeInfo) and ret_type_info.BaseType: + ret_type: Any = ret_type_info.ToLLVM(Gen) + elif use_infer_fallback and mangled_name: + inferred: Any = Gen._infer_return_type(mangled_name) + if isinstance(inferred, _ir.IntType) and inferred.width == 32: + try: + stub_ft: Any = self.Trans.ImportHandler._LookupStubFuncType(mangled_name, Gen) + if stub_ft and hasattr(stub_ft, 'return_type'): + inferred = stub_ft.return_type + except Exception: + pass + ret_type = inferred + else: + ret_type = Gen._CType2LLVM('i32', False) + param_type_infos = list(sym_info.FuncPtrParams or []) + if isinstance(ret_type, _ir.VoidType): + ret_type = _ir.IntType(32) + llvm_param_types: list[Any] = [] + for ParamIdx, (ParamName, pt) in enumerate(param_type_infos): + if isinstance(pt, CTypeInfo) and pt.BaseType: + lp: Any = pt.ToLLVM(Gen) + elif isinstance(pt, str): + lp = Gen._CType2LLVM(pt, '*' in pt) + else: + # fast-fail:形参类型未解析出 BaseType 时不再硬编码 'i32' 造形参。 + # 旧回退会把"类型解析失败"伪装成 llvmlite 的 + # Type of #{N} arg mismatch: {expected} != {actual} + # (形参被静默写成 i32 ⇒ 报错方向与真因相反,且不含符号/位置信息, + # 如 "Type of #2 arg mismatch: i32 != %".SymbolTable"*"), + # 这里直接抛出带上下文的编译错误暴露真实缺失。 + if isinstance(pt, CTypeInfo): + _PtDiag: str = ( + f"CTypeInfo(BaseType=None, Name={pt.Name!r}, PtrCount={pt.PtrCount}," + f" IsTypedef={pt.IsTypedef}, IsFuncPtr={pt.IsFuncPtr}, IsState={pt.IsState}," + f" OriginalType={pt.OriginalType!r})") + else: + _PtDiag = f"{type(pt).__name__}({pt!r})" + raise TypeError( + f"[fast-fail] 函数 '{sym_info.Name}' 第 {ParamIdx + 1} 个形参 '{ParamName}' 的" + f"类型未解析出 BaseType,无法构建 LLVM 形参类型({_PtDiag});" + f"定义位置={getattr(sym_info, 'file', None)!r}:{sym_info.Lineno}," + f" IsVariadic={is_variadic}, mangled_name={mangled_name!r}" + ) + if isinstance(lp, _ir.VoidType): + lp = _ir.IntType(8).as_pointer() + llvm_param_types.append(lp) + return ret_type, llvm_param_types, is_variadic + + def GetOrCreateFuncDecl( + self, + name: str, + ret_type: Any, + param_types: list[Any], + Gen: Any, + *, + is_variadic: bool = False, + replace_name: str | None = None, + ) -> Any: + """创建或替换 LLVM 函数声明。 + + 若 name 已存在且类型不匹配,通过 _replace_function_decl 替换。 + replace_name 传递给 _replace_function_decl(通常为未 mangle 的 func_name)。 + """ + if name not in Gen.functions: + # Gen.functions 未登记、但 module.globals 已有同名函数(本模块其它调用点 + # 或依赖 stub 先建过声明)→ 复用并登记,绝不重建:llvmlite 的 NameScope + # 按名字去重(GlobalValue.deduplicate_name=False),重复 ir.Function 必抛 + # DuplicatedNameError。 + g_existing: Any = Gen.module.globals.get(name) + if g_existing is not None: + Gen.functions[name] = g_existing + return g_existing + func_type = _ir.FunctionType(ret_type, param_types, var_arg=is_variadic) + func_decl = Gen._CreateOrReuseFuncDecl(name, func_type) + Gen.functions[name] = func_decl + else: + existing = Gen.functions[name] + existing_type = getattr(existing, 'ftype', None) + new_type = _ir.FunctionType(ret_type, param_types, var_arg=is_variadic) + if existing_type and existing_type != new_type: + replaced = self.Trans.FunctionHandler._replace_function_decl(Gen, replace_name or name, new_type) + if replaced and replaced != existing: + Gen.functions[name] = replaced + return Gen.functions[name] + + def GetOrCreateStubFuncDecl( + self, + decl_name: str, + stub_func_type: Any, + Gen: Any, + *, + alias: str | None = None, + check_existing: bool = True, + ) -> Any: + """创建 stub 函数声明,可选注册别名。 + + check_existing=False 时跳过 Gen.functions 存在性检查(用于确定不存在的场景), + 但**仍复用 module.globals 里的同名声明**——llvmlite 的 NameScope 按名字去重 + (GlobalValue.deduplicate_name=False),"强制新建同名 global"在 llvmlite 下 + 不成立、必抛 DuplicatedNameError,故此路必须幂等。 + 真实事故:同一模块两处 `self.Gen.ExprGen.translate_value(...)`(VarGenMixin.vp) + 都走 check_existing=False,第一处建声明成功、第二处抛 DuplicatedNameError。 + """ + if check_existing and decl_name in Gen.functions: + func_decl = Gen.functions[decl_name] + else: + g_ex_decl: Any = Gen.module.globals.get(decl_name) + if g_ex_decl is not None: + func_decl = g_ex_decl + else: + func_decl = Gen._CreateOrReuseFuncDecl(decl_name, stub_func_type) + Gen.functions[decl_name] = func_decl + if alias and alias != decl_name: + if not check_existing or alias not in Gen.functions: + Gen.functions[alias] = func_decl + return func_decl + + StrictMode: bool = _config.mode == 'strict' \ No newline at end of file diff --git a/lib/core/Handles/HandlesClassDef.py b/lib/core/Handles/HandlesClassDef.py index f2e7907..b01a06e 100644 --- a/lib/core/Handles/HandlesClassDef.py +++ b/lib/core/Handles/HandlesClassDef.py @@ -1,93 +1,93 @@ -from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin -import vast as ast -import copy -import sys -import llvmlite.ir as ir -from lib.core.Exportable import EnumMember as ExportEnumMember -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +import vast as ast +import copy +import sys +import llvmlite.ir as ir +from lib.core.Exportable import EnumMember as ExportEnumMember +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta from lib.core.SymbolUtils import ( IsTModule, FindStructNameInAnnotationWithContext, MangleTypeArgByRegistry, EnumClassIntWidth, EnumBodyMemberNames, EnumMemberCountMismatchError, - ClassHasFuncLevelCVTable, IsVtableSlotMethod) -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.includes import t -from lib.includes.t import CTypeRegistry - -# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CAutoPtr,后者继承 -# CPtr)、值引用标记(CRef/CTempRef,`&T`/`&temp T`)、内建 rawptr(= t.CPtr)、 -# str/bytes(= i8*)。枚举值是整数常量,指针成员语义不成立,命中即明确报错 -# (不静默当 int)。 -_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({ - 'CPtr', 'CAutoPtr', 'CRef', 'CTempRef', 'rawptr', 'str', 'bytes', -}) - - -class ClassHandle(BaseHandle): - def _is_exception_class(self, Node: ast.ClassDef) -> bool: - if not Node.bases: - return False - for base in Node.bases: - if hasattr(base, 'id'): - if base.id == 'Exception': - return True - if base.id in self.Trans.exception_registry: - return True - elif hasattr(base, 'attr'): - if base.attr == 'Exception': - return True - if base.attr in self.Trans.exception_registry: - return True - return False - - def _get_exception_parent(self, Node: ast.ClassDef) -> str | None: - if not Node.bases: - return None - for base in Node.bases: - if hasattr(base, 'id'): - if base.id != 'Exception' and base.id in self.Trans.exception_registry: - return base.id - elif hasattr(base, 'attr'): - if base.attr != 'Exception' and base.attr in self.Trans.exception_registry: - return base.attr - return None - - def _RegisterExceptionClass(self, Node: ast.ClassDef) -> None: - ClassName: str = Node.name - if ClassName in self.Trans.exception_registry: - return - code: int = self.Trans._next_exception_code - self.Trans._next_exception_code += 1 - self.Trans.exception_registry[ClassName] = code - parent: str | None = self._get_exception_parent(Node) - if parent: - self.Trans.exception_parents[ClassName] = parent - ExcTypeInfo: CTypeInfo = CTypeInfo() - ExcTypeInfo.Name = ClassName - ExcTypeInfo.IsExceptionClass = True - ExcTypeInfo.value = code - self.Trans.SymbolTable.insert(ClassName, ExcTypeInfo) - - def _is_generic_class(self, Node: ast.ClassDef) -> bool: - if hasattr(Node, 'type_params') and Node.type_params: - return True - return False - - def _mangle_generic_class_name(self, class_name: str, type_args: list[str]) -> str: - """泛型类特化名:class_name + '[arg1][arg2]'。 - - 单项 mangling 走 SymbolUtils.MangleTypeArgByRegistry(声明端 - DeclarationGenerator 的泛型分支以 ResolveGenericTypeArgStr 调用同一函数, - 两侧拼名规则唯一),'][' 分隔与 HandlesTypeMerge / DeclarationGenerator 一致。 - """ - mangled_args: list[str] = [MangleTypeArgByRegistry(ta) for ta in type_args] - return class_name + '[' + ']['.join(mangled_args) + ']' - + ClassHasFuncLevelCVTable, IsVtableSlotMethod) +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes import t +from lib.includes.t import CTypeRegistry + +# 枚举成员不允许的指针形态名:t.CPtr 家族(CPtr/CAutoPtr,后者继承 +# CPtr)、值引用标记(CRef/CTempRef,`&T`/`&temp T`)、内建 rawptr(= t.CPtr)、 +# str/bytes(= i8*)。枚举值是整数常量,指针成员语义不成立,命中即明确报错 +# (不静默当 int)。 +_ENUM_PTR_ANNOTATION_NAMES: frozenset[str] = frozenset({ + 'CPtr', 'CAutoPtr', 'CRef', 'CTempRef', 'rawptr', 'str', 'bytes', +}) + + +class ClassHandle(BaseHandle): + def _is_exception_class(self, Node: ast.ClassDef) -> bool: + if not Node.bases: + return False + for base in Node.bases: + if hasattr(base, 'id'): + if base.id == 'Exception': + return True + if base.id in self.Trans.exception_registry: + return True + elif hasattr(base, 'attr'): + if base.attr == 'Exception': + return True + if base.attr in self.Trans.exception_registry: + return True + return False + + def _get_exception_parent(self, Node: ast.ClassDef) -> str | None: + if not Node.bases: + return None + for base in Node.bases: + if hasattr(base, 'id'): + if base.id != 'Exception' and base.id in self.Trans.exception_registry: + return base.id + elif hasattr(base, 'attr'): + if base.attr != 'Exception' and base.attr in self.Trans.exception_registry: + return base.attr + return None + + def _RegisterExceptionClass(self, Node: ast.ClassDef) -> None: + ClassName: str = Node.name + if ClassName in self.Trans.exception_registry: + return + code: int = self.Trans._next_exception_code + self.Trans._next_exception_code += 1 + self.Trans.exception_registry[ClassName] = code + parent: str | None = self._get_exception_parent(Node) + if parent: + self.Trans.exception_parents[ClassName] = parent + ExcTypeInfo: CTypeInfo = CTypeInfo() + ExcTypeInfo.Name = ClassName + ExcTypeInfo.IsExceptionClass = True + ExcTypeInfo.value = code + self.Trans.SymbolTable.insert(ClassName, ExcTypeInfo) + + def _is_generic_class(self, Node: ast.ClassDef) -> bool: + if hasattr(Node, 'type_params') and Node.type_params: + return True + return False + + def _mangle_generic_class_name(self, class_name: str, type_args: list[str]) -> str: + """泛型类特化名:class_name + '[arg1][arg2]'。 + + 单项 mangling 走 SymbolUtils.MangleTypeArgByRegistry(声明端 + DeclarationGenerator 的泛型分支以 ResolveGenericTypeArgStr 调用同一函数, + 两侧拼名规则唯一),'][' 分隔与 HandlesTypeMerge / DeclarationGenerator 一致。 + """ + mangled_args: list[str] = [MangleTypeArgByRegistry(ta) for ta in type_args] + return class_name + '[' + ']['.join(mangled_args) + ']' + def _HasClassDefinition(self, Name: str, Gen: LlvmGeneratorMixin) -> bool: """类型名是否有类定义——泛型"类类型参数"的唯一判据。 @@ -125,166 +125,166 @@ class ClassHandle(BaseHandle): return Node.attr return None - def _specialize_generic_class(self, ClassName: str, type_args: list[str], Gen: LlvmGeneratorMixin, type_names: list[str] | None = None, declare_only: bool = False) -> str | None: - if not hasattr(self, '_generic_class_templates') or ClassName not in self._generic_class_templates: - return None - # 防御:共享符号表构建阶段 Gen 可能为 None,此时无法发射特化 IR - if Gen is None: - return None - template: dict = self._generic_class_templates[ClassName] - Node: ast.ClassDef = template['node'] - type_param_names: list[str] = template['type_params'] - if len(type_args) != len(type_param_names): - return None - spec_key: str = ClassName + '<' + ','.join(type_args) + '>' - # 缓存必须在 Gen 上(每个模块独立),不能在 ClassHandler 上跨模块共享, - # 否则临时 Gen(如共享符号表构建阶段)的特化会污染正式翻译阶段的缓存 - if not hasattr(Gen, '_generic_class_specializations'): - Gen._generic_class_specializations = {} - if spec_key in Gen._generic_class_specializations: - cached: object = Gen._generic_class_specializations[spec_key] - # 缓存格式: (spec_name, was_declare_only) - if isinstance(cached, tuple): - cached_name: str = cached[0] - cached_declare_only: bool = cached[1] - # 完整特化请求但缓存只有 declare_only 版本 → 需要补发方法体 - if not declare_only and cached_declare_only: - spec_name = cached_name - # 标记为正在完整特化,防止递归 - Gen._generic_class_specializations[spec_key] = (spec_name, False) - else: - return cached_name - else: - # 旧格式(纯字符串):直接返回 - return cached - else: - spec_name: str = self._mangle_generic_class_name(ClassName, type_args) - # 预注册到缓存,防止特化过程中方法体引用自身类型导致无限递归 - Gen._generic_class_specializations[spec_key] = (spec_name, declare_only) - # 内建类型名集合(必须在 type_map/cptr_params 检测之前定义) - _BUILTIN_TYPE_NAMES: set = { - 'CInt', 'CUInt', 'CInt8T', 'CUInt8T', 'CInt16T', 'CUInt16T', - 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CChar', 'CShort', - 'CLong', 'CDouble', 'CFloat', 'CFloat32T', 'CFloat64T', 'CBool', - 'CVoid', 'CSizeT', 'CSSizeT', 'CPtr', 'CArray', 'CEnum', 'CUnion', - 'CStruct', 'CDefine', - 'int', 'double', 'float', 'i8', 'i16', 'i32', 'i64', 'void', - 'half', 'fp128', 'char', 'bool', - } - # 值类型/类类型不再用"内建名白名单"判定:见下方 class_type_names, - # 唯一判据是"是否有类定义"(_HasClassDefinition:Gen._struct_sha1_map - # / Gen.structs)。内建(int/CInt/…)与语言内建 `str`(LLVM 映射 i8*) - # 都不在类表里 → 天然判为值类型,无需名表。 - # 历史教训(名表判据的代价,保留以便回归时理解):`str` 曾漏在 - # "非类类型名"集合外 → `list[str]` 的类型参数被误判为类类型,特化后 - # 方法签名被额外包一层 `| t.CPtr`(append/set 形参 i8**、get/pop 返回 - # i8**)→ 调用点多解一层 load(读字符串首 8 字节)→ ASan 崩溃。 - # 注意 _BUILTIN_TYPE_NAMES 本身仍保留:它用于 type_map 替换 - # 与 _t_c_imported_names 注册,不能把 'str' 并进去 - # (否则 HandlesTypeMerge 会把注解 `str` 当 `t.str` 解析)。 - type_map: dict[str, str] = {} - for i, tp_name in enumerate(type_param_names): - if type_names and i < len(type_names): - tn: str = type_names[i] - # 判断是否为 t 模块内建类型(裸名或 't.' 前缀,如 'CInt'/'t.CInt'): - # 内建类型必须用源码名(type_name)替换 T(t.CInt → i32),否则 - # 用 mangled type_arg(如 'i32')替换会产生 'i32.__sizeof__' 这类 - # 源码中不存在的名称([FATAL][__sizeof__] 类型 'i32' 无法解析)。 - # 真正的类类型参数(如 'AST'/'ast.AST')才用 mangled type_arg - # (带 sha1 前缀 + ptr 后缀),避免模板内部嵌套注解被替换成裸短名 - # 后重新特化出只有 declare、无方法体的错误特化(list[AST].__len__ - # undefined symbol 根因)。_make_replacement_node 会把 mangled 名 - # 还原为 'sha1.AST | t.CPtr',重新解析时拼回一致的 mangled 名。 - tn_bare: str = tn.split('.')[-1] if '.' in tn else tn - if tn_bare in _BUILTIN_TYPE_NAMES: - type_map[tp_name] = tn - elif i < len(type_args): - type_map[tp_name] = type_args[i] - else: - type_map[tp_name] = tn - else: - type_map[tp_name] = type_args[i] - # 检测哪些类型参数在原始类型实参中带有指针语义(| t.CPtr / rawptr[T]) - # 当 type_arg 以 '*' 结尾(mangling 把每个 CPtr 层渲染为一层 '*')且对应的 - # type_name(去模块前缀后)不是内建类型名时,说明调用方写了 - # list[AST | t.CPtr] / list[rawptr[AST]]。注意用 tn_bare 判断: - # 't.CPtr' 去前缀后是 'CPtr'(内建),不算类类型参数。 - cptr_params: set[str] = set() - for i, tp_name in enumerate(type_param_names): - type_arg: str = type_args[i] - if type_arg.endswith('*'): - tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i] - tn_bare_c: str = tn.split('.')[-1] if '.' in tn else tn - if tn_bare_c not in _BUILTIN_TYPE_NAMES: - cptr_params.add(tp_name) - # Bug 2 修复:识别特化为 class 类型的类型参数 - # 这类参数在方法签名中是裸 T(无 | t.CPtr),特化后为裸 ClassName(值类型) - # 需要加 | t.CPtr 使其成为指针类型(class 实例应按指针传递) - # 判据 = "是否有类定义"(_HasClassDefinition),存**裸名**:注解侧的 - # 比较也用裸名(T 替换后的 'sha1.Param' 去前缀)——此前存带模块前缀的 - # tn(如 'ast.AST')而比较用裸名 'AST',前缀形态永不命中。 - class_type_names: set[str] = set() - for i, tp_name in enumerate(type_param_names): - tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i] - tn_bare_cls: str = tn.split('.')[-1] if '.' in tn else tn - if self._HasClassDefinition(tn_bare_cls, Gen): - class_type_names.add(tn_bare_cls) - # 仅对 t 模块内建类型名注册 _t_c_imported_names(如 CInt -> ('t','CInt')) - # 类名(如 AST)不应注册为 t.AST,否则会污染名称解析 - if type_names: - if not hasattr(self.Trans, '_t_c_imported_names'): - self.Trans._t_c_imported_names = {} - for tn in type_names: - if tn in _BUILTIN_TYPE_NAMES and tn not in self.Trans._t_c_imported_names: - self.Trans._t_c_imported_names[tn] = ('t', tn) - SpecNode: ast.ClassDef = copy.deepcopy(Node) - SpecNode.type_params = [] - SpecNode.name = spec_name - for item in SpecNode.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - if item.annotation: - item.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(item.annotation, type_map, cptr_params) - elif isinstance(item, ast.FunctionDef): - for arg in item.args.args: - if arg.annotation: - arg.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(arg.annotation, type_map, cptr_params) - # class 类型参数加指针语义:T -> GNode -> GNode | t.CPtr - # T 替换后的真实形态是 Attribute 链('.GNode' → - # Name('').GNode,见 _make_replacement_node/_make_name_node), - # 不是 ast.Name:此前只认 ast.Name → 方法形参漏加 | t.CPtr, - # 特化后按值结构体传参(被调者栈帧副本地址入链 → 悬垂写)。 - raw_name: str | None = self._BareClassAnnotationName(arg.annotation) - if raw_name is not None and raw_name in class_type_names: - arg.annotation = ast.BinOp( - left=arg.annotation, - op=ast.BitOr(), - right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - ) - if item.returns: - item.returns = self.Trans.FunctionHandler._replace_type_in_annotation(item.returns, type_map, cptr_params) - # 返回类型也加指针语义(同一裸名判据,形态同上) - raw_name: str | None = self._BareClassAnnotationName(item.returns) - if raw_name is not None and raw_name in class_type_names: - item.returns = ast.BinOp( - left=item.returns, - op=ast.BitOr(), - right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - ) - self.Trans.FunctionHandler._apply_type_map_to_body(item.body, type_map, cptr_params) - saved_builder: ir.IRBuilder = Gen.builder - saved_func: ir.Function = Gen.func - saved_variables: dict = dict(Gen.variables) if Gen.variables else {} - saved_direct_values: dict = dict(Gen._direct_values) if Gen._direct_values else {} - saved_var_type_info: dict = dict(Gen.var_type_info) if Gen.var_type_info else {} - saved_var_signedness: dict = dict(Gen.var_signedness) if Gen.var_signedness else {} - saved_global_vars: set = set(Gen.global_vars) if Gen.global_vars else set() - saved_var_scopes: list = [dict(s) for s in self.Trans.VarScopes] if self.Trans.VarScopes else [] - saved_block: ir.Block | None = None - if Gen.builder and Gen.builder.block and not Gen.builder.block.is_terminated: - saved_block = Gen.builder.block - # 切换 module_sha1 为泛型模板定义模块的 SHA1, - # 使结构体类型名和方法定义名都用定义模块的 SHA1 前缀, - # 与调用端 _struct_sha1_map 查到的 SHA1 一致。 + def _specialize_generic_class(self, ClassName: str, type_args: list[str], Gen: LlvmGeneratorMixin, type_names: list[str] | None = None, declare_only: bool = False) -> str | None: + if not hasattr(self, '_generic_class_templates') or ClassName not in self._generic_class_templates: + return None + # 防御:共享符号表构建阶段 Gen 可能为 None,此时无法发射特化 IR + if Gen is None: + return None + template: dict = self._generic_class_templates[ClassName] + Node: ast.ClassDef = template['node'] + type_param_names: list[str] = template['type_params'] + if len(type_args) != len(type_param_names): + return None + spec_key: str = ClassName + '<' + ','.join(type_args) + '>' + # 缓存必须在 Gen 上(每个模块独立),不能在 ClassHandler 上跨模块共享, + # 否则临时 Gen(如共享符号表构建阶段)的特化会污染正式翻译阶段的缓存 + if not hasattr(Gen, '_generic_class_specializations'): + Gen._generic_class_specializations = {} + if spec_key in Gen._generic_class_specializations: + cached: object = Gen._generic_class_specializations[spec_key] + # 缓存格式: (spec_name, was_declare_only) + if isinstance(cached, tuple): + cached_name: str = cached[0] + cached_declare_only: bool = cached[1] + # 完整特化请求但缓存只有 declare_only 版本 → 需要补发方法体 + if not declare_only and cached_declare_only: + spec_name = cached_name + # 标记为正在完整特化,防止递归 + Gen._generic_class_specializations[spec_key] = (spec_name, False) + else: + return cached_name + else: + # 旧格式(纯字符串):直接返回 + return cached + else: + spec_name: str = self._mangle_generic_class_name(ClassName, type_args) + # 预注册到缓存,防止特化过程中方法体引用自身类型导致无限递归 + Gen._generic_class_specializations[spec_key] = (spec_name, declare_only) + # 内建类型名集合(必须在 type_map/cptr_params 检测之前定义) + _BUILTIN_TYPE_NAMES: set = { + 'CInt', 'CUInt', 'CInt8T', 'CUInt8T', 'CInt16T', 'CUInt16T', + 'CInt32T', 'CUInt32T', 'CInt64T', 'CUInt64T', 'CChar', 'CShort', + 'CLong', 'CDouble', 'CFloat', 'CFloat32T', 'CFloat64T', 'CBool', + 'CVoid', 'CSizeT', 'CSSizeT', 'CPtr', 'CArray', 'CEnum', 'CUnion', + 'CStruct', 'CDefine', + 'int', 'double', 'float', 'i8', 'i16', 'i32', 'i64', 'void', + 'half', 'fp128', 'char', 'bool', + } + # 值类型/类类型不再用"内建名白名单"判定:见下方 class_type_names, + # 唯一判据是"是否有类定义"(_HasClassDefinition:Gen._struct_sha1_map + # / Gen.structs)。内建(int/CInt/…)与语言内建 `str`(LLVM 映射 i8*) + # 都不在类表里 → 天然判为值类型,无需名表。 + # 历史教训(名表判据的代价,保留以便回归时理解):`str` 曾漏在 + # "非类类型名"集合外 → `list[str]` 的类型参数被误判为类类型,特化后 + # 方法签名被额外包一层 `| t.CPtr`(append/set 形参 i8**、get/pop 返回 + # i8**)→ 调用点多解一层 load(读字符串首 8 字节)→ ASan 崩溃。 + # 注意 _BUILTIN_TYPE_NAMES 本身仍保留:它用于 type_map 替换 + # 与 _t_c_imported_names 注册,不能把 'str' 并进去 + # (否则 HandlesTypeMerge 会把注解 `str` 当 `t.str` 解析)。 + type_map: dict[str, str] = {} + for i, tp_name in enumerate(type_param_names): + if type_names and i < len(type_names): + tn: str = type_names[i] + # 判断是否为 t 模块内建类型(裸名或 't.' 前缀,如 'CInt'/'t.CInt'): + # 内建类型必须用源码名(type_name)替换 T(t.CInt → i32),否则 + # 用 mangled type_arg(如 'i32')替换会产生 'i32.__sizeof__' 这类 + # 源码中不存在的名称([FATAL][__sizeof__] 类型 'i32' 无法解析)。 + # 真正的类类型参数(如 'AST'/'ast.AST')才用 mangled type_arg + # (带 sha1 前缀 + ptr 后缀),避免模板内部嵌套注解被替换成裸短名 + # 后重新特化出只有 declare、无方法体的错误特化(list[AST].__len__ + # undefined symbol 根因)。_make_replacement_node 会把 mangled 名 + # 还原为 'sha1.AST | t.CPtr',重新解析时拼回一致的 mangled 名。 + tn_bare: str = tn.split('.')[-1] if '.' in tn else tn + if tn_bare in _BUILTIN_TYPE_NAMES: + type_map[tp_name] = tn + elif i < len(type_args): + type_map[tp_name] = type_args[i] + else: + type_map[tp_name] = tn + else: + type_map[tp_name] = type_args[i] + # 检测哪些类型参数在原始类型实参中带有指针语义(| t.CPtr / rawptr[T]) + # 当 type_arg 以 '*' 结尾(mangling 把每个 CPtr 层渲染为一层 '*')且对应的 + # type_name(去模块前缀后)不是内建类型名时,说明调用方写了 + # list[AST | t.CPtr] / list[rawptr[AST]]。注意用 tn_bare 判断: + # 't.CPtr' 去前缀后是 'CPtr'(内建),不算类类型参数。 + cptr_params: set[str] = set() + for i, tp_name in enumerate(type_param_names): + type_arg: str = type_args[i] + if type_arg.endswith('*'): + tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i] + tn_bare_c: str = tn.split('.')[-1] if '.' in tn else tn + if tn_bare_c not in _BUILTIN_TYPE_NAMES: + cptr_params.add(tp_name) + # Bug 2 修复:识别特化为 class 类型的类型参数 + # 这类参数在方法签名中是裸 T(无 | t.CPtr),特化后为裸 ClassName(值类型) + # 需要加 | t.CPtr 使其成为指针类型(class 实例应按指针传递) + # 判据 = "是否有类定义"(_HasClassDefinition),存**裸名**:注解侧的 + # 比较也用裸名(T 替换后的 'sha1.Param' 去前缀)——此前存带模块前缀的 + # tn(如 'ast.AST')而比较用裸名 'AST',前缀形态永不命中。 + class_type_names: set[str] = set() + for i, tp_name in enumerate(type_param_names): + tn: str = type_names[i] if type_names and i < len(type_names) else type_args[i] + tn_bare_cls: str = tn.split('.')[-1] if '.' in tn else tn + if self._HasClassDefinition(tn_bare_cls, Gen): + class_type_names.add(tn_bare_cls) + # 仅对 t 模块内建类型名注册 _t_c_imported_names(如 CInt -> ('t','CInt')) + # 类名(如 AST)不应注册为 t.AST,否则会污染名称解析 + if type_names: + if not hasattr(self.Trans, '_t_c_imported_names'): + self.Trans._t_c_imported_names = {} + for tn in type_names: + if tn in _BUILTIN_TYPE_NAMES and tn not in self.Trans._t_c_imported_names: + self.Trans._t_c_imported_names[tn] = ('t', tn) + SpecNode: ast.ClassDef = copy.deepcopy(Node) + SpecNode.type_params = [] + SpecNode.name = spec_name + for item in SpecNode.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + if item.annotation: + item.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(item.annotation, type_map, cptr_params) + elif isinstance(item, ast.FunctionDef): + for arg in item.args.args: + if arg.annotation: + arg.annotation = self.Trans.FunctionHandler._replace_type_in_annotation(arg.annotation, type_map, cptr_params) + # class 类型参数加指针语义:T -> GNode -> GNode | t.CPtr + # T 替换后的真实形态是 Attribute 链('.GNode' → + # Name('').GNode,见 _make_replacement_node/_make_name_node), + # 不是 ast.Name:此前只认 ast.Name → 方法形参漏加 | t.CPtr, + # 特化后按值结构体传参(被调者栈帧副本地址入链 → 悬垂写)。 + raw_name: str | None = self._BareClassAnnotationName(arg.annotation) + if raw_name is not None and raw_name in class_type_names: + arg.annotation = ast.BinOp( + left=arg.annotation, + op=ast.BitOr(), + right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + ) + if item.returns: + item.returns = self.Trans.FunctionHandler._replace_type_in_annotation(item.returns, type_map, cptr_params) + # 返回类型也加指针语义(同一裸名判据,形态同上) + raw_name: str | None = self._BareClassAnnotationName(item.returns) + if raw_name is not None and raw_name in class_type_names: + item.returns = ast.BinOp( + left=item.returns, + op=ast.BitOr(), + right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + ) + self.Trans.FunctionHandler._apply_type_map_to_body(item.body, type_map, cptr_params) + saved_builder: ir.IRBuilder = Gen.builder + saved_func: ir.Function = Gen.func + saved_variables: dict = dict(Gen.variables) if Gen.variables else {} + saved_direct_values: dict = dict(Gen._direct_values) if Gen._direct_values else {} + saved_var_type_info: dict = dict(Gen.var_type_info) if Gen.var_type_info else {} + saved_var_signedness: dict = dict(Gen.var_signedness) if Gen.var_signedness else {} + saved_global_vars: set = set(Gen.global_vars) if Gen.global_vars else set() + saved_var_scopes: list = [dict(s) for s in self.Trans.VarScopes] if self.Trans.VarScopes else [] + saved_block: ir.Block | None = None + if Gen.builder and Gen.builder.block and not Gen.builder.block.is_terminated: + saved_block = Gen.builder.block + # 切换 module_sha1 为泛型模板定义模块的 SHA1, + # 使结构体类型名和方法定义名都用定义模块的 SHA1 前缀, + # 与调用端 _struct_sha1_map 查到的 SHA1 一致。 template_sha1: str | None = template.get('sha1') if not template_sha1: # 模板注册表缺 sha1 时回退到"模板类定义模块"(_struct_sha1_map 即 @@ -292,1527 +292,1527 @@ class ClassHandle(BaseHandle): # DeclarationGenerator._get_type_str 的泛型前缀、调用点 # Gen._struct_sha1_map[spec_name] 同一条规则源;否则 declare/define # 两侧前缀分裂(使用方模块 vs 模板模块)→ 跨模块 undefined symbol。 - template_sha1 = Gen._struct_sha1_map.get(ClassName) - saved_gen_sha1: str | None = getattr(Gen, 'module_sha1', None) - saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None) - if template_sha1: - Gen.module_sha1 = template_sha1 - self.Trans._module_sha1 = template_sha1 - try: - self._EmitClassLlvm(SpecNode, Gen, declare_only=declare_only) - finally: - # 确保 Gen 状态恢复,即使 _EmitClassLlvm 抛出异常 - Gen.builder = saved_builder - if saved_block is not None and Gen.builder is not None: - Gen.builder.position_at_end(saved_block) - Gen.func = saved_func - Gen.variables = saved_variables - Gen._direct_values = saved_direct_values - Gen.var_type_info = saved_var_type_info - Gen.var_signedness = saved_var_signedness - Gen.global_vars = saved_global_vars - self.Trans.VarScopes = saved_var_scopes - # 恢复原始 module_sha1 - Gen.module_sha1 = saved_gen_sha1 - self.Trans._module_sha1 = saved_trans_sha1 - Gen._generic_class_specializations[spec_key] = (spec_name, declare_only) - # 缓存 type_args/type_names,供后续 declare_only=False 完整特化补发方法体使用 - # (如 _HandleMethodCallLlvm 中发现方法未定义时触发) - if not hasattr(Gen, '_generic_class_spec_type_info'): - Gen._generic_class_spec_type_info = {} - Gen._generic_class_spec_type_info[spec_key] = (list(type_args), list(type_names) if type_names else None) - if not hasattr(self.Trans, '_generic_class_specializations'): - self.Trans._generic_class_specializations = {} - self.Trans._generic_class_specializations[spec_key] = spec_name - # 用模板定义模块的 SHA1 注册到 _struct_sha1_map,与方法定义端一致 - if template_sha1: - Gen._struct_sha1_map[spec_name] = template_sha1 - elif hasattr(self.Trans, '_module_sha1') and self.Trans._module_sha1: - Gen._struct_sha1_map[spec_name] = self.Trans._module_sha1 - return spec_name - - def _resolve_base_kind(self, base_name: str, Gen: LlvmGeneratorMixin) -> str | None: - """解析基类名称的类型种类 - - 查询顺序: - 1. _t_type_symbols(from t import CEnum as en) - 2. t 模块属性(t.CEnum / t.CUnion / t.CStruct / t.REnum) - 3. 返回 None 表示不是已知标记基类 - - Returns: - 'enum' | 'union' | 'renum' | 'struct' | None - """ - # 1) 查 _t_type_symbols(别名导入) - symtab: object = self.Trans.SymbolTable - cls: type | None = symtab._t_type_symbols.get(base_name) - if cls is not None: - if issubclass(cls, (t.CEnum, t.REnum)): - return 'renum' if issubclass(cls, t.REnum) else 'enum' - if issubclass(cls, t.CUnion): - return 'union' - if issubclass(cls, t.CStruct): - return 'struct' - # CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记, - # 不应被编译为真实结构体。 - if issubclass(cls, t.CType): - return 'ctype_marker' - - # 2) 查 t 模块属性(直接引用 t.CEnum 等) - t_cls: type | None = getattr(t, base_name, None) - if isinstance(t_cls, type) and issubclass(t_cls, t.CType): - if issubclass(t_cls, (t.CEnum, t.REnum)): - return 'renum' if issubclass(t_cls, t.REnum) else 'enum' - if issubclass(t_cls, t.CUnion): - return 'union' - if issubclass(t_cls, t.CStruct): - return 'struct' - # CType 及其子类是"类型强转"标记,不编译为结构体。 - return 'ctype_marker' - - return None - - @staticmethod - def _is_classname_ctype_marker(class_name: str) -> bool: - """检查类名是否是 t.CType 的"类型强转"标记子类。 - - 只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/ - CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等) - 才应跳过编译。有 BASE position 的基本类型(CInt/CChar/CSizeT/CLong 等) - 有具体大小,应被正常编译。CType 基类本身无 bases,base 检测无法触发, - 需要直接按类名检查。 - - 注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault, - 所以这里直接用 getattr(t, name) 检查 t 模块属性。 - - Returns: - True 如果 class_name 是 CType 标记子类名(应跳过编译) - """ - if not class_name: - return False - # 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian) - t_cls: type | None = getattr(t, class_name, None) - if not isinstance(t_cls, type) or not issubclass(t_cls, t.CType): - return False - if issubclass(t_cls, (t.CStruct, t.CEnum, t.REnum, t.CUnion)): - return False - # 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过 - # CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过 - if t.CType.BASE in t_cls.position: - try: - inst = t_cls() - size_val = getattr(inst, 'Size', None) - if size_val is not None and size_val > 0: - return False - except Exception: - pass - return True - - def _resolve_decorator_kind(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> str | None: - """从装饰器列表中解析结构体类型种类 - - 支持 @t.CStruct, @t.CUnion, @t.CEnum, @t.REnum - 也支持 from t import CStruct 等别名形式 - - Returns: - 'enum' | 'union' | 'renum' | 'struct' | None - """ - if not hasattr(Node, 'decorator_list') or not Node.decorator_list: - return None - symtab: object = self.Trans.SymbolTable - for decorator in Node.decorator_list: - attr_name: str | None = None - # @t.CStruct 形式 - if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): - if IsTModule(decorator.value.id, symtab): - attr_name = decorator.attr - # @CStruct 形式 (from t import CStruct) - elif isinstance(decorator, ast.Name): - cls: type | None = getattr(t, decorator.id, None) - if cls is None and hasattr(symtab, '_t_type_symbols'): - cls = symtab._t_type_symbols.get(decorator.id) - if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): - attr_name = cls.__name__ - if attr_name: - # 查 _t_type_symbols 和 t 模块属性以确定种类 - resolved_cls: type | None = None - if hasattr(symtab, '_t_type_symbols'): - resolved_cls = symtab._t_type_symbols.get(attr_name) - if resolved_cls is None: - resolved_cls = getattr(t, attr_name, None) - if resolved_cls is not None and isinstance(resolved_cls, type): - if issubclass(resolved_cls, t.REnum): - return 'renum' - if issubclass(resolved_cls, t.CEnum): - return 'enum' - if issubclass(resolved_cls, t.CUnion): - return 'union' - if issubclass(resolved_cls, t.CStruct): - return 'struct' - return None - - def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool, bool]: - """解析类的 Object/CVTable 标志和父类信息 - - Returns: - tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, - VtableByClassLevel) - VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态(= 类级 - CVTable / 继承链判定),供 vtable 槽筛选(SymbolUtils.IsVtableSlotMethod) - 使用——与 TPV _should_method_be_virtual 第 3/4 条的判据一致。 - """ - ClassName: str = Node.name - IsCpythonObject: bool = False - IsCVTable: bool = False - IsNoVTable: bool = False - if hasattr(Node, 'decorator_list') and Node.decorator_list: - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Attribute): - if hasattr(decorator.value, 'id') and IsTModule(decorator.value.id, self.Trans.SymbolTable): - if decorator.attr == 'Object': - IsCpythonObject = True - elif decorator.attr == 'CVTable': - IsCVTable = True - elif decorator.attr == 'NoVTable': - IsNoVTable = True - elif isinstance(decorator, ast.Name): - if decorator.id == 'Object': - IsCpythonObject = True - elif decorator.id == 'CVTable': - IsCVTable = True - elif decorator.id == 'NoVTable': - IsNoVTable = True - elif isinstance(decorator, ast.Call): - # 检测 @c.Attribute(t.attr.packed) - if isinstance(decorator.func, ast.Attribute): - if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute': - for arg in decorator.args: - if isinstance(arg, ast.Attribute): - if isinstance(arg.value, ast.Attribute): - if IsTModule(getattr(arg.value.value, 'id', ''), self.Trans.SymbolTable) and arg.value.attr == 'attr' and arg.attr == 'packed': - Gen.class_packed.add(ClassName) - class_graph: dict = Gen._class_graph - if class_graph.get(ClassName, {}).get('has_methods', False) and not IsNoVTable: - if not IsCpythonObject: - IsCpythonObject = True - # 多继承支持(与 TPV _detect_vtable_status 对齐):不再因"作为其他类的基类" - # 自动启用 vtable。vtable 状态统一由继承链决定(见下方 HasParentClass 判定): - # 类自身无 @t.CVTable 时,父类有 vtable → 子类有;父类无 vtable → 子类静态。 - # 否则基类(如 C(A,B) 中的 A/B)被自动加 vptr 后,其字段区在子类中的偏移 - # 与自身布局不一致,继承方法按自身布局读 self 会字段错位。 - HasParentClass: bool = False - ParentClassName: str | None = None - ParentClasses: list[str] = [] - ParentIsNoVTable: bool = False - if Node.bases: - for base in Node.bases: - base_name: str | None = None - if hasattr(base, 'id'): - base_name = base.id - elif hasattr(base, 'attr'): - base_name = base.attr - if base_name: - kind: str | None = self._resolve_base_kind(base_name, Gen) - if kind is not None or base_name in t._MARKER_BASES: - continue # 标记基类,跳过 - # 多继承:收集所有真实父类(声明顺序),不再 break - ParentClasses.append(base_name) - if ParentClassName is None: - ParentClassName = base_name - if base_name in class_graph or base_name in Gen.class_members or base_name in Gen.structs: - HasParentClass = True - # 检测父类是否有 @t.NoVTable 标记(非多态继承) - if class_graph.get(base_name, {}).get('has_novtable', False): - ParentIsNoVTable = True - # 跨模块父类:检查 _cross_module_novtable 集合 - if base_name in Gen._cross_module_novtable: - ParentIsNoVTable = True - if HasParentClass and not IsCVTable and not IsNoVTable and not ParentIsNoVTable: - # 继承链统一 vtable(与 TPV _detect_vtable_status 一致): - # 父类有 vtable → 子类启用;父类无 vtable → 子类不启用(静态); - # 父类 vtable 状态未知(跨模块/未注册)→ 保守启用。 - # 沿继承链追溯父类的 vtable 状态(支持空中间基类 Stmt/ExprNode): - # Stmt/ExprNode 无方法但 @t.CVTable,跨模块加载时 _has_methods_prescan=False - # 未注册 class_vtable,若只查直接父类会误判子类为静态类 → - # __before_init__ base=0 不写 vtable → 实例 vtable 为 null → 虚调用段错误。 - # 修复:沿 class_parent 链向上查,任一祖先有 vtable 即判定子类有 vtable。 - _parent_has_vtable: bool = False - _p_chain: str | None = ParentClassName - _p_visited_chain: set[str] = {ClassName} - _class_parent_map: dict = getattr(Gen, 'class_parent', {}) - while _p_chain and _p_chain not in _p_visited_chain: - _p_visited_chain.add(_p_chain) - if (_p_chain in Gen.class_vtable - or _p_chain in Gen._cross_module_vtable_classes): - _parent_has_vtable = True - break - _p_chain = _class_parent_map.get(_p_chain) - parent_has_vtable: bool = _parent_has_vtable - parent_known: bool = (ParentClassName in class_graph - or ParentClassName in Gen.class_members - or ParentClassName in Gen.structs) - if parent_has_vtable or not parent_known: - IsCVTable = True - # 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable: - # 与 TPV ClassGenBase._detect_vtable_status 的三条 `_has_any_func_cvtable` - # 分支逐条对齐(类级 NoVTable / 父类 NoVTable / 无真实父类)。 - # 注意"父类已知且无 vtable" **不**触发(TPV 同分支返回 0)—— - # 父类按无 vptr 布局而子类插 vptr 会让继承方法字段错位。 - VtableByClassLevel: bool = IsCVTable - if (not VtableByClassLevel and ClassHasFuncLevelCVTable(Node) - and (IsNoVTable or ParentIsNoVTable or not HasParentClass)): - IsCVTable = True - if IsCVTable: - Gen.class_vtable.add(ClassName) - # 沿继承链向上传播,将所有祖先类加入 vtable - visited: set[str] = {ClassName} - ancestor: str | None = ParentClassName - while ancestor and ancestor not in visited: - visited.add(ancestor) - Gen.class_vtable.add(ancestor) - # 查询祖先类的父类 - ancestor_info: dict | None = class_graph.get(ancestor) - if ancestor_info: - ancestor_bases: list = ancestor_info.get('bases', []) - next_ancestor: str | None = None - for ab in ancestor_bases: - if ab not in t._MARKER_BASES and ab not in visited: - next_ancestor = ab - break - ancestor = next_ancestor - else: - # 类图中没有,尝试从 class_parent 查询 - ancestor = Gen.class_parent.get(ancestor) - return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel - - def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None: - """注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。 - - 调用点两处:类处理阶段(_EmitClassLlvm)与前置声明之前的本模块预扫描 - (PrescanModuleGenericClassTemplates)。注册内容唯一(node/type_params/ - 定义模块 SHA1),两个调用点不得各写一份。 - """ - if not hasattr(self, '_generic_class_templates'): - self._generic_class_templates = {} - self._generic_class_templates[Node.name] = { - 'node': Node, - 'type_params': [tp.name for tp in Node.type_params], - # 记录泛型模板定义模块的 SHA1,用于特化时统一命名空间 - 'sha1': getattr(self.Trans, '_module_sha1', None), - } - - def PrescanModuleGenericClassTemplates(self, ModuleBody: object) -> None: - """前置声明之前预注册**本模块**泛型类模板。 - - 判据与 includes 侧对泛型类的注册(HandlesImports._LoadModuleDeclarationsFromFile/ - _EmitImportFromDeclarations 的 `node.type_params` 非空)**同源**,即 - _is_generic_class,不看类名白名单。 - - 类处理阶段(下方 _EmitClassLlvm)才注册本模块模板,而顶层函数前置声明 - (GenerateLlvmDirect 的 _EmitFunctionForwardDeclLlvm)已需要它:模块级函数 - 参数/返回注解写自身泛型类(`def f(b: Box[Node])`)时 - _SubscriptHitsGenericTemplate 判为不命中 → TypeAnnotationResolver 解析为空 → - VoidType → 抛"无法识别的参数类型注解";而同模块方法参数(类处理阶段之后) - 却可用,二者分裂。预注册后两处走同一 _generic_class_templates。 - """ - for Node in ModuleBody: - if isinstance(Node, ast.ClassDef) and self._is_generic_class(Node): - self._RegisterGenericClassTemplate(Node) - - # ================================================================== - # _EmitNestedClassDefs - 递归发射类体内的嵌套 class / struct - # - # 命名与引用口径(用户 2026-10-01 定,与 union/renum 嵌套命名同源): - # · 内层结构体名 = `{外类}_{内类}`(Gen.structs / class_members 等同键); - # · 引用:限定名 `外类.内类`(TypeAnnotationResolver._from_node_attribute 的 - # 扁平名回退)与扁平名 `外类_内类`(裸名直查 SymbolTable)都可用; - # · 多层嵌套自然递归为 A_B_C(deepcopy 后改名再递归)。 - # 用 deepcopy 而不是临时改原名:源码 AST 节点还会被 _CollectOwnMemberTypes / - # SymbolExtractor / vpi 写出等路径读到,不得留下改名的副作用。 - # ================================================================== - def _EmitNestedClassDefs(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: - """把类体内的嵌套 ClassDef 逐个以 `{外类}_{内类}` 递归发射(见上方注释)""" - if Node is None: - return - Body: list | None = getattr(Node, 'body', None) - ClassName: str | None = getattr(Node, 'name', None) - if not Body or not ClassName: - return - for Item in list(Body): - if not isinstance(Item, ast.ClassDef): - continue - InnerName: str | None = getattr(Item, 'name', None) - if not InnerName: - continue - NestedName: str = f'{ClassName}_{InnerName}' - # 已发射过(多遍/重复调用)只补名字登记,不重复发射 - if NestedName in Gen.structs and NestedName in Gen.class_members: - self._RegisterNestedClassName(ClassName, InnerName, NestedName) - continue - NestedNode: ast.ClassDef = copy.deepcopy(Item) - NestedNode.name = NestedName - self._EmitClassLlvm(NestedNode, Gen) - self._RegisterNestedClassName(ClassName, InnerName, NestedName) - - def _RegisterNestedClassName(self, ClassName: str, InnerName: str, NestedName: str) -> None: - """登记嵌套类名的三种写法(裸名 / `外类.内类` / 扁平名) - - 与 _EmitREnumLlvm 的变体三键登记同口径(见本文件 REnum 变体注册处), - 供"限定名扁平回退"与"裸名直查"两条解析路径命中。 - """ - MemberNode: CTypeInfo = CTypeInfo() - MemberNode.Name = NestedName - MemberNode.BaseType = t.CStruct(name=NestedName) - MemberNode.IsStruct = True - self.Trans.SymbolTable.insert(NestedName, MemberNode) - self.Trans.SymbolTable.insert(f'{ClassName}.{InnerName}', MemberNode) - - def _EmitClassLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, declare_only: bool = False) -> None: - ClassName: str = Node.name - # t.CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是"类型强转"标记, - # 不应被编译为真实结构体。CType 基类本身无 bases,base 检测无法触发, - # 此处在方法入口直接按类名跳过。 - if self._is_classname_ctype_marker(ClassName): - return - if self._is_generic_class(Node): - self._RegisterGenericClassTemplate(Node) - return - if self._is_exception_class(Node): - self._RegisterExceptionClass(Node) - return - IsCenum: bool = False - IsCunion: bool = False - IsRenum: bool = False - # 先检查装饰器形式(@t.CStruct / @t.CEnum / @t.CUnion / @t.REnum) - deco_kind: str | None = self._resolve_decorator_kind(Node, Gen) - if deco_kind == 'enum': - IsCenum = True - elif deco_kind == 'union': - IsCunion = True - elif deco_kind == 'renum': - IsRenum = True - # 再检查基类继承形式(class X(t.CStruct):),与装饰器合并 - if not IsCenum and not IsCunion and not IsRenum and Node.bases: - for base in Node.bases: - base_name: str | None = None - if hasattr(base, 'attr'): - base_name = base.attr - elif hasattr(base, 'id'): - base_name = base.id - if base_name: - kind: str | None = self._resolve_base_kind(base_name, Gen) - if kind == 'enum': - IsCenum = True - break - elif kind == 'union': - IsCunion = True - elif kind == 'renum': - IsRenum = True - elif kind == 'ctype_marker': - # CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是 - # "类型强转"标记,不应被编译为真实结构体。直接 return 跳过编译。 - return - if IsCenum: - self._RegisterEnumMembers(Node) - return - if IsCunion: - self._EmitUnionLlvm(Node, Gen) - return - if IsRenum: - self._EmitREnumLlvm(Node, Gen) - return - IsCpythonObject: bool - IsCVTable: bool - ParentClassName: str | None - IsNoVTable: bool - VtableByClassLevel: bool - IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel = self._resolve_class_flags(Node, Gen) - # 嵌套类 / 嵌套 struct:类体里再写 class / struct 此前被**静默忽略**(本函数的 - # 字段/方法遍历只认 AnnAssign / FunctionDef,CollectAnnAssignMember 对其它节点 - # 直接返回 None)。现按既有扁平命名先例发射为独立结构体 `{外类}_{内类}` - # (与 _EmitUnionLlvm:1569 / _EmitREnumLlvm:1715 的 `f"{ClassName}_{Nested}"` - # 同源),并登记 SymbolTable 三键,使 `外类.内类` 与扁平名 `外类_内类` 都能解析。 - # 时机:在 flags 解析之后、class_members 初始化之前 —— 让嵌套结构体先就绪, - # 外层字段注解引用 `外类.内类` 时即可命中(_resolve_class_flags 内的名字键 - # 均已按 ClassName 建立,递归改名不影响外层)。 - self._EmitNestedClassDefs(Node, Gen) - # 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body - Gen._classes_in_progress.add(ClassName) - # 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class) - # 和 CollectAnnAssignMember 触发的嵌套 _generate_structs 用不完整 class_members 调用 set_body - Gen._collecting_members_set.add(ClassName) - if ClassName not in Gen.class_methods: - Gen.class_methods[ClassName] = [] - else: - # 防御性清除:移除导入阶段可能添加的构造函数(__new__/__init__/__before_init__)。 - # 构造函数不参与虚分派,放入 vtable 会导致子类与基类 vtable 大小不一致, - # 索引偏移破坏多态分派(如 Module 有 __new__/__init__ 使 vtable=7,而 AST 无它们 vtable=5, - # 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。 - Gen.class_methods[ClassName] = [ - m for m in Gen.class_methods[ClassName] - if m.split('.')[-1] not in ('__new__', '__init__', '__before_init__') - ] - # 当前文件定义的类是权威定义,覆盖之前导入阶段可能添加的外部声明。 - # 关键:如果本地类是 @t.NoVTable 但同名导入类有 vtable(名称冲突), - # 必须清除 vtable 状态,否则 _generate_structs 会错误添加 vtable 指针。 - if IsNoVTable: - Gen.class_vtable.discard(ClassName) - Gen._cross_module_vtable_classes.discard(ClassName) - Gen._cross_module_novtable.add(ClassName) - # 清除可能从导入 stub 加载的旧 struct body(含错误 vtable 指针), - # 让 _generate_structs 用本地 class_members 重新生成。 - # 字段名必须是 llvmlite 的**公开属性 `elements`**(is_opaque = elements is None; - # set_body 仅在 elements is not None 时拒绝)——写 `_elements` 在本版本是空操作, - # 会让"权威重建点"永远无法刷新已冻结的错误布局。 - _old_st = Gen.structs.get(ClassName) - if isinstance(_old_st, ir.IdentifiedStructType) and _old_st.elements is not None: - _old_st.elements = None - _ih = getattr(Gen, '_import_handler_ref', None) - if _ih is not None: - _ck = (id(Gen), ClassName) - if _ck in _ih._struct_Load_cache: - del _ih._struct_Load_cache[_ck] + template_sha1 = Gen._struct_sha1_map.get(ClassName) + saved_gen_sha1: str | None = getattr(Gen, 'module_sha1', None) + saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None) + if template_sha1: + Gen.module_sha1 = template_sha1 + self.Trans._module_sha1 = template_sha1 + try: + self._EmitClassLlvm(SpecNode, Gen, declare_only=declare_only) + finally: + # 确保 Gen 状态恢复,即使 _EmitClassLlvm 抛出异常 + Gen.builder = saved_builder + if saved_block is not None and Gen.builder is not None: + Gen.builder.position_at_end(saved_block) + Gen.func = saved_func + Gen.variables = saved_variables + Gen._direct_values = saved_direct_values + Gen.var_type_info = saved_var_type_info + Gen.var_signedness = saved_var_signedness + Gen.global_vars = saved_global_vars + self.Trans.VarScopes = saved_var_scopes + # 恢复原始 module_sha1 + Gen.module_sha1 = saved_gen_sha1 + self.Trans._module_sha1 = saved_trans_sha1 + Gen._generic_class_specializations[spec_key] = (spec_name, declare_only) + # 缓存 type_args/type_names,供后续 declare_only=False 完整特化补发方法体使用 + # (如 _HandleMethodCallLlvm 中发现方法未定义时触发) + if not hasattr(Gen, '_generic_class_spec_type_info'): + Gen._generic_class_spec_type_info = {} + Gen._generic_class_spec_type_info[spec_key] = (list(type_args), list(type_names) if type_names else None) + if not hasattr(self.Trans, '_generic_class_specializations'): + self.Trans._generic_class_specializations = {} + self.Trans._generic_class_specializations[spec_key] = spec_name + # 用模板定义模块的 SHA1 注册到 _struct_sha1_map,与方法定义端一致 + if template_sha1: + Gen._struct_sha1_map[spec_name] = template_sha1 + elif hasattr(self.Trans, '_module_sha1') and self.Trans._module_sha1: + Gen._struct_sha1_map[spec_name] = self.Trans._module_sha1 + return spec_name + + def _resolve_base_kind(self, base_name: str, Gen: LlvmGeneratorMixin) -> str | None: + """解析基类名称的类型种类 + + 查询顺序: + 1. _t_type_symbols(from t import CEnum as en) + 2. t 模块属性(t.CEnum / t.CUnion / t.CStruct / t.REnum) + 3. 返回 None 表示不是已知标记基类 + + Returns: + 'enum' | 'union' | 'renum' | 'struct' | None + """ + # 1) 查 _t_type_symbols(别名导入) + symtab: object = self.Trans.SymbolTable + cls: type | None = symtab._t_type_symbols.get(base_name) + if cls is not None: + if issubclass(cls, (t.CEnum, t.REnum)): + return 'renum' if issubclass(cls, t.REnum) else 'enum' + if issubclass(cls, t.CUnion): + return 'union' + if issubclass(cls, t.CStruct): + return 'struct' + # CType 及其子类(CChar/CInt/CVoid/CPtr 等)是"类型强转"标记, + # 不应被编译为真实结构体。 + if issubclass(cls, t.CType): + return 'ctype_marker' + + # 2) 查 t 模块属性(直接引用 t.CEnum 等) + t_cls: type | None = getattr(t, base_name, None) + if isinstance(t_cls, type) and issubclass(t_cls, t.CType): + if issubclass(t_cls, (t.CEnum, t.REnum)): + return 'renum' if issubclass(t_cls, t.REnum) else 'enum' + if issubclass(t_cls, t.CUnion): + return 'union' + if issubclass(t_cls, t.CStruct): + return 'struct' + # CType 及其子类是"类型强转"标记,不编译为结构体。 + return 'ctype_marker' + + return None + + @staticmethod + def _is_classname_ctype_marker(class_name: str) -> bool: + """检查类名是否是 t.CType 的"类型强转"标记子类。 + + 只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/ + CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等) + 才应跳过编译。有 BASE position 的基本类型(CInt/CChar/CSizeT/CLong 等) + 有具体大小,应被正常编译。CType 基类本身无 bases,base 检测无法触发, + 需要直接按类名检查。 + + 注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault, + 所以这里直接用 getattr(t, name) 检查 t 模块属性。 + + Returns: + True 如果 class_name 是 CType 标记子类名(应跳过编译) + """ + if not class_name: + return False + # 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian) + t_cls: type | None = getattr(t, class_name, None) + if not isinstance(t_cls, type) or not issubclass(t_cls, t.CType): + return False + if issubclass(t_cls, (t.CStruct, t.CEnum, t.REnum, t.CUnion)): + return False + # 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过 + # CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过 + if t.CType.BASE in t_cls.position: + try: + inst = t_cls() + size_val = getattr(inst, 'Size', None) + if size_val is not None and size_val > 0: + return False + except Exception: + pass + return True + + def _resolve_decorator_kind(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> str | None: + """从装饰器列表中解析结构体类型种类 + + 支持 @t.CStruct, @t.CUnion, @t.CEnum, @t.REnum + 也支持 from t import CStruct 等别名形式 + + Returns: + 'enum' | 'union' | 'renum' | 'struct' | None + """ + if not hasattr(Node, 'decorator_list') or not Node.decorator_list: + return None + symtab: object = self.Trans.SymbolTable + for decorator in Node.decorator_list: + attr_name: str | None = None + # @t.CStruct 形式 + if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): + if IsTModule(decorator.value.id, symtab): + attr_name = decorator.attr + # @CStruct 形式 (from t import CStruct) + elif isinstance(decorator, ast.Name): + cls: type | None = getattr(t, decorator.id, None) + if cls is None and hasattr(symtab, '_t_type_symbols'): + cls = symtab._t_type_symbols.get(decorator.id) + if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): + attr_name = cls.__name__ + if attr_name: + # 查 _t_type_symbols 和 t 模块属性以确定种类 + resolved_cls: type | None = None + if hasattr(symtab, '_t_type_symbols'): + resolved_cls = symtab._t_type_symbols.get(attr_name) + if resolved_cls is None: + resolved_cls = getattr(t, attr_name, None) + if resolved_cls is not None and isinstance(resolved_cls, type): + if issubclass(resolved_cls, t.REnum): + return 'renum' + if issubclass(resolved_cls, t.CEnum): + return 'enum' + if issubclass(resolved_cls, t.CUnion): + return 'union' + if issubclass(resolved_cls, t.CStruct): + return 'struct' + return None + + def _resolve_class_flags(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> tuple[bool, bool, str | None, bool, bool]: + """解析类的 Object/CVTable 标志和父类信息 + + Returns: + tuple: (IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, + VtableByClassLevel) + VtableByClassLevel:**忽略方法级 CVTable 标记**算出的 vtable 状态(= 类级 + CVTable / 继承链判定),供 vtable 槽筛选(SymbolUtils.IsVtableSlotMethod) + 使用——与 VPC _should_method_be_virtual 第 3/4 条的判据一致。 + """ + ClassName: str = Node.name + IsCpythonObject: bool = False + IsCVTable: bool = False + IsNoVTable: bool = False + if hasattr(Node, 'decorator_list') and Node.decorator_list: + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Attribute): + if hasattr(decorator.value, 'id') and IsTModule(decorator.value.id, self.Trans.SymbolTable): + if decorator.attr == 'Object': + IsCpythonObject = True + elif decorator.attr == 'CVTable': + IsCVTable = True + elif decorator.attr == 'NoVTable': + IsNoVTable = True + elif isinstance(decorator, ast.Name): + if decorator.id == 'Object': + IsCpythonObject = True + elif decorator.id == 'CVTable': + IsCVTable = True + elif decorator.id == 'NoVTable': + IsNoVTable = True + elif isinstance(decorator, ast.Call): + # 检测 @c.Attribute(t.attr.packed) + if isinstance(decorator.func, ast.Attribute): + if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute': + for arg in decorator.args: + if isinstance(arg, ast.Attribute): + if isinstance(arg.value, ast.Attribute): + if IsTModule(getattr(arg.value.value, 'id', ''), self.Trans.SymbolTable) and arg.value.attr == 'attr' and arg.attr == 'packed': + Gen.class_packed.add(ClassName) + class_graph: dict = Gen._class_graph + if class_graph.get(ClassName, {}).get('has_methods', False) and not IsNoVTable: + if not IsCpythonObject: + IsCpythonObject = True + # 多继承支持(与 VPC _detect_vtable_status 对齐):不再因"作为其他类的基类" + # 自动启用 vtable。vtable 状态统一由继承链决定(见下方 HasParentClass 判定): + # 类自身无 @t.CVTable 时,父类有 vtable → 子类有;父类无 vtable → 子类静态。 + # 否则基类(如 C(A,B) 中的 A/B)被自动加 vptr 后,其字段区在子类中的偏移 + # 与自身布局不一致,继承方法按自身布局读 self 会字段错位。 + HasParentClass: bool = False + ParentClassName: str | None = None + ParentClasses: list[str] = [] + ParentIsNoVTable: bool = False + if Node.bases: + for base in Node.bases: + base_name: str | None = None + if hasattr(base, 'id'): + base_name = base.id + elif hasattr(base, 'attr'): + base_name = base.attr + if base_name: + kind: str | None = self._resolve_base_kind(base_name, Gen) + if kind is not None or base_name in t._MARKER_BASES: + continue # 标记基类,跳过 + # 多继承:收集所有真实父类(声明顺序),不再 break + ParentClasses.append(base_name) + if ParentClassName is None: + ParentClassName = base_name + if base_name in class_graph or base_name in Gen.class_members or base_name in Gen.structs: + HasParentClass = True + # 检测父类是否有 @t.NoVTable 标记(非多态继承) + if class_graph.get(base_name, {}).get('has_novtable', False): + ParentIsNoVTable = True + # 跨模块父类:检查 _cross_module_novtable 集合 + if base_name in Gen._cross_module_novtable: + ParentIsNoVTable = True + if HasParentClass and not IsCVTable and not IsNoVTable and not ParentIsNoVTable: + # 继承链统一 vtable(与 VPC _detect_vtable_status 一致): + # 父类有 vtable → 子类启用;父类无 vtable → 子类不启用(静态); + # 父类 vtable 状态未知(跨模块/未注册)→ 保守启用。 + # 沿继承链追溯父类的 vtable 状态(支持空中间基类 Stmt/ExprNode): + # Stmt/ExprNode 无方法但 @t.CVTable,跨模块加载时 _has_methods_prescan=False + # 未注册 class_vtable,若只查直接父类会误判子类为静态类 → + # __before_init__ base=0 不写 vtable → 实例 vtable 为 null → 虚调用段错误。 + # 修复:沿 class_parent 链向上查,任一祖先有 vtable 即判定子类有 vtable。 + _parent_has_vtable: bool = False + _p_chain: str | None = ParentClassName + _p_visited_chain: set[str] = {ClassName} + _class_parent_map: dict = getattr(Gen, 'class_parent', {}) + while _p_chain and _p_chain not in _p_visited_chain: + _p_visited_chain.add(_p_chain) + if (_p_chain in Gen.class_vtable + or _p_chain in Gen._cross_module_vtable_classes): + _parent_has_vtable = True + break + _p_chain = _class_parent_map.get(_p_chain) + parent_has_vtable: bool = _parent_has_vtable + parent_known: bool = (ParentClassName in class_graph + or ParentClassName in Gen.class_members + or ParentClassName in Gen.structs) + if parent_has_vtable or not parent_known: + IsCVTable = True + # 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable: + # 与 VPC ClassGenBase._detect_vtable_status 的三条 `_has_any_func_cvtable` + # 分支逐条对齐(类级 NoVTable / 父类 NoVTable / 无真实父类)。 + # 注意"父类已知且无 vtable" **不**触发(VPC 同分支返回 0)—— + # 父类按无 vptr 布局而子类插 vptr 会让继承方法字段错位。 + VtableByClassLevel: bool = IsCVTable + if (not VtableByClassLevel and ClassHasFuncLevelCVTable(Node) + and (IsNoVTable or ParentIsNoVTable or not HasParentClass)): + IsCVTable = True + if IsCVTable: + Gen.class_vtable.add(ClassName) + # 沿继承链向上传播,将所有祖先类加入 vtable + visited: set[str] = {ClassName} + ancestor: str | None = ParentClassName + while ancestor and ancestor not in visited: + visited.add(ancestor) + Gen.class_vtable.add(ancestor) + # 查询祖先类的父类 + ancestor_info: dict | None = class_graph.get(ancestor) + if ancestor_info: + ancestor_bases: list = ancestor_info.get('bases', []) + next_ancestor: str | None = None + for ab in ancestor_bases: + if ab not in t._MARKER_BASES and ab not in visited: + next_ancestor = ab + break + ancestor = next_ancestor + else: + # 类图中没有,尝试从 class_parent 查询 + ancestor = Gen.class_parent.get(ancestor) + return IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel + + def _RegisterGenericClassTemplate(self, Node: ast.ClassDef) -> None: + """注册泛型类模板到 `_generic_class_templates`(判据唯一:`_is_generic_class`)。 + + 调用点两处:类处理阶段(_EmitClassLlvm)与前置声明之前的本模块预扫描 + (PrescanModuleGenericClassTemplates)。注册内容唯一(node/type_params/ + 定义模块 SHA1),两个调用点不得各写一份。 + """ + if not hasattr(self, '_generic_class_templates'): + self._generic_class_templates = {} + self._generic_class_templates[Node.name] = { + 'node': Node, + 'type_params': [tp.name for tp in Node.type_params], + # 记录泛型模板定义模块的 SHA1,用于特化时统一命名空间 + 'sha1': getattr(self.Trans, '_module_sha1', None), + } + + def PrescanModuleGenericClassTemplates(self, ModuleBody: object) -> None: + """前置声明之前预注册**本模块**泛型类模板。 + + 判据与 includes 侧对泛型类的注册(HandlesImports._LoadModuleDeclarationsFromFile/ + _EmitImportFromDeclarations 的 `node.type_params` 非空)**同源**,即 + _is_generic_class,不看类名白名单。 + + 类处理阶段(下方 _EmitClassLlvm)才注册本模块模板,而顶层函数前置声明 + (GenerateLlvmDirect 的 _EmitFunctionForwardDeclLlvm)已需要它:模块级函数 + 参数/返回注解写自身泛型类(`def f(b: Box[Node])`)时 + _SubscriptHitsGenericTemplate 判为不命中 → TypeAnnotationResolver 解析为空 → + VoidType → 抛"无法识别的参数类型注解";而同模块方法参数(类处理阶段之后) + 却可用,二者分裂。预注册后两处走同一 _generic_class_templates。 + """ + for Node in ModuleBody: + if isinstance(Node, ast.ClassDef) and self._is_generic_class(Node): + self._RegisterGenericClassTemplate(Node) + + # ================================================================== + # _EmitNestedClassDefs - 递归发射类体内的嵌套 class / struct + # + # 命名与引用口径(用户 2026-10-01 定,与 union/renum 嵌套命名同源): + # · 内层结构体名 = `{外类}_{内类}`(Gen.structs / class_members 等同键); + # · 引用:限定名 `外类.内类`(TypeAnnotationResolver._from_node_attribute 的 + # 扁平名回退)与扁平名 `外类_内类`(裸名直查 SymbolTable)都可用; + # · 多层嵌套自然递归为 A_B_C(deepcopy 后改名再递归)。 + # 用 deepcopy 而不是临时改原名:源码 AST 节点还会被 _CollectOwnMemberTypes / + # SymbolExtractor / vpi 写出等路径读到,不得留下改名的副作用。 + # ================================================================== + def _EmitNestedClassDefs(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: + """把类体内的嵌套 ClassDef 逐个以 `{外类}_{内类}` 递归发射(见上方注释)""" + if Node is None: + return + Body: list | None = getattr(Node, 'body', None) + ClassName: str | None = getattr(Node, 'name', None) + if not Body or not ClassName: + return + for Item in list(Body): + if not isinstance(Item, ast.ClassDef): + continue + InnerName: str | None = getattr(Item, 'name', None) + if not InnerName: + continue + NestedName: str = f'{ClassName}_{InnerName}' + # 已发射过(多遍/重复调用)只补名字登记,不重复发射 + if NestedName in Gen.structs and NestedName in Gen.class_members: + self._RegisterNestedClassName(ClassName, InnerName, NestedName) + continue + NestedNode: ast.ClassDef = copy.deepcopy(Item) + NestedNode.name = NestedName + self._EmitClassLlvm(NestedNode, Gen) + self._RegisterNestedClassName(ClassName, InnerName, NestedName) + + def _RegisterNestedClassName(self, ClassName: str, InnerName: str, NestedName: str) -> None: + """登记嵌套类名的三种写法(裸名 / `外类.内类` / 扁平名) + + 与 _EmitREnumLlvm 的变体三键登记同口径(见本文件 REnum 变体注册处), + 供"限定名扁平回退"与"裸名直查"两条解析路径命中。 + """ + MemberNode: CTypeInfo = CTypeInfo() + MemberNode.Name = NestedName + MemberNode.BaseType = t.CStruct(name=NestedName) + MemberNode.IsStruct = True + self.Trans.SymbolTable.insert(NestedName, MemberNode) + self.Trans.SymbolTable.insert(f'{ClassName}.{InnerName}', MemberNode) + + def _EmitClassLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, declare_only: bool = False) -> None: + ClassName: str = Node.name + # t.CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是"类型强转"标记, + # 不应被编译为真实结构体。CType 基类本身无 bases,base 检测无法触发, + # 此处在方法入口直接按类名跳过。 + if self._is_classname_ctype_marker(ClassName): + return + if self._is_generic_class(Node): + self._RegisterGenericClassTemplate(Node) + return + if self._is_exception_class(Node): + self._RegisterExceptionClass(Node) + return + IsCenum: bool = False + IsCunion: bool = False + IsRenum: bool = False + # 先检查装饰器形式(@t.CStruct / @t.CEnum / @t.CUnion / @t.REnum) + deco_kind: str | None = self._resolve_decorator_kind(Node, Gen) + if deco_kind == 'enum': + IsCenum = True + elif deco_kind == 'union': + IsCunion = True + elif deco_kind == 'renum': + IsRenum = True + # 再检查基类继承形式(class X(t.CStruct):),与装饰器合并 + if not IsCenum and not IsCunion and not IsRenum and Node.bases: + for base in Node.bases: + base_name: str | None = None + if hasattr(base, 'attr'): + base_name = base.attr + elif hasattr(base, 'id'): + base_name = base.id + if base_name: + kind: str | None = self._resolve_base_kind(base_name, Gen) + if kind == 'enum': + IsCenum = True + break + elif kind == 'union': + IsCunion = True + elif kind == 'renum': + IsRenum = True + elif kind == 'ctype_marker': + # CType 及其子类(CChar/CInt/CVoid/CPtr/CTypeDefault 等)是 + # "类型强转"标记,不应被编译为真实结构体。直接 return 跳过编译。 + return + if IsCenum: + self._RegisterEnumMembers(Node) + return + if IsCunion: + self._EmitUnionLlvm(Node, Gen) + return + if IsRenum: + self._EmitREnumLlvm(Node, Gen) + return + IsCpythonObject: bool + IsCVTable: bool + ParentClassName: str | None + IsNoVTable: bool + VtableByClassLevel: bool + IsCpythonObject, IsCVTable, ParentClassName, IsNoVTable, VtableByClassLevel = self._resolve_class_flags(Node, Gen) + # 嵌套类 / 嵌套 struct:类体里再写 class / struct 此前被**静默忽略**(本函数的 + # 字段/方法遍历只认 AnnAssign / FunctionDef,CollectAnnAssignMember 对其它节点 + # 直接返回 None)。现按既有扁平命名先例发射为独立结构体 `{外类}_{内类}` + # (与 _EmitUnionLlvm:1569 / _EmitREnumLlvm:1715 的 `f"{ClassName}_{Nested}"` + # 同源),并登记 SymbolTable 三键,使 `外类.内类` 与扁平名 `外类_内类` 都能解析。 + # 时机:在 flags 解析之后、class_members 初始化之前 —— 让嵌套结构体先就绪, + # 外层字段注解引用 `外类.内类` 时即可命中(_resolve_class_flags 内的名字键 + # 均已按 ClassName 建立,递归改名不影响外层)。 + self._EmitNestedClassDefs(Node, Gen) + # 跟踪正在发射的类,防止递归特化时 _generate_structs 过早为空成员类设置 body + Gen._classes_in_progress.add(ClassName) + # 标记正在收集字段,阻止继承处理(_ResolveGenericSlice → _specialize_generic_class) + # 和 CollectAnnAssignMember 触发的嵌套 _generate_structs 用不完整 class_members 调用 set_body + Gen._collecting_members_set.add(ClassName) + if ClassName not in Gen.class_methods: + Gen.class_methods[ClassName] = [] + else: + # 防御性清除:移除导入阶段可能添加的构造函数(__new__/__init__/__before_init__)。 + # 构造函数不参与虚分派,放入 vtable 会导致子类与基类 vtable 大小不一致, + # 索引偏移破坏多态分派(如 Module 有 __new__/__init__ 使 vtable=7,而 AST 无它们 vtable=5, + # 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。 + Gen.class_methods[ClassName] = [ + m for m in Gen.class_methods[ClassName] + if m.split('.')[-1] not in ('__new__', '__init__', '__before_init__') + ] + # 当前文件定义的类是权威定义,覆盖之前导入阶段可能添加的外部声明。 + # 关键:如果本地类是 @t.NoVTable 但同名导入类有 vtable(名称冲突), + # 必须清除 vtable 状态,否则 _generate_structs 会错误添加 vtable 指针。 + if IsNoVTable: + Gen.class_vtable.discard(ClassName) + Gen._cross_module_vtable_classes.discard(ClassName) + Gen._cross_module_novtable.add(ClassName) + # 清除可能从导入 stub 加载的旧 struct body(含错误 vtable 指针), + # 让 _generate_structs 用本地 class_members 重新生成。 + # 字段名必须是 llvmlite 的**公开属性 `elements`**(is_opaque = elements is None; + # set_body 仅在 elements is not None 时拒绝)——写 `_elements` 在本版本是空操作, + # 会让"权威重建点"永远无法刷新已冻结的错误布局。 + _old_st = Gen.structs.get(ClassName) + if isinstance(_old_st, ir.IdentifiedStructType) and _old_st.elements is not None: + _old_st.elements = None + _ih = getattr(Gen, '_import_handler_ref', None) + if _ih is not None: + _ck = (id(Gen), ClassName) + if _ck in _ih._struct_Load_cache: + del _ih._struct_Load_cache[_ck] Gen.class_members[ClassName] = [] # 本地定义是权威重建点:先撤销"已展平"登记,重建完成(且父类齐全)后再登记。 - Gen._members_flattened.discard(ClassName) - Gen.class_member_defaults[ClassName] = {} - Gen.class_member_signeds[ClassName] = {} - Gen.class_member_bitfields[ClassName] = {} - Gen.class_member_byteorders[ClassName] = {} - Gen.class_member_bitoffsets[ClassName] = {} - ParentClass: str | None = None - ParentClasses: list[str] = [] - # 本类展平是否完整(父类若在此刻尚未就绪会被跳过 ⇒ 不登记已展平, - # 留待查询侧 HandlesImports.EnsureClassMembersFlattened 补齐,顺序无关)。 - _local_flat_complete: bool = True - if Node.bases: - class_graph: dict = Gen._class_graph - for base in Node.bases: - if hasattr(base, 'id'): - base_name = base.id - elif hasattr(base, 'attr'): - base_name = base.attr - elif isinstance(base, ast.Subscript): - # 泛型特化基类:GSListNode[GNode] - # 特化泛型类,用特化名作为父类名 - spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(base) - if spec_name: - base_name = spec_name - else: - continue - else: - continue - if base_name == ClassName: - continue - if base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): - continue - # 多继承:收集所有真实父类(声明顺序),不再 break。 - # 总是记录父类名,即使父类 class_members 未加载。 - # _get_member_offset 会沿 class_parent 链回退查找继承字段。 - if ParentClass is None: - ParentClass = base_name - Gen.class_parent[ClassName] = ParentClass - ParentClasses.append(base_name) - if ParentClasses: - Gen.class_parents[ClassName] = ParentClasses - if ParentClasses: - # ============================================================ - # 多继承字段展平:遍历所有父类(声明顺序),字段名**不去重**, - # __vtable__ 跳过(vtable 指针由 _generate_structs 按 class_vtable 统一添加)。 - # 布局: [父类1字段..., 父类2字段..., 子类字段...](MRO 正序,与 - # DeclarationGenerator stub / TPV 参考实现一致)。 - # - # 关键修复(2026-08-10):原实现用 `inherited + class_members`(prepend) - # 产出 MRO 反序布局,且按字段名去重(如多个 Mixin 各自声明的 TempDir - # 被剔除)——导致定义模块 struct 与跨模块 stub 布局不一致,所有多继承 - # 类字段运行时错位(ASan 下读到 0xbe 填充崩溃)。Mixin 方法体以自身 - # 局部索引独立编译,调用侧 this_adjust + 块内索引依赖"展平块与 Mixin - # 自身逐字段一致",因此跨父类不能去重。菱形继承(A→D, B→D)会让 D - # 字段重复——当前项目 Mixin 为兄弟无共享祖先;若未来出现真菱形需改为 - # "按继承源去重"而非"按字段名去重"。 - # ============================================================ - # 按继承源去重(2026-08-18):菱形继承(子类同时继承 A、B、D 且 - # A/B 也继承 D)会让 D 的字段被重复展平。典型:Generator 同时继承 - # ClassGenMixin/ClassGenEnum/ClassGenGeneric/ClassGenVTable/ClassGenDef/ - # ClassGenBase,而前 5 个都继承 ClassGenBase → ClassGenBase 41 字段 - # 被展平 6 次,struct 膨胀且 this_adjust/字段 GEP 全部错位。 - # 修复:递归展平整个继承树,每个类只处理一次;每类只贡献 - # "非祖先字段"(class_members[类] 是含祖先的展平结果,需剔除祖先 - # 部分)。同名字段(如 TempDir 在 CallGenBase/TypeGenBase 各自声明) - # 因非继承关系,两个类都贡献,各自保留——符合下方 L654 注释的 - # "按继承源去重"而非"按字段名去重"。 - def _FlattenInherited(cls_name: str, _seen_cls: set, - _out: list, _gen) -> None: - if cls_name in _seen_cls: - return - _seen_cls.add(cls_name) - for _gp in _gen.class_parents.get(cls_name, []) or []: - _FlattenInherited(_gp, _seen_cls, _out, _gen) - # 收集该类的全部祖先字段名(含间接),剔除自身已展平结果中 - # 属于祖先的部分(避免 class_members[类] 里混合的祖先字段重复) - _anc_names: set[str] = set() - _stack: list[str] = [cls_name] - _vis: set[str] = set() - while _stack: - _c = _stack.pop() - for _gp in _gen.class_parents.get(_c, []) or []: - if _gp in _vis: - continue - _vis.add(_gp) - _stack.append(_gp) - for _mn, _mt in _gen.class_members.get(_gp, []) or []: - _anc_names.add(_mn) - for _mn, _mt in _gen.class_members.get(cls_name, []) or []: - if _mn == '__vtable__': - continue - if _mn in _anc_names: - continue - _out.append((_mn, _mt)) - _pd: dict = dict(_gen.class_member_defaults.get(cls_name, {})) - if _mn in _pd: - _gen.class_member_defaults[ClassName][_mn] = _pd[_mn] - - _seen_cls: set[str] = set() - _flat: list[tuple] = [] - for ParentClass in ParentClasses: - if ParentClass not in class_graph and ParentClass not in Gen.class_members: - # 父类尚未就绪(跨模块父类在此刻未加载 / 特化表未生成)⇒ - # 本类展平不完整,不登记 _members_flattened。 - _local_flat_complete = False - continue - _FlattenInherited(ParentClass, _seen_cls, _flat, Gen) - if _flat: - # 【幂等(2026-09-30)】该祖先块已作为**连续整段**存在于本表时不再追加: - # 本处是**追加**(`class_members + _flat`),与导入侧的 - # `PrependInheritedBlock`(前置,自带表头判据)判据不对称,"块已在表中段/ - # 尾部"的情形谁都不判 ⇒ 整块再插一次,表里多出 len(_flat) 条重复条目, - # body 随之多出同名槽(现场:output/.stub.ll 的 TypeGenCType 73 槽 - # = temp 声明 41 槽 + 其 [9:41] 的原样副本;同族 7 个类, - # 下游即 Repl.vp:802 的"基座 pointee 未匹配任何 struct")。 - # 判据(名字 + IR 类型逐项连续匹配)与成因见 - # HandlesImports.InheritedBlockAlreadyPresent;只跳过重复、不改其余布局。 - from lib.core.Handles.HandlesImports import NormalizeAncestralCopies as _NormAnc - # 【规范化(2026-09-30)】_NormAnc 同时完成两件事: - # ① "块已整段在头/尾 ⇒ 不再追加"(等价于原 InheritedBlockAlreadyPresent); - # ② 剥掉两端挂着的"祖先块前缀的**陈旧半截副本**"——此前某次展平写入的是 - # 半截祖先块(父类表当时只收到前 k 项),本次整块追加就让那 k 条留在表里 - # 成重复(现场:ClassGenDef 表 50 / 去重 42 / body 42 ⇒ 8 条陈旧副本)。 - # 判据与成因见 HandlesImports.NormalizeAncestralCopies。 - _Trimmed: list - _HasFull: bool - _Trimmed, _HasFull = _NormAnc(list(Gen.class_members[ClassName]), _flat, InsertAtHead=False) - _Dropped: int = len(Gen.class_members[ClassName]) - len(_Trimmed) - if _Dropped > 0: - _vlog().warning( - f"[成员展平] 类 '{ClassName}' 追加祖先块({len(_flat)} 项)前剥掉 " - f"{_Dropped} 条**陈旧半截副本**(重名条目不占布局槽位,却会让 " - f"base/下标回退失准):表 {len(Gen.class_members[ClassName])} → " - f"{len(_Trimmed)} 项", - "成员展平") - Gen.class_members[ClassName] = _Trimmed - if _HasFull: - _vlog().warning( - f"[成员展平] 类 '{ClassName}' 的祖先块({len(_flat)} 项)已存在于成员表" - f"(表 {len(Gen.class_members[ClassName])} 项)⇒ 跳过重复追加" - f"(重复会让 body 多出同名槽、与 Phase 1 声明布局分叉)", - "成员展平") - else: - Gen.class_members[ClassName] = _Trimmed + _flat - # ============================================================ - # 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先), - # vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 TPV 一致), - # 不再生成子类包装器 "ClassName.method"(旧逻辑注册了包装器但未生成函数体, - # 导致链接 undefined reference,如 C.get_b / ColoredCircle.color_code / Vec2Both.extra_val)。 - # ============================================================ - all_parent_methods: list[str] = [] - parent_method_names_seen: set[str] = set() - for ParentClass in ParentClasses: - for pm in list(Gen.class_methods.get(ParentClass, [])): - method_name: str = pm.split('.')[-1] if '.' in pm else pm - if method_name in ('__new__', '__init__', '__before_init__'): - continue - if method_name in parent_method_names_seen: - continue # 先声明父类优先,菱形去重 - parent_method_names_seen.add(method_name) - all_parent_methods.append(pm) - if all_parent_methods: - # 构建子类已有方法映射(方法短名 -> 方法全名)。 - # 子类覆盖判定:既查 class_methods(prescan/导入阶段已注册), - # 也直接扫描 ClassDef 自身的方法(与 TPV _collect_vtable_methods 一致)。 - child_method_map: dict[str, str] = {} - for m in Gen.class_methods[ClassName]: - method_name: str = m.split('.')[-1] if '.' in m else m - child_method_map[method_name] = m - for _item_self in Node.body: - if isinstance(_item_self, ast.FunctionDef): - _mn_self: str = _item_self.name - if _mn_self in ('__new__', '__init__', '__before_init__'): - continue - child_method_map[_mn_self] = f"{ClassName}.{_mn_self}" - # 父类方法在前(保证 vtable 布局一致性,子类多态分派索引正确), - # 子类新增方法在后。子类覆盖的方法放在父类方法的位置(用子类实现)。 - new_methods: list = [] - covered_names: set[str] = set() - for pm in all_parent_methods: - method_name: str = pm.split('.')[-1] if '.' in pm else pm - if method_name in child_method_map: - # 子类覆盖了父类方法 — 用子类的实现,放在父类的位置 - new_methods.append(child_method_map[method_name]) - covered_names.add(method_name) - else: - # 父类独有方法 — 直接用父类方法原名(定义类名,如 B.get_b) - new_methods.append(pm) - covered_names.add(method_name) - # 添加子类新增方法(不在父类中的) - for m in list(Gen.class_methods[ClassName]): - method_name: str = m.split('.')[-1] if '.' in m else m - if method_name not in covered_names: - new_methods.append(m) - Gen.class_methods[ClassName] = new_methods - # 提取首个裸字符串字面量作为结构体 __doc__ docstring - if (Node.body and isinstance(Node.body[0], ast.Expr) - and isinstance(Node.body[0].value, ast.Constant) - and isinstance(Node.body[0].value.value, str)): - Gen._create_doc_global(ClassName, Node.body[0].value.value) - else: - Gen._create_doc_global(ClassName, None) - # 解析 __provides__/__requires__/__require_must__ 编译期元数据 - for item in Node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - field_name: str = item.target.id - if field_name in ('__provides__', '__requires__', '__require_must__') and item.value: - values: list[str] = [] - if isinstance(item.value, ast.List): - for elt in item.value.elts: - if isinstance(elt, ast.Constant) and isinstance(elt.value, str): - values.append(elt.value) - if field_name == '__provides__': - Gen.class_provides[ClassName] = values - elif field_name == '__requires__': - Gen.class_requires[ClassName] = values - elif field_name == '__require_must__': - Gen.class_require_must[ClassName] = values - # 自有字段追加段的起点(供下方"字段名遮蔽"检查切分自有/继承两段; - # 此时 class_members[ClassName] 只含继承前缀,见 :825)。 - _OwnFieldStart: int = len(Gen.class_members[ClassName]) - # 【嵌套重入防重(2026-09-30)】本循环**每轮都可能重入**同一张表: - # CollectAnnAssignMember → ResolveAnnotationType → 泛型特化(list[X] 等) - # → _generate_structs / _TryLoadClassMembersFromPyi → _EmitExternalClassDeclLlvm - # → 可按 .vpi **清空并重填**本类成员表(HandlesImports.py:3408 的 force 分支, - # 由 HandlesImports.py:2647/2654 与 FuncGen.py:1057 触发;该分支不受 - # "字段收集中"保护,见 HandlesImports.py:3299 的 `not force` 豁免)。 - # 此时本循环后面的 `append` 会把**嵌套帧已经登记过的同一字段**再写一条 —— - # 现场:AST 的最后一个字段 `children: list[rawptr[AST]] | t.CPtr` 是全类唯一 - # 触发泛型特化的注解,处理它时嵌套帧重填了 AST 表(7 项),本循环恢复后 - # 又追加一次 `children` ⇒ 表 8 项 / 去重 7 项,重名条目 `children@7`; - # 该表又被祖先块展平**原样拷贝**给 AST 的每个子类(_FlattenInherited 只按 - # 祖先名过滤、不按名去重)⇒ StructGen 的"[结构体布局] 成员表含重名条目" - # 在大量 AST 子类上齐发(AST/Stmt/ExprNode/MatchCase/ExceptHandler…)。 - # 判据刻意收窄:**只跳过"本轮循环开始之后才出现"的同名条目**(嵌套帧刚登记 - # 的那条)。循环前已在表里的同名(继承前缀/同名字段)照旧追加 ⇒ 上方 - # "字段名遮蔽"fail-fast 的判据(自有段切分)与既有布局语义逐字不变。 - _StartNames: set[str] = {m[0] for m in Gen.class_members[ClassName]} - for item in Node.body: - # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - if item.target.id in ('__provides__', '__requires__', '__require_must__'): - continue - parsed = self.CollectAnnAssignMember(item, Gen) - if parsed: - VarName, MemberType, TypeInfo = parsed - if VarName not in _StartNames \ - and VarName in {m[0] for m in Gen.class_members[ClassName]}: - continue - Gen.class_members[ClassName].append((VarName, MemberType)) - # const / volatile 类字段登记(统一登记入口,见 BaseGen)。 - # 键 `类名.字段名` + 裸字段名;与 TransPyV 侧 ClassGenDef 的 - # FieldEntry.IsConst/IsVolatile/PointeeVolatile 同口径。 - Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation) - if TypeInfo: - if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8: - if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): - element_class: str | None = FindStructNameInAnnotationWithContext( - item.annotation, self.Trans, Gen) - # Fallback: FindStructNameInAnnotation 不处理 Subscript slice 为 BinOp 的情况 - # (如 list[AST | t.CPtr] | t.CPtr),使用 TypeInfo.Name 作为回退。 - # TypeInfo.Name 由 _HandleSubscript 设置为特化名(如 'list[sha1.AST*]'), - # 经 _MergeCTypeInfoInto 传播后保留。 - if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: - element_class = TypeInfo.Name - if element_class: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = element_class - # 泛型特化结构体指针字段(如 GSList[BasicBlock] | t.CPtr): - # 记录特化名,供后续 _try_access_opaque_generic_member 在所有候选特化版本 - # 都有目标字段(如 Head/Tail/Count)时选择正确特化版本。 - # 现有 i8* 分支不覆盖结构体指针字段,这里补充。 - elif (isinstance(MemberType, ir.PointerType) - and isinstance(MemberType.pointee, ir.IdentifiedStructType) - and isinstance(item.annotation, ast.BinOp) - and isinstance(item.annotation.op, ast.BitOr)): + Gen._members_flattened.discard(ClassName) + Gen.class_member_defaults[ClassName] = {} + Gen.class_member_signeds[ClassName] = {} + Gen.class_member_bitfields[ClassName] = {} + Gen.class_member_byteorders[ClassName] = {} + Gen.class_member_bitoffsets[ClassName] = {} + ParentClass: str | None = None + ParentClasses: list[str] = [] + # 本类展平是否完整(父类若在此刻尚未就绪会被跳过 ⇒ 不登记已展平, + # 留待查询侧 HandlesImports.EnsureClassMembersFlattened 补齐,顺序无关)。 + _local_flat_complete: bool = True + if Node.bases: + class_graph: dict = Gen._class_graph + for base in Node.bases: + if hasattr(base, 'id'): + base_name = base.id + elif hasattr(base, 'attr'): + base_name = base.attr + elif isinstance(base, ast.Subscript): + # 泛型特化基类:GSListNode[GNode] + # 特化泛型类,用特化名作为父类名 + spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(base) + if spec_name: + base_name = spec_name + else: + continue + else: + continue + if base_name == ClassName: + continue + if base_name in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): + continue + # 多继承:收集所有真实父类(声明顺序),不再 break。 + # 总是记录父类名,即使父类 class_members 未加载。 + # _get_member_offset 会沿 class_parent 链回退查找继承字段。 + if ParentClass is None: + ParentClass = base_name + Gen.class_parent[ClassName] = ParentClass + ParentClasses.append(base_name) + if ParentClasses: + Gen.class_parents[ClassName] = ParentClasses + if ParentClasses: + # ============================================================ + # 多继承字段展平:遍历所有父类(声明顺序),字段名**不去重**, + # __vtable__ 跳过(vtable 指针由 _generate_structs 按 class_vtable 统一添加)。 + # 布局: [父类1字段..., 父类2字段..., 子类字段...](MRO 正序,与 + # DeclarationGenerator stub / VPC 参考实现一致)。 + # + # 关键修复(2026-08-10):原实现用 `inherited + class_members`(prepend) + # 产出 MRO 反序布局,且按字段名去重(如多个 Mixin 各自声明的 TempDir + # 被剔除)——导致定义模块 struct 与跨模块 stub 布局不一致,所有多继承 + # 类字段运行时错位(ASan 下读到 0xbe 填充崩溃)。Mixin 方法体以自身 + # 局部索引独立编译,调用侧 this_adjust + 块内索引依赖"展平块与 Mixin + # 自身逐字段一致",因此跨父类不能去重。菱形继承(A→D, B→D)会让 D + # 字段重复——当前项目 Mixin 为兄弟无共享祖先;若未来出现真菱形需改为 + # "按继承源去重"而非"按字段名去重"。 + # ============================================================ + # 按继承源去重(2026-08-18):菱形继承(子类同时继承 A、B、D 且 + # A/B 也继承 D)会让 D 的字段被重复展平。典型:Generator 同时继承 + # ClassGenMixin/ClassGenEnum/ClassGenGeneric/ClassGenVTable/ClassGenDef/ + # ClassGenBase,而前 5 个都继承 ClassGenBase → ClassGenBase 41 字段 + # 被展平 6 次,struct 膨胀且 this_adjust/字段 GEP 全部错位。 + # 修复:递归展平整个继承树,每个类只处理一次;每类只贡献 + # "非祖先字段"(class_members[类] 是含祖先的展平结果,需剔除祖先 + # 部分)。同名字段(如 TempDir 在 CallGenBase/TypeGenBase 各自声明) + # 因非继承关系,两个类都贡献,各自保留——符合下方 L654 注释的 + # "按继承源去重"而非"按字段名去重"。 + def _FlattenInherited(cls_name: str, _seen_cls: set, + _out: list, _gen) -> None: + if cls_name in _seen_cls: + return + _seen_cls.add(cls_name) + for _gp in _gen.class_parents.get(cls_name, []) or []: + _FlattenInherited(_gp, _seen_cls, _out, _gen) + # 收集该类的全部祖先字段名(含间接),剔除自身已展平结果中 + # 属于祖先的部分(避免 class_members[类] 里混合的祖先字段重复) + _anc_names: set[str] = set() + _stack: list[str] = [cls_name] + _vis: set[str] = set() + while _stack: + _c = _stack.pop() + for _gp in _gen.class_parents.get(_c, []) or []: + if _gp in _vis: + continue + _vis.add(_gp) + _stack.append(_gp) + for _mn, _mt in _gen.class_members.get(_gp, []) or []: + _anc_names.add(_mn) + for _mn, _mt in _gen.class_members.get(cls_name, []) or []: + if _mn == '__vtable__': + continue + if _mn in _anc_names: + continue + _out.append((_mn, _mt)) + _pd: dict = dict(_gen.class_member_defaults.get(cls_name, {})) + if _mn in _pd: + _gen.class_member_defaults[ClassName][_mn] = _pd[_mn] + + _seen_cls: set[str] = set() + _flat: list[tuple] = [] + for ParentClass in ParentClasses: + if ParentClass not in class_graph and ParentClass not in Gen.class_members: + # 父类尚未就绪(跨模块父类在此刻未加载 / 特化表未生成)⇒ + # 本类展平不完整,不登记 _members_flattened。 + _local_flat_complete = False + continue + _FlattenInherited(ParentClass, _seen_cls, _flat, Gen) + if _flat: + # 【幂等(2026-09-30)】该祖先块已作为**连续整段**存在于本表时不再追加: + # 本处是**追加**(`class_members + _flat`),与导入侧的 + # `PrependInheritedBlock`(前置,自带表头判据)判据不对称,"块已在表中段/ + # 尾部"的情形谁都不判 ⇒ 整块再插一次,表里多出 len(_flat) 条重复条目, + # body 随之多出同名槽(现场:output/.stub.ll 的 TypeGenCType 73 槽 + # = temp 声明 41 槽 + 其 [9:41] 的原样副本;同族 7 个类, + # 下游即 Repl.vp:802 的"基座 pointee 未匹配任何 struct")。 + # 判据(名字 + IR 类型逐项连续匹配)与成因见 + # HandlesImports.InheritedBlockAlreadyPresent;只跳过重复、不改其余布局。 + from lib.core.Handles.HandlesImports import NormalizeAncestralCopies as _NormAnc + # 【规范化(2026-09-30)】_NormAnc 同时完成两件事: + # ① "块已整段在头/尾 ⇒ 不再追加"(等价于原 InheritedBlockAlreadyPresent); + # ② 剥掉两端挂着的"祖先块前缀的**陈旧半截副本**"——此前某次展平写入的是 + # 半截祖先块(父类表当时只收到前 k 项),本次整块追加就让那 k 条留在表里 + # 成重复(现场:ClassGenDef 表 50 / 去重 42 / body 42 ⇒ 8 条陈旧副本)。 + # 判据与成因见 HandlesImports.NormalizeAncestralCopies。 + _Trimmed: list + _HasFull: bool + _Trimmed, _HasFull = _NormAnc(list(Gen.class_members[ClassName]), _flat, InsertAtHead=False) + _Dropped: int = len(Gen.class_members[ClassName]) - len(_Trimmed) + if _Dropped > 0: + _vlog().warning( + f"[成员展平] 类 '{ClassName}' 追加祖先块({len(_flat)} 项)前剥掉 " + f"{_Dropped} 条**陈旧半截副本**(重名条目不占布局槽位,却会让 " + f"base/下标回退失准):表 {len(Gen.class_members[ClassName])} → " + f"{len(_Trimmed)} 项", + "成员展平") + Gen.class_members[ClassName] = _Trimmed + if _HasFull: + _vlog().warning( + f"[成员展平] 类 '{ClassName}' 的祖先块({len(_flat)} 项)已存在于成员表" + f"(表 {len(Gen.class_members[ClassName])} 项)⇒ 跳过重复追加" + f"(重复会让 body 多出同名槽、与 Phase 1 声明布局分叉)", + "成员展平") + else: + Gen.class_members[ClassName] = _Trimmed + _flat + # ============================================================ + # 多继承方法合并:遍历所有父类(声明顺序)收集方法(短名去重,先声明父类优先), + # vtable 槽/方法名用"实际定义类.方法"(如 B.get_b,与 VPC 一致), + # 不再生成子类包装器 "ClassName.method"(旧逻辑注册了包装器但未生成函数体, + # 导致链接 undefined reference,如 C.get_b / ColoredCircle.color_code / Vec2Both.extra_val)。 + # ============================================================ + all_parent_methods: list[str] = [] + parent_method_names_seen: set[str] = set() + for ParentClass in ParentClasses: + for pm in list(Gen.class_methods.get(ParentClass, [])): + method_name: str = pm.split('.')[-1] if '.' in pm else pm + if method_name in ('__new__', '__init__', '__before_init__'): + continue + if method_name in parent_method_names_seen: + continue # 先声明父类优先,菱形去重 + parent_method_names_seen.add(method_name) + all_parent_methods.append(pm) + if all_parent_methods: + # 构建子类已有方法映射(方法短名 -> 方法全名)。 + # 子类覆盖判定:既查 class_methods(prescan/导入阶段已注册), + # 也直接扫描 ClassDef 自身的方法(与 VPC _collect_vtable_methods 一致)。 + child_method_map: dict[str, str] = {} + for m in Gen.class_methods[ClassName]: + method_name: str = m.split('.')[-1] if '.' in m else m + child_method_map[method_name] = m + for _item_self in Node.body: + if isinstance(_item_self, ast.FunctionDef): + _mn_self: str = _item_self.name + if _mn_self in ('__new__', '__init__', '__before_init__'): + continue + child_method_map[_mn_self] = f"{ClassName}.{_mn_self}" + # 父类方法在前(保证 vtable 布局一致性,子类多态分派索引正确), + # 子类新增方法在后。子类覆盖的方法放在父类方法的位置(用子类实现)。 + new_methods: list = [] + covered_names: set[str] = set() + for pm in all_parent_methods: + method_name: str = pm.split('.')[-1] if '.' in pm else pm + if method_name in child_method_map: + # 子类覆盖了父类方法 — 用子类的实现,放在父类的位置 + new_methods.append(child_method_map[method_name]) + covered_names.add(method_name) + else: + # 父类独有方法 — 直接用父类方法原名(定义类名,如 B.get_b) + new_methods.append(pm) + covered_names.add(method_name) + # 添加子类新增方法(不在父类中的) + for m in list(Gen.class_methods[ClassName]): + method_name: str = m.split('.')[-1] if '.' in m else m + if method_name not in covered_names: + new_methods.append(m) + Gen.class_methods[ClassName] = new_methods + # 提取首个裸字符串字面量作为结构体 __doc__ docstring + if (Node.body and isinstance(Node.body[0], ast.Expr) + and isinstance(Node.body[0].value, ast.Constant) + and isinstance(Node.body[0].value.value, str)): + Gen._create_doc_global(ClassName, Node.body[0].value.value) + else: + Gen._create_doc_global(ClassName, None) + # 解析 __provides__/__requires__/__require_must__ 编译期元数据 + for item in Node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + field_name: str = item.target.id + if field_name in ('__provides__', '__requires__', '__require_must__') and item.value: + values: list[str] = [] + if isinstance(item.value, ast.List): + for elt in item.value.elts: + if isinstance(elt, ast.Constant) and isinstance(elt.value, str): + values.append(elt.value) + if field_name == '__provides__': + Gen.class_provides[ClassName] = values + elif field_name == '__requires__': + Gen.class_requires[ClassName] = values + elif field_name == '__require_must__': + Gen.class_require_must[ClassName] = values + # 自有字段追加段的起点(供下方"字段名遮蔽"检查切分自有/继承两段; + # 此时 class_members[ClassName] 只含继承前缀,见 :825)。 + _OwnFieldStart: int = len(Gen.class_members[ClassName]) + # 【嵌套重入防重(2026-09-30)】本循环**每轮都可能重入**同一张表: + # CollectAnnAssignMember → ResolveAnnotationType → 泛型特化(list[X] 等) + # → _generate_structs / _TryLoadClassMembersFromPyi → _EmitExternalClassDeclLlvm + # → 可按 .vpi **清空并重填**本类成员表(HandlesImports.py:3408 的 force 分支, + # 由 HandlesImports.py:2647/2654 与 FuncGen.py:1057 触发;该分支不受 + # "字段收集中"保护,见 HandlesImports.py:3299 的 `not force` 豁免)。 + # 此时本循环后面的 `append` 会把**嵌套帧已经登记过的同一字段**再写一条 —— + # 现场:AST 的最后一个字段 `children: list[rawptr[AST]] | t.CPtr` 是全类唯一 + # 触发泛型特化的注解,处理它时嵌套帧重填了 AST 表(7 项),本循环恢复后 + # 又追加一次 `children` ⇒ 表 8 项 / 去重 7 项,重名条目 `children@7`; + # 该表又被祖先块展平**原样拷贝**给 AST 的每个子类(_FlattenInherited 只按 + # 祖先名过滤、不按名去重)⇒ StructGen 的"[结构体布局] 成员表含重名条目" + # 在大量 AST 子类上齐发(AST/Stmt/ExprNode/MatchCase/ExceptHandler…)。 + # 判据刻意收窄:**只跳过"本轮循环开始之后才出现"的同名条目**(嵌套帧刚登记 + # 的那条)。循环前已在表里的同名(继承前缀/同名字段)照旧追加 ⇒ 上方 + # "字段名遮蔽"fail-fast 的判据(自有段切分)与既有布局语义逐字不变。 + _StartNames: set[str] = {m[0] for m in Gen.class_members[ClassName]} + for item in Node.body: + # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + if item.target.id in ('__provides__', '__requires__', '__require_must__'): + continue + parsed = self.CollectAnnAssignMember(item, Gen) + if parsed: + VarName, MemberType, TypeInfo = parsed + if VarName not in _StartNames \ + and VarName in {m[0] for m in Gen.class_members[ClassName]}: + continue + Gen.class_members[ClassName].append((VarName, MemberType)) + # const / volatile 类字段登记(统一登记入口,见 BaseGen)。 + # 键 `类名.字段名` + 裸字段名;与 ViperC 侧 ClassGenDef 的 + # FieldEntry.IsConst/IsVolatile/PointeeVolatile 同口径。 + Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation) + if TypeInfo: + if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8: + if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): + element_class: str | None = FindStructNameInAnnotationWithContext( + item.annotation, self.Trans, Gen) + # Fallback: FindStructNameInAnnotation 不处理 Subscript slice 为 BinOp 的情况 + # (如 list[AST | t.CPtr] | t.CPtr),使用 TypeInfo.Name 作为回退。 + # TypeInfo.Name 由 _HandleSubscript 设置为特化名(如 'list[sha1.AST*]'), + # 经 _MergeCTypeInfoInto 传播后保留。 + if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: + element_class = TypeInfo.Name + if element_class: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = element_class + # 泛型特化结构体指针字段(如 GSList[BasicBlock] | t.CPtr): + # 记录特化名,供后续 _try_access_opaque_generic_member 在所有候选特化版本 + # 都有目标字段(如 Head/Tail/Count)时选择正确特化版本。 + # 现有 i8* 分支不覆盖结构体指针字段,这里补充。 + elif (isinstance(MemberType, ir.PointerType) + and isinstance(MemberType.pointee, ir.IdentifiedStructType) + and isinstance(item.annotation, ast.BinOp) + and isinstance(item.annotation.op, ast.BitOr)): spec_in_anno: str | None = FindStructNameInAnnotationWithContext( - item.annotation, self.Trans, Gen) - if spec_in_anno and '[' in spec_in_anno: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = spec_in_anno - # Subscript 容器字段(list[X] / rawptr[list[X]]):非 BinOp 联合, - # 上面的 i8*/BinOp 分支不命中 → 容器身份丢失 → 字段方法调用 - # (get/len…)分派失败 "Undefined method 'get' in class 'rawptr'" - # (Converter.vp L190)。与 HandlesImports 对称,用与特化生成端 - # 同源的 _resolve_type_arg_str 记录 element_class。 - if isinstance(item.annotation, ast.Subscript): - _ann_sub_c: ast.Subscript = item.annotation - _subv_c: ast.AST = _ann_sub_c.value - _subname_c: str = getattr(_subv_c, 'id', None) or getattr(_subv_c, 'attr', '') - _elem_slice_c: ast.AST | None = None - if _subname_c == 'list': - _elem_slice_c = _ann_sub_c.slice - elif _subname_c == 'rawptr': - _slice_c: ast.AST = _ann_sub_c.slice - if isinstance(_slice_c, ast.Subscript): - # rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]): - # 容器身份 = 泛型特化名。list 继续走 _elem_slice_c 统一登记; - # 其余泛型容器直接登记 element_class(阻塞 B 根因链一环: - # 原实现只认内层 list,rawptr[GSList[Param]] 在此空转并跳过 - # 裸结构分支 → element_class 缺失 → 字段/局部变量方法分派 - # 丢失容器身份,FakeDuck 错绑 LinkedNode.append 写穿)。 - _inner_sub_c: ast.Subscript = _slice_c - _iname_c: str = getattr(_inner_sub_c.value, 'id', None) or getattr(_inner_sub_c.value, 'attr', '') - if _iname_c == 'list': - _elem_slice_c = _inner_sub_c.slice - else: - _ec_gc: str | None = None - if _iname_c: - try: - _ec_gc = f'{_iname_c}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_c.slice)}]' - except Exception: - _ec_gc = None - if _ec_gc: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = _ec_gc - else: - # rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内 - # 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复 - # struct 身份走 char*→struct cast。HEAD 态 `Token | t.CPtr` 联合 - # 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。 - # 与 HandlesImports 的 rawptr 分支对称。 - _slc_name_c: str | None = None - if isinstance(_slice_c, ast.Name): - _slc_name_c = _slice_c.id - elif isinstance(_slice_c, ast.Attribute): - _slc_name_c = _slice_c.attr - _ec_target_c: str | None = None - if _slc_name_c: - if _slc_name_c in Gen.structs: - _ec_target_c = _slc_name_c - else: - _st_sha1_c: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_slc_name_c) - if _st_sha1_c and f'{_st_sha1_c}.{_slc_name_c}' in Gen.structs: - _ec_target_c = f'{_st_sha1_c}.{_slc_name_c}' - else: - # 短名模糊扫描(对齐 HandlesAnnAssign 变量侧 - # 补偿 L597-602):跨模块 `mod.Class` 形态的 - # rawptr[Class] 字段在 class_sha1_map 尚无该短名 - # 时上面两路全 miss → element_class 静默缺失 → - # 字段方法调用(factory.PermPool.alloc)链尾类名 - # 丢失 → TPC 调用分派返回 i32 0 哨兵 → 误报 - # "Unknown module: ...PermPool"。 - for _ec_k_c in Gen.structs: - _ec_short_c: str = _ec_k_c.split('.')[-1] if '.' in _ec_k_c else _ec_k_c - if _ec_short_c == _slc_name_c: - _ec_target_c = _ec_k_c - break - if _ec_target_c: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = _ec_target_c - if _elem_slice_c is not None: - _ec_c: str | None = None - try: - _ec_c = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_c)}]' - except Exception: - _ec_c = None - if _ec_c: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = _ec_c - if hasattr(TypeInfo.BaseType, 'IsSigned'): - Gen.class_member_signeds[ClassName][VarName] = TypeInfo.BaseType.IsSigned - else: - Gen.class_member_signeds[ClassName][VarName] = None - if TypeInfo.IsBitField: - Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth - else: - Gen.class_member_bitfields[ClassName][VarName] = 0 - if TypeInfo.ByteOrder: - Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder - else: - Gen.class_member_byteorders[ClassName][VarName] = "" - if item.value: - const: ir.Constant | None = self._BuildScalarConstant(item.value, MemberType, Gen) - if const: - Gen.class_member_defaults[ClassName][VarName] = const - else: - Gen.class_member_signeds[ClassName][VarName] = None - Gen.class_member_bitfields[ClassName][VarName] = 0 - # ============================================================ - # 【字段名遮蔽 fail-fast】类体声明的字段名与**任一祖先声明的字段名**重名 ⇒ 终止编译 - # - # 这不是风格问题:本仓两条既有机制决定了"同名"必然产出**写错槽**,且是静默的。 - # ① 子类同名声明会成为**自有字段**,与**同名继承项**在 class_members 里并存; - # 跨模块侧的前置过滤器(HandlesImports._FlattenExt 前置段 :2516-2521)按 - # "名字已在表内"过滤——它本意只是防同一函数重入时重复前置,却会把**同名继承项 - # 整条丢掉** ⇒ 继承段与 struct body 的继承前缀失去一一对应:被丢项**之后**的 - # 继承字段表内下标整体前移 k(k = 被丢项数),而查询侧回退路径用的是 - # `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。 - # 现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 - # LastBuiltinsTypedefResult(下标 32)前移为 27 = CTypedefNameIdxBuilt(i32) - # ⇒ `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 报 - # "指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。 - # ② 父类方法按**父类布局**访问该名字的槽、子类方法按**自有槽**访问 ⇒ 同名两槽 - # split-brain(一侧写、另一侧读),运行期静默错值。 - # 唯一合法写法:字段只在其**归属类**声明一次;子类要新字段就换名字。 - # 合法例外(不在本检查范围):两个**互不为祖先**的类各自声明同名字段 - # (如 CallGenBase / TypeGenBase 的 TempDir)——那不属于遮蔽。 - # 判据来源:自有 = 本类类体追加段(:903 快照起);祖先 = 沿 class_parents 传递收集 - # 各祖先 class_members 的声明名(含仅从 .vpi 加载的祖先;祖先未就绪则该项不成判据, - # 不产生误报)。 - # ============================================================ - _AncestorFieldNames: dict[str, str] = {} - _AncSeen: set[str] = set() - # class_parents 可能尚未建立(HandlesImports 同族代码也用 hasattr 兜底), - # 缺失时视为"无祖先"——只可能少报、不可能误报。 - _ParentsMap: dict = getattr(Gen, 'class_parents', None) or {} - _AncStack: list[str] = list(_ParentsMap.get(ClassName) or []) - while _AncStack: - _Anc: str = _AncStack.pop() - if not _Anc or _Anc in _AncSeen: - continue - _AncSeen.add(_Anc) - for _An, _ in Gen.class_members.get(_Anc) or []: - if _An != '__vtable__': - _AncestorFieldNames.setdefault(_An, _Anc) - _AncStack.extend(_ParentsMap.get(_Anc) or []) - _OwnFieldNames: list[str] = [n for n, _ in Gen.class_members[ClassName][_OwnFieldStart:]] - _Shadowed: list[str] = sorted({_n for _n in _OwnFieldNames if _n in _AncestorFieldNames}) - if _Shadowed: - _Decl: ast.AST | None = None - for _it in Node.body: - if (isinstance(_it, ast.AnnAssign) and isinstance(_it.target, ast.Name) - and _it.target.id in _Shadowed): - _Decl = _it - break - _src: str = getattr(Gen, '_current_source_file', '') or '' - _ln: int = (getattr(_Decl, 'lineno', 0) or getattr(Node, 'lineno', 0) or 0) - _col: int = getattr(_Decl, 'col_offset', -1) if _Decl is not None else -1 - # 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行(同 - # HandlesBase._FailFastFixedArrayKeyword / _ErrorStack 各站点)。 - _off: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _off > 0 and _ln > _off: - _ln -= _off - _code: str = '' - try: - _code = (Gen._get_source_line(_ln) or '').strip() - except Exception: - _code = '' - _Pairs: str = '、'.join(f"'{_n}'(祖先 '{_AncestorFieldNames[_n]}' 已声明)" - for _n in _Shadowed) - _msg: str = ( - f"类 '{ClassName}' 的类体字段与祖先类声明的字段**重名**:{_Pairs}。" - f"字段只能在其归属类声明一次(子类要新字段请改名;若确实要复用父类字段," - f"删掉子类的这条声明即可)——重名会让该名字在 class_members 里变成" - f"「自有字段 + 同名继承项」两份,跨模块展平的按名过滤会丢掉继承项," - f"使其后所有继承字段的表内下标整体前移、写入落到类型不同的槽" - f"(典型:指针值写进 i32 槽 → 「指针隐式转整数已禁止」);" - f"父类方法与子类方法还会各自访问不同槽(同一变量 split-brain)。" - f"位置 {_src}:{_ln}:{_col}") - _vlog().compile_error(_msg, _src, _ln, _code) - sys.exit(1) - # 字段收集完成,清除标志,允许后续 _generate_structs 正常设置 body - Gen._collecting_members_set.discard(ClassName) - existing_members: set[str] = {m[0] for m in Gen.class_members[ClassName]} - for item in Node.body: - if isinstance(item, ast.FunctionDef): - self._current_method_args = {} - for arg in item.args.args: - if arg.arg != 'self' and arg.annotation: - self._current_method_args[arg.arg] = arg.annotation - self._scan_method_body_for_self_members(item.body, ClassName, existing_members, Gen) - self._current_method_args = {} - for item in Node.body: - if isinstance(item, ast.FunctionDef): - MethodName: str = item.name - FullMethodName: str = f"{ClassName}.{MethodName}" - # property setter/deleter 使用不同的函数名后缀 - item_meta: FuncMeta = self.Trans.FunctionHandler._ExtractFuncMeta(item.decorator_list) - if FuncMeta.PROPERTY_SETTER in item_meta: - FullMethodName = FullMethodName + '$set' - elif FuncMeta.PROPERTY_DELETER in item_meta: - FullMethodName = FullMethodName + '$del' - # 构造函数(__new__/__init__)不放入 vtable,避免子类与基类 - # vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC - # 自动生成,不会出现在 FunctionDef 中,此处一并防御。 - # - # 方法级修饰符(`novirtual def m(...)` ⇒ @t.NoVTable / - # `virtual def m(...)` ⇒ @t.CVTable):是否占槽按 - # SymbolUtils.IsVtableSlotMethod 判定——与 TPV - # ClassGenBase._should_method_be_virtual 同判据(**唯一规则源**), - # 故 TPC/TPV 的虚表槽位顺序与数量逐槽一致(含"类级 NoVTable + - # 方法级 CVTable ⇒ 仍生成虚表且只有该方法进槽"这一组合形态)。 - # 仅在本类确有 vtable(IsCVTable)时筛选:无 vtable 的类不进 - # Vtables,class_methods 仍保留全部方法供静态解析/继承包装使用。 - if MethodName not in ('__new__', '__init__', '__before_init__'): - _IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(item, VtableByClassLevel) - if _IsSlot and FullMethodName not in Gen.class_methods[ClassName]: - Gen.class_methods[ClassName].append(FullMethodName) - Gen.register_method(ClassName, FullMethodName, _IsSlot) - else: - Gen.register_method(ClassName, FullMethodName) - if IsCVTable: - Gen.class_vtable.add(ClassName) - Gen._generate_structs() - Gen._create_Vtable_globals() - if _local_flat_complete: - # 登记"该表已按父类展平":供 _TryLoadClassMembersFromPyi 的 guard 与 - # FuncGen._member_base 的查询前补齐使用(与跨模块路径同一口径)。 - Gen._members_flattened.add(ClassName) - # _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整), - # 当前类的方法已全部注册(L559-571),此处显式创建其 vtable - if IsCVTable: - Gen._create_vtable_for_class(ClassName) - if ClassName in Gen.structs: - self._EmitNewFunctionLlvm(ClassName, Gen, IsCVTable=IsCVTable, IsCpythonObject=IsCpythonObject) - for item in Node.body: - if isinstance(item, ast.FunctionDef): - self.Trans.FunctionHandler._EmitFunctionForwardDeclLlvm(item, Gen, ClassName=ClassName) - if not declare_only: - for item in Node.body: - if isinstance(item, ast.FunctionDef): - self.Trans.FunctionHandler._EmitFunctionLlvm(item, Gen, ClassName=ClassName) - if IsCVTable and ClassName in Gen.Vtables: - self._fill_vtable_with_methods(ClassName, Gen) - # 多继承支持:不再生成子类包装器(_generate_inherited_method_wrappers)。 - # 包装器只对 self 做 bitcast 基址转换,不调整字段偏移——对多继承中 - # 第二个及之后的父类(字段区偏移 > 0)会错位。继承方法统一走 - # MRO 祖先链查找 + this 调整(见 HandlesExprCall._adjust_this_for_inherited_method), - # 与 TPV 一致。 - Gen._classes_in_progress.discard(ClassName) - - def _generate_inherited_method_wrappers(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: - """为子类继承但未覆写的方法生成包装函数,使跨模块调用能正确链接。 - - 例如 Label 继承 Widget.place,生成 Label.place 函数, - 内部将 self 从 Label* bitcast 为 Widget*,然后调用 Widget.place。 - """ - # 收集本类方法 + 沿 MRO 继承链收集父类方法名 - # 原代码只遍历 class_methods[ClassName],遗漏父类独有方法(如 __enter__), - # 导致子类调用继承的方法时链接失败(undefined reference) - methods: list = list(Gen.class_methods.get(ClassName, [])) - seen_shorts: set[str] = {m.split('.')[-1] if '.' in m else m for m in methods} - mro_list: list[str] = Gen._get_mro_chain(ClassName) - for anc in mro_list[1:]: # 跳过自身,先声明父类优先(MRO 已去重) - parent_methods: list = Gen.class_methods.get(anc, []) - for pm in parent_methods: - pm_short: str = pm.split('.')[-1] if '.' in pm else pm - # 避免重复:如果子类已覆写同名方法,不添加 - if pm_short not in seen_shorts: - seen_shorts.add(pm_short) - methods.append(pm) - if not methods: - return - if ClassName not in Gen.structs: - return - - for method_name in methods: - method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name - child_full: str = f"{ClassName}.{method_short}" - - # 如果子类已经有该方法的实现,跳过 - if Gen._find_function(child_full): - continue - - # 沿 MRO 继承链查找父类实现(先声明父类优先) - parent_func: ir.Function | None = None - parent_class: str | None = None - for anc in Gen._get_mro_chain(ClassName)[1:]: - parent_full: str = f"{anc}.{method_short}" - parent_func = Gen._find_function(parent_full) - if parent_func: - parent_class = anc - break - - if not parent_func or not parent_class: - continue - - # 父类和子类都需要有 struct 定义 - if parent_class not in Gen.structs: - continue - - # 构造子类包装函数签名:与父类函数相同,但 self 参数类型为子类指针 - parent_ftype: ir.FunctionType = parent_func.function_type - parent_ret_type: ir.Type = parent_ftype.return_type - parent_param_types: list[ir.Type] = list(parent_ftype.args) - is_vararg: bool = parent_ftype.var_arg - - # 判断父类方法是否是静态方法: - # 如果第一个参数类型是父类指针,说明是实例方法;否则是静态方法 - parent_struct_ptr_type: ir.PointerType | None = ir.PointerType(Gen.structs[parent_class]) if parent_class in Gen.structs else None - is_static: bool = True - if parent_param_types and parent_struct_ptr_type: - first_param: ir.Type = parent_param_types[0] - # 检查第一个参数是否是父类指针(或可以 bitcast 为父类指针的指针类型) - if isinstance(first_param, ir.PointerType): - if isinstance(first_param.pointee, ir.IdentifiedStructType): - if first_param.pointee.name == Gen.structs[parent_class].name: - is_static = False - elif first_param == ir.IntType(8): - # i8* 可能是前向声明时的 self 类型,视为实例方法 - is_static = False - - if is_static: - # 静态方法:签名与父类完全一致,不需要 self 参数 - child_param_types: list[ir.Type] = list(parent_param_types) - child_struct_ptr: ir.PointerType | None = None - else: - # 实例方法:替换第一个参数(self)的类型为子类指针 - child_struct_ptr = ir.PointerType(Gen.structs[ClassName]) - if parent_param_types: - child_param_types = [child_struct_ptr] + parent_param_types[1:] - else: - child_param_types = [child_struct_ptr] - - child_func_type: ir.FunctionType = ir.FunctionType(parent_ret_type, child_param_types, var_arg=is_vararg) - child_mangled: str = Gen._mangle_func_name(child_full) - child_func: ir.Function = Gen._get_or_declare_function(child_mangled, child_func_type) - Gen.functions[child_full] = child_func - - # 生成函数体 - entry_block: ir.Block = child_func.append_basic_block(name="entry") - saved_builder: ir.IRBuilder = Gen.builder - saved_func: ir.Function = Gen.func - Gen.builder = Gen._NewIrBuilder(entry_block) - Gen.func = child_func - - # 准备参数 - call_args: list[ir.Value] = [] - if is_static: - # 静态方法:参数直接传递,不做 bitcast - for arg in child_func.args: - call_args.append(arg) - else: - # 实例方法:bitcast self,其余参数直接传递 - for i, arg in enumerate(child_func.args): - if i == 0 and parent_param_types: - actual_self_type: ir.Type = parent_param_types[0] - casted_self: ir.Value = Gen.builder.bitcast(arg, actual_self_type, name="self_cast") - call_args.append(casted_self) - else: - call_args.append(arg) - - result: ir.Value = Gen.builder.call(parent_func, call_args, name="inherited_call") - - if isinstance(parent_ret_type, ir.VoidType): - Gen.builder.ret_void() - else: - Gen.builder.ret(result) - - Gen.builder = saved_builder - Gen.func = saved_func - - def _fill_vtable_with_methods(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: - methods: list = Gen.class_methods.get(ClassName, []) - if not methods or ClassName not in Gen.Vtables: - return - Vtable: ir.GlobalVariable = Gen.Vtables[ClassName] - VtableType: ir.Type = Vtable.type.pointee - null_i8ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) - initializers: list[ir.Constant] = [] - for mi, method_name in enumerate(methods): - # 多继承:vtable 槽直接用完整方法名(实际定义类.方法,如 B.get_b), - # 与 TPV 一致,避免"子类名.方法"包装器导致链接 undefined reference。 - func: ir.Function | None = Gen._find_function(method_name) - if not func: - func = Gen._find_function(f"{method_name}__") - if not func: - method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name - func = Gen._find_function(f"{ClassName}.{method_short}") - if not func: - func = Gen._find_function(f"{ClassName}.{method_short}__") - if not func: - # 沿 MRO 祖先链查找定义类(先声明父类优先) - for anc in Gen._get_mro_chain(ClassName)[1:]: - func = Gen._find_function(f"{anc}.{method_short}") - if not func: - func = Gen._find_function(f"{anc}.{method_short}__") - if func: - break - if func: - initializers.append(func.bitcast(ir.PointerType(ir.IntType(8)))) - else: - initializers.append(null_i8ptr) - if initializers: - Vtable.initializer = ir.Constant(VtableType, initializers) - - def _scan_method_body_for_self_members(self, body: list[ast.AST], ClassName: str, existing_members: set[str], Gen: LlvmGeneratorMixin) -> None: - for stmt in body: - self._scan_node_for_self_assign(stmt, ClassName, existing_members, Gen) - - def _scan_node_for_self_assign(self, node: ast.AST, ClassName: str, existing_members: set[str], Gen: LlvmGeneratorMixin) -> None: - if isinstance(node, ast.AnnAssign): - if (isinstance(node.target, ast.Attribute) - and isinstance(node.target.value, ast.Name) - and node.target.value.id == 'self'): - VarName: str = node.target.attr - if VarName not in existing_members: - # fail-fast(项目约定:类型信息缺失立即终止,不兜底)。 - # 此前三处静默 i32 兜底(FromNode 返回 None / _ctype_to_llvm 得 void / - # 解析抛异常)会把"声明没解析出来"伪装成"该成员是 i32",下游赋值/取址 - # 检查于是报出与真因无关的错——实测:`free_lists: uint64*` 被当成 i32, - # `self.free_lists = base`(i8*) 因此误报"指针隐式转整数已禁止"。 - AnnotationText: str = ast.unparse(node.annotation) - try: - TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(node.annotation, self.Trans.SymbolTable) - if TypeInfo is None: - raise ValueError( - f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 " - f"'{AnnotationText}' 解析不出类型(CTypeInfo.FromNode 返回 None)" - f"—— 拒绝静默当作 i32" - ) - MemberType: ir.Type = Gen._ctype_to_llvm(TypeInfo) - if isinstance(MemberType, ir.VoidType): - raise ValueError( - f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 " - f"'{AnnotationText}' 解析为 void(BaseType=" - f"{getattr(TypeInfo, 'BaseType', None)} PtrCount=" - f"{getattr(TypeInfo, 'PtrCount', None)} IsTypedef=" - f"{getattr(TypeInfo, 'IsTypedef', None)} Name=" - f"{getattr(TypeInfo, 'Name', None)})—— 拒绝静默当作 i32" - ) - except Exception as e: # 成员类型解析失败:带注解原文与类/成员名终止 - if isinstance(e, ValueError) and str(e).startswith('[FATAL]'): - raise - raise ValueError( - f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 " - f"'{AnnotationText}' 解析失败:{type(e).__name__}: {e}" - ) from e - Gen.class_members[ClassName].append((VarName, MemberType)) - Gen.class_member_signeds[ClassName][VarName] = None - Gen.class_member_bitfields[ClassName][VarName] = 0 - Gen.class_member_byteorders[ClassName][VarName] = "" - Gen.class_member_bitoffsets[ClassName][VarName] = 0 - existing_members.add(VarName) - len_name = f'{VarName}__len' - if isinstance(MemberType, ir.PointerType) and node.value and isinstance(node.value, ast.List): - if len_name not in existing_members: - Gen.class_members[ClassName].append((len_name, ir.IntType(64))) - Gen.class_member_signeds[ClassName][len_name] = None - Gen.class_member_bitfields[ClassName][len_name] = 0 - Gen.class_member_byteorders[ClassName][len_name] = "" - Gen.class_member_bitoffsets[ClassName][len_name] = 0 - Gen.class_member_defaults[ClassName][len_name] = ir.Constant(ir.IntType(64), len(node.value.elts)) - existing_members.add(len_name) - elif isinstance(node, ast.Assign): - for target in node.targets: - if (isinstance(target, ast.Attribute) - and isinstance(target.value, ast.Name) - and target.value.id == 'self'): - VarName = target.attr - if VarName not in existing_members: - # 类型判定:只接受**能唯一确定**的取值形态(下表);推不出即 - # fail-fast,绝不静默产出 i32 槽(项目约定:类型信息缺失立即 - # 终止;与同函数 `self.X: T` 分支 :1356-1386 同口径)。 - # 同族现场:`free_lists: uint64*` 被当成 i32 后, - # `self.free_lists = base`(i8*) 误报"指针隐式转整数已禁止"。 - # 【必须与 DeclarationGenerator._CollectOwnMemberTypes 的 - # 同名规则逐条一致】否则 stub 与定义端 struct 布局分叉 - # (stub 少/多一个槽 ⇒ 跨模块字段偏移整体错位): - # 整型字面量 → i32;布尔字面量 → i32(Python 中 bool 是 int - # 子类,维持既有 int 分支语义);浮点字面量 → double; - # 字符串字面量 → i8*;值为**本方法带注解的形参** → 该形参类型; - # 白名单调用 float()/double() → double、char() → i8、 - # int()/long()/short() → i32、`x.len()` → i32。 - # 其余(None 字面量、BinOp、任意调用/属性链…)→ fail-fast: - # 这些形态推不出成员类型,静默 i32 只会把错误推到下游 - # (写指针值 → "指针隐式转整数已禁止")。 - MemberType: ir.Type | None = None - if node.value is not None: - try: - if isinstance(node.value, ast.Constant): - if isinstance(node.value.value, bool): - MemberType = ir.IntType(32) - elif isinstance(node.value.value, int): - MemberType = ir.IntType(32) - elif isinstance(node.value.value, float): - MemberType = ir.DoubleType() - elif isinstance(node.value.value, str): - MemberType = ir.PointerType(ir.IntType(8)) - elif isinstance(node.value, ast.Name): - if hasattr(self, '_current_method_args'): - arg_info = self._current_method_args.get(node.value.id) - if arg_info: - TypeInfo = CTypeInfo.FromNode(arg_info, self.Trans.SymbolTable) - if TypeInfo: - InferredType = Gen._ctype_to_llvm(TypeInfo) - if not isinstance(InferredType, ir.VoidType): - MemberType = InferredType - elif isinstance(node.value, ast.Call): - if isinstance(node.value.func, ast.Name): - if node.value.func.id in ('float', 'double'): - MemberType = ir.DoubleType() - elif node.value.func.id == 'char': - # char() 是内建 i8 强转(与 float()/double() 并列) - MemberType = ir.IntType(8) - elif node.value.func.id in ('int', 'long', 'short'): - MemberType = ir.IntType(32) - elif isinstance(node.value.func, ast.Attribute): - if node.value.func.attr == 'len': - MemberType = ir.IntType(32) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - if MemberType is None: - _src_f: str = getattr(Gen, '_current_source_file', '') or '' - _ln_f: int = getattr(node, 'lineno', 0) or 0 - _col_f: int = getattr(node, 'col_offset', -1) - # 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行 - _off_f: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _off_f > 0 and _ln_f > _off_f: - _ln_f -= _off_f - _code_f: str = '' - try: - _code_f = (Gen._get_source_line(_ln_f) or '').strip() - except Exception: - _code_f = '' - _val_txt: str = '' - try: - _val_txt = ast.unparse(node.value) if node.value is not None else '<无值>' - except Exception: - _val_txt = '<无法反解析>' - _msg_f: str = ( - f"类 '{ClassName}' 的字段 '{VarName}' 没有声明类型" - f"(既不在类体注解、也不在祖先中),且首次赋值 " - f"`self.{VarName} = {_val_txt}` 的取值形态推不出成员类型" - f"——拒绝静默当成 i32(那会把错误推到下游:写指针值时报" - f"「指针隐式转整数已禁止」)。请在类体声明该字段并给注解" - f"(如 `{VarName}: t.CInt32T` / `{VarName}: t.CPtr`)," - f"或在赋值处给出可定型的取值。" - f"位置 {_src_f}:{_ln_f}:{_col_f}") - _vlog().compile_error(_msg_f, _src_f, _ln_f, _code_f) - sys.exit(1) - Gen.class_members[ClassName].append((VarName, MemberType)) - Gen.class_member_signeds[ClassName][VarName] = None - Gen.class_member_bitfields[ClassName][VarName] = 0 - Gen.class_member_byteorders[ClassName][VarName] = "" - Gen.class_member_bitoffsets[ClassName][VarName] = 0 - existing_members.add(VarName) - for child in ast.iter_child_nodes(node): - if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): - continue - self._scan_node_for_self_assign(child, ClassName, existing_members, Gen) - - def _EmitUnionLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: - ClassName: str = Node.name - union_member_types: list[ir.Type] = [] - for item in Node.body: - if isinstance(item, ast.ClassDef): - nested_class_name: str = item.name - nested_member_types: list[ir.Type] = [] - for nested_item in item.body: - parsed = self.CollectAnnAssignMember(nested_item, Gen) - if parsed: - nested_member_types.append(parsed[1]) - if nested_member_types: - nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, f"{ClassName}_{nested_class_name}") - nested_struct_type.set_body(*nested_member_types) - Gen.structs[f"{ClassName}_{nested_class_name}"] = nested_struct_type - union_member_types.append(nested_struct_type) - nested_class_full_name: str = f"{ClassName}_{nested_class_name}" - if nested_class_full_name not in Gen.class_members: - Gen.class_members[nested_class_full_name] = [] - if nested_class_full_name not in Gen.class_member_signeds: - Gen.class_member_signeds[nested_class_full_name] = {} - for nested_item in item.body: - parsed = self.CollectAnnAssignMember(nested_item, Gen) - if parsed: - VarName, MemberType, TypeInfo = parsed - Gen.class_members[nested_class_full_name].append((VarName, MemberType)) - if TypeInfo and hasattr(TypeInfo.BaseType, 'IsSigned'): - Gen.class_member_signeds[nested_class_full_name][VarName] = TypeInfo.BaseType.IsSigned - else: - Gen.class_member_signeds[nested_class_full_name][VarName] = None - if union_member_types: - max_size: int = 0 - for member_type in union_member_types: - # 统一走 Gen._get_struct_size(与 __sizeof__ / memhub alloc 同源)。 - # llvmlite 并无 ir.DataLayout 类,原先那行必然抛 AttributeError, - # 再被 except 吞成 size=8 —— 静默少算,union 载荷区被开得过小。 - size: int = Gen._get_struct_size(member_type) - max_size = max(max_size, size) - union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName) - union_type.set_body(ir.ArrayType(ir.IntType(8), max_size)) - Gen.structs[ClassName] = union_type - UnionNode: CTypeInfo = CTypeInfo() - UnionNode.Name = ClassName - UnionNode.IsUnion = True - self.Trans.SymbolTable.insert(ClassName, UnionNode) - - def _EmitREnumLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: - ClassName: str = Node.name - variant_info: list[tuple] = [] - variant_names: list[str] = [] - - # 预注册 REnum opaque 结构体到 Gen.structs 和 SymbolTable, - # 使自引用成员(如 LLVMType | t.CPtr)在收集成员时能正确解析为 %LLVMType*。 - # 若不预注册,GetCTypeInfo 找不到 LLVMType → 回退到 IsEnum → i32 → | t.CPtr → i32*(错误)。 - PreRenumType: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName) - Gen.structs[ClassName] = PreRenumType - PreTypeInfo: CTypeInfo = CTypeInfo() - PreTypeInfo.Name = ClassName - PreTypeInfo.BaseType = t.REnum(ClassName) - PreTypeInfo.IsRenum = True - PreTypeInfo.IsEnum = True - # REnum 在内存布局上是 struct({ i32 __tag, }), - # 必须设置 IsStruct=True 才能让 HandlesTypeMerge 的 CPtr 吸收逻辑生效: - # `REnumType | t.CPtr` 应得到 REnumType*(PtrCount=1)而非 REnumType**(PtrCount=2)。 - # 否则 include-imported 函数返回类型会出现双指针,与声明单指针不匹配 → 运行时崩溃。 - PreTypeInfo.IsStruct = True - PreTypeInfo.RenumVariants = [] - self.Trans.SymbolTable.insert(ClassName, PreTypeInfo) - - # 第一遍:收集显式赋值的 tag 值,确定起始索引 - explicit_values: dict[str, int] = {} - for item in Node.body: - if isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name): - if item.value and isinstance(item.value, ast.Constant): - explicit_values[target.id] = item.value.value - # 类变体从 max(explicit_values, default=-1) + 1 开始 - next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0 - variant_index: int = 0 - - for item in Node.body: - if isinstance(item, ast.ClassDef): - VariantName: str = item.name - variant_names.append(VariantName) - member_types: list[ir.Type] = [] - member_names: list[str] = [] - member_annotations: list[ast.AST | None] = [] - for nested_item in item.body: - parsed = self.CollectAnnAssignMember(nested_item, Gen) - if parsed: - VarName, MemberType, TypeInfo = parsed - member_types.append(MemberType) - member_names.append(VarName) - member_annotations.append(nested_item.annotation if TypeInfo else None) - # 类变体使用专用计数器,避免与显式值冲突 - tag: int = next_class_tag - next_class_tag += 1 - variant_info.append((VariantName, member_types, member_names, member_annotations, item.lineno, tag)) - elif isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name): - VarName = target.id - variant_names.append(VarName) - value: int | None = None - if item.value and isinstance(item.value, ast.Constant): - value = item.value.value - else: - value = variant_index - variant_index += 1 - variant_info.append((VarName, [], [], [], item.lineno, value)) - # REnum 布局是所有变体的联合体。每个字段位置必须能容纳任何变体在该位置的字段。 - # Bug 修复:原来只选总尺寸最大的变体,但不同变体在同一位置可能有不同尺寸的字段。 - # 例如 MetaType: Concrete 位置3是 i32(4字节),但 Var 位置3是 MetaType*(8字节)。 - # 用 Concrete 的布局会导致 Var.Instance 指针被截断为4字节 → 运行时崩溃。 - # 修复:按每个位置的 max 字段尺寸构建布局。 - def _ir_type_size(t: ir.Type) -> int: - if isinstance(t, ir.IntType): - return max(4, t.width // 8) - elif isinstance(t, ir.PointerType): - return 8 - elif isinstance(t, ir.FloatType): - return 4 - elif isinstance(t, ir.DoubleType): - return 8 - elif isinstance(t, ir.ArrayType): - if isinstance(t.element, ir.IntType): - return t.element.width // 8 * t.count - return 8 * t.count - else: - return 8 - - all_variant_types: list[list[ir.Type]] = [] - max_field_count: int = 0 - for _, member_types, _, _, _, _ in variant_info: - if member_types: - full_types: list[ir.Type] = [ir.IntType(32)] + member_types - all_variant_types.append(full_types) - if len(full_types) > max_field_count: - max_field_count = len(full_types) - max_variant_struct: list[ir.Type] = [] - for pos in range(max_field_count): - pos_max_size: int = 0 - for vt in all_variant_types: - if pos < len(vt): - sz: int = _ir_type_size(vt[pos]) - if sz > pos_max_size: - pos_max_size = sz - # 按最大尺寸选择 LLVM 类型:<=4 用 i32,>4 用 i64(8字节,可容纳指针) - if pos_max_size <= 4: - max_variant_struct.append(ir.IntType(32)) - else: - max_variant_struct.append(ir.IntType(64)) - if not max_variant_struct: - max_variant_struct = [ir.IntType(32), ir.IntType(32)] - # 复用预注册的 opaque 结构体,设置 body 使其完整 - PreRenumType.set_body(*max_variant_struct) - for VariantName, member_types, member_names, member_annotations, lineno, tag_value in variant_info: - NestedStructName: str = f"{ClassName}_{VariantName}" - if member_types: - padded_member_types: list[ir.Type] = list(max_variant_struct) - nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, NestedStructName) - nested_struct_type.set_body(*padded_member_types) - Gen.structs[NestedStructName] = nested_struct_type - if NestedStructName not in Gen.class_members: - Gen.class_members[NestedStructName] = [] - if NestedStructName not in Gen.class_member_signeds: - Gen.class_member_signeds[NestedStructName] = {} - Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32))) - Gen.class_member_signeds[NestedStructName]['__tag'] = None - for i, (mname, mtype) in enumerate(zip(member_names, member_types)): - Gen.class_members[NestedStructName].append((mname, mtype)) - try: - ti: CTypeInfo | None = CTypeInfo.FromNode(member_annotations[i], self.Trans.SymbolTable) - if ti and hasattr(ti.BaseType, 'IsSigned'): - Gen.class_member_signeds[NestedStructName][mname] = ti.BaseType.IsSigned - else: - Gen.class_member_signeds[NestedStructName][mname] = None - except Exception as e: # 回退:签名信息解析失败时设为 None - _vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e) - Gen.class_member_signeds[NestedStructName][mname] = None - MemberNode: CTypeInfo = CTypeInfo() - MemberNode.Name = VariantName - MemberNode.BaseType = t.CEnum(ClassName) - MemberNode.value = tag_value - MemberNode.EnumName = ClassName - MemberNode.Lineno = lineno - MemberNode.IsEnumMember = True - self.Trans.SymbolTable.insert(VariantName, MemberNode) - # 同模块 REnum 变体也注册限定名和下划线名, - # 对齐跨模块导入(HandlesImports.py)的注册逻辑。 - # 避免裸名被同名的工厂函数覆盖后,路径 2 的限定名查找失败。 - self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode) - # 更新预注册的 REnum 类型信息(填充变体列表) - PreTypeInfo.RenumVariants = variant_names - + item.annotation, self.Trans, Gen) + if spec_in_anno and '[' in spec_in_anno: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = spec_in_anno + # Subscript 容器字段(list[X] / rawptr[list[X]]):非 BinOp 联合, + # 上面的 i8*/BinOp 分支不命中 → 容器身份丢失 → 字段方法调用 + # (get/len…)分派失败 "Undefined method 'get' in class 'rawptr'" + # (Converter.vp L190)。与 HandlesImports 对称,用与特化生成端 + # 同源的 _resolve_type_arg_str 记录 element_class。 + if isinstance(item.annotation, ast.Subscript): + _ann_sub_c: ast.Subscript = item.annotation + _subv_c: ast.AST = _ann_sub_c.value + _subname_c: str = getattr(_subv_c, 'id', None) or getattr(_subv_c, 'attr', '') + _elem_slice_c: ast.AST | None = None + if _subname_c == 'list': + _elem_slice_c = _ann_sub_c.slice + elif _subname_c == 'rawptr': + _slice_c: ast.AST = _ann_sub_c.slice + if isinstance(_slice_c, ast.Subscript): + # rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]): + # 容器身份 = 泛型特化名。list 继续走 _elem_slice_c 统一登记; + # 其余泛型容器直接登记 element_class(阻塞 B 根因链一环: + # 原实现只认内层 list,rawptr[GSList[Param]] 在此空转并跳过 + # 裸结构分支 → element_class 缺失 → 字段/局部变量方法分派 + # 丢失容器身份,FakeDuck 错绑 LinkedNode.append 写穿)。 + _inner_sub_c: ast.Subscript = _slice_c + _iname_c: str = getattr(_inner_sub_c.value, 'id', None) or getattr(_inner_sub_c.value, 'attr', '') + if _iname_c == 'list': + _elem_slice_c = _inner_sub_c.slice + else: + _ec_gc: str | None = None + if _iname_c: + try: + _ec_gc = f'{_iname_c}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_c.slice)}]' + except Exception: + _ec_gc = None + if _ec_gc: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = _ec_gc + else: + # rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内 + # 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复 + # struct 身份走 char*→struct cast。HEAD 态 `Token | t.CPtr` 联合 + # 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。 + # 与 HandlesImports 的 rawptr 分支对称。 + _slc_name_c: str | None = None + if isinstance(_slice_c, ast.Name): + _slc_name_c = _slice_c.id + elif isinstance(_slice_c, ast.Attribute): + _slc_name_c = _slice_c.attr + _ec_target_c: str | None = None + if _slc_name_c: + if _slc_name_c in Gen.structs: + _ec_target_c = _slc_name_c + else: + _st_sha1_c: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_slc_name_c) + if _st_sha1_c and f'{_st_sha1_c}.{_slc_name_c}' in Gen.structs: + _ec_target_c = f'{_st_sha1_c}.{_slc_name_c}' + else: + # 短名模糊扫描(对齐 HandlesAnnAssign 变量侧 + # 补偿 L597-602):跨模块 `mod.Class` 形态的 + # rawptr[Class] 字段在 class_sha1_map 尚无该短名 + # 时上面两路全 miss → element_class 静默缺失 → + # 字段方法调用(factory.PermPool.alloc)链尾类名 + # 丢失 → TPC 调用分派返回 i32 0 哨兵 → 误报 + # "Unknown module: ...PermPool"。 + for _ec_k_c in Gen.structs: + _ec_short_c: str = _ec_k_c.split('.')[-1] if '.' in _ec_k_c else _ec_k_c + if _ec_short_c == _slc_name_c: + _ec_target_c = _ec_k_c + break + if _ec_target_c: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = _ec_target_c + if _elem_slice_c is not None: + _ec_c: str | None = None + try: + _ec_c = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_c)}]' + except Exception: + _ec_c = None + if _ec_c: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = _ec_c + if hasattr(TypeInfo.BaseType, 'IsSigned'): + Gen.class_member_signeds[ClassName][VarName] = TypeInfo.BaseType.IsSigned + else: + Gen.class_member_signeds[ClassName][VarName] = None + if TypeInfo.IsBitField: + Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth + else: + Gen.class_member_bitfields[ClassName][VarName] = 0 + if TypeInfo.ByteOrder: + Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder + else: + Gen.class_member_byteorders[ClassName][VarName] = "" + if item.value: + const: ir.Constant | None = self._BuildScalarConstant(item.value, MemberType, Gen) + if const: + Gen.class_member_defaults[ClassName][VarName] = const + else: + Gen.class_member_signeds[ClassName][VarName] = None + Gen.class_member_bitfields[ClassName][VarName] = 0 + # ============================================================ + # 【字段名遮蔽 fail-fast】类体声明的字段名与**任一祖先声明的字段名**重名 ⇒ 终止编译 + # + # 这不是风格问题:本仓两条既有机制决定了"同名"必然产出**写错槽**,且是静默的。 + # ① 子类同名声明会成为**自有字段**,与**同名继承项**在 class_members 里并存; + # 跨模块侧的前置过滤器(HandlesImports._FlattenExt 前置段 :2516-2521)按 + # "名字已在表内"过滤——它本意只是防同一函数重入时重复前置,却会把**同名继承项 + # 整条丢掉** ⇒ 继承段与 struct body 的继承前缀失去一一对应:被丢项**之后**的 + # 继承字段表内下标整体前移 k(k = 被丢项数),而查询侧回退路径用的是 + # `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。 + # 现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 + # LastBuiltinsTypedefResult(下标 32)前移为 27 = CTypedefNameIdxBuilt(i32) + # ⇒ `self.Gen.TypeGenCType.LastBuiltinsTypedefResult = "t.CPtr"` 报 + # "指针隐式转整数已禁止:不能把指针类型 i8* 赋给整数类型 i32"。 + # ② 父类方法按**父类布局**访问该名字的槽、子类方法按**自有槽**访问 ⇒ 同名两槽 + # split-brain(一侧写、另一侧读),运行期静默错值。 + # 唯一合法写法:字段只在其**归属类**声明一次;子类要新字段就换名字。 + # 合法例外(不在本检查范围):两个**互不为祖先**的类各自声明同名字段 + # (如 CallGenBase / TypeGenBase 的 TempDir)——那不属于遮蔽。 + # 判据来源:自有 = 本类类体追加段(:903 快照起);祖先 = 沿 class_parents 传递收集 + # 各祖先 class_members 的声明名(含仅从 .vpi 加载的祖先;祖先未就绪则该项不成判据, + # 不产生误报)。 + # ============================================================ + _AncestorFieldNames: dict[str, str] = {} + _AncSeen: set[str] = set() + # class_parents 可能尚未建立(HandlesImports 同族代码也用 hasattr 兜底), + # 缺失时视为"无祖先"——只可能少报、不可能误报。 + _ParentsMap: dict = getattr(Gen, 'class_parents', None) or {} + _AncStack: list[str] = list(_ParentsMap.get(ClassName) or []) + while _AncStack: + _Anc: str = _AncStack.pop() + if not _Anc or _Anc in _AncSeen: + continue + _AncSeen.add(_Anc) + for _An, _ in Gen.class_members.get(_Anc) or []: + if _An != '__vtable__': + _AncestorFieldNames.setdefault(_An, _Anc) + _AncStack.extend(_ParentsMap.get(_Anc) or []) + _OwnFieldNames: list[str] = [n for n, _ in Gen.class_members[ClassName][_OwnFieldStart:]] + _Shadowed: list[str] = sorted({_n for _n in _OwnFieldNames if _n in _AncestorFieldNames}) + if _Shadowed: + _Decl: ast.AST | None = None + for _it in Node.body: + if (isinstance(_it, ast.AnnAssign) and isinstance(_it.target, ast.Name) + and _it.target.id in _Shadowed): + _Decl = _it + break + _src: str = getattr(Gen, '_current_source_file', '') or '' + _ln: int = (getattr(_Decl, 'lineno', 0) or getattr(Node, 'lineno', 0) or 0) + _col: int = getattr(_Decl, 'col_offset', -1) if _Decl is not None else -1 + # 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行(同 + # HandlesBase._FailFastFixedArrayKeyword / _ErrorStack 各站点)。 + _off: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _off > 0 and _ln > _off: + _ln -= _off + _code: str = '' + try: + _code = (Gen._get_source_line(_ln) or '').strip() + except Exception: + _code = '' + _Pairs: str = '、'.join(f"'{_n}'(祖先 '{_AncestorFieldNames[_n]}' 已声明)" + for _n in _Shadowed) + _msg: str = ( + f"类 '{ClassName}' 的类体字段与祖先类声明的字段**重名**:{_Pairs}。" + f"字段只能在其归属类声明一次(子类要新字段请改名;若确实要复用父类字段," + f"删掉子类的这条声明即可)——重名会让该名字在 class_members 里变成" + f"「自有字段 + 同名继承项」两份,跨模块展平的按名过滤会丢掉继承项," + f"使其后所有继承字段的表内下标整体前移、写入落到类型不同的槽" + f"(典型:指针值写进 i32 槽 → 「指针隐式转整数已禁止」);" + f"父类方法与子类方法还会各自访问不同槽(同一变量 split-brain)。" + f"位置 {_src}:{_ln}:{_col}") + _vlog().compile_error(_msg, _src, _ln, _code) + sys.exit(1) + # 字段收集完成,清除标志,允许后续 _generate_structs 正常设置 body + Gen._collecting_members_set.discard(ClassName) + existing_members: set[str] = {m[0] for m in Gen.class_members[ClassName]} + for item in Node.body: + if isinstance(item, ast.FunctionDef): + self._current_method_args = {} + for arg in item.args.args: + if arg.arg != 'self' and arg.annotation: + self._current_method_args[arg.arg] = arg.annotation + self._scan_method_body_for_self_members(item.body, ClassName, existing_members, Gen) + self._current_method_args = {} + for item in Node.body: + if isinstance(item, ast.FunctionDef): + MethodName: str = item.name + FullMethodName: str = f"{ClassName}.{MethodName}" + # property setter/deleter 使用不同的函数名后缀 + item_meta: FuncMeta = self.Trans.FunctionHandler._ExtractFuncMeta(item.decorator_list) + if FuncMeta.PROPERTY_SETTER in item_meta: + FullMethodName = FullMethodName + '$set' + elif FuncMeta.PROPERTY_DELETER in item_meta: + FullMethodName = FullMethodName + '$del' + # 构造函数(__new__/__init__)不放入 vtable,避免子类与基类 + # vtable 大小不一致导致虚分派索引偏移。__before_init__ 由 TPC + # 自动生成,不会出现在 FunctionDef 中,此处一并防御。 + # + # 方法级修饰符(`novirtual def m(...)` ⇒ @t.NoVTable / + # `virtual def m(...)` ⇒ @t.CVTable):是否占槽按 + # SymbolUtils.IsVtableSlotMethod 判定——与 VPC + # ClassGenBase._should_method_be_virtual 同判据(**唯一规则源**), + # 故 TPC/VPC 的虚表槽位顺序与数量逐槽一致(含"类级 NoVTable + + # 方法级 CVTable ⇒ 仍生成虚表且只有该方法进槽"这一组合形态)。 + # 仅在本类确有 vtable(IsCVTable)时筛选:无 vtable 的类不进 + # Vtables,class_methods 仍保留全部方法供静态解析/继承包装使用。 + if MethodName not in ('__new__', '__init__', '__before_init__'): + _IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(item, VtableByClassLevel) + if _IsSlot and FullMethodName not in Gen.class_methods[ClassName]: + Gen.class_methods[ClassName].append(FullMethodName) + Gen.register_method(ClassName, FullMethodName, _IsSlot) + else: + Gen.register_method(ClassName, FullMethodName) + if IsCVTable: + Gen.class_vtable.add(ClassName) + Gen._generate_structs() + Gen._create_Vtable_globals() + if _local_flat_complete: + # 登记"该表已按父类展平":供 _TryLoadClassMembersFromPyi 的 guard 与 + # FuncGen._member_base 的查询前补齐使用(与跨模块路径同一口径)。 + Gen._members_flattened.add(ClassName) + # _create_Vtable_globals 会跳过正在发射中的类(方法列表可能不完整), + # 当前类的方法已全部注册(L559-571),此处显式创建其 vtable + if IsCVTable: + Gen._create_vtable_for_class(ClassName) + if ClassName in Gen.structs: + self._EmitNewFunctionLlvm(ClassName, Gen, IsCVTable=IsCVTable, IsCpythonObject=IsCpythonObject) + for item in Node.body: + if isinstance(item, ast.FunctionDef): + self.Trans.FunctionHandler._EmitFunctionForwardDeclLlvm(item, Gen, ClassName=ClassName) + if not declare_only: + for item in Node.body: + if isinstance(item, ast.FunctionDef): + self.Trans.FunctionHandler._EmitFunctionLlvm(item, Gen, ClassName=ClassName) + if IsCVTable and ClassName in Gen.Vtables: + self._fill_vtable_with_methods(ClassName, Gen) + # 多继承支持:不再生成子类包装器(_generate_inherited_method_wrappers)。 + # 包装器只对 self 做 bitcast 基址转换,不调整字段偏移——对多继承中 + # 第二个及之后的父类(字段区偏移 > 0)会错位。继承方法统一走 + # MRO 祖先链查找 + this 调整(见 HandlesExprCall._adjust_this_for_inherited_method), + # 与 VPC 一致。 + Gen._classes_in_progress.discard(ClassName) + + def _generate_inherited_method_wrappers(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: + """为子类继承但未覆写的方法生成包装函数,使跨模块调用能正确链接。 + + 例如 Label 继承 Widget.place,生成 Label.place 函数, + 内部将 self 从 Label* bitcast 为 Widget*,然后调用 Widget.place。 + """ + # 收集本类方法 + 沿 MRO 继承链收集父类方法名 + # 原代码只遍历 class_methods[ClassName],遗漏父类独有方法(如 __enter__), + # 导致子类调用继承的方法时链接失败(undefined reference) + methods: list = list(Gen.class_methods.get(ClassName, [])) + seen_shorts: set[str] = {m.split('.')[-1] if '.' in m else m for m in methods} + mro_list: list[str] = Gen._get_mro_chain(ClassName) + for anc in mro_list[1:]: # 跳过自身,先声明父类优先(MRO 已去重) + parent_methods: list = Gen.class_methods.get(anc, []) + for pm in parent_methods: + pm_short: str = pm.split('.')[-1] if '.' in pm else pm + # 避免重复:如果子类已覆写同名方法,不添加 + if pm_short not in seen_shorts: + seen_shorts.add(pm_short) + methods.append(pm) + if not methods: + return + if ClassName not in Gen.structs: + return + + for method_name in methods: + method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name + child_full: str = f"{ClassName}.{method_short}" + + # 如果子类已经有该方法的实现,跳过 + if Gen._find_function(child_full): + continue + + # 沿 MRO 继承链查找父类实现(先声明父类优先) + parent_func: ir.Function | None = None + parent_class: str | None = None + for anc in Gen._get_mro_chain(ClassName)[1:]: + parent_full: str = f"{anc}.{method_short}" + parent_func = Gen._find_function(parent_full) + if parent_func: + parent_class = anc + break + + if not parent_func or not parent_class: + continue + + # 父类和子类都需要有 struct 定义 + if parent_class not in Gen.structs: + continue + + # 构造子类包装函数签名:与父类函数相同,但 self 参数类型为子类指针 + parent_ftype: ir.FunctionType = parent_func.function_type + parent_ret_type: ir.Type = parent_ftype.return_type + parent_param_types: list[ir.Type] = list(parent_ftype.args) + is_vararg: bool = parent_ftype.var_arg + + # 判断父类方法是否是静态方法: + # 如果第一个参数类型是父类指针,说明是实例方法;否则是静态方法 + parent_struct_ptr_type: ir.PointerType | None = ir.PointerType(Gen.structs[parent_class]) if parent_class in Gen.structs else None + is_static: bool = True + if parent_param_types and parent_struct_ptr_type: + first_param: ir.Type = parent_param_types[0] + # 检查第一个参数是否是父类指针(或可以 bitcast 为父类指针的指针类型) + if isinstance(first_param, ir.PointerType): + if isinstance(first_param.pointee, ir.IdentifiedStructType): + if first_param.pointee.name == Gen.structs[parent_class].name: + is_static = False + elif first_param == ir.IntType(8): + # i8* 可能是前向声明时的 self 类型,视为实例方法 + is_static = False + + if is_static: + # 静态方法:签名与父类完全一致,不需要 self 参数 + child_param_types: list[ir.Type] = list(parent_param_types) + child_struct_ptr: ir.PointerType | None = None + else: + # 实例方法:替换第一个参数(self)的类型为子类指针 + child_struct_ptr = ir.PointerType(Gen.structs[ClassName]) + if parent_param_types: + child_param_types = [child_struct_ptr] + parent_param_types[1:] + else: + child_param_types = [child_struct_ptr] + + child_func_type: ir.FunctionType = ir.FunctionType(parent_ret_type, child_param_types, var_arg=is_vararg) + child_mangled: str = Gen._mangle_func_name(child_full) + child_func: ir.Function = Gen._get_or_declare_function(child_mangled, child_func_type) + Gen.functions[child_full] = child_func + + # 生成函数体 + entry_block: ir.Block = child_func.append_basic_block(name="entry") + saved_builder: ir.IRBuilder = Gen.builder + saved_func: ir.Function = Gen.func + Gen.builder = Gen._NewIrBuilder(entry_block) + Gen.func = child_func + + # 准备参数 + call_args: list[ir.Value] = [] + if is_static: + # 静态方法:参数直接传递,不做 bitcast + for arg in child_func.args: + call_args.append(arg) + else: + # 实例方法:bitcast self,其余参数直接传递 + for i, arg in enumerate(child_func.args): + if i == 0 and parent_param_types: + actual_self_type: ir.Type = parent_param_types[0] + casted_self: ir.Value = Gen.builder.bitcast(arg, actual_self_type, name="self_cast") + call_args.append(casted_self) + else: + call_args.append(arg) + + result: ir.Value = Gen.builder.call(parent_func, call_args, name="inherited_call") + + if isinstance(parent_ret_type, ir.VoidType): + Gen.builder.ret_void() + else: + Gen.builder.ret(result) + + Gen.builder = saved_builder + Gen.func = saved_func + + def _fill_vtable_with_methods(self, ClassName: str, Gen: LlvmGeneratorMixin) -> None: + methods: list = Gen.class_methods.get(ClassName, []) + if not methods or ClassName not in Gen.Vtables: + return + Vtable: ir.GlobalVariable = Gen.Vtables[ClassName] + VtableType: ir.Type = Vtable.type.pointee + null_i8ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + initializers: list[ir.Constant] = [] + for mi, method_name in enumerate(methods): + # 多继承:vtable 槽直接用完整方法名(实际定义类.方法,如 B.get_b), + # 与 VPC 一致,避免"子类名.方法"包装器导致链接 undefined reference。 + func: ir.Function | None = Gen._find_function(method_name) + if not func: + func = Gen._find_function(f"{method_name}__") + if not func: + method_short: str = method_name.split('.')[-1] if '.' in method_name else method_name + func = Gen._find_function(f"{ClassName}.{method_short}") + if not func: + func = Gen._find_function(f"{ClassName}.{method_short}__") + if not func: + # 沿 MRO 祖先链查找定义类(先声明父类优先) + for anc in Gen._get_mro_chain(ClassName)[1:]: + func = Gen._find_function(f"{anc}.{method_short}") + if not func: + func = Gen._find_function(f"{anc}.{method_short}__") + if func: + break + if func: + initializers.append(func.bitcast(ir.PointerType(ir.IntType(8)))) + else: + initializers.append(null_i8ptr) + if initializers: + Vtable.initializer = ir.Constant(VtableType, initializers) + + def _scan_method_body_for_self_members(self, body: list[ast.AST], ClassName: str, existing_members: set[str], Gen: LlvmGeneratorMixin) -> None: + for stmt in body: + self._scan_node_for_self_assign(stmt, ClassName, existing_members, Gen) + + def _scan_node_for_self_assign(self, node: ast.AST, ClassName: str, existing_members: set[str], Gen: LlvmGeneratorMixin) -> None: + if isinstance(node, ast.AnnAssign): + if (isinstance(node.target, ast.Attribute) + and isinstance(node.target.value, ast.Name) + and node.target.value.id == 'self'): + VarName: str = node.target.attr + if VarName not in existing_members: + # fail-fast(项目约定:类型信息缺失立即终止,不兜底)。 + # 此前三处静默 i32 兜底(FromNode 返回 None / _ctype_to_llvm 得 void / + # 解析抛异常)会把"声明没解析出来"伪装成"该成员是 i32",下游赋值/取址 + # 检查于是报出与真因无关的错——实测:`free_lists: uint64*` 被当成 i32, + # `self.free_lists = base`(i8*) 因此误报"指针隐式转整数已禁止"。 + AnnotationText: str = ast.unparse(node.annotation) + try: + TypeInfo: CTypeInfo | None = CTypeInfo.FromNode(node.annotation, self.Trans.SymbolTable) + if TypeInfo is None: + raise ValueError( + f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 " + f"'{AnnotationText}' 解析不出类型(CTypeInfo.FromNode 返回 None)" + f"—— 拒绝静默当作 i32" + ) + MemberType: ir.Type = Gen._ctype_to_llvm(TypeInfo) + if isinstance(MemberType, ir.VoidType): + raise ValueError( + f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 " + f"'{AnnotationText}' 解析为 void(BaseType=" + f"{getattr(TypeInfo, 'BaseType', None)} PtrCount=" + f"{getattr(TypeInfo, 'PtrCount', None)} IsTypedef=" + f"{getattr(TypeInfo, 'IsTypedef', None)} Name=" + f"{getattr(TypeInfo, 'Name', None)})—— 拒绝静默当作 i32" + ) + except Exception as e: # 成员类型解析失败:带注解原文与类/成员名终止 + if isinstance(e, ValueError) and str(e).startswith('[FATAL]'): + raise + raise ValueError( + f"[FATAL] 类 '{ClassName}' 成员 '{VarName}' 的注解 " + f"'{AnnotationText}' 解析失败:{type(e).__name__}: {e}" + ) from e + Gen.class_members[ClassName].append((VarName, MemberType)) + Gen.class_member_signeds[ClassName][VarName] = None + Gen.class_member_bitfields[ClassName][VarName] = 0 + Gen.class_member_byteorders[ClassName][VarName] = "" + Gen.class_member_bitoffsets[ClassName][VarName] = 0 + existing_members.add(VarName) + len_name = f'{VarName}__len' + if isinstance(MemberType, ir.PointerType) and node.value and isinstance(node.value, ast.List): + if len_name not in existing_members: + Gen.class_members[ClassName].append((len_name, ir.IntType(64))) + Gen.class_member_signeds[ClassName][len_name] = None + Gen.class_member_bitfields[ClassName][len_name] = 0 + Gen.class_member_byteorders[ClassName][len_name] = "" + Gen.class_member_bitoffsets[ClassName][len_name] = 0 + Gen.class_member_defaults[ClassName][len_name] = ir.Constant(ir.IntType(64), len(node.value.elts)) + existing_members.add(len_name) + elif isinstance(node, ast.Assign): + for target in node.targets: + if (isinstance(target, ast.Attribute) + and isinstance(target.value, ast.Name) + and target.value.id == 'self'): + VarName = target.attr + if VarName not in existing_members: + # 类型判定:只接受**能唯一确定**的取值形态(下表);推不出即 + # fail-fast,绝不静默产出 i32 槽(项目约定:类型信息缺失立即 + # 终止;与同函数 `self.X: T` 分支 :1356-1386 同口径)。 + # 同族现场:`free_lists: uint64*` 被当成 i32 后, + # `self.free_lists = base`(i8*) 误报"指针隐式转整数已禁止"。 + # 【必须与 DeclarationGenerator._CollectOwnMemberTypes 的 + # 同名规则逐条一致】否则 stub 与定义端 struct 布局分叉 + # (stub 少/多一个槽 ⇒ 跨模块字段偏移整体错位): + # 整型字面量 → i32;布尔字面量 → i32(Python 中 bool 是 int + # 子类,维持既有 int 分支语义);浮点字面量 → double; + # 字符串字面量 → i8*;值为**本方法带注解的形参** → 该形参类型; + # 白名单调用 float()/double() → double、char() → i8、 + # int()/long()/short() → i32、`x.len()` → i32。 + # 其余(None 字面量、BinOp、任意调用/属性链…)→ fail-fast: + # 这些形态推不出成员类型,静默 i32 只会把错误推到下游 + # (写指针值 → "指针隐式转整数已禁止")。 + MemberType: ir.Type | None = None + if node.value is not None: + try: + if isinstance(node.value, ast.Constant): + if isinstance(node.value.value, bool): + MemberType = ir.IntType(32) + elif isinstance(node.value.value, int): + MemberType = ir.IntType(32) + elif isinstance(node.value.value, float): + MemberType = ir.DoubleType() + elif isinstance(node.value.value, str): + MemberType = ir.PointerType(ir.IntType(8)) + elif isinstance(node.value, ast.Name): + if hasattr(self, '_current_method_args'): + arg_info = self._current_method_args.get(node.value.id) + if arg_info: + TypeInfo = CTypeInfo.FromNode(arg_info, self.Trans.SymbolTable) + if TypeInfo: + InferredType = Gen._ctype_to_llvm(TypeInfo) + if not isinstance(InferredType, ir.VoidType): + MemberType = InferredType + elif isinstance(node.value, ast.Call): + if isinstance(node.value.func, ast.Name): + if node.value.func.id in ('float', 'double'): + MemberType = ir.DoubleType() + elif node.value.func.id == 'char': + # char() 是内建 i8 强转(与 float()/double() 并列) + MemberType = ir.IntType(8) + elif node.value.func.id in ('int', 'long', 'short'): + MemberType = ir.IntType(32) + elif isinstance(node.value.func, ast.Attribute): + if node.value.func.attr == 'len': + MemberType = ir.IntType(32) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if MemberType is None: + _src_f: str = getattr(Gen, '_current_source_file', '') or '' + _ln_f: int = getattr(node, 'lineno', 0) or 0 + _col_f: int = getattr(node, 'col_offset', -1) + # 行号还原:AST 行号含 _inject_auto_imports 注入的 N 行 + _off_f: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _off_f > 0 and _ln_f > _off_f: + _ln_f -= _off_f + _code_f: str = '' + try: + _code_f = (Gen._get_source_line(_ln_f) or '').strip() + except Exception: + _code_f = '' + _val_txt: str = '' + try: + _val_txt = ast.unparse(node.value) if node.value is not None else '<无值>' + except Exception: + _val_txt = '<无法反解析>' + _msg_f: str = ( + f"类 '{ClassName}' 的字段 '{VarName}' 没有声明类型" + f"(既不在类体注解、也不在祖先中),且首次赋值 " + f"`self.{VarName} = {_val_txt}` 的取值形态推不出成员类型" + f"——拒绝静默当成 i32(那会把错误推到下游:写指针值时报" + f"「指针隐式转整数已禁止」)。请在类体声明该字段并给注解" + f"(如 `{VarName}: t.CInt32T` / `{VarName}: t.CPtr`)," + f"或在赋值处给出可定型的取值。" + f"位置 {_src_f}:{_ln_f}:{_col_f}") + _vlog().compile_error(_msg_f, _src_f, _ln_f, _code_f) + sys.exit(1) + Gen.class_members[ClassName].append((VarName, MemberType)) + Gen.class_member_signeds[ClassName][VarName] = None + Gen.class_member_bitfields[ClassName][VarName] = 0 + Gen.class_member_byteorders[ClassName][VarName] = "" + Gen.class_member_bitoffsets[ClassName][VarName] = 0 + existing_members.add(VarName) + for child in ast.iter_child_nodes(node): + if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef, ast.ClassDef)): + continue + self._scan_node_for_self_assign(child, ClassName, existing_members, Gen) + + def _EmitUnionLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: + ClassName: str = Node.name + union_member_types: list[ir.Type] = [] + for item in Node.body: + if isinstance(item, ast.ClassDef): + nested_class_name: str = item.name + nested_member_types: list[ir.Type] = [] + for nested_item in item.body: + parsed = self.CollectAnnAssignMember(nested_item, Gen) + if parsed: + nested_member_types.append(parsed[1]) + if nested_member_types: + nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, f"{ClassName}_{nested_class_name}") + nested_struct_type.set_body(*nested_member_types) + Gen.structs[f"{ClassName}_{nested_class_name}"] = nested_struct_type + union_member_types.append(nested_struct_type) + nested_class_full_name: str = f"{ClassName}_{nested_class_name}" + if nested_class_full_name not in Gen.class_members: + Gen.class_members[nested_class_full_name] = [] + if nested_class_full_name not in Gen.class_member_signeds: + Gen.class_member_signeds[nested_class_full_name] = {} + for nested_item in item.body: + parsed = self.CollectAnnAssignMember(nested_item, Gen) + if parsed: + VarName, MemberType, TypeInfo = parsed + Gen.class_members[nested_class_full_name].append((VarName, MemberType)) + if TypeInfo and hasattr(TypeInfo.BaseType, 'IsSigned'): + Gen.class_member_signeds[nested_class_full_name][VarName] = TypeInfo.BaseType.IsSigned + else: + Gen.class_member_signeds[nested_class_full_name][VarName] = None + if union_member_types: + max_size: int = 0 + for member_type in union_member_types: + # 统一走 Gen._get_struct_size(与 __sizeof__ / memhub alloc 同源)。 + # llvmlite 并无 ir.DataLayout 类,原先那行必然抛 AttributeError, + # 再被 except 吞成 size=8 —— 静默少算,union 载荷区被开得过小。 + size: int = Gen._get_struct_size(member_type) + max_size = max(max_size, size) + union_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName) + union_type.set_body(ir.ArrayType(ir.IntType(8), max_size)) + Gen.structs[ClassName] = union_type + UnionNode: CTypeInfo = CTypeInfo() + UnionNode.Name = ClassName + UnionNode.IsUnion = True + self.Trans.SymbolTable.insert(ClassName, UnionNode) + + def _EmitREnumLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: + ClassName: str = Node.name + variant_info: list[tuple] = [] + variant_names: list[str] = [] + + # 预注册 REnum opaque 结构体到 Gen.structs 和 SymbolTable, + # 使自引用成员(如 LLVMType | t.CPtr)在收集成员时能正确解析为 %LLVMType*。 + # 若不预注册,GetCTypeInfo 找不到 LLVMType → 回退到 IsEnum → i32 → | t.CPtr → i32*(错误)。 + PreRenumType: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, ClassName) + Gen.structs[ClassName] = PreRenumType + PreTypeInfo: CTypeInfo = CTypeInfo() + PreTypeInfo.Name = ClassName + PreTypeInfo.BaseType = t.REnum(ClassName) + PreTypeInfo.IsRenum = True + PreTypeInfo.IsEnum = True + # REnum 在内存布局上是 struct({ i32 __tag, }), + # 必须设置 IsStruct=True 才能让 HandlesTypeMerge 的 CPtr 吸收逻辑生效: + # `REnumType | t.CPtr` 应得到 REnumType*(PtrCount=1)而非 REnumType**(PtrCount=2)。 + # 否则 include-imported 函数返回类型会出现双指针,与声明单指针不匹配 → 运行时崩溃。 + PreTypeInfo.IsStruct = True + PreTypeInfo.RenumVariants = [] + self.Trans.SymbolTable.insert(ClassName, PreTypeInfo) + + # 第一遍:收集显式赋值的 tag 值,确定起始索引 + explicit_values: dict[str, int] = {} + for item in Node.body: + if isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + if item.value and isinstance(item.value, ast.Constant): + explicit_values[target.id] = item.value.value + # 类变体从 max(explicit_values, default=-1) + 1 开始 + next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0 + variant_index: int = 0 + + for item in Node.body: + if isinstance(item, ast.ClassDef): + VariantName: str = item.name + variant_names.append(VariantName) + member_types: list[ir.Type] = [] + member_names: list[str] = [] + member_annotations: list[ast.AST | None] = [] + for nested_item in item.body: + parsed = self.CollectAnnAssignMember(nested_item, Gen) + if parsed: + VarName, MemberType, TypeInfo = parsed + member_types.append(MemberType) + member_names.append(VarName) + member_annotations.append(nested_item.annotation if TypeInfo else None) + # 类变体使用专用计数器,避免与显式值冲突 + tag: int = next_class_tag + next_class_tag += 1 + variant_info.append((VariantName, member_types, member_names, member_annotations, item.lineno, tag)) + elif isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + VarName = target.id + variant_names.append(VarName) + value: int | None = None + if item.value and isinstance(item.value, ast.Constant): + value = item.value.value + else: + value = variant_index + variant_index += 1 + variant_info.append((VarName, [], [], [], item.lineno, value)) + # REnum 布局是所有变体的联合体。每个字段位置必须能容纳任何变体在该位置的字段。 + # Bug 修复:原来只选总尺寸最大的变体,但不同变体在同一位置可能有不同尺寸的字段。 + # 例如 MetaType: Concrete 位置3是 i32(4字节),但 Var 位置3是 MetaType*(8字节)。 + # 用 Concrete 的布局会导致 Var.Instance 指针被截断为4字节 → 运行时崩溃。 + # 修复:按每个位置的 max 字段尺寸构建布局。 + def _ir_type_size(t: ir.Type) -> int: + if isinstance(t, ir.IntType): + return max(4, t.width // 8) + elif isinstance(t, ir.PointerType): + return 8 + elif isinstance(t, ir.FloatType): + return 4 + elif isinstance(t, ir.DoubleType): + return 8 + elif isinstance(t, ir.ArrayType): + if isinstance(t.element, ir.IntType): + return t.element.width // 8 * t.count + return 8 * t.count + else: + return 8 + + all_variant_types: list[list[ir.Type]] = [] + max_field_count: int = 0 + for _, member_types, _, _, _, _ in variant_info: + if member_types: + full_types: list[ir.Type] = [ir.IntType(32)] + member_types + all_variant_types.append(full_types) + if len(full_types) > max_field_count: + max_field_count = len(full_types) + max_variant_struct: list[ir.Type] = [] + for pos in range(max_field_count): + pos_max_size: int = 0 + for vt in all_variant_types: + if pos < len(vt): + sz: int = _ir_type_size(vt[pos]) + if sz > pos_max_size: + pos_max_size = sz + # 按最大尺寸选择 LLVM 类型:<=4 用 i32,>4 用 i64(8字节,可容纳指针) + if pos_max_size <= 4: + max_variant_struct.append(ir.IntType(32)) + else: + max_variant_struct.append(ir.IntType(64)) + if not max_variant_struct: + max_variant_struct = [ir.IntType(32), ir.IntType(32)] + # 复用预注册的 opaque 结构体,设置 body 使其完整 + PreRenumType.set_body(*max_variant_struct) + for VariantName, member_types, member_names, member_annotations, lineno, tag_value in variant_info: + NestedStructName: str = f"{ClassName}_{VariantName}" + if member_types: + padded_member_types: list[ir.Type] = list(max_variant_struct) + nested_struct_type: ir.IdentifiedStructType = ir.IdentifiedStructType(Gen.module, NestedStructName) + nested_struct_type.set_body(*padded_member_types) + Gen.structs[NestedStructName] = nested_struct_type + if NestedStructName not in Gen.class_members: + Gen.class_members[NestedStructName] = [] + if NestedStructName not in Gen.class_member_signeds: + Gen.class_member_signeds[NestedStructName] = {} + Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32))) + Gen.class_member_signeds[NestedStructName]['__tag'] = None + for i, (mname, mtype) in enumerate(zip(member_names, member_types)): + Gen.class_members[NestedStructName].append((mname, mtype)) + try: + ti: CTypeInfo | None = CTypeInfo.FromNode(member_annotations[i], self.Trans.SymbolTable) + if ti and hasattr(ti.BaseType, 'IsSigned'): + Gen.class_member_signeds[NestedStructName][mname] = ti.BaseType.IsSigned + else: + Gen.class_member_signeds[NestedStructName][mname] = None + except Exception as e: # 回退:签名信息解析失败时设为 None + _vlog().warning(f"HandlesClassDef: 忽略异常 {e}", exc_info=e) + Gen.class_member_signeds[NestedStructName][mname] = None + MemberNode: CTypeInfo = CTypeInfo() + MemberNode.Name = VariantName + MemberNode.BaseType = t.CEnum(ClassName) + MemberNode.value = tag_value + MemberNode.EnumName = ClassName + MemberNode.Lineno = lineno + MemberNode.IsEnumMember = True + self.Trans.SymbolTable.insert(VariantName, MemberNode) + # 同模块 REnum 变体也注册限定名和下划线名, + # 对齐跨模块导入(HandlesImports.py)的注册逻辑。 + # 避免裸名被同名的工厂函数覆盖后,路径 2 的限定名查找失败。 + self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode) + # 更新预注册的 REnum 类型信息(填充变体列表) + PreTypeInfo.RenumVariants = variant_names + def _EnumMemberAnnotationIsPtr(self, annotation: ast.AST | None) -> bool: """枚举成员注解是否含指针形态(逐层下标 + 联合类型逐边),True=含指针 - 与 TPV 侧 ClassGenEnum._enum_member_annotation_is_ptr 同口径。枚举值是 + 与 VPC 侧 ClassGenEnum._enum_member_annotation_is_ptr 同口径。枚举值是 整数常量,指针成员语义不成立,必须明确报错(fail-fast)而非静默接受: 此前 TPC 完全忽略成员注解,`Name: t.CPtr` 会被当成普通 i32 成员放行。 指针标记既按名字匹配(含 str/bytes = i8*、rawptr = t.CPtr),也按 @@ -1888,277 +1888,277 @@ class ClassHandle(BaseHandle): EnumTypeInfo.BaseType = t.CEnum(ClassName) EnumTypeInfo.IsEnum = True EnumTypeInfo.set('enum_width', EnumWidth) - self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo) - enum_export = self.Trans.Exportable.add_enum( - name=ClassName, - lineno=Node.lineno, - is_public=True - ) + self.Trans.SymbolTable.insert(ClassName, EnumTypeInfo) + enum_export = self.Trans.Exportable.add_enum( + name=ClassName, + lineno=Node.lineno, + is_public=True + ) next_enum_value: int = 0 # 已登记成员名(用于"可见名数 vs 登记数"兜底判据,见 SymbolUtils.EnumBodyMemberNames) - RegisteredNames: list[str] = [] - for item in Node.body: - if isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name): - VarName: str = target.id - value: int | str | None = None - if item.value: - if isinstance(item.value, ast.Constant): - value = item.value.value - next_enum_value = value + 1 - elif isinstance(item.value, ast.UnaryOp) and isinstance(item.value.op, ast.USub): - if isinstance(item.value.operand, ast.Constant): - value = -item.value.operand.value - next_enum_value = value + 1 - elif isinstance(item.value, ast.Name): - value = item.value.id - ref_info: CTypeInfo | None = self.Trans.SymbolTable.lookup(value) - if ref_info: - if ref_info.value is not None and isinstance(ref_info.value, int): - next_enum_value = ref_info.value + 1 - else: - value = next_enum_value - next_enum_value += 1 - MemberNode: CTypeInfo = CTypeInfo() - MemberNode.Name = VarName - MemberNode.BaseType = t.CEnum(ClassName) - MemberNode.value = value - MemberNode.EnumName = ClassName - MemberNode.Lineno = item.lineno - MemberNode.IsEnumMember = True - MemberNode.set('enum_width', EnumWidth) - self.Trans.SymbolTable.insert(VarName, MemberNode) - RegisteredNames.append(VarName) - enum_export.members.append(ExportEnumMember( - name=VarName, - value=value, - lineno=item.lineno - )) - elif isinstance(item, ast.AnnAssign): - if isinstance(item.target, ast.Name): - VarName = item.target.id - # [枚举规格 fail-fast] 成员不允许指针类型(t.CPtr 家族 / - # rawptr[...] / str / bytes / 联合里夹带指针)——此前成员注解 - # 被整体忽略,指针成员被静默当 i32 放行。 - if self._EnumMemberAnnotationIsPtr(item.annotation): - # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 - _lineno: int = int(getattr(item, 'lineno', 0) or 0) - _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) - if _line_offset > 0 and _lineno > _line_offset: - _lineno -= _line_offset - raise TypeError( - f"[fast-fail] 枚举 '{ClassName}' 成员 '{VarName}' 不能是指针类型" - f"(枚举值只支持整数成员类型),line {_lineno}" - ) - value = None - if item.value: - if isinstance(item.value, ast.Constant): - value = item.value.value - next_enum_value = value + 1 - elif isinstance(item.value, ast.UnaryOp) and isinstance(item.value.op, ast.USub): - if isinstance(item.value.operand, ast.Constant): - value = -item.value.operand.value - next_enum_value = value + 1 - elif isinstance(item.value, ast.Name): - value = item.value.id - ref_info: CTypeInfo | None = self.Trans.SymbolTable.lookup(value) - if ref_info: - if ref_info.value is not None and isinstance(ref_info.value, int): - next_enum_value = ref_info.value + 1 - else: - value = next_enum_value - next_enum_value += 1 - MemberNode: CTypeInfo = CTypeInfo() - MemberNode.Name = VarName - MemberNode.BaseType = t.CEnum(ClassName) - MemberNode.value = value - MemberNode.EnumName = ClassName - MemberNode.Lineno = item.lineno - MemberNode.IsEnumMember = True - MemberNode.set('enum_width', EnumWidth) - self.Trans.SymbolTable.insert(VarName, MemberNode) - RegisteredNames.append(VarName) - enum_export.members.append(ExportEnumMember( - name=VarName, - value=value, - lineno=item.lineno - )) - # [兜底判据 fail-fast] "源码里可见的成员名字数" vs "实际登记成员数"。 - # 任何注册器不认的成员写法(逗号组带值 `A, B = 1`、逗号组带注解 - # `A, B: T`、单名尾随逗号 `A,`,以及未来出现的新形态)都会让可见名数 > - # 登记数——此前是**静默丢成员**(ASTKind 71→3、OpKind 32→2;与 - # Debugger/enum-semantics.md §15、rawptr-migration-and-fastfail.md §27 - # 同族,这是第三次复发),此处一律 fail-fast,不再静默跳过。 - # 判据口径与 TPV ClassGenEnum._translate_enum_def 完全一致(同一规则源)。 - Visible: list[tuple[str, object]] = EnumBodyMemberNames(Node.body) - if len(Visible) != len(RegisteredNames): - Rest: list[str] = list(RegisteredNames) - BadNode: object = Node - for Nm, Nd in Visible: - if Nm in Rest: - Rest.remove(Nm) - elif BadNode is Node: - BadNode = Nd - raise TypeError(EnumMemberCountMismatchError( - ClassName, Visible, RegisteredNames, self._EnumSiteLoc(BadNode), - int(getattr(self.Trans, '_line_offset', 0) or 0))) - - def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None: - NewFuncName: str = f'{ClassName}.__before_init__' - existing_func: ir.Function | None = Gen.functions.get(NewFuncName) - if existing_func and len(existing_func.blocks) > 0: - return - MangledName: str = Gen._mangle_name(NewFuncName) - StructType: ir.Type = Gen.structs[ClassName] - if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) - StructType = Gen.structs.get(ClassName, StructType) - StructPtrType: ir.PointerType = ir.PointerType(StructType) - FuncType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [StructPtrType]) - if existing_func: - func: ir.Function = existing_func - else: - func = Gen._get_or_declare_function(MangledName, FuncType) - # 泛型特化类(ClassName 含 '[',如 GSList[NameEntry])的 __before_init__ - # 在多模块各自特化生成同名定义(external linkage)→ ld.lld duplicate - # symbol warning。__before_init__ 由相同模板+类型参数合成,定义相同, - # 设 linkonce_odr 让链接器合并重复定义。 - if '[' in ClassName: - func.linkage = 'linkonce_odr' - Gen.functions[NewFuncName] = func - EntryBlock: ir.Block = func.append_basic_block(name="entry") - saved_builder: ir.IRBuilder = Gen.builder - saved_func: ir.Function = Gen.func - Gen.builder = Gen._NewIrBuilder(EntryBlock) - Gen.func = func - StructPtr: ir.Argument = func.args[0] - base: int = 1 if IsCVTable else 0 - if IsCVTable and ClassName in Gen.Vtables: - VtablePtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="vtable_slot") - VtableAddr: ir.Value = Gen.builder.bitcast(Gen.Vtables[ClassName], ir.PointerType(ir.IntType(8)), name="vtable_addr") - Gen.builder.store(VtableAddr, VtablePtr) - members: list[tuple] = Gen.class_members.get(ClassName, []) - defaults: dict = Gen.class_member_defaults.get(ClassName, {}) - struct_elems: tuple = StructType.elements if hasattr(StructType, 'elements') and StructType.elements is not None else () - for i, (member_name, member_type) in enumerate(members): - elem_idx: int = base + i - if len(struct_elems) > 0 and elem_idx >= len(struct_elems): - break - ElemPtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"{ClassName}_{member_name}") - if member_name in defaults: - try: - val: ir.Constant = defaults[member_name] - if not isinstance(val, ir.Constant): - val = ir.Constant(ElemPtr.type.pointee, val) - Gen.builder.store(val, ElemPtr) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - else: - try: - ZeroVal: ir.Constant = ir.Constant(ElemPtr.type.pointee, None) - Gen.builder.store(ZeroVal, ElemPtr) - except Exception: - pass - Gen.builder.ret_void() - Gen.builder = saved_builder - Gen.func = saved_func - - def _PreRegisterClassMembers(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: - """兜底:把一个类的**自有字段**从 ClassDef 体收集进 `Gen.class_members[类名]`。 - - 调用点:[FuncGen._get_member_offset] 的"struct body 已存在、而 - `class_members[类名]` 为空"分支——该类的成员表在本次翻译里从未被收集 - (时序/跨模块窗口),而按名定位字段槽位必须有表。 - 此前本方法**全仓无定义**(调用点却存在)⇒ 一旦进入即 - `AttributeError: 'ClassHandle' object has no attribute '_PreRegisterClassMembers'` - (现场:翻译 TransPyV/App/lib/core/Gen/Call/CallGenLeafKit.vp 终止编译)。 - - 判据与实现刻意与既有两处成员收集段同源: - - `_EmitClassLlvm` 的自有字段段(`CollectAnnAssignMember` → 追加 `(名, 类型)`); - - `HandlesImports._EmitExternalClassDeclLlvm` 的同名段。 - 但**只做收集**: - - 不展平继承前缀——调用方随后经 `_member_base` → - `EnsureClassMembersFlattened` 按父类补齐(表空且有父类时会被前置); - - 不调用 `_generate_structs()`:body 已存在,重入 `set_body` 会把残缺布局 - 永久固化(llvmlite 的 `set_body` 只能设一次,见 - HandlesImports._EmitExternalClassDeclLlvm 的 `_collecting_members_set` 说明); - - 只追加**尚无同名**的条目(幂等:重复命中不会把表撑长,也就不会让 - `base + 表内下标` 整体后移)。 - """ - if Node is None or Gen is None: - return - ClassName: str = getattr(Node, 'name', None) - if not ClassName: - return - if ClassName not in Gen.class_members: - Gen.class_members[ClassName] = [] - # 【防重 · 实时判据(2026-09-30)】原实现循环前快照一次名字集合,循环内只更新 - # 自己追加的项;而 CollectAnnAssignMember 每轮都可能重入并**清空重填**本类表 - # (泛型特化 → _generate_structs → _TryLoadClassMembersFromPyi(force=True) → - # HandlesImports.py:3415)⇒ 快照失效后会重复追加同一字段。改为每轮实时读表, - # 判据语义("名字已在表里就不追加")与改前逐字相同。 - Gen._collecting_members_set.add(ClassName) - try: - for item in Node.body: - # 编译期元数据字段(__provides__/__requires__/__require_must__)不是布局槽位 - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - if item.target.id in ('__provides__', '__requires__', '__require_must__'): - continue - parsed = self.CollectAnnAssignMember(item, Gen) - if not parsed: - continue - VarName, MemberType, TypeInfo = parsed - if VarName in {n for n, _ in Gen.class_members[ClassName]}: - continue - Gen.class_members[ClassName].append((VarName, MemberType)) - Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation) - _Signeds: dict = getattr(Gen, 'class_member_signeds', {}).get(ClassName) - if isinstance(_Signeds, dict): - _Signeds[VarName] = None - _BitFields: dict = getattr(Gen, 'class_member_bitfields', {}).get(ClassName) - if isinstance(_BitFields, dict): - _BitFields[VarName] = 0 - finally: - Gen._collecting_members_set.discard(ClassName) - - def _BuildScalarConstant(self, node: ast.AST, llvm_type: ir.Type, Gen: LlvmGeneratorMixin | None = None) -> ir.Constant | None: - """构建标量常量,委托给统一的 _BuildScalarConstantUnified 方法。""" - if Gen is None: - Gen = self.Trans.LlvmGen - return self.Trans._BuildScalarConstantUnified(node, llvm_type, Gen) - - def _BuildStructConstant(self, call_node: ast.Call, StructName: str, Gen: LlvmGeneratorMixin) -> ir.Constant | None: - struct_type: ir.Type = Gen.structs[StructName] - members: list[tuple] = Gen.class_members.get(StructName, []) - defaults: dict = Gen.class_member_defaults.get(StructName, {}) - member_values: list[ir.Constant] = [] - for member_name, member_type in members: - if member_name in defaults: - val: ir.Constant = defaults[member_name] - if not isinstance(val, ir.Constant): - try: - val = ir.Constant(member_type, val) - except Exception: # 回退:常量转换失败时使用零值 - val = ir.Constant(member_type, 0) - member_values.append(val) - else: - try: - if isinstance(member_type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.PointerType, ir.ArrayType)): - member_values.append(ir.Constant(member_type, None)) - else: - member_values.append(ir.Constant(member_type, ir.Undefined)) - except Exception: # 回退:常量创建失败时使用零值 - member_values.append(ir.Constant(member_type, 0)) - if call_node.args: - for i, arg in enumerate(call_node.args): - if i < len(members): - _, member_type = members[i] - const: ir.Constant | None = self._BuildScalarConstant(arg, member_type, Gen) - if const is not None: - member_values[i] = const - try: - return ir.Constant(struct_type, member_values) - except Exception: # 回退:结构体常量创建失败 - return None + RegisteredNames: list[str] = [] + for item in Node.body: + if isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + VarName: str = target.id + value: int | str | None = None + if item.value: + if isinstance(item.value, ast.Constant): + value = item.value.value + next_enum_value = value + 1 + elif isinstance(item.value, ast.UnaryOp) and isinstance(item.value.op, ast.USub): + if isinstance(item.value.operand, ast.Constant): + value = -item.value.operand.value + next_enum_value = value + 1 + elif isinstance(item.value, ast.Name): + value = item.value.id + ref_info: CTypeInfo | None = self.Trans.SymbolTable.lookup(value) + if ref_info: + if ref_info.value is not None and isinstance(ref_info.value, int): + next_enum_value = ref_info.value + 1 + else: + value = next_enum_value + next_enum_value += 1 + MemberNode: CTypeInfo = CTypeInfo() + MemberNode.Name = VarName + MemberNode.BaseType = t.CEnum(ClassName) + MemberNode.value = value + MemberNode.EnumName = ClassName + MemberNode.Lineno = item.lineno + MemberNode.IsEnumMember = True + MemberNode.set('enum_width', EnumWidth) + self.Trans.SymbolTable.insert(VarName, MemberNode) + RegisteredNames.append(VarName) + enum_export.members.append(ExportEnumMember( + name=VarName, + value=value, + lineno=item.lineno + )) + elif isinstance(item, ast.AnnAssign): + if isinstance(item.target, ast.Name): + VarName = item.target.id + # [枚举规格 fail-fast] 成员不允许指针类型(t.CPtr 家族 / + # rawptr[...] / str / bytes / 联合里夹带指针)——此前成员注解 + # 被整体忽略,指针成员被静默当 i32 放行。 + if self._EnumMemberAnnotationIsPtr(item.annotation): + # 注入行偏移还原:AST 行号含 _inject_auto_imports 注入的 N 行 + _lineno: int = int(getattr(item, 'lineno', 0) or 0) + _line_offset: int = int(getattr(self.Trans, '_line_offset', 0) or 0) + if _line_offset > 0 and _lineno > _line_offset: + _lineno -= _line_offset + raise TypeError( + f"[fast-fail] 枚举 '{ClassName}' 成员 '{VarName}' 不能是指针类型" + f"(枚举值只支持整数成员类型),line {_lineno}" + ) + value = None + if item.value: + if isinstance(item.value, ast.Constant): + value = item.value.value + next_enum_value = value + 1 + elif isinstance(item.value, ast.UnaryOp) and isinstance(item.value.op, ast.USub): + if isinstance(item.value.operand, ast.Constant): + value = -item.value.operand.value + next_enum_value = value + 1 + elif isinstance(item.value, ast.Name): + value = item.value.id + ref_info: CTypeInfo | None = self.Trans.SymbolTable.lookup(value) + if ref_info: + if ref_info.value is not None and isinstance(ref_info.value, int): + next_enum_value = ref_info.value + 1 + else: + value = next_enum_value + next_enum_value += 1 + MemberNode: CTypeInfo = CTypeInfo() + MemberNode.Name = VarName + MemberNode.BaseType = t.CEnum(ClassName) + MemberNode.value = value + MemberNode.EnumName = ClassName + MemberNode.Lineno = item.lineno + MemberNode.IsEnumMember = True + MemberNode.set('enum_width', EnumWidth) + self.Trans.SymbolTable.insert(VarName, MemberNode) + RegisteredNames.append(VarName) + enum_export.members.append(ExportEnumMember( + name=VarName, + value=value, + lineno=item.lineno + )) + # [兜底判据 fail-fast] "源码里可见的成员名字数" vs "实际登记成员数"。 + # 任何注册器不认的成员写法(逗号组带值 `A, B = 1`、逗号组带注解 + # `A, B: T`、单名尾随逗号 `A,`,以及未来出现的新形态)都会让可见名数 > + # 登记数——此前是**静默丢成员**(ASTKind 71→3、OpKind 32→2;与 + # Debugger/enum-semantics.md §15、rawptr-migration-and-fastfail.md §27 + # 同族,这是第三次复发),此处一律 fail-fast,不再静默跳过。 + # 判据口径与 VPC ClassGenEnum._translate_enum_def 完全一致(同一规则源)。 + Visible: list[tuple[str, object]] = EnumBodyMemberNames(Node.body) + if len(Visible) != len(RegisteredNames): + Rest: list[str] = list(RegisteredNames) + BadNode: object = Node + for Nm, Nd in Visible: + if Nm in Rest: + Rest.remove(Nm) + elif BadNode is Node: + BadNode = Nd + raise TypeError(EnumMemberCountMismatchError( + ClassName, Visible, RegisteredNames, self._EnumSiteLoc(BadNode), + int(getattr(self.Trans, '_line_offset', 0) or 0))) + + def _EmitNewFunctionLlvm(self, ClassName: str, Gen: LlvmGeneratorMixin, IsCVTable: bool = False, IsCpythonObject: bool = False) -> None: + NewFuncName: str = f'{ClassName}.__before_init__' + existing_func: ir.Function | None = Gen.functions.get(NewFuncName) + if existing_func and len(existing_func.blocks) > 0: + return + MangledName: str = Gen._mangle_name(NewFuncName) + StructType: ir.Type = Gen.structs[ClassName] + if isinstance(StructType, ir.IdentifiedStructType) and (StructType.elements is None or len(StructType.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + StructType = Gen.structs.get(ClassName, StructType) + StructPtrType: ir.PointerType = ir.PointerType(StructType) + FuncType: ir.FunctionType = ir.FunctionType(ir.VoidType(), [StructPtrType]) + if existing_func: + func: ir.Function = existing_func + else: + func = Gen._get_or_declare_function(MangledName, FuncType) + # 泛型特化类(ClassName 含 '[',如 GSList[NameEntry])的 __before_init__ + # 在多模块各自特化生成同名定义(external linkage)→ ld.lld duplicate + # symbol warning。__before_init__ 由相同模板+类型参数合成,定义相同, + # 设 linkonce_odr 让链接器合并重复定义。 + if '[' in ClassName: + func.linkage = 'linkonce_odr' + Gen.functions[NewFuncName] = func + EntryBlock: ir.Block = func.append_basic_block(name="entry") + saved_builder: ir.IRBuilder = Gen.builder + saved_func: ir.Function = Gen.func + Gen.builder = Gen._NewIrBuilder(EntryBlock) + Gen.func = func + StructPtr: ir.Argument = func.args[0] + base: int = 1 if IsCVTable else 0 + if IsCVTable and ClassName in Gen.Vtables: + VtablePtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="vtable_slot") + VtableAddr: ir.Value = Gen.builder.bitcast(Gen.Vtables[ClassName], ir.PointerType(ir.IntType(8)), name="vtable_addr") + Gen.builder.store(VtableAddr, VtablePtr) + members: list[tuple] = Gen.class_members.get(ClassName, []) + defaults: dict = Gen.class_member_defaults.get(ClassName, {}) + struct_elems: tuple = StructType.elements if hasattr(StructType, 'elements') and StructType.elements is not None else () + for i, (member_name, member_type) in enumerate(members): + elem_idx: int = base + i + if len(struct_elems) > 0 and elem_idx >= len(struct_elems): + break + ElemPtr: ir.Value = Gen.builder.gep(StructPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), elem_idx)], name=f"{ClassName}_{member_name}") + if member_name in defaults: + try: + val: ir.Constant = defaults[member_name] + if not isinstance(val, ir.Constant): + val = ir.Constant(ElemPtr.type.pointee, val) + Gen.builder.store(val, ElemPtr) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + else: + try: + ZeroVal: ir.Constant = ir.Constant(ElemPtr.type.pointee, None) + Gen.builder.store(ZeroVal, ElemPtr) + except Exception: + pass + Gen.builder.ret_void() + Gen.builder = saved_builder + Gen.func = saved_func + + def _PreRegisterClassMembers(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin) -> None: + """兜底:把一个类的**自有字段**从 ClassDef 体收集进 `Gen.class_members[类名]`。 + + 调用点:[FuncGen._get_member_offset] 的"struct body 已存在、而 + `class_members[类名]` 为空"分支——该类的成员表在本次翻译里从未被收集 + (时序/跨模块窗口),而按名定位字段槽位必须有表。 + 此前本方法**全仓无定义**(调用点却存在)⇒ 一旦进入即 + `AttributeError: 'ClassHandle' object has no attribute '_PreRegisterClassMembers'` + (现场:翻译 ViperC/App/lib/core/Gen/Call/CallGenLeafKit.vp 终止编译)。 + + 判据与实现刻意与既有两处成员收集段同源: + - `_EmitClassLlvm` 的自有字段段(`CollectAnnAssignMember` → 追加 `(名, 类型)`); + - `HandlesImports._EmitExternalClassDeclLlvm` 的同名段。 + 但**只做收集**: + - 不展平继承前缀——调用方随后经 `_member_base` → + `EnsureClassMembersFlattened` 按父类补齐(表空且有父类时会被前置); + - 不调用 `_generate_structs()`:body 已存在,重入 `set_body` 会把残缺布局 + 永久固化(llvmlite 的 `set_body` 只能设一次,见 + HandlesImports._EmitExternalClassDeclLlvm 的 `_collecting_members_set` 说明); + - 只追加**尚无同名**的条目(幂等:重复命中不会把表撑长,也就不会让 + `base + 表内下标` 整体后移)。 + """ + if Node is None or Gen is None: + return + ClassName: str = getattr(Node, 'name', None) + if not ClassName: + return + if ClassName not in Gen.class_members: + Gen.class_members[ClassName] = [] + # 【防重 · 实时判据(2026-09-30)】原实现循环前快照一次名字集合,循环内只更新 + # 自己追加的项;而 CollectAnnAssignMember 每轮都可能重入并**清空重填**本类表 + # (泛型特化 → _generate_structs → _TryLoadClassMembersFromPyi(force=True) → + # HandlesImports.py:3415)⇒ 快照失效后会重复追加同一字段。改为每轮实时读表, + # 判据语义("名字已在表里就不追加")与改前逐字相同。 + Gen._collecting_members_set.add(ClassName) + try: + for item in Node.body: + # 编译期元数据字段(__provides__/__requires__/__require_must__)不是布局槽位 + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + if item.target.id in ('__provides__', '__requires__', '__require_must__'): + continue + parsed = self.CollectAnnAssignMember(item, Gen) + if not parsed: + continue + VarName, MemberType, TypeInfo = parsed + if VarName in {n for n, _ in Gen.class_members[ClassName]}: + continue + Gen.class_members[ClassName].append((VarName, MemberType)) + Gen.RegisterFieldQualifiers(ClassName, VarName, TypeInfo, item.annotation) + _Signeds: dict = getattr(Gen, 'class_member_signeds', {}).get(ClassName) + if isinstance(_Signeds, dict): + _Signeds[VarName] = None + _BitFields: dict = getattr(Gen, 'class_member_bitfields', {}).get(ClassName) + if isinstance(_BitFields, dict): + _BitFields[VarName] = 0 + finally: + Gen._collecting_members_set.discard(ClassName) + + def _BuildScalarConstant(self, node: ast.AST, llvm_type: ir.Type, Gen: LlvmGeneratorMixin | None = None) -> ir.Constant | None: + """构建标量常量,委托给统一的 _BuildScalarConstantUnified 方法。""" + if Gen is None: + Gen = self.Trans.LlvmGen + return self.Trans._BuildScalarConstantUnified(node, llvm_type, Gen) + + def _BuildStructConstant(self, call_node: ast.Call, StructName: str, Gen: LlvmGeneratorMixin) -> ir.Constant | None: + struct_type: ir.Type = Gen.structs[StructName] + members: list[tuple] = Gen.class_members.get(StructName, []) + defaults: dict = Gen.class_member_defaults.get(StructName, {}) + member_values: list[ir.Constant] = [] + for member_name, member_type in members: + if member_name in defaults: + val: ir.Constant = defaults[member_name] + if not isinstance(val, ir.Constant): + try: + val = ir.Constant(member_type, val) + except Exception: # 回退:常量转换失败时使用零值 + val = ir.Constant(member_type, 0) + member_values.append(val) + else: + try: + if isinstance(member_type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.PointerType, ir.ArrayType)): + member_values.append(ir.Constant(member_type, None)) + else: + member_values.append(ir.Constant(member_type, ir.Undefined)) + except Exception: # 回退:常量创建失败时使用零值 + member_values.append(ir.Constant(member_type, 0)) + if call_node.args: + for i, arg in enumerate(call_node.args): + if i < len(members): + _, member_type = members[i] + const: ir.Constant | None = self._BuildScalarConstant(arg, member_type, Gen) + if const is not None: + member_values[i] = const + try: + return ir.Constant(struct_type, member_values) + except Exception: # 回退:结构体常量创建失败 + return None diff --git a/lib/core/Handles/HandlesExprAttr.py b/lib/core/Handles/HandlesExprAttr.py index 615f926..6ebf997 100644 --- a/lib/core/Handles/HandlesExprAttr.py +++ b/lib/core/Handles/HandlesExprAttr.py @@ -1,28 +1,28 @@ -from __future__ import annotations -from typing import Any, TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta -from lib.core.VLogger import get_logger as _vlog -from lib.core.SymbolUtils import DefineDeclTypeValue, GlobalDeclShadowsDefine -import vast as ast -import llvmlite.ir as ir - - -class ExprAttrHandle(BaseHandle): - # ========== 辅助方法 ========== - - def _apply_bswap_on_Load(self, val: ir.Value, ClassName: str, AttrName: str) -> ir.Value: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - byte_order: str = getattr(Gen, 'class_member_byteorders', {}).get(ClassName, {}).get(AttrName, "") - return Gen._apply_bswap_if_big(val, byte_order, f"bswap_load_{AttrName}") - - def _load_arr_elem_with_bswap(self, Gen: LlvmCodeGenerator, ElemPtr: ir.Value, arr_byte_order: str) -> ir.Value: - """加载数组元素并按字节序应用 bswap""" - val: ir.Value = Gen._load(ElemPtr, name="arr_elem_load") - return Gen._apply_bswap_if_big(val, arr_byte_order, "bswap_arr_load") - +from __future__ import annotations +from typing import Any, TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle, FuncMeta +from lib.core.VLogger import get_logger as _vlog +from lib.core.SymbolUtils import DefineDeclTypeValue, GlobalDeclShadowsDefine +import vast as ast +import llvmlite.ir as ir + + +class ExprAttrHandle(BaseHandle): + # ========== 辅助方法 ========== + + def _apply_bswap_on_Load(self, val: ir.Value, ClassName: str, AttrName: str) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + byte_order: str = getattr(Gen, 'class_member_byteorders', {}).get(ClassName, {}).get(AttrName, "") + return Gen._apply_bswap_if_big(val, byte_order, f"bswap_load_{AttrName}") + + def _load_arr_elem_with_bswap(self, Gen: LlvmCodeGenerator, ElemPtr: ir.Value, arr_byte_order: str) -> ir.Value: + """加载数组元素并按字节序应用 bswap""" + val: ir.Value = Gen._load(ElemPtr, name="arr_elem_load") + return Gen._apply_bswap_if_big(val, arr_byte_order, "bswap_arr_load") + def _RenumVariantPayloadType(self, Gen: LlvmCodeGenerator, ClassName: str | None, AttrName: str) -> Any: """REnum **变体载荷**字段的声明类型(供 `_gep_and_Load_member` 载入后收口)。 @@ -89,7 +89,7 @@ class ExprAttrHandle(BaseHandle): ResolvedKey: str | None) -> str | None: """左值注解**靶型提示**:解析出的类**不声明**该字段时,用赋值左侧注解指向的类兜一次。 - 与 TPV `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:赋值左侧的 + 与 VPC `translate_value` 的 `target_ty`(使用处真实站点种类)同思路:赋值左侧的 注解是**源码级权威意图**(RHS 必须能赋给它)。仅在**两个条件同时成立**时改判: ① 当前解析出的类(或解析不出)**不声明**该字段 —— 此刻原本会 fail-fast; ② 靶型类**确实声明**了该字段。 @@ -215,746 +215,746 @@ class ExprAttrHandle(BaseHandle): _LShift, ir.Constant(StorageType, _Rest), name=f"{AttrName}_bf_sign_ashr") return Masked - def _gep_and_Load_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, offset: int | None, AttrName: str, ClassName: str, ST: ir.Type | None = None) -> ir.Value | None: - """从结构体指针通过 GEP 取成员,处理 array/struct pointer/class_members 特殊情况""" - if offset is None: - self._NoteAttrNone(Gen, ClassName, AttrName, - f"偏移为 None(字段 '{AttrName}' 未进成员表)") - return None - if isinstance(ObjVal.type, ir.PointerType): - pointee: Any = ObjVal.type.pointee - # opaque pointee:GEP 无法在编译期算元素类型(llvmlite 抛裸 TypeError, - # _GuardIrBuilder 会先 fail-fast)。先按**同短名**找一份已具化实例并 bitcast - # (与写入路径 HandlesAssign 的孪生体搜索共用 _FindConcreteTwin); - # 找不到则保持原样、由守卫报出同短名实例清单(暴露"body 从未 set_body"), - # **不静默兜底**。 - if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: - _TwinR: Any = None - try: - _TwinR = Gen._FindConcreteTwin(pointee) - if _TwinR is None: - _TwinR = Gen._FindConcreteTwin(Gen.structs.get(ClassName)) - except Exception: - _TwinR = None - if _TwinR is not None: - ObjVal = Gen.builder.bitcast( - ObjVal, ir.PointerType(_TwinR), - name=f"concrete_twin_read_{AttrName}") - pointee = _TwinR - if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset >= len(pointee.elements): - # sha1 冲突修复:pointee 字段不足时,尝试 bitcast 到 ST(更完整的版本) - if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_gep_{ClassName}") - else: - _st_len: Any = (len(ST.elements) - if isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None - else 'None/opaque') - self._NoteAttrNone( - Gen, ClassName, AttrName, - f"偏移越界:字段 '{AttrName}' 需槽位 {offset}," - f"但 body 只有 {len(pointee.elements)} 个元素," - f"stub 版结构体(st)也仅 {_st_len} 个 ⇒ 布局与成员表不同源" - f"(成员表 {len(Gen.class_members.get(ClassName, []))} 项)") - return None - MemberPtr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) - if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.ArrayType): - return MemberPtr - if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.IdentifiedStructType): - return MemberPtr - # 检查 pointer-to-pointer 且实际元素是 ArrayType - if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.PointerType): - actual_elem_type: Any = None - if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): - actual_elem_type = ST.elements[offset] - if isinstance(actual_elem_type, ir.ArrayType): - return Gen.builder.bitcast(MemberPtr, ir.PointerType(actual_elem_type), name=f"cast_arr_{AttrName}") - # 检查 class_members 中的 ArrayType - if ClassName in Gen.class_members: - for member_name, member_type in Gen.class_members[ClassName]: - if member_name == AttrName and isinstance(member_type, ir.ArrayType): - return Gen.builder.bitcast(MemberPtr, ir.PointerType(member_type), name=f"cast_arr_{AttrName}") - val: Any = Gen._load(MemberPtr, name=AttrName) - # REnum 变体载荷槽收口(判据见 _RenumVariantPayloadType):变体 struct 的 body - # 是主 REnum 镜像,载荷槽按**最大变体**宽度物化('Pointee' 槽是 i64),而字段 - # 声明类型可能是指针 ⇒ 载入后按声明类型转换;非 REnum 变体键逐字不变。 - _WantTy: Any = self._RenumVariantPayloadType(Gen, ClassName, AttrName) - if _WantTy is not None: - val = self._CoerceVariantPayload(Gen, val, _WantTy) - return self._apply_bswap_on_Load(val, ClassName, AttrName) - - def _try_Load_struct_from_stub(self, Gen: LlvmCodeGenerator, ClassName: str) -> tuple[ir.Type | None, bool]: - """尝试从 stub 加载结构体定义,返回 (struct_type, ok)""" - if ClassName not in Gen.structs: - return None, False - st: Any = Gen.structs[ClassName] - if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) - st = Gen.structs.get(ClassName, st) - if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): - return st, False - return st, True - - def _find_specialized_struct_with_body(self, Gen: LlvmCodeGenerator, opaque_struct: Any) -> tuple[ir.IdentifiedStructType, str] | None: - """查找 opaque 非特化泛型结构体的特化版本(如 GSList -> GSList[Param])。 - 所有特化版本布局相同,可用于 GEP 偏移计算。""" - if not isinstance(opaque_struct, ir.IdentifiedStructType): - return None - base_name: str = opaque_struct.name or "" - short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name - if not short_name or '[' in short_name: - return None - for CN2, ST2 in Gen.structs.items(): - if not isinstance(ST2, ir.IdentifiedStructType): - continue - if ST2 is opaque_struct: - continue - if ST2.elements is None: - continue - st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") - if st2_short.startswith(short_name + '['): - return (ST2, CN2) - return None - - def _try_access_opaque_generic_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, opaque_struct: Any, Node: ast.Attribute, orig_CN: str, parent_hint: str | None = None) -> ir.Value | None: - """通过特化版本访问 opaque 非特化泛型结构体成员。 - - 遍历所有候选特化版本(如 GSList[Param]/GSList[NameEntry]), - 优先选择 class_members 中包含 Node.attr 字段的版本, - 避免选到错误特化版本(如 GSList[NameEntry] 而非 GSList[Param]) - 导致 _get_member_offset 返回 None 而生成 ret null。 - - parent_hint: 父上下文提供的特化提示(如 'GSList[BasicBlock]'), - 当所有候选都有目标字段(如 Head/Tail/Count)时, - 用此提示选择正确特化版本。""" - # 收集所有候选特化版本(有 body 的) - candidates: list[tuple[ir.IdentifiedStructType, str]] = [] - if isinstance(opaque_struct, ir.IdentifiedStructType): - base_name: str = opaque_struct.name or "" - short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name - if short_name and '[' not in short_name: - for CN2, ST2 in Gen.structs.items(): - if not isinstance(ST2, ir.IdentifiedStructType): - continue - if ST2 is opaque_struct: - continue - if ST2.elements is None: - continue - st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") - if st2_short.startswith(short_name + '['): - candidates.append((ST2, CN2)) - if not candidates: - return None - # 优先级 1: parent_hint 精确匹配(如 'GSList[BasicBlock]') - # 当父对象字段声明记录了特化名时,直接使用,避免 Head/Tail/Count 等公共字段歧义 - if parent_hint: - hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint - for spec_st, spec_CN in candidates: - spec_short: str = spec_CN.split('.')[-1] if '.' in spec_CN else spec_CN - if spec_short == hint_short: - CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") - for lookup_CN in (spec_CN, orig_CN): - bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) - if Node.attr in bitfield_offsets: - return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) - offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) - if offset is not None: - return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) - break - # 优先级 2: class_members 中包含 Node.attr 字段的特化版本 - best_candidate: tuple[ir.IdentifiedStructType, str] | None = None - for spec_st, spec_CN in candidates: - for lookup_CN in (spec_CN, orig_CN): - lookup_short: str = lookup_CN.split('.')[-1] if '.' in lookup_CN else lookup_CN - for chk_CN in (lookup_CN, lookup_short): - if chk_CN in Gen.class_members: - if any(name == Node.attr for name, _ in Gen.class_members[chk_CN]): - best_candidate = (spec_st, spec_CN) - break - if best_candidate: - break - if best_candidate: - break - if best_candidate is None: - best_candidate = candidates[0] - spec_st: ir.IdentifiedStructType - spec_CN: str - spec_st, spec_CN = best_candidate - CastedObj = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") - for lookup_CN in (spec_CN, orig_CN): - bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) - if Node.attr in bitfield_offsets: - return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) - offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) - if offset is not None: - return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) - return None - - def _make_define_constant(self, Gen: LlvmCodeGenerator, val: int | float | str, Name: str | None = None) -> ir.Value | None: - """将 CDefine 值转为 LLVM 常量。 - - 带类型注册优先:若该 define 登记了**声明整数类型**(`Gen._define_decl_types`), - 按该类型的位宽与符号性物化(u32 `DWORD(-11)` → `i32 0xFFFFFFF5`),而不是按 - 数值大小猜 i32/i64(后者会把 `4294967285` 误判成 i64,与 i32/ULONG 使用点不符)。 - """ - if isinstance(val, int) and not isinstance(val, bool): + def _gep_and_Load_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, offset: int | None, AttrName: str, ClassName: str, ST: ir.Type | None = None) -> ir.Value | None: + """从结构体指针通过 GEP 取成员,处理 array/struct pointer/class_members 特殊情况""" + if offset is None: + self._NoteAttrNone(Gen, ClassName, AttrName, + f"偏移为 None(字段 '{AttrName}' 未进成员表)") + return None + if isinstance(ObjVal.type, ir.PointerType): + pointee: Any = ObjVal.type.pointee + # opaque pointee:GEP 无法在编译期算元素类型(llvmlite 抛裸 TypeError, + # _GuardIrBuilder 会先 fail-fast)。先按**同短名**找一份已具化实例并 bitcast + # (与写入路径 HandlesAssign 的孪生体搜索共用 _FindConcreteTwin); + # 找不到则保持原样、由守卫报出同短名实例清单(暴露"body 从未 set_body"), + # **不静默兜底**。 + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: + _TwinR: Any = None + try: + _TwinR = Gen._FindConcreteTwin(pointee) + if _TwinR is None: + _TwinR = Gen._FindConcreteTwin(Gen.structs.get(ClassName)) + except Exception: + _TwinR = None + if _TwinR is not None: + ObjVal = Gen.builder.bitcast( + ObjVal, ir.PointerType(_TwinR), + name=f"concrete_twin_read_{AttrName}") + pointee = _TwinR + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset >= len(pointee.elements): + # sha1 冲突修复:pointee 字段不足时,尝试 bitcast 到 ST(更完整的版本) + if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(ST), name=f"cast_gep_{ClassName}") + else: + _st_len: Any = (len(ST.elements) + if isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None + else 'None/opaque') + self._NoteAttrNone( + Gen, ClassName, AttrName, + f"偏移越界:字段 '{AttrName}' 需槽位 {offset}," + f"但 body 只有 {len(pointee.elements)} 个元素," + f"stub 版结构体(st)也仅 {_st_len} 个 ⇒ 布局与成员表不同源" + f"(成员表 {len(Gen.class_members.get(ClassName, []))} 项)") + return None + MemberPtr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=AttrName) + if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.ArrayType): + return MemberPtr + if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.IdentifiedStructType): + return MemberPtr + # 检查 pointer-to-pointer 且实际元素是 ArrayType + if isinstance(MemberPtr.type, ir.PointerType) and isinstance(MemberPtr.type.pointee, ir.PointerType): + actual_elem_type: Any = None + if ST is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None and offset < len(ST.elements): + actual_elem_type = ST.elements[offset] + if isinstance(actual_elem_type, ir.ArrayType): + return Gen.builder.bitcast(MemberPtr, ir.PointerType(actual_elem_type), name=f"cast_arr_{AttrName}") + # 检查 class_members 中的 ArrayType + if ClassName in Gen.class_members: + for member_name, member_type in Gen.class_members[ClassName]: + if member_name == AttrName and isinstance(member_type, ir.ArrayType): + return Gen.builder.bitcast(MemberPtr, ir.PointerType(member_type), name=f"cast_arr_{AttrName}") + val: Any = Gen._load(MemberPtr, name=AttrName) + # REnum 变体载荷槽收口(判据见 _RenumVariantPayloadType):变体 struct 的 body + # 是主 REnum 镜像,载荷槽按**最大变体**宽度物化('Pointee' 槽是 i64),而字段 + # 声明类型可能是指针 ⇒ 载入后按声明类型转换;非 REnum 变体键逐字不变。 + _WantTy: Any = self._RenumVariantPayloadType(Gen, ClassName, AttrName) + if _WantTy is not None: + val = self._CoerceVariantPayload(Gen, val, _WantTy) + return self._apply_bswap_on_Load(val, ClassName, AttrName) + + def _try_Load_struct_from_stub(self, Gen: LlvmCodeGenerator, ClassName: str) -> tuple[ir.Type | None, bool]: + """尝试从 stub 加载结构体定义,返回 (struct_type, ok)""" + if ClassName not in Gen.structs: + return None, False + st: Any = Gen.structs[ClassName] + if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + st = Gen.structs.get(ClassName, st) + if isinstance(st, ir.IdentifiedStructType) and (st.elements is None or len(st.elements) == 0): + return st, False + return st, True + + def _find_specialized_struct_with_body(self, Gen: LlvmCodeGenerator, opaque_struct: Any) -> tuple[ir.IdentifiedStructType, str] | None: + """查找 opaque 非特化泛型结构体的特化版本(如 GSList -> GSList[Param])。 + 所有特化版本布局相同,可用于 GEP 偏移计算。""" + if not isinstance(opaque_struct, ir.IdentifiedStructType): + return None + base_name: str = opaque_struct.name or "" + short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name + if not short_name or '[' in short_name: + return None + for CN2, ST2 in Gen.structs.items(): + if not isinstance(ST2, ir.IdentifiedStructType): + continue + if ST2 is opaque_struct: + continue + if ST2.elements is None: + continue + st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") + if st2_short.startswith(short_name + '['): + return (ST2, CN2) + return None + + def _try_access_opaque_generic_member(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, opaque_struct: Any, Node: ast.Attribute, orig_CN: str, parent_hint: str | None = None) -> ir.Value | None: + """通过特化版本访问 opaque 非特化泛型结构体成员。 + + 遍历所有候选特化版本(如 GSList[Param]/GSList[NameEntry]), + 优先选择 class_members 中包含 Node.attr 字段的版本, + 避免选到错误特化版本(如 GSList[NameEntry] 而非 GSList[Param]) + 导致 _get_member_offset 返回 None 而生成 ret null。 + + parent_hint: 父上下文提供的特化提示(如 'GSList[BasicBlock]'), + 当所有候选都有目标字段(如 Head/Tail/Count)时, + 用此提示选择正确特化版本。""" + # 收集所有候选特化版本(有 body 的) + candidates: list[tuple[ir.IdentifiedStructType, str]] = [] + if isinstance(opaque_struct, ir.IdentifiedStructType): + base_name: str = opaque_struct.name or "" + short_name: str = base_name.split('.')[-1] if '.' in base_name else base_name + if short_name and '[' not in short_name: + for CN2, ST2 in Gen.structs.items(): + if not isinstance(ST2, ir.IdentifiedStructType): + continue + if ST2 is opaque_struct: + continue + if ST2.elements is None: + continue + st2_short: str = (ST2.name or "").split('.')[-1] if '.' in (ST2.name or "") else (ST2.name or "") + if st2_short.startswith(short_name + '['): + candidates.append((ST2, CN2)) + if not candidates: + return None + # 优先级 1: parent_hint 精确匹配(如 'GSList[BasicBlock]') + # 当父对象字段声明记录了特化名时,直接使用,避免 Head/Tail/Count 等公共字段歧义 + if parent_hint: + hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint + for spec_st, spec_CN in candidates: + spec_short: str = spec_CN.split('.')[-1] if '.' in spec_CN else spec_CN + if spec_short == hint_short: + CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") + for lookup_CN in (spec_CN, orig_CN): + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) + if offset is not None: + return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) + break + # 优先级 2: class_members 中包含 Node.attr 字段的特化版本 + best_candidate: tuple[ir.IdentifiedStructType, str] | None = None + for spec_st, spec_CN in candidates: + for lookup_CN in (spec_CN, orig_CN): + lookup_short: str = lookup_CN.split('.')[-1] if '.' in lookup_CN else lookup_CN + for chk_CN in (lookup_CN, lookup_short): + if chk_CN in Gen.class_members: + if any(name == Node.attr for name, _ in Gen.class_members[chk_CN]): + best_candidate = (spec_st, spec_CN) + break + if best_candidate: + break + if best_candidate: + break + if best_candidate is None: + best_candidate = candidates[0] + spec_st: ir.IdentifiedStructType + spec_CN: str + spec_st, spec_CN = best_candidate + CastedObj = Gen.builder.bitcast(ObjVal, ir.PointerType(spec_st), name=f"cast_spec_{orig_CN}") + for lookup_CN in (spec_CN, orig_CN): + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(lookup_CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(CastedObj, lookup_CN, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, lookup_CN) + if offset is not None: + return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, lookup_CN, spec_st) + return None + + def _make_define_constant(self, Gen: LlvmCodeGenerator, val: int | float | str, Name: str | None = None) -> ir.Value | None: + """将 CDefine 值转为 LLVM 常量。 + + 带类型注册优先:若该 define 登记了**声明整数类型**(`Gen._define_decl_types`), + 按该类型的位宽与符号性物化(u32 `DWORD(-11)` → `i32 0xFFFFFFF5`),而不是按 + 数值大小猜 i32/i64(后者会把 `4294967285` 误判成 i64,与 i32/ULONG 使用点不符)。 + """ + if isinstance(val, int) and not isinstance(val, bool): _dt: tuple[int, int] | None = DefineDeclTypeValue( - getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), Name, val) - if _dt is not None: - return ir.Constant(ir.IntType(_dt[0]), _dt[1]) - if isinstance(val, int): - # 位宽按值推断(与 TPV ExprGenCdef / define_min_width 同口径):能装进 32 位 - # 位模式(含无符号高半区 [2^31, 2^32-1],如 GENERIC_READ / SHARED 掩码)就用 i32, - # 否则 i64。此前用 abs(val) > 2^31-1 判定,会把无符号高半区误判成 i64,与 32 位 - # 使用点(ULONG/DWORD)组合时触发「64→32 隐式窄化」。 - if -2147483648 <= val <= 4294967295: - return ir.Constant(ir.IntType(32), val if val <= 2147483647 else val - 4294967296) - return ir.Constant(ir.IntType(64), val) - elif isinstance(val, float): - return ir.Constant(ir.DoubleType(), val) - elif isinstance(val, str): - return Gen._create_string_global(val) - return None - - def _lookup_cdefine(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: - """在符号表和模块中查找 CDefine 常量值 - - 注意:不再强制依赖 _ImportedModules。即使该集合为空(例如自举编译 - 阶段尚未填充),也始终检查符号表与 Gen._define_constants,确保 - ast.CONST_INT 这类 CDefine 常量能够被解析为立即数而非全局变量引用。 - """ - PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] - imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) - if imported_modules: - for mod_name in imported_modules: - if mod_name.endswith(f".{VarName}") or mod_name == VarName: - key: str = f"{mod_name}.{AttrName}" - if key not in PossibleKeys: - PossibleKeys.append(key) - # 始终遍历符号表(不依赖 imported_modules 是否为空) - for lookup_key in PossibleKeys: - SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) - if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: - return self._make_define_constant(Gen, SymInfo.DefineValue, AttrName) - # 也检查 _define_constants(LlvmGenerator 预扫描阶段填充) - define_constants: Any = getattr(Gen, '_define_constants', {}) - for key in (f"{VarName}.{AttrName}", AttrName): - if key in define_constants: - return self._make_define_constant(Gen, define_constants[key], AttrName) - # 回退:_all_define_constants(Phase2Translator 从所有 .pyi 预提取的跨模块 CDefine, - # 与 _HandleNameLlvm 的裸名回退同源)。**模块属性访问**(`win32file.STD_OUTPUT_HANDLE`) - # 此前只查 SymbolTable/_define_constants → 未命中就落到 _lookup_module_global 生成 - # `@STD_OUTPUT_HANDLE` 全局引用(无定义 → 链接期 undefined symbol)。 - # 与裸名路径同口径:仅当本模块没有同名变量/(**已定义的**)全局时才用常量。 - # 注意:跨模块 stub 注入的 `@X = external global`(无初值)**不算**遮蔽 —— 否则 - # 一条错误的 stub declare 会把 .vpi 的 CDefine 常量顶掉,退化成未定义全局符号。 - _has_shadow: bool = AttrName in Gen.variables or GlobalDeclShadowsDefine(Gen, AttrName) - if not _has_shadow: - all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} - for key2 in (f"{VarName}.{AttrName}", AttrName): - if key2 in all_dc: - return self._make_define_constant(Gen, all_dc[key2], AttrName) - return None - - def _NormalizeModuleGlobalValue(self, Gen: LlvmCodeGenerator, Val: Any, AttrName: str) -> Any: - """模块限定全局读取归一化:类指针全局的「多一层」元素类型还原为对象指针 - - `Mod.g`(g 为模块级全局)的取值统一经 _lookup_module_global:注解为 - `X | t.CPtr` 的模块级全局(如 GenApi._g_gen: rawptr[Generator.Generator]、 - VLogger.logger: Logger | t.CPtr)在本编译器里物化成「比对象多一层」的 - 元素类型 T**(写入端见 main.vp `GenApi._g_gen = gen`:bitcast T*→T** 后 - 直接存值,未取地址)⇒ 其位形就是对象指针 T*,静态类型多了一层。 - 裸名形态(`g.Field`)由 _handle_attr_name 的既有同款补偿 bitcast 处理; - 模块限定形态(`Mod.g.Field`)此前无补偿 ⇒ 载入值停在 T**,其 pointee 是 - T*(指针而非结构体)⇒ _handle_attr_nested 的 char* 分支与 struct 分支都 - 不命中而返回 None ⇒ 带注解赋值 RHS 求值为空 fail-fast - (App/lib/core/Repl.vp:763 `GenApi._g_gen.TypeGenCDef.CDefineNames`)、 - 调用接收者 None 而静默丢调用(VpiReader.vp:4066 - `GenApi._g_gen.ClassGenBase._collect_parents(...)`)。此处做同款逆运算: - 只改静态类型、不改位形(bitcast 不产指令)。 - 判据限定「pointee 仍是指向结构体的指针」⇒ `str | t.CPtr` 一类真双层指针 - 全局(@_argv / @_g_pyi_cdef_names = i8**,实参直传 i8** 形参)不受影响。 - """ - if not isinstance(Val, ir.Value) or not isinstance(Val.type, ir.PointerType): - return Val - Inner: Any = Val.type.pointee - if isinstance(Inner, ir.PointerType) and isinstance(Inner.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - return Gen.builder.bitcast(Val, Inner, name=f"cast_{AttrName}_global") - return Val - - def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: - """在模块全局变量中查找 import 的属性""" - imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) - if not imported_modules: - return None - resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) - if VarName not in imported_modules and resolved_mod not in imported_modules: - return None - PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] - for mod_name in imported_modules: - if mod_name.endswith(f".{VarName}") or mod_name == VarName: - key: str = f"{mod_name}.{AttrName}" - if key not in PossibleKeys: - PossibleKeys.append(key) - for key in PossibleKeys: - if key in Gen.module.globals: - GVar: Any = Gen.module.globals[key] - if isinstance(GVar, ir.Function): - return GVar - if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - return GVar - return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar, name=AttrName), AttrName) - # SHA1 前缀查找 - ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) - sha1_candidates: list[str] = [VarName, resolved_mod] - for mod_name in imported_modules: - if mod_name.endswith(f".{VarName}") or mod_name == VarName: - if mod_name not in sha1_candidates: - sha1_candidates.append(mod_name) - for cand in sha1_candidates: - sha1: str | None = ModuleSha1Map.get(cand) - if sha1: - prefixed: str = f"{sha1}.{AttrName}" - if prefixed in Gen.module.globals: - GVar2: Any = Gen.module.globals[prefixed] - if isinstance(GVar2, ir.Function): - return GVar2 - if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - return GVar2 - return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar2, name=AttrName), AttrName) - if AttrName in Gen.module.globals: - gv: Any = Gen.module.globals[AttrName] - if AttrName in Gen.variables and Gen.variables[AttrName] is None: - str_gv_name: str = AttrName + '_str' - if str_gv_name in Gen.module.globals: - str_gv: Any = Gen.module.globals[str_gv_name] - return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") - return self._NormalizeModuleGlobalValue(Gen, Gen.builder.load(gv, name=AttrName), AttrName) - return None - - def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: - """根据变量名解析其结构体类名""" - ClassName: str | None = Gen.var_struct_class.get(VarName) - if not ClassName and getattr(Gen, 'global_struct_class', None): - ClassName = Gen.global_struct_class.get(VarName) - if ClassName: - Gen.var_struct_class[VarName] = ClassName - if not ClassName and VarName in Gen.module.globals: - GVar: Any = Gen.module.globals[VarName] - if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found: Any = Gen.find_struct_by_pointee(GVar.type.pointee) - if found: - ClassName = found[0] - Gen.var_struct_class[VarName] = ClassName - if getattr(Gen, 'global_struct_class', None): - Gen.global_struct_class[VarName] = ClassName - if not ClassName and VarName in Gen.variables: - VarPtr: Any = Gen.variables[VarName] - if VarPtr is not None and isinstance(VarPtr.type, ir.PointerType): - pointee: Any = VarPtr.type.pointee - if isinstance(pointee, ir.PointerType): - pointee = pointee.pointee - found = Gen.find_struct_by_pointee(pointee) - if found: - ClassName = found[0] - return ClassName - - def _check_struct_match(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, ClassName: str) -> bool: - """检查 ObjVal 是否指向 ClassName 对应的结构体""" - if ClassName not in Gen.structs: - return False - if isinstance(ObjVal.type, ir.PointerType): - ST: Any = Gen.structs[ClassName] - if ObjVal.type.pointee == ST: - return True - if isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType): - # 精确名匹配(含 sha1 前缀)或短名匹配(去掉 sha1 前缀) - if ObjVal.type.pointee.name == ST.name: - return True - if Gen._extract_short_name(ObjVal.type.pointee.name) == Gen._extract_short_name(ST.name): - return True - return False - - # ========== _HandleAttributeLlvm 子方法 ========== - - def _handle_attr_self(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: - """处理 self.xxx 属性访问""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - ClassName: str | None = self.Trans._CurrentCpythonObjectClass - if not ClassName: - return None - # 检查 property getter - PropKey: str = f'{ClassName}.{Node.attr}' - PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) - if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: - SelfVar: Any = Gen._get_var_ptr('self') - if SelfVar: - SelfPtr: Any = Gen._load(SelfVar, name="self") - GetterFunc: Any = Gen._get_function(PropKey) - if GetterFunc and SelfPtr: - return Gen.builder.call(GetterFunc, [SelfPtr], name=f"prop_{PropKey}") - offset: int | None = Gen._get_member_offset(Node.attr, ClassName) - SelfVar = Gen._get_var_ptr('self') - if not SelfVar: - return None - SelfPtr = Gen._load(SelfVar, name="self") - if not isinstance(SelfPtr.type, ir.PointerType): - return None - pointee: Any = SelfPtr.type.pointee - if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: - # 修复 Bug 4/5:NoVTable fix 清除 struct elements 后,_handle_attr_self - # 必须主动从 stub 文件重新加载 struct 定义,否则 self.Trans 等属性访问 - # 会因 pointee.elements 为 None 返回 None,导致 AnnAssign/Store 不生成 IR。 - # 与 _handle_attr_nested 的处理保持一致。 - # 策略:先用 ClassName 直接尝试加载;若失败,遍历 Gen.structs 按名字匹配 - # 找到对应的 dict key(可能是 sha1 前缀形式),再用该 key 加载。 - loaded: bool = False - for TryCN in (ClassName,): - st, ok = self._try_Load_struct_from_stub(Gen, TryCN) - if ok and isinstance(st, ir.IdentifiedStructType) and st.elements is not None: - pointee = st - loaded = True - break - if not loaded: - for CN2, ST2 in Gen.structs.items(): - if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name: - st2, ok2 = self._try_Load_struct_from_stub(Gen, CN2) - if ok2 and isinstance(st2, ir.IdentifiedStructType) and st2.elements is not None: - pointee = st2 - loaded = True - break - if not loaded: - # 最后回退:在 Gen.structs 中查找同名非 opaque 版本 - for CN2, ST2 in Gen.structs.items(): - if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name and ST2.elements is not None: - pointee = ST2 - break - # 当 pointee 元素数不足以容纳 offset 时(常见于跨模块继承:子类 struct - # 在父类 class_members 加载前生成,只有部分继承字段),从 Gen.structs - # 中查找同名且元素数更多的完整定义。 - if (isinstance(pointee, ir.IdentifiedStructType) - and offset is not None - and pointee.elements is not None - and offset >= len(pointee.elements)): - _cur_count: int = len(pointee.elements) - for CN2, ST2 in Gen.structs.items(): - if (isinstance(ST2, ir.IdentifiedStructType) - and ST2.name == pointee.name - and ST2.elements is not None - and len(ST2.elements) > _cur_count - and offset < len(ST2.elements)): - pointee = ST2 - break - if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset is not None and offset < len(pointee.elements): - if isinstance(pointee.elements[offset], ir.VoidType): - return None - return self._gep_and_Load_member(Gen, SelfPtr, offset, Node.attr, ClassName, pointee) - DynamicVarName: str = f"self.{Node.attr}" - if DynamicVarName in Gen.variables: - return Gen._load(Gen.variables[DynamicVarName], name=Node.attr) - return None - - def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: - """处理枚举成员访问 (VarName 是枚举类型名)""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) - if not SymInfo or not SymInfo.IsEnum: - return None - # 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖) - FullKey: str = f"{VarName}.{Node.attr}" - MemberInfo: Any = self.Trans.SymbolTable.lookup(FullKey) - if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: - # 回退到短名查找 - MemberInfo = self.Trans.SymbolTable.lookup(Node.attr) - if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: - # 最后遍历符号表 - for key, info in self.Trans.SymbolTable.items(): - if info.IsEnumMember and info.EnumName == VarName and key == Node.attr: - if isinstance(info.value, int): - return ir.Constant(Gen._enum_int_type(VarName, info), info.value) - return None - if isinstance(MemberInfo.value, int): - return ir.Constant(Gen._enum_int_type(VarName, MemberInfo), MemberInfo.value) - return None - - def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: - """处理 x.attr 形式 (x 是普通 Name)""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - AttrName: str = Node.attr - - # __doc__ 魔法字符串:函数/结构体的 docstring - if AttrName == '__doc__': - doc_ptr: ir.Value | None = Gen._get_doc_ptr(VarName) - if doc_ptr is not None: - return doc_ptr - - # 先尝试枚举成员 - result: Any = self._handle_attr_enum(Node, VarName) - if result is not None: - return result - - # 尝试 CDefine 常量 - result = self._lookup_cdefine(Gen, VarName, AttrName) - if result is not None: - return result - - # 冗余回退:直接以短名/全名查符号表的 CDefine 条目 - # 覆盖 _ImportedModules 未填充但符号表已注册 CDefine 的场景 - # (例如自举编译期 ast.CONST_INT 在 LlvmGenerator 预扫描阶段 - # 已注册到 SymbolTable,但 _ImportedModules 仍为空)。 - for _cdefine_key in (AttrName, f"{VarName}.{AttrName}"): - _sym: Any = self.Trans.SymbolTable.lookup(_cdefine_key) - if _sym and _sym.IsDefine and _sym.DefineValue is not None: - _const_val: Any = self._make_define_constant(Gen, _sym.DefineValue, AttrName) - if _const_val is not None: - return _const_val - - # 尝试 import 模块全局变量 - result = self._lookup_module_global(Gen, VarName, AttrName) - if result is not None: - return result - - # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) - # 当 a 是 str 变量时, a 本身是 char*, 但 a__meta__ 存储了 __data__/__mbuddy__ 等字段 - # 直接通过 a.__field__ 访问影子结构体字段 (语法糖) - _fd_meta_name: str = f"{VarName}__meta__" - if _fd_meta_name in Gen.variables and '_str' in Gen.structs: - _fd_offset = Gen._get_member_offset(AttrName, '_str') - if _fd_offset is not None: - _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] - _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") - return Gen._load(_fd_field_ptr, name=f"{VarName}_{AttrName}_val") - - # 解析结构体类名:若 VarName 是结构体变量,优先走结构体字段访问路径 - # 避免结构体字段名(如 stdout)与全局变量名冲突时被误解析为全局变量 - ClassName: str | None = self._resolve_var_classname(Gen, VarName) - # 查找端键归一:var_struct_class 可能记录裸短名('Tuple'),而 Gen.structs - # 的键是带 class_sha1 前缀的全名(如 '43ec967d0517abc1.Tuple'),或反之。 - # 单级 Name 字段取值(tp.elts)要求 ClassName 能直接定位 Gen.structs 并 - # 加载 stub body 后才能 GEP —— 与 _handle_attr_nested 各分支一致先归一。 - if ClassName and ClassName not in Gen.structs: - _cn_key: str | None = self._find_struct_key_for_class_hint(Gen, ClassName) - if _cn_key: - ClassName = _cn_key - - # 尝试 attr 直接作为全局变量(仅当 VarName 不是结构体变量时) - if not ClassName and AttrName in Gen.module.globals: - gv: Any = Gen.module.globals[AttrName] - if AttrName in Gen.variables and Gen.variables[AttrName] is None: - str_gv_name: str = AttrName + '_str' - if str_gv_name in Gen.module.globals: - str_gv: Any = Gen.module.globals[str_gv_name] - return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") - return Gen.builder.load(gv, name=AttrName) - - if not ClassName: - # 单级 Name 字段取值的类名恢复(跨模块 stub 下 var_struct_class 缺失的 - # 兜底):本地变量(如 `tp: rawptr[Tuple] = (rawptr[Tuple])(node)`)可能 - # 因 rawptr[X] 被折叠为 i8* 而漏登记。若变量 alloca / 值本身可辨识为 - # 结构体指针,按实际 pointee 恢复真实 Gen.structs 键并回填,避免属性 - # 访问求值 None 被 fail-fast 拦截(parser.vp `tp_elts = tp.elts` 根因)。 - if (VarName in Gen.variables or VarName in Gen._reg_values - or VarName in Gen._direct_values or VarName in Gen.global_vars): - _recovered: str | None = self._recover_struct_class(Gen, VarName) - if _recovered: - ClassName = _recovered - Gen.var_struct_class[VarName] = _recovered - if getattr(Gen, 'global_struct_class', None) is not None: - Gen.global_struct_class[VarName] = _recovered - if not ClassName: - self._NoteAttrNone(Gen, None, AttrName, - f"类名未解析('{VarName}' 的 var_struct_class 为空且无法由 " - f"alloca/值类型恢复)") - return None - - # 检查 property getter - PropKey: str = f'{ClassName}.{AttrName}' - PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) - if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: - ObjVal: Any = self.HandleExprLlvm(Node.value) - GetterFunc: Any = Gen._get_function(PropKey) - if GetterFunc and ObjVal: - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") - return Gen.builder.call(GetterFunc, [ObjVal], name=f"prop_{PropKey}") - - # 获取类型信息 - TypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName) - IsUnion: bool = TypeInfo.IsUnion if TypeInfo else False - IsCenum: bool = TypeInfo.IsEnum if TypeInfo else False - IsRenum: bool = TypeInfo.IsRenum if TypeInfo else False - - # REnum 处理 - if IsRenum: - if AttrName == 'tag': - ObjVal = self.HandleExprLlvm(Node.value) - if ObjVal: - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") - tag_ptr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="tag_ptr") - return Gen._load(tag_ptr, name="tag_val") - NestedStructName: str = f"{ClassName}_{AttrName}" - if NestedStructName in Gen.structs: - NestedStructType: Any = Gen.structs[NestedStructName] - ObjVal = self.HandleExprLlvm(Node.value) - if ObjVal: - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") - return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") - - # CEnum 处理 - if IsCenum: - for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"): - info: Any = self.Trans.SymbolTable.lookup(qname) - if info and info.IsEnumMember and isinstance(info.value, int): - return ir.Constant(Gen._enum_int_type(ClassName, info), info.value) - for key, info in self.Trans.SymbolTable.items(): - if key == AttrName: - if info.IsEnumMember and info.EnumName == ClassName: - if isinstance(info.value, int): - return ir.Constant(Gen._enum_int_type(ClassName, info), info.value) - break - - # CUnion 处理 - if IsUnion: - NestedStructName = f"{ClassName}_{AttrName}" - if NestedStructName in Gen.structs: - NestedStructType = Gen.structs[NestedStructName] - ObjVal = self.HandleExprLlvm(Node.value) - if ObjVal: - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") - return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") - - # 普通结构体成员访问 - ObjVal = self.HandleExprLlvm(Node.value) - if not ObjVal: - self._NoteAttrNone(Gen, ClassName, AttrName, - f"基座 '{VarName}' 自身求值为空(类名={ClassName!r})") - return None - # char* → struct* cast - if self._is_char_pointer(ObjVal): - if ClassName in Gen.structs: - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - # 二级指针解引用。模块级全局指针变量(如 _g_gen: rawptr[Generator.Generator]) - # 的全局元素类型比实际存值多一层:写入端(main.vp `GenApi._g_gen = gen`)生成 - # 为 bitcast T*→T** 后直接存值,未取地址;读取端必须做其逆运算 bitcast,绝不能 - # 再 load——否则把 T* 当 T** 再解引用,接收者被取成对象首字段的位,方法内读 - # self->Gen 即野指针(ASan: CallGenBase.SetTempDir 读悬空地址崩溃)。 - if (isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType) - and isinstance(Node.value, ast.Name) and Node.value.id in Gen.module.globals): - ObjVal = Gen.builder.bitcast(ObjVal, ObjVal.type.pointee, name=f"cast_{VarName}_global") - elif isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") - - # 检查结构体匹配 - struct_match: bool = self._check_struct_match(Gen, ObjVal, ClassName) - if not struct_match and ClassName and VarName: - if VarName in Gen.var_struct_class: - CN: str = Gen.var_struct_class[VarName] - if CN != ClassName and CN in Gen.structs: - ClassName = CN - struct_match = self._check_struct_match(Gen, ObjVal, ClassName) - # 最终回退:var_struct_class 可能被前一个函数污染(如 cur 在不同函数中 - # 分别是 Line/Param/BasicBlock),通过 ObjVal 的实际 pointee 类型恢复正确类名。 - # 但若 var_struct_class 已记录特化名(如 ndarray[double]),且 ObjVal 的 - # pointee 是对应的 opaque 基础类型(如 ndarray),则 bitcast 到特化版本, - # 不覆盖为裸模板名(否则后续方法调用会查找 ndarray.xxx 而非 ndarray[double].xxx) - if not struct_match and isinstance(ObjVal.type, ir.PointerType): - found: Any = Gen.find_struct_by_pointee(ObjVal.type.pointee) - if found: - found_name: str = found[0] - # 检查 var_struct_class 是否已记录 found_name 的特化版本 - preserved: bool = False - if VarName and VarName in Gen.var_struct_class: - cur_spec: str = Gen.var_struct_class[VarName] - if ('[' in cur_spec - and cur_spec.split('[')[0] == found_name - and cur_spec in Gen.structs): - # bitcast ObjVal (opaque base*) 到特化类型指针,保留特化名 - ObjVal = Gen.builder.bitcast( - ObjVal, ir.PointerType(Gen.structs[cur_spec]), - name=f"cast_spec_{cur_spec}") - ClassName = cur_spec - struct_match = True - preserved = True - if not preserved: - ClassName = found_name - struct_match = True - if VarName: - Gen.var_struct_class[VarName] = ClassName - elif isinstance(ObjVal.type.pointee, ir.IdentifiedStructType): - # sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时, - # 用短名匹配,优先选字段最多的完整版本 - pointee_short: str = Gen._extract_short_name(ObjVal.type.pointee.name) - best_elem_count: int = 0 - for CN, ST in Gen.structs.items(): - if isinstance(ST, ir.IdentifiedStructType): - st_short: str = Gen._extract_short_name(ST.name) - if st_short == pointee_short: - elem_count: int = len(ST.elements) if ST.elements else 0 - if elem_count > best_elem_count: - ClassName = CN - best_elem_count = elem_count - struct_match = True - if struct_match and VarName: - # 同样保护特化名:若已记录的特化名的短名基础部分与 ClassName 匹配,保留 - cur_spec2: str = Gen.var_struct_class.get(VarName, '') - if ('[' in cur_spec2 - and cur_spec2.split('[')[0] == ClassName - and cur_spec2 in Gen.structs): - ObjVal = Gen.builder.bitcast( - ObjVal, ir.PointerType(Gen.structs[cur_spec2]), - name=f"cast_spec_{cur_spec2}") - ClassName = cur_spec2 - else: - Gen.var_struct_class[VarName] = ClassName - # 通用指针槽兜底:静态类名(var_struct_class/键归一后)有效且 ObjVal 是指针, - # 但 pointee 非 struct(如 cast 局部变量 tp: rawptr[Tuple] 的槽被建为 i32**, - # Name 求值 load 一次得到 i32*,上述 struct 匹配/回退均不命中 → return None)。 - # 此时按静态类名信任 bitcast 到 struct* 再走成员 GEP(tp.elts 根因: - # var_struct_class='Tuple'、structs 有 'Tuple',仅槽位为通用指针形态)。 - if (not struct_match and ClassName and ClassName in Gen.structs - and isinstance(ObjVal.type, ir.PointerType) - and not isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) - and not isinstance(ObjVal.type.pointee, ir.LiteralStructType)): - ObjVal = Gen.builder.bitcast( - ObjVal, ir.PointerType(Gen.structs[ClassName]), - name=f"cast_slot_{ClassName}") - struct_match = True - if not struct_match: - self._NoteAttrNone( - Gen, ClassName, AttrName, - f"结构体未匹配(ObjVal 类型={ObjVal.type},类名={ClassName!r};" - f"pointee 既非该类 struct,静态类名也未能 bitcast 兜底)") - return None - - # 左值注解靶型提示(见 _apply_target_struct_hint):类不声明该字段时改用注解类 - ClassName = self._apply_target_struct_hint(Gen, AttrName, ClassName) - st: Any - ok: bool - st, ok = self._try_Load_struct_from_stub(Gen, ClassName) - if not ok: - self._NoteAttrNone(Gen, ClassName, AttrName, - f"stub 无 body(Gen.structs[{ClassName!r}] 存在但 elements 为空/opaque)") - return None - - # bitfield 处理 - bitfield_offsets: Any = Gen.class_member_bitoffsets.get(ClassName, {}) - bitfields: Any = Gen.class_member_bitfields.get(ClassName, {}) - if AttrName in bitfield_offsets and bitfields.get(AttrName, 0) > 0: - return self._load_bitfield_member(ObjVal, ClassName, AttrName) - if AttrName in bitfields and bitfields[AttrName] > 0: - if ClassName not in Gen.class_member_bitoffsets: - Gen.class_member_bitoffsets[ClassName] = {} - if AttrName not in Gen.class_member_bitoffsets[ClassName]: - bo: int = 0 - for bf_name, bf_width in bitfields.items(): - if bf_name == AttrName: - break - bo += bf_width - Gen.class_member_bitoffsets[ClassName][AttrName] = bo - return self._load_bitfield_member(ObjVal, ClassName, AttrName) - + getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None), Name, val) + if _dt is not None: + return ir.Constant(ir.IntType(_dt[0]), _dt[1]) + if isinstance(val, int): + # 位宽按值推断(与 VPC ExprGenCdef / define_min_width 同口径):能装进 32 位 + # 位模式(含无符号高半区 [2^31, 2^32-1],如 GENERIC_READ / SHARED 掩码)就用 i32, + # 否则 i64。此前用 abs(val) > 2^31-1 判定,会把无符号高半区误判成 i64,与 32 位 + # 使用点(ULONG/DWORD)组合时触发「64→32 隐式窄化」。 + if -2147483648 <= val <= 4294967295: + return ir.Constant(ir.IntType(32), val if val <= 2147483647 else val - 4294967296) + return ir.Constant(ir.IntType(64), val) + elif isinstance(val, float): + return ir.Constant(ir.DoubleType(), val) + elif isinstance(val, str): + return Gen._create_string_global(val) + return None + + def _lookup_cdefine(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: + """在符号表和模块中查找 CDefine 常量值 + + 注意:不再强制依赖 _ImportedModules。即使该集合为空(例如自举编译 + 阶段尚未填充),也始终检查符号表与 Gen._define_constants,确保 + ast.CONST_INT 这类 CDefine 常量能够被解析为立即数而非全局变量引用。 + """ + PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if imported_modules: + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + key: str = f"{mod_name}.{AttrName}" + if key not in PossibleKeys: + PossibleKeys.append(key) + # 始终遍历符号表(不依赖 imported_modules 是否为空) + for lookup_key in PossibleKeys: + SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) + if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: + return self._make_define_constant(Gen, SymInfo.DefineValue, AttrName) + # 也检查 _define_constants(LlvmGenerator 预扫描阶段填充) + define_constants: Any = getattr(Gen, '_define_constants', {}) + for key in (f"{VarName}.{AttrName}", AttrName): + if key in define_constants: + return self._make_define_constant(Gen, define_constants[key], AttrName) + # 回退:_all_define_constants(Phase2Translator 从所有 .pyi 预提取的跨模块 CDefine, + # 与 _HandleNameLlvm 的裸名回退同源)。**模块属性访问**(`win32file.STD_OUTPUT_HANDLE`) + # 此前只查 SymbolTable/_define_constants → 未命中就落到 _lookup_module_global 生成 + # `@STD_OUTPUT_HANDLE` 全局引用(无定义 → 链接期 undefined symbol)。 + # 与裸名路径同口径:仅当本模块没有同名变量/(**已定义的**)全局时才用常量。 + # 注意:跨模块 stub 注入的 `@X = external global`(无初值)**不算**遮蔽 —— 否则 + # 一条错误的 stub declare 会把 .vpi 的 CDefine 常量顶掉,退化成未定义全局符号。 + _has_shadow: bool = AttrName in Gen.variables or GlobalDeclShadowsDefine(Gen, AttrName) + if not _has_shadow: + all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} + for key2 in (f"{VarName}.{AttrName}", AttrName): + if key2 in all_dc: + return self._make_define_constant(Gen, all_dc[key2], AttrName) + return None + + def _NormalizeModuleGlobalValue(self, Gen: LlvmCodeGenerator, Val: Any, AttrName: str) -> Any: + """模块限定全局读取归一化:类指针全局的「多一层」元素类型还原为对象指针 + + `Mod.g`(g 为模块级全局)的取值统一经 _lookup_module_global:注解为 + `X | t.CPtr` 的模块级全局(如 GenApi._g_gen: rawptr[Generator.Generator]、 + VLogger.logger: Logger | t.CPtr)在本编译器里物化成「比对象多一层」的 + 元素类型 T**(写入端见 main.vp `GenApi._g_gen = gen`:bitcast T*→T** 后 + 直接存值,未取地址)⇒ 其位形就是对象指针 T*,静态类型多了一层。 + 裸名形态(`g.Field`)由 _handle_attr_name 的既有同款补偿 bitcast 处理; + 模块限定形态(`Mod.g.Field`)此前无补偿 ⇒ 载入值停在 T**,其 pointee 是 + T*(指针而非结构体)⇒ _handle_attr_nested 的 char* 分支与 struct 分支都 + 不命中而返回 None ⇒ 带注解赋值 RHS 求值为空 fail-fast + (App/lib/core/Repl.vp:763 `GenApi._g_gen.TypeGenCDef.CDefineNames`)、 + 调用接收者 None 而静默丢调用(VpiReader.vp:4066 + `GenApi._g_gen.ClassGenBase._collect_parents(...)`)。此处做同款逆运算: + 只改静态类型、不改位形(bitcast 不产指令)。 + 判据限定「pointee 仍是指向结构体的指针」⇒ `str | t.CPtr` 一类真双层指针 + 全局(@_argv / @_g_pyi_cdef_names = i8**,实参直传 i8** 形参)不受影响。 + """ + if not isinstance(Val, ir.Value) or not isinstance(Val.type, ir.PointerType): + return Val + Inner: Any = Val.type.pointee + if isinstance(Inner, ir.PointerType) and isinstance(Inner.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + return Gen.builder.bitcast(Val, Inner, name=f"cast_{AttrName}_global") + return Val + + def _lookup_module_global(self, Gen: LlvmCodeGenerator, VarName: str, AttrName: str) -> ir.Value | None: + """在模块全局变量中查找 import 的属性""" + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if not imported_modules: + return None + resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + if VarName not in imported_modules and resolved_mod not in imported_modules: + return None + PossibleKeys: list[str] = [f"{VarName}.{AttrName}", AttrName] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + key: str = f"{mod_name}.{AttrName}" + if key not in PossibleKeys: + PossibleKeys.append(key) + for key in PossibleKeys: + if key in Gen.module.globals: + GVar: Any = Gen.module.globals[key] + if isinstance(GVar, ir.Function): + return GVar + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar + return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar, name=AttrName), AttrName) + # SHA1 前缀查找 + ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) + sha1_candidates: list[str] = [VarName, resolved_mod] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + if mod_name not in sha1_candidates: + sha1_candidates.append(mod_name) + for cand in sha1_candidates: + sha1: str | None = ModuleSha1Map.get(cand) + if sha1: + prefixed: str = f"{sha1}.{AttrName}" + if prefixed in Gen.module.globals: + GVar2: Any = Gen.module.globals[prefixed] + if isinstance(GVar2, ir.Function): + return GVar2 + if isinstance(GVar2.type, ir.PointerType) and isinstance(GVar2.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + return GVar2 + return self._NormalizeModuleGlobalValue(Gen, Gen._load(GVar2, name=AttrName), AttrName) + if AttrName in Gen.module.globals: + gv: Any = Gen.module.globals[AttrName] + if AttrName in Gen.variables and Gen.variables[AttrName] is None: + str_gv_name: str = AttrName + '_str' + if str_gv_name in Gen.module.globals: + str_gv: Any = Gen.module.globals[str_gv_name] + return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") + return self._NormalizeModuleGlobalValue(Gen, Gen.builder.load(gv, name=AttrName), AttrName) + return None + + def _resolve_var_classname(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: + """根据变量名解析其结构体类名""" + ClassName: str | None = Gen.var_struct_class.get(VarName) + if not ClassName and getattr(Gen, 'global_struct_class', None): + ClassName = Gen.global_struct_class.get(VarName) + if ClassName: + Gen.var_struct_class[VarName] = ClassName + if not ClassName and VarName in Gen.module.globals: + GVar: Any = Gen.module.globals[VarName] + if isinstance(GVar.type, ir.PointerType) and isinstance(GVar.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: Any = Gen.find_struct_by_pointee(GVar.type.pointee) + if found: + ClassName = found[0] + Gen.var_struct_class[VarName] = ClassName + if getattr(Gen, 'global_struct_class', None): + Gen.global_struct_class[VarName] = ClassName + if not ClassName and VarName in Gen.variables: + VarPtr: Any = Gen.variables[VarName] + if VarPtr is not None and isinstance(VarPtr.type, ir.PointerType): + pointee: Any = VarPtr.type.pointee + if isinstance(pointee, ir.PointerType): + pointee = pointee.pointee + found = Gen.find_struct_by_pointee(pointee) + if found: + ClassName = found[0] + return ClassName + + def _check_struct_match(self, Gen: LlvmCodeGenerator, ObjVal: ir.Value, ClassName: str) -> bool: + """检查 ObjVal 是否指向 ClassName 对应的结构体""" + if ClassName not in Gen.structs: + return False + if isinstance(ObjVal.type, ir.PointerType): + ST: Any = Gen.structs[ClassName] + if ObjVal.type.pointee == ST: + return True + if isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType): + # 精确名匹配(含 sha1 前缀)或短名匹配(去掉 sha1 前缀) + if ObjVal.type.pointee.name == ST.name: + return True + if Gen._extract_short_name(ObjVal.type.pointee.name) == Gen._extract_short_name(ST.name): + return True + return False + + # ========== _HandleAttributeLlvm 子方法 ========== + + def _handle_attr_self(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: + """处理 self.xxx 属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + if not ClassName: + return None + # 检查 property getter + PropKey: str = f'{ClassName}.{Node.attr}' + PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: + SelfVar: Any = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: Any = Gen._load(SelfVar, name="self") + GetterFunc: Any = Gen._get_function(PropKey) + if GetterFunc and SelfPtr: + return Gen.builder.call(GetterFunc, [SelfPtr], name=f"prop_{PropKey}") + offset: int | None = Gen._get_member_offset(Node.attr, ClassName) + SelfVar = Gen._get_var_ptr('self') + if not SelfVar: + return None + SelfPtr = Gen._load(SelfVar, name="self") + if not isinstance(SelfPtr.type, ir.PointerType): + return None + pointee: Any = SelfPtr.type.pointee + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is None: + # 修复 Bug 4/5:NoVTable fix 清除 struct elements 后,_handle_attr_self + # 必须主动从 stub 文件重新加载 struct 定义,否则 self.Trans 等属性访问 + # 会因 pointee.elements 为 None 返回 None,导致 AnnAssign/Store 不生成 IR。 + # 与 _handle_attr_nested 的处理保持一致。 + # 策略:先用 ClassName 直接尝试加载;若失败,遍历 Gen.structs 按名字匹配 + # 找到对应的 dict key(可能是 sha1 前缀形式),再用该 key 加载。 + loaded: bool = False + for TryCN in (ClassName,): + st, ok = self._try_Load_struct_from_stub(Gen, TryCN) + if ok and isinstance(st, ir.IdentifiedStructType) and st.elements is not None: + pointee = st + loaded = True + break + if not loaded: + for CN2, ST2 in Gen.structs.items(): + if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name: + st2, ok2 = self._try_Load_struct_from_stub(Gen, CN2) + if ok2 and isinstance(st2, ir.IdentifiedStructType) and st2.elements is not None: + pointee = st2 + loaded = True + break + if not loaded: + # 最后回退:在 Gen.structs 中查找同名非 opaque 版本 + for CN2, ST2 in Gen.structs.items(): + if isinstance(ST2, ir.IdentifiedStructType) and ST2.name == pointee.name and ST2.elements is not None: + pointee = ST2 + break + # 当 pointee 元素数不足以容纳 offset 时(常见于跨模块继承:子类 struct + # 在父类 class_members 加载前生成,只有部分继承字段),从 Gen.structs + # 中查找同名且元素数更多的完整定义。 + if (isinstance(pointee, ir.IdentifiedStructType) + and offset is not None + and pointee.elements is not None + and offset >= len(pointee.elements)): + _cur_count: int = len(pointee.elements) + for CN2, ST2 in Gen.structs.items(): + if (isinstance(ST2, ir.IdentifiedStructType) + and ST2.name == pointee.name + and ST2.elements is not None + and len(ST2.elements) > _cur_count + and offset < len(ST2.elements)): + pointee = ST2 + break + if isinstance(pointee, ir.IdentifiedStructType) and pointee.elements is not None and offset is not None and offset < len(pointee.elements): + if isinstance(pointee.elements[offset], ir.VoidType): + return None + return self._gep_and_Load_member(Gen, SelfPtr, offset, Node.attr, ClassName, pointee) + DynamicVarName: str = f"self.{Node.attr}" + if DynamicVarName in Gen.variables: + return Gen._load(Gen.variables[DynamicVarName], name=Node.attr) + return None + + def _handle_attr_enum(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: + """处理枚举成员访问 (VarName 是枚举类型名)""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + SymInfo: Any = self.Trans.SymbolTable.lookup(VarName) + if not SymInfo or not SymInfo.IsEnum: + return None + # 优先用带前缀的 key 查找(避免短名被同名函数/变量覆盖) + FullKey: str = f"{VarName}.{Node.attr}" + MemberInfo: Any = self.Trans.SymbolTable.lookup(FullKey) + if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: + # 回退到短名查找 + MemberInfo = self.Trans.SymbolTable.lookup(Node.attr) + if not MemberInfo or not MemberInfo.IsEnumMember or MemberInfo.EnumName != VarName: + # 最后遍历符号表 + for key, info in self.Trans.SymbolTable.items(): + if info.IsEnumMember and info.EnumName == VarName and key == Node.attr: + if isinstance(info.value, int): + return ir.Constant(Gen._enum_int_type(VarName, info), info.value) + return None + if isinstance(MemberInfo.value, int): + return ir.Constant(Gen._enum_int_type(VarName, MemberInfo), MemberInfo.value) + return None + + def _handle_attr_name(self, Node: ast.Attribute, VarName: str) -> ir.Value | None: + """处理 x.attr 形式 (x 是普通 Name)""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + AttrName: str = Node.attr + + # __doc__ 魔法字符串:函数/结构体的 docstring + if AttrName == '__doc__': + doc_ptr: ir.Value | None = Gen._get_doc_ptr(VarName) + if doc_ptr is not None: + return doc_ptr + + # 先尝试枚举成员 + result: Any = self._handle_attr_enum(Node, VarName) + if result is not None: + return result + + # 尝试 CDefine 常量 + result = self._lookup_cdefine(Gen, VarName, AttrName) + if result is not None: + return result + + # 冗余回退:直接以短名/全名查符号表的 CDefine 条目 + # 覆盖 _ImportedModules 未填充但符号表已注册 CDefine 的场景 + # (例如自举编译期 ast.CONST_INT 在 LlvmGenerator 预扫描阶段 + # 已注册到 SymbolTable,但 _ImportedModules 仍为空)。 + for _cdefine_key in (AttrName, f"{VarName}.{AttrName}"): + _sym: Any = self.Trans.SymbolTable.lookup(_cdefine_key) + if _sym and _sym.IsDefine and _sym.DefineValue is not None: + _const_val: Any = self._make_define_constant(Gen, _sym.DefineValue, AttrName) + if _const_val is not None: + return _const_val + + # 尝试 import 模块全局变量 + result = self._lookup_module_global(Gen, VarName, AttrName) + if result is not None: + return result + + # FakeDuck: 检查是否有影子结构体 a__meta__ (str 变量的 per-instance 字段存储) + # 当 a 是 str 变量时, a 本身是 char*, 但 a__meta__ 存储了 __data__/__mbuddy__ 等字段 + # 直接通过 a.__field__ 访问影子结构体字段 (语法糖) + _fd_meta_name: str = f"{VarName}__meta__" + if _fd_meta_name in Gen.variables and '_str' in Gen.structs: + _fd_offset = Gen._get_member_offset(AttrName, '_str') + if _fd_offset is not None: + _fd_meta_ptr: ir.Value = Gen.variables[_fd_meta_name] + _fd_field_ptr: ir.Value = Gen.builder.gep(_fd_meta_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), _fd_offset)], name=f"{VarName}_{AttrName}_ptr") + return Gen._load(_fd_field_ptr, name=f"{VarName}_{AttrName}_val") + + # 解析结构体类名:若 VarName 是结构体变量,优先走结构体字段访问路径 + # 避免结构体字段名(如 stdout)与全局变量名冲突时被误解析为全局变量 + ClassName: str | None = self._resolve_var_classname(Gen, VarName) + # 查找端键归一:var_struct_class 可能记录裸短名('Tuple'),而 Gen.structs + # 的键是带 class_sha1 前缀的全名(如 '43ec967d0517abc1.Tuple'),或反之。 + # 单级 Name 字段取值(tp.elts)要求 ClassName 能直接定位 Gen.structs 并 + # 加载 stub body 后才能 GEP —— 与 _handle_attr_nested 各分支一致先归一。 + if ClassName and ClassName not in Gen.structs: + _cn_key: str | None = self._find_struct_key_for_class_hint(Gen, ClassName) + if _cn_key: + ClassName = _cn_key + + # 尝试 attr 直接作为全局变量(仅当 VarName 不是结构体变量时) + if not ClassName and AttrName in Gen.module.globals: + gv: Any = Gen.module.globals[AttrName] + if AttrName in Gen.variables and Gen.variables[AttrName] is None: + str_gv_name: str = AttrName + '_str' + if str_gv_name in Gen.module.globals: + str_gv: Any = Gen.module.globals[str_gv_name] + return Gen.builder.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"{AttrName}_str_ptr") + return Gen.builder.load(gv, name=AttrName) + + if not ClassName: + # 单级 Name 字段取值的类名恢复(跨模块 stub 下 var_struct_class 缺失的 + # 兜底):本地变量(如 `tp: rawptr[Tuple] = (rawptr[Tuple])(node)`)可能 + # 因 rawptr[X] 被折叠为 i8* 而漏登记。若变量 alloca / 值本身可辨识为 + # 结构体指针,按实际 pointee 恢复真实 Gen.structs 键并回填,避免属性 + # 访问求值 None 被 fail-fast 拦截(parser.vp `tp_elts = tp.elts` 根因)。 + if (VarName in Gen.variables or VarName in Gen._reg_values + or VarName in Gen._direct_values or VarName in Gen.global_vars): + _recovered: str | None = self._recover_struct_class(Gen, VarName) + if _recovered: + ClassName = _recovered + Gen.var_struct_class[VarName] = _recovered + if getattr(Gen, 'global_struct_class', None) is not None: + Gen.global_struct_class[VarName] = _recovered + if not ClassName: + self._NoteAttrNone(Gen, None, AttrName, + f"类名未解析('{VarName}' 的 var_struct_class 为空且无法由 " + f"alloca/值类型恢复)") + return None + + # 检查 property getter + PropKey: str = f'{ClassName}.{AttrName}' + PropInfo: Any = self.Trans.SymbolTable.lookup(PropKey) + if PropInfo and PropInfo.MetaList and FuncMeta.PROPERTY_GETTER in PropInfo.MetaList: + ObjVal: Any = self.HandleExprLlvm(Node.value) + GetterFunc: Any = Gen._get_function(PropKey) + if GetterFunc and ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + return Gen.builder.call(GetterFunc, [ObjVal], name=f"prop_{PropKey}") + + # 获取类型信息 + TypeInfo: Any = self.Trans.SymbolTable.lookup(ClassName) + IsUnion: bool = TypeInfo.IsUnion if TypeInfo else False + IsCenum: bool = TypeInfo.IsEnum if TypeInfo else False + IsRenum: bool = TypeInfo.IsRenum if TypeInfo else False + + # REnum 处理 + if IsRenum: + if AttrName == 'tag': + ObjVal = self.HandleExprLlvm(Node.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + tag_ptr: Any = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="tag_ptr") + return Gen._load(tag_ptr, name="tag_val") + NestedStructName: str = f"{ClassName}_{AttrName}" + if NestedStructName in Gen.structs: + NestedStructType: Any = Gen.structs[NestedStructName] + ObjVal = self.HandleExprLlvm(Node.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") + + # CEnum 处理 + if IsCenum: + for qname in (f"{ClassName}.{AttrName}", f"{ClassName}_{AttrName}"): + info: Any = self.Trans.SymbolTable.lookup(qname) + if info and info.IsEnumMember and isinstance(info.value, int): + return ir.Constant(Gen._enum_int_type(ClassName, info), info.value) + for key, info in self.Trans.SymbolTable.items(): + if key == AttrName: + if info.IsEnumMember and info.EnumName == ClassName: + if isinstance(info.value, int): + return ir.Constant(Gen._enum_int_type(ClassName, info), info.value) + break + + # CUnion 处理 + if IsUnion: + NestedStructName = f"{ClassName}_{AttrName}" + if NestedStructName in Gen.structs: + NestedStructType = Gen.structs[NestedStructName] + ObjVal = self.HandleExprLlvm(Node.value) + if ObjVal: + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + return Gen.builder.bitcast(ObjVal, ir.PointerType(NestedStructType), name=f"cast_{NestedStructName}") + + # 普通结构体成员访问 + ObjVal = self.HandleExprLlvm(Node.value) + if not ObjVal: + self._NoteAttrNone(Gen, ClassName, AttrName, + f"基座 '{VarName}' 自身求值为空(类名={ClassName!r})") + return None + # char* → struct* cast + if self._is_char_pointer(ObjVal): + if ClassName in Gen.structs: + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + # 二级指针解引用。模块级全局指针变量(如 _g_gen: rawptr[Generator.Generator]) + # 的全局元素类型比实际存值多一层:写入端(main.vp `GenApi._g_gen = gen`)生成 + # 为 bitcast T*→T** 后直接存值,未取地址;读取端必须做其逆运算 bitcast,绝不能 + # 再 load——否则把 T* 当 T** 再解引用,接收者被取成对象首字段的位,方法内读 + # self->Gen 即野指针(ASan: CallGenBase.SetTempDir 读悬空地址崩溃)。 + if (isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType) + and isinstance(Node.value, ast.Name) and Node.value.id in Gen.module.globals): + ObjVal = Gen.builder.bitcast(ObjVal, ObjVal.type.pointee, name=f"cast_{VarName}_global") + elif isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + + # 检查结构体匹配 + struct_match: bool = self._check_struct_match(Gen, ObjVal, ClassName) + if not struct_match and ClassName and VarName: + if VarName in Gen.var_struct_class: + CN: str = Gen.var_struct_class[VarName] + if CN != ClassName and CN in Gen.structs: + ClassName = CN + struct_match = self._check_struct_match(Gen, ObjVal, ClassName) + # 最终回退:var_struct_class 可能被前一个函数污染(如 cur 在不同函数中 + # 分别是 Line/Param/BasicBlock),通过 ObjVal 的实际 pointee 类型恢复正确类名。 + # 但若 var_struct_class 已记录特化名(如 ndarray[double]),且 ObjVal 的 + # pointee 是对应的 opaque 基础类型(如 ndarray),则 bitcast 到特化版本, + # 不覆盖为裸模板名(否则后续方法调用会查找 ndarray.xxx 而非 ndarray[double].xxx) + if not struct_match and isinstance(ObjVal.type, ir.PointerType): + found: Any = Gen.find_struct_by_pointee(ObjVal.type.pointee) + if found: + found_name: str = found[0] + # 检查 var_struct_class 是否已记录 found_name 的特化版本 + preserved: bool = False + if VarName and VarName in Gen.var_struct_class: + cur_spec: str = Gen.var_struct_class[VarName] + if ('[' in cur_spec + and cur_spec.split('[')[0] == found_name + and cur_spec in Gen.structs): + # bitcast ObjVal (opaque base*) 到特化类型指针,保留特化名 + ObjVal = Gen.builder.bitcast( + ObjVal, ir.PointerType(Gen.structs[cur_spec]), + name=f"cast_spec_{cur_spec}") + ClassName = cur_spec + struct_match = True + preserved = True + if not preserved: + ClassName = found_name + struct_match = True + if VarName: + Gen.var_struct_class[VarName] = ClassName + elif isinstance(ObjVal.type.pointee, ir.IdentifiedStructType): + # sha1 冲突 fallback:find_struct_by_pointee 因 sha1 前缀不匹配失败时, + # 用短名匹配,优先选字段最多的完整版本 + pointee_short: str = Gen._extract_short_name(ObjVal.type.pointee.name) + best_elem_count: int = 0 + for CN, ST in Gen.structs.items(): + if isinstance(ST, ir.IdentifiedStructType): + st_short: str = Gen._extract_short_name(ST.name) + if st_short == pointee_short: + elem_count: int = len(ST.elements) if ST.elements else 0 + if elem_count > best_elem_count: + ClassName = CN + best_elem_count = elem_count + struct_match = True + if struct_match and VarName: + # 同样保护特化名:若已记录的特化名的短名基础部分与 ClassName 匹配,保留 + cur_spec2: str = Gen.var_struct_class.get(VarName, '') + if ('[' in cur_spec2 + and cur_spec2.split('[')[0] == ClassName + and cur_spec2 in Gen.structs): + ObjVal = Gen.builder.bitcast( + ObjVal, ir.PointerType(Gen.structs[cur_spec2]), + name=f"cast_spec_{cur_spec2}") + ClassName = cur_spec2 + else: + Gen.var_struct_class[VarName] = ClassName + # 通用指针槽兜底:静态类名(var_struct_class/键归一后)有效且 ObjVal 是指针, + # 但 pointee 非 struct(如 cast 局部变量 tp: rawptr[Tuple] 的槽被建为 i32**, + # Name 求值 load 一次得到 i32*,上述 struct 匹配/回退均不命中 → return None)。 + # 此时按静态类名信任 bitcast 到 struct* 再走成员 GEP(tp.elts 根因: + # var_struct_class='Tuple'、structs 有 'Tuple',仅槽位为通用指针形态)。 + if (not struct_match and ClassName and ClassName in Gen.structs + and isinstance(ObjVal.type, ir.PointerType) + and not isinstance(ObjVal.type.pointee, ir.IdentifiedStructType) + and not isinstance(ObjVal.type.pointee, ir.LiteralStructType)): + ObjVal = Gen.builder.bitcast( + ObjVal, ir.PointerType(Gen.structs[ClassName]), + name=f"cast_slot_{ClassName}") + struct_match = True + if not struct_match: + self._NoteAttrNone( + Gen, ClassName, AttrName, + f"结构体未匹配(ObjVal 类型={ObjVal.type},类名={ClassName!r};" + f"pointee 既非该类 struct,静态类名也未能 bitcast 兜底)") + return None + + # 左值注解靶型提示(见 _apply_target_struct_hint):类不声明该字段时改用注解类 + ClassName = self._apply_target_struct_hint(Gen, AttrName, ClassName) + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, ClassName) + if not ok: + self._NoteAttrNone(Gen, ClassName, AttrName, + f"stub 无 body(Gen.structs[{ClassName!r}] 存在但 elements 为空/opaque)") + return None + + # bitfield 处理 + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(ClassName, {}) + bitfields: Any = Gen.class_member_bitfields.get(ClassName, {}) + if AttrName in bitfield_offsets and bitfields.get(AttrName, 0) > 0: + return self._load_bitfield_member(ObjVal, ClassName, AttrName) + if AttrName in bitfields and bitfields[AttrName] > 0: + if ClassName not in Gen.class_member_bitoffsets: + Gen.class_member_bitoffsets[ClassName] = {} + if AttrName not in Gen.class_member_bitoffsets[ClassName]: + bo: int = 0 + for bf_name, bf_width in bitfields.items(): + if bf_name == AttrName: + break + bo += bf_width + Gen.class_member_bitoffsets[ClassName][AttrName] = bo + return self._load_bitfield_member(ObjVal, ClassName, AttrName) + # 偏移与 GEP 必须**同源**:把"实际将被索引的那个 struct 实例"传给偏移计算。 # 同一类键上可能存在两个实例(同名类 sha1 冲突双注册 / 一方仍 opaque), # 用类键上那份算偏移、用 ObjVal 这份做 GEP ⇒ 整类字段偏移平移 @@ -965,1606 +965,1606 @@ class ExprAttrHandle(BaseHandle): _ObjPointee: Any = ObjVal.type.pointee if isinstance(_ObjPointee, ir.IdentifiedStructType): _StructHint = _ObjPointee - offset = Gen._get_member_offset(AttrName, ClassName, _StructHint) - # 检查 offset 是否超出实际结构体 - if isinstance(ObjVal.type, ir.PointerType): - actual_pointee: Any = ObjVal.type.pointee - if isinstance(actual_pointee, ir.IdentifiedStructType): - actual_elems: Any = actual_pointee.elements - if actual_elems is not None and offset is not None and offset >= len(actual_elems): - self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) - actual_elems = actual_pointee.elements - if actual_elems is not None and offset >= len(actual_elems): - # sha1 冲突修复:ObjVal 的 pointee 可能是字段不全的版本(如 2 字段 Function), - # 而 st(来自 _try_Load_struct_from_stub)可能是字段完整的版本。 - # bitcast ObjVal 到 st 的类型,使 GEP 能正确访问完整字段。 - if st is not None and isinstance(st, ir.IdentifiedStructType) and st.elements is not None and offset < len(st.elements): - ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_load_{ClassName}") - else: - self._NoteAttrNone( - Gen, ClassName, AttrName, - f"槽位越界(_handle_attr_name 侧):字段 '{AttrName}' 需槽位 " - f"{offset},但 ObjVal 的 pointee 只有 {len(actual_elems)} 个元素," - f"st 版结构体也不够({len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'None/opaque'})") - return None - result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st) - return result - - def _recover_struct_class(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: - """由变量的 LLVM 存储类型恢复结构体类键(var_struct_class 缺失时兜底)。 - - 单级 Name 字段取值(如 tp.elts)的本地变量,若其注解 rawptr[X] 被 TypeMerge - builtins 分支折叠成 i8*、且 var_struct_class 未登记 X,属性访问层无法解析 - 类名。此处直接检查 Gen.variables / _reg_values / _direct_values / - global_vars 中该变量的 alloca(或值)指针 pointee:能辨识出 - IdentifiedStruct 就按 pointee 恢复真实 Gen.structs 键(精确匹配失败时按 - 短名找元素最多的完整版本),供调用方回填并继续 GEP 成员访问。不发射任何 - 额外 IR(纯查表)。 - """ - if not VarName: - return None - probe: ir.Type | None = None - vp: Any = Gen.variables.get(VarName) - if vp is not None and isinstance(vp.type, ir.PointerType): - probe = vp.type.pointee - if isinstance(probe, ir.PointerType): - probe = probe.pointee - if probe is None: - for reg in (Gen._reg_values.get(VarName), Gen._direct_values.get(VarName)): - if reg is not None and isinstance(reg.type, ir.PointerType): - probe = reg.type.pointee - if isinstance(probe, ir.PointerType): - probe = probe.pointee - break - if probe is None and VarName in Gen.global_vars and hasattr(Gen, 'module') and VarName in Gen.module.globals: - gv: Any = Gen.module.globals[VarName] - if isinstance(gv, ir.GlobalVariable) and isinstance(gv.type, ir.PointerType): - probe = gv.type.pointee - if isinstance(probe, ir.PointerType): - probe = probe.pointee - if probe is None or not isinstance(probe, ir.IdentifiedStructType): - return None - found: Any = Gen.find_struct_by_pointee(probe) - if found: - return found[0] - return self._pointee_llvm_to_struct_key(Gen, probe) - - def _handle_attr_nested(self, Node: ast.Attribute) -> ir.Value | None: - """处理 a.b.c 嵌套属性访问""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if isinstance(Node.value.value, ast.Name): - ParentVarName: str = Node.value.value.id - ParentAttrName: str = Node.value.attr - # __doc__ 魔法字符串:ClassName.method.__doc__ 或 module.ClassName.__doc__ - if Node.attr == '__doc__': - full_symbol: str = f"{ParentVarName}.{ParentAttrName}" - doc_ptr: ir.Value | None = Gen._get_doc_ptr(full_symbol) - if doc_ptr is not None: - return doc_ptr - # 回退:module.ClassName.__doc__ → 仅用 ClassName 查找 - doc_ptr = Gen._get_doc_ptr(ParentAttrName) - if doc_ptr is not None: - return doc_ptr - enum_result: Any = self._try_resolve_cross_module_enum(ParentVarName, ParentAttrName, Node.attr) - if enum_result is not None: - return enum_result - # 尝试跨模块子模块全局变量解析: a.b.c 其中 a.b 是已知子模块, c 是该模块的全局变量 - imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) - if imported_modules: - full_ModulePath: str = f"{ParentVarName}.{ParentAttrName}" - if full_ModulePath in imported_modules: - result: Any = self._lookup_module_global(Gen, full_ModulePath, Node.attr) - if result is not None: - return result - elif isinstance(Node.value.value, ast.Attribute) and isinstance(Node.value.value.value, ast.Name): - # 三层嵌套:module.ClassName.method.__doc__ - if Node.attr == '__doc__': - ModuleName: str = Node.value.value.value.id - ClassName2: str = Node.value.value.attr - MethodName: str = Node.value.attr - FullSymbol3: str = f"{ModuleName}.{ClassName2}.{MethodName}" - doc_ptr3: ir.Value | None = Gen._get_doc_ptr(FullSymbol3) - if doc_ptr3 is not None: - return doc_ptr3 - # 回退:ClassName.method.__doc__ - doc_ptr3 = Gen._get_doc_ptr(f"{ClassName2}.{MethodName}") - if doc_ptr3 is not None: - return doc_ptr3 - # 三层嵌套:module.submodule.EnumClass.MEMBER - ModuleAlias3: str = f"{Node.value.value.value.id}.{Node.value.value.attr}" - EnumClassName3: str = Node.value.attr - enum_result3: Any = self._try_resolve_cross_module_enum(ModuleAlias3, EnumClassName3, Node.attr) - if enum_result3 is not None: - return enum_result3 - cdefine_result: Any = self._try_resolve_nested_cdefine(Node) - if cdefine_result is not None: - return cdefine_result - ObjVal: Any = self.HandleExprLlvm(Node.value) - if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): - # 取证(纯记账):基座求值为空/非指针时,把**内层链的失败原因**一并挂上—— - # 嵌套读取的内层记录随后会被本节点的记录覆盖,报错点(本节点)与真因(内层某级) - # 相隔数层的可读性问题由此解决(现场:`GenApi._g_gen.TypeGenCType.CTypedefNames`)。 - _prev_an: Any = getattr(Gen, '_LastAttrNone', None) - _chain_an: str = '' - if (isinstance(_prev_an, tuple) and len(_prev_an) >= 3 and _prev_an[2] - and _prev_an[1] and _prev_an[1] != Node.attr): - _chain_an = f";内层字段 '{_prev_an[1]}' 的失败原因:{_prev_an[2]}" - self._NoteAttrNone( - Gen, None, Node.attr, - f"基座 {self._ExprShape(Node.value)} 求值为空或非指针" - f"(type={getattr(ObjVal, 'type', None)}){_chain_an}") - return None - pointee: Any = ObjVal.type.pointee - - # char* → struct* cast + 成员访问 - if isinstance(pointee, ir.IntType) and pointee.width == 8: - # 两级自动解引用链(变量 = 指针.字段.成员,如 blk = func.Blocks.Head) - # 根因修复:跨模块 stub 会把容器/类内指针字段(rawptr[GSList[BasicBlock]] - # 等)退化为 i8*,此处 ObjVal 是刚从结构体字段 load 出的 i8*。必须恢复 - # 该字段声明的元素/特化类(如 'GSList[BasicBlock]')才能 - # bitcast + GEP + load 出成员。否则本函数返回 None → AnnAssign/Assign - # 静默跳过 store → 目标 alloca 永不初始化(_peephole_opt 读未初始化 - # 栈变量随机崩溃,见 __function.vp L783 注释)。 - # 解析顺序:_resolve_chain_result_class 沿链前向解析父表达式(可为任意 - # 级数,如 ve.Alloca.Ty)的结果类;失败回退 _get_attr_class 直查 - # class_member_element_class;再回退 _resolve_attr_parent_hint(与下方 - # struct 分支同一 parent_hint 机制:func 的 var_struct_class + - # class_member_element_class 给出 GSList[BasicBlock] 提示);三者都兼容 - # 裸名/带 sha1 前缀的键。 - AttrClassName: str | None = self._resolve_chain_result_class(Node.value, Gen) - if not AttrClassName: - AttrClassName = self._get_attr_class(Node.value, Gen) - if not AttrClassName: - AttrClassName = self._resolve_attr_parent_hint(Node.value, Gen) - if not AttrClassName: - # 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory): - # 上面三个解析器都按「字段所属类」思路找,对「模块别名.变量」返回 None - # (attr_class=None 即此处的 key-miss 现场,StructGenMixin.vp:139 与 - # ClassGenVTable.vp:99 根因)。而该变量的类已由 HandlesImports 的 - # _EmitExternalGlobalDeclLlvm / HandlesAnnAssign 登记在 - # var_struct_class / global_struct_class(键为**裸变量名**)⇒ 直读。 - _mva: ast.AST | None = Node.value - if isinstance(_mva, ast.Attribute) and isinstance(_mva.value, ast.Name): - _mname: str = _mva.value.id - _sha1_map2 = getattr(Gen, 'ModuleSha1Map', {}) or {} - _imported2 = getattr(self.Trans, '_ImportedModules', None) or [] - _aliases2 = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {} - try: - _rmod: str = self.Trans.SymbolTable.resolve_alias(_mname) - except Exception: - _rmod = _mname - _is_mod2: bool = (_mname in _sha1_map2 or _rmod in _sha1_map2 - or _mname in _imported2 or _rmod in _imported2 - or _mname in _aliases2 or _rmod in _aliases2) - if not _is_mod2: - for _k2 in _sha1_map2: - if _k2 == _mname or _k2.endswith(f'.{_mname}'): - _is_mod2 = True - break - if _is_mod2: - AttrClassName = (getattr(Gen, 'var_struct_class', {}).get(_mva.attr) - or getattr(Gen, 'global_struct_class', {}).get(_mva.attr)) - ResolvedKey: str | None = self._find_struct_key_for_class_hint(Gen, AttrClassName) - # 左值注解靶型提示:解析出的类不声明该字段时,用注解类兜一次(两条件同时成立才改判) - ResolvedKey = self._apply_target_struct_hint(Gen, Node.attr, ResolvedKey) - if ResolvedKey is None or ResolvedKey not in Gen.structs: - self._NoteAttrNone( - Gen, AttrClassName, Node.attr, - f"基座字段的所属类未能在 Gen.structs 命中键(类名提示={AttrClassName!r}," - f"structs 键数={len(Gen.structs)})") - return None - st: Any - ok: bool - st, ok = self._try_Load_struct_from_stub(Gen, ResolvedKey) - if not isinstance(st, ir.IdentifiedStructType): - self._NoteAttrNone(Gen, ResolvedKey, Node.attr, - f"类 '{ResolvedKey}' 的 struct 实例类型异常({type(st).__name__})") - return None - if st.elements is None: - # stub 仍 opaque(如跨模块泛型仅声明):走泛型特化候选访问 - spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st, Node, ResolvedKey, AttrClassName) - if spec_val is not None: - return spec_val - self._NoteAttrNone( - Gen, ResolvedKey, Node.attr, - f"类 '{ResolvedKey}' 的 body 仍为 opaque(elements=None),且无泛型特化成员可访问") - return None - CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_{ResolvedKey}") - if Node.attr in Gen.class_member_bitoffsets.get(ResolvedKey, {}): - return self._load_bitfield_member(CastedObj, ResolvedKey, Node.attr) - offset: int | None = Gen._get_member_offset(Node.attr, ResolvedKey) - if offset is None: - self._NoteAttrNone( - Gen, ResolvedKey, Node.attr, - f"字段 '{Node.attr}' 在类 '{ResolvedKey}' 中取不到偏移" - f"(成员表 {len(Gen.class_members.get(ResolvedKey) or [])} 项 / body {len(st.elements)} 槽)") - return None - if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType): - self._NoteAttrNone( - Gen, ResolvedKey, Node.attr, - f"字段 '{Node.attr}' 的槽 {offset} 是 void(占位槽:类型信息缺失)") - return None - return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, ResolvedKey, st) - - # 已知结构体类型 → 直接成员访问 - if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - # 父上下文特化提示:Node.value 是 Attribute(Name, attr)(如 - # func.Blocks)时,从 var_struct_class/global_struct_class + - # class_member_element_class 取字段声明的特化名(如 'GSList[BasicBlock]'), - # 避免在所有 GSList[T] 都有 Head/Tail/Count 字段时选错特化版本。 - # 与 char* 分支共用 _resolve_attr_parent_hint(兼容裸名/sha1 前缀键); - # 父表达式为多级链(Node.value 是 Attribute)时 _resolve_attr_parent_hint - # 无法取提示,用 _resolve_chain_result_class 沿链前向解析补充。 - parent_hint: str | None = self._resolve_attr_parent_hint(Node, Gen) - if not parent_hint and isinstance(Node.value, ast.Attribute): - parent_hint = self._resolve_chain_result_class(Node.value, Gen) or parent_hint - # 同短名候选:**唯一**时才放行(两个及以上同名类无法判定是哪一个, - # 宁可留痕失败也不能选错布局——同名类冲突在本仓一律以 sha1 全名区分)。 - _UniqShortCN: str | None = None - if isinstance(pointee, ir.IdentifiedStructType): - _PShort2: str = (getattr(pointee, 'name', '') or '').rsplit('.', 1)[-1] - _ShortCNs: list[str] = [ - _c5 for _c5, _s5 in Gen.structs.items() - if isinstance(_s5, ir.IdentifiedStructType) - and (getattr(_s5, 'name', '') or '').rsplit('.', 1)[-1] == _PShort2 - ] - if len(_ShortCNs) == 1: - _UniqShortCN = _ShortCNs[0] - for CN, ST in Gen.structs.items(): - if ((pointee == ST) or self._PointeeMatchesStruct(pointee, ST) - or (_UniqShortCN is not None and CN == _UniqShortCN)): - st, ok = self._try_Load_struct_from_stub(Gen, CN) - if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: - spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN, parent_hint) - if spec_val is not None: - return spec_val - break - bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) - if Node.attr in bitfield_offsets: - return self._load_bitfield_member(ObjVal, CN, Node.attr) - offset = Gen._get_member_offset(Node.attr, CN) - if offset is None: - self._NoteAttrNone( - Gen, CN, Node.attr, - f"字段 '{Node.attr}' 在类 '{CN}' 中取不到偏移" - f"(成员表 {len(Gen.class_members.get(CN) or [])} 项 / " - f"body {len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'opaque'} 槽)") - break - # 泛型基础类型字段特化(根本修复): - # 当字段声明为特化泛型(如 GSList[BasicBlock])时,基础类型(GSList)的 - # Head/Tail/Count 字段类型是泛型占位 T*。直接按基础类型访问会返回 T*, - # 导致变量类型被污染(后续字段访问/循环体翻译静默失败,见 - # llvmlite __function.vp _find_max_ssa_number 死循环根因)。 - # 用 class_member_element_class 记录的特化类型(parent_hint)重新访问, - # 返回真实元素类型(如 BasicBlock*)。 - if (parent_hint is not None - and isinstance(st, ir.IdentifiedStructType) - and st.elements is not None and offset < len(st.elements) - and isinstance(st.elements[offset], ir.PointerType) - and isinstance(st.elements[offset].pointee, ir.IdentifiedStructType)): - _field_short: str = (st.elements[offset].pointee.name or '').split('.')[-1] - if (len(_field_short) == 1 and _field_short.isalpha() and _field_short.isupper()): - _hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint - for CN2, ST2 in Gen.structs.items(): - if not isinstance(ST2, ir.IdentifiedStructType): - continue - if ST2 is ST: - continue - if ST2.elements is None: - continue - _spec_short: str = CN2.split('.')[-1] if '.' in CN2 else CN2 - if _spec_short == _hint_short: - _casted: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(ST2), name=f"cast_spec_{CN}") - _off2: int | None = Gen._get_member_offset(Node.attr, CN2) - if _off2 is not None: - return self._gep_and_Load_member(Gen, _casted, _off2, Node.attr, CN2, ST2) - break - return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) - # 结构体分支未命中任何类:pointee 与 Gen.structs 全部键都不等(同名类双实例 / - # 该类未注册)——这是"基座类型已知却读不到字段"的最后一种来源,必须留痕。 - _PShortN: str = (getattr(pointee, 'name', '') or '').rsplit('.', 1)[-1] - _SameShort: list[str] = [] - for _cn3, _st3 in Gen.structs.items(): - if not isinstance(_st3, ir.IdentifiedStructType): - continue - if (getattr(_st3, 'name', '') or '').rsplit('.', 1)[-1] == _PShortN: - _SameShort.append(_cn3) - self._NoteAttrNone( - Gen, None, Node.attr, - f"基座 pointee {pointee} 未匹配 Gen.structs 中任何 struct(键数={len(Gen.structs)})" - f";分级匹配(身份 / IR 全名 / 同 sha1 前缀)与「同短名 '{_PShortN}' 唯一候选」" - f"**全部未命中**,同短名候选={_SameShort if _SameShort else '无'}" - f"(候选 >1 时会被主动拒绝,避免选错同名类)" - f"——即该类在本 Gen 的 structs 里没有可用实例(另一 Gen/缓存的实例漏进来了," - f"或该类从未注册)" - f";基座形态={self._ExprShape(Node.value)}") - return None - - def _handle_attr_call_result(self, Node: ast.Attribute) -> ir.Value | None: - """处理 func().attr 属性访问""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - # Check if this is c.Deref(...) — we need the pointer, not the Loaded value - ObjVal: Any = None - is_cderef: bool = (isinstance(Node.value, ast.Call) and - isinstance(Node.value.func, ast.Attribute) and - isinstance(Node.value.func.value, ast.Name) and - Node.value.func.value.id == 'c' and - Node.value.func.attr == 'Deref' and - Node.value.args) - if is_cderef: - deref_arg: ast.expr = Node.value.args[0] - inner_val: Any = self.HandleExprLlvm(deref_arg) - if inner_val and isinstance(inner_val.type, ir.PointerType): - if isinstance(inner_val.type.pointee, ir.PointerType): - ObjVal = Gen._load(inner_val, name="deref_load_ptr") - else: - ObjVal = inner_val - if ObjVal is None: - ObjVal = self.HandleExprLlvm(Node.value) - # 联合类型转换 (X | t.CPtr)(arg) 的属性访问(治本修复): - # TPC 联合转换对非 CType 类(如 ast.Name / ast.Call)目标类型解析失败, - # 结果可能退回 Ptr(i8) 或错误类型,导致下方 find_struct_by_pointee 失败、 - # 属性访问被静默丢弃——((ast.Name|t.CPtr)(ov)).id 编译后 store 0 次 - # (with 语句 asname 未绑定、pool 未定义根因)。从联合 BinOp 提取真实 - # 基础类并 bitcast 到 Ptr(类) 再访问成员。 - # - # 该形态的每个 return None 都改成 fail-fast(见 _FailFastAttrCallBase): - # `(T | t.CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `T*(x)` 统一 - # 产成该 Call),返回 None 会让调用方只报"表达式编译期求值为空",看不出是 - # 类名没被当成类、结构体不透明、还是成员偏移取不到(现场:TransPyV - # ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`,而同一函数前三条 - # `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 - _IsUnionCastBase: bool = (isinstance(Node.value, ast.Call) - and isinstance(Node.value.func, ast.BinOp) - and isinstance(Node.value.func.op, ast.BitOr)) - _UnionCastCls: str | None = None - if _IsUnionCastBase: - _UnionCastCls = self._extract_union_base_class(Node.value.func) - if ObjVal is not None and _UnionCastCls and _UnionCastCls in Gen.structs: - target_ty: Any = ir.PointerType(Gen.structs[_UnionCastCls]) - if isinstance(ObjVal.type, ir.PointerType): - if ObjVal.type != target_ty: - ObjVal = Gen.builder.bitcast(ObjVal, target_ty, name="cast_union_attr") - elif isinstance(ObjVal.type, ir.IntType): - ObjVal = Gen.builder.inttoptr(ObjVal, target_ty, name="int2ptr_union_attr") - if not ObjVal: - self._FailFastAttrCallBase("基座强转调用求值为空(HandleExprLlvm 返回 None)", - Node, Gen, _IsUnionCastBase, _UnionCastCls, None) - return None - if not isinstance(ObjVal.type, ir.PointerType): - self._FailFastAttrCallBase("基座强转结果不是指针类型", - Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) - return None - pointee: Any = ObjVal.type.pointee - if isinstance(pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr") - pointee = ObjVal.type.pointee - found: Any = Gen.find_struct_by_pointee(pointee) - if not found: - self._FailFastAttrCallBase("被指结构体未在 structs 登记(find_struct_by_pointee 未命中)", - Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) - return None - CN: str - ST: Any - CN, ST = found - st: Any - ok: bool - st, ok = self._try_Load_struct_from_stub(Gen, CN) - if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: - spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) - if spec_val is not None: - return spec_val - self._FailFastAttrCallBase("结构体不透明(stub / .vpi 未加载出字段)", - Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) - return None - bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) - if Node.attr in bitfield_offsets: - return self._load_bitfield_member(ObjVal, CN, Node.attr) - offset: int | None = Gen._get_member_offset(Node.attr, CN) - if offset is None: - self._FailFastAttrCallBase("成员偏移取不到(class_members / 继承链均未命中)", - Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) - return None - return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) - - def _FailFastAttrCallBase(self, Stage: str, Node: ast.Attribute, Gen: Any, - IsUnionCastBase: bool, UnionCls: str | None, - ObjVal: Any) -> None: - """`(T | t.CPtr)(x).attr` 属性访问失败时的 fail-fast(非该形态时静默返回,行为不变)。 - - `(T | t.CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `(T*)(x)` / `T*(x)` - 统一产成该 Call),其属性访问必须有值产出。此前一律 `return None`,调用方只能 - 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",把"类名没被当成类 / - 结构体不透明 / 成员偏移取不到"三种完全不同的根因混成一条报文 - (现场:TransPyV ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`)。 - 此处逐项列出判定依据,直接指认失败环节。 - """ - if not IsUnionCastBase: - return - from lib.core.LLVMCG.FuncGen import FailFastError - _Parts: list[str] = [ - f"属性 '{Node.attr}' 的基座是强转调用 '{self._ExprText(Node.value)}'," - f"失败环节:{Stage}", - f"联合基类名={UnionCls!r}" - f";短名 in structs={'是' if (UnionCls and UnionCls in Gen.structs) else '否'}" - f";structs 同短名键=" - f"{[k for k in Gen.structs if UnionCls and k.split('.')[-1] == UnionCls][:6]}" - f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(UnionCls)!r}" - f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(UnionCls)!r}", - ] - if ObjVal is None: - _Parts.append("基座求值结果=None") - else: - _Parts.append(f"基座求值类型={ObjVal.type!r}") - if isinstance(ObjVal.type, ir.PointerType): - _Pt: Any = ObjVal.type.pointee - _Parts.append(f"被指类型={_Pt!r};elements=" - f"{getattr(_Pt, 'elements', 'n/a')}") - _Found: Any = Gen.find_struct_by_pointee(_Pt) - _Parts.append( - f"find_struct_by_pointee={'命中 ' + str(_Found[0]) if _Found else '未命中'}") - if _Found: - _CN: str = _Found[0] - _Parts.append( - f"class_members[{_CN!r}]=" - f"{[m[0] for m in Gen.class_members.get(_CN, [])][:12]}" - f"({len(Gen.class_members.get(_CN, []))} 项)" - f";成员 '{Node.attr}' 偏移={self._SafeOffset(Gen, Node.attr, _CN)!r}") - if UnionCls: - _Parts.append( - f"class_members[{UnionCls!r}]=" - f"{[m[0] for m in Gen.class_members.get(UnionCls, [])][:12]}" - f"({len(Gen.class_members.get(UnionCls, []))} 项)" - f";class_parent={getattr(Gen, 'class_parent', {}).get(UnionCls)!r}") - raise FailFastError(";".join(_Parts)) - - @staticmethod - def _SafeOffset(Gen: Any, Attr: str, ClassName: str) -> Any: - """取证用成员偏移查询:本身绝不能再抛异常掩盖原始故障。""" - try: - return Gen._get_member_offset(Attr, ClassName) - except Exception as _E: - return f"<查询异常 {type(_E).__name__}: {_E}>" - - def _extract_union_base_class(self, NodeFunc: ast.BinOp) -> str | None: - """从 (X | t.CPtr) 联合 BinOp 提取第一个非指针/非修饰符的类名 - - X 可以是任意类(如 ast.Name / ast.Call / memhub.MemPool),不限于 - t.CType 子类——联合转换 (X | t.CPtr)(arg) 的属性访问需要真实类名 - 才能 bitcast 到 Ptr(类) 并定位成员偏移。 - """ - if not isinstance(NodeFunc, ast.BinOp) or not isinstance(NodeFunc.op, ast.BitOr): - return None - skip_names = { - 'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', - 'CStatic', 'CExtern', 'CExport', 'CAuto', 'CRegister', - 'CDefine', 'CPass', 'CTypeDefault', 'State', - 'BigEndian', 'LittleEndian', - } - operands: list = [] - - def _collect(node: ast.AST) -> None: - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - _collect(node.left) - _collect(node.right) - else: - operands.append(node) - - _collect(NodeFunc) - for op in operands: - name: str | None = None - if isinstance(op, ast.Attribute): - name = op.attr - elif isinstance(op, ast.Name): - name = op.id - if name and name not in skip_names: - return name - return None - - def _handle_attr_subscript_result(self, Node: ast.Attribute) -> ir.Value | None: - """处理 arr[i].attr 属性访问""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - ObjVal: Any = self.HandleExprLlvm(Node.value) - if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): - return None - pointee: Any = ObjVal.type.pointee - if not isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - return None - found: Any = Gen.find_struct_by_pointee(pointee) - if not found: - return None - CN: str - ST: Any - CN, ST = found - st: Any - ok: bool - st, ok = self._try_Load_struct_from_stub(Gen, CN) - if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: - spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) - if spec_val is not None: - return spec_val - return None - offset: int | None = Gen._get_member_offset(Node.attr, CN) - if offset is None: - return None - return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) - - def _handle_attr_fallback(self, Node: ast.Attribute) -> ir.Value | None: - """兜底:对任意值尝试指针解引用后查找结构体成员""" - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - ObjVal: Any = self.HandleExprLlvm(Node.value) - if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): - return None - pointee: Any = ObjVal.type.pointee - if isinstance(pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr_fallback") - pointee = ObjVal.type.pointee - if not isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - return None - found: Any = Gen.find_struct_by_pointee(pointee) - if not found: - return None - CN: str - ST: Any - CN, ST = found - st: Any - ok: bool - st, ok = self._try_Load_struct_from_stub(Gen, CN) - if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: - spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) - if spec_val is not None: - return spec_val - return None - offset: int | None = Gen._get_member_offset(Node.attr, CN) - if offset is None: - return None - return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) - - # ========== 主入口 ========== - - def _HandleAttributeLlvm(self, Node: ast.Attribute) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - - if isinstance(Node.value, ast.Name): - VarName: str = Node.value.id - # 处理 t/c import 的别名重定向 - t_c_imported: Any = getattr(self.Trans, '_t_c_imported_names', {}) - if VarName != 'self' and VarName in t_c_imported: - src_module: str - src_name: str - src_module, src_name = t_c_imported[VarName] - virtual_attr: ast.Attribute = ast.Attribute( - value=ast.Name(id=src_module, ctx=ast.Load()), - attr=src_name, ctx=ast.Load() - ) - ast.copy_location(virtual_attr, Node) - return self._HandleAttributeLlvm(ast.Attribute( - value=virtual_attr, attr=Node.attr, ctx=ast.Load() - )) - if VarName == 'self': - return self._handle_attr_self(Node, VarName) - return self._handle_attr_name(Node, VarName) - - elif isinstance(Node.value, ast.Attribute): - return self._handle_attr_nested(Node) - - elif isinstance(Node.value, ast.Call): - return self._handle_attr_call_result(Node) - - elif isinstance(Node.value, ast.Subscript): - return self._handle_attr_subscript_result(Node) - - # 兜底 - return self._handle_attr_fallback(Node) - - # ========== HandleSubscript 及其辅助 ========== - - def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - # pointee-volatile(rawptr[t.CVolatile | T])基址:下标 GEP 名带基址名, - # 顶层 _load/_store 经 BaseGen._IsPtrVolatile 回查登记名,使其 load/store - # 带 LLVM volatile。基址名取不到时后缀为空 ⇒ GEP 名仍是 "subscript", - # 既有代码 IR 逐字节不变。 - _PVSuffix: str = Gen._SubscriptSuffix(Node.value) - # 查询数组元素字节序 - arr_byte_order: str = '' - if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders: - arr_byte_order = Gen.local_var_byteorders[Node.value.id] - ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) - if ClassName and Gen._has_function(f'{ClassName}.__getitem__'): - obj_val: Any = self.HandleExprLlvm(Node.value) - idx_val: Any = self.HandleExprLlvm(Node.slice) - if obj_val and idx_val: - # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 - # 需要先 inttoptr 转换为 i8*,再 bitcast 到目标 struct 指针 - if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: - obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") - if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: - obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64: - idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx") - _getitem_func: Any = Gen._get_function(f'{ClassName}.__getitem__') - # 处理泛型特化类型不匹配:obj_val 可能是通用类型,而函数期望特化类型 - if _getitem_func.ftype.args and _getitem_func.ftype.args[0] != obj_val.type: - _expected_type = _getitem_func.ftype.args[0] - if isinstance(_expected_type, ir.PointerType) and isinstance(obj_val.type, ir.PointerType): - obj_val = Gen.builder.bitcast(obj_val, _expected_type, name=f"cast_{ClassName}_getitem") - result: Any = Gen.builder.call(_getitem_func, [obj_val, idx_val], name=f"call_{ClassName}.__getitem__") - return result - ValueVal: Any = self.HandleExprLlvm(Node.value) - if not ValueVal: - return None - IndexVal: Any = self.HandleExprLlvm(Node.slice) - if not IndexVal: - return None - if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: - Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx") - IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx") - if isinstance(ValueVal.type, ir.IntType): - var_ptr: Any = self._get_int_ptr(Node.value) - if var_ptr: - i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") - ptr: Any = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") - return Gen._load(ptr, name="int_subscript_val") - return None - # 当变量本身是二级指针(如 elem_ptr: T | t.CPtr = AST**), - # 但 HandleExprLlvm 已自动 load 返回 AST*(一级指针), - # 需要检查 Gen.variables 中的原始 alloca 类型, - # 若是二级指针且存储的是结构体指针,应 bitcast ValueVal 为 AST** 后 GEP 再 load, - # 避免 GEP AST 结构体数组返回错误的 AST*(指向存储位置而非实际对象)。 - # 仅当 var_ptr_element 标记为 True 时触发(由 AnnAssign 检测 (S|t.CPtr)|t.CPtr 注解设置), - # 防止 pts: Point | t.CPtr(简单结构体指针)错误走二级指针路径。 - if (isinstance(Node.value, ast.Name) - and isinstance(ValueVal.type, ir.PointerType) - and isinstance(ValueVal.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))): - VarName: str = Node.value.id - _vpe_load: bool = (getattr(Gen, 'var_ptr_element', {}).get(VarName, False) - or getattr(Gen, 'global_var_ptr_element', {}).get(VarName, False)) - if _vpe_load and VarName in Gen.variables and Gen.variables[VarName] is not None: - VarAlloca: ir.Value = Gen.variables[VarName] - if (isinstance(VarAlloca.type, ir.PointerType) - and isinstance(VarAlloca.type.pointee, ir.PointerType)): - # 原始 alloca 是二级指针,ValueVal 是已 load 的一级结构体指针 - # 该指针指向数据缓冲区,缓冲区存储的是 AST* 指针数组, - # 需 bitcast ValueVal 为 AST** 后 GEP 指针数组再 load 出 AST* - PtrToStruct: ir.Type = ValueVal.type.pointee - BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, - ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp_load") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_load") - ElemPtrLoad: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name="subscript_pp_load") - return Gen._load(ElemPtrLoad, name="ptr_elem_load") - if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") - ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") - if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8: - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - if isinstance(ValueVal.type, ir.PointerType): - pointee: Any = ValueVal.type.pointee - if isinstance(pointee, ir.ArrayType): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") - elem_type: Any = pointee.element - if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - else: - if isinstance(pointee, ir.IntType) and pointee.width == 8: - # 指针槽变量优先(8fbba 修复):T|t.CPtr 指针槽(elem_ptr 指向 - # 8 字节指针数组,如 _list.vp 泛型方法体)→ i8** load i8*。 - # 必须放在 _infer_element_struct_class 之前——泛型 T 未解析时 - # var_struct_class 误注册 llvmlite.Value,若先推断 elem_class - # 会 cast 到 Value*,与函数声明 i8* 不匹配(llc 报错)。 - if isinstance(Node.value, ast.Name): - _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) - or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) - if _ptr_elem_flag: - CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") - ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") - return Gen._load(ElemPtr, name="str_ptr_load") - elem_class: str | None = self._infer_element_struct_class(Node.value, Gen) - if elem_class and elem_class in Gen.structs: - CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index") - ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name=f"subscript{_PVSuffix}") - if isinstance(pointee, ir.IntType): - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - return ElemPtr - # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) - # elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] 应加载 8 字节 - if isinstance(Node.value, ast.Name): - _ptr_elem_flag = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) - or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) - if _ptr_elem_flag: - CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") - ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") - return Gen._load(ElemPtr, name="str_ptr_load") - arr_type: Any = self._infer_array_member_type(Node.value, Gen) - if arr_type and isinstance(arr_type, ir.ArrayType): - arr_ptr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(arr_type), name=f"cast_arr_subscript") - zero = ir.Constant(ir.IntType(32), 0) - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - arr_elem_type: Any = arr_type.element - if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - elif isinstance(ValueVal.type, ir.ArrayType): - zero = ir.Constant(ir.IntType(32), 0) - if isinstance(Node.value, ast.Name): - VarName: str = Node.value.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: Any = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - var_arr_elem_type: Any = VarPtr.type.pointee.element - if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - if isinstance(Node.value, ast.Attribute): - AttrPtr: Any = self._get_attr_ptr(Node.value) - if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - attr_arr_elem_type: Any = AttrPtr.type.pointee.element - if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - if isinstance(Node.value, ast.Subscript): - InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) - if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): - inner_pointee: Any = InnerPtr.type.pointee - if isinstance(inner_pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") - inner_elem_type: Any = inner_pointee.element - if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") - Gen._store(ValueVal, arr_alloc) - ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") - alloc_arr_elem_type: Any = ValueVal.type.element - if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): - return ElemPtr - return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) - return None - - def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - # 同 _HandleSubscriptLlvm:pointee-volatile 基址名后缀(空后缀 ⇒ 名不变) - _PVSuffix: str = Gen._SubscriptSuffix(Node.value) - ValueVal: Any = self.HandleExprLlvm(Node.value) - if not ValueVal: - return None - IndexVal: Any = self.HandleExprLlvm(Node.slice) - if not IndexVal: - return None - if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: - Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx_ptr") - IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_ptr") - if isinstance(ValueVal.type, ir.PointerType): - pointee: Any = ValueVal.type.pointee - if isinstance(pointee, ir.ArrayType): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - elif isinstance(pointee, ir.PointerType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr") - ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - else: - # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr) - # elem_ptr 是 i8* 但实际指向 i8* (指针数组), 应按 8 字节步长 GEP - if isinstance(Node.value, ast.Name): - _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) - or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) - if _ptr_elem_flag: - CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr") - ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") - ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - elif isinstance(ValueVal.type, ir.IntType): - var_ptr: Any = self._get_int_ptr(Node.value) - if var_ptr: - i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") - ElemPtr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") - return ElemPtr - elif isinstance(ValueVal.type, ir.ArrayType): - zero = ir.Constant(ir.IntType(32), 0) - if isinstance(Node.value, ast.Name): - VarName: str = Node.value.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: Any = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - if isinstance(Node.value, ast.Subscript): - InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) - if InnerPtr: - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - if isinstance(Node.value, ast.Attribute): - AttrPtr: Any = self._get_attr_ptr(Node.value) - if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): - if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: - if IndexVal.type.width < 32: - IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") - else: - IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") - ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") - Gen._store(ValueVal, arr_alloc) - ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") - return ElemPtr - return None - - # ========== _get_attr_ptr / _get_int_ptr / _get_attr_class ========== - - def _ConcretePointeeFor(self, Gen: LlvmCodeGenerator, ClassName: str | None, - Pointee: Any) -> Any: - """取该类的**已具化实例**(GEP 基址可用的那份):类键那份 → 同短名孪生体 → None。 - - 为什么需要:同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名 - '.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份" - 并不保证一致;typed pointer 下 GEP 必须在**编译期**从 pointee 算元素类型, - 撞 opaque 即抛裸 TypeError('NoneType' object is not subscriptable)。 - - 判据(全仓同族点同一套,不引入新判据): - ① 类键那份(`Gen.structs[ClassName]`)可经 `_TryLoadStructFromStub` 补出 body; - ② 否则按**同短名**取一份已具化实例(`Gen._FindConcreteTwin`,槽数最多); - ③ 都拿不到 ⇒ None(**不兜底**,交 IR 边界守卫 fail-fast 报同短名实例清单)。 - """ - _Twin: Any = None - if ClassName: - _ST: Any = Gen.structs.get(ClassName) - if isinstance(_ST, ir.IdentifiedStructType) and not _ST.elements: - self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) - _ST = Gen.structs.get(ClassName, _ST) - if isinstance(_ST, ir.IdentifiedStructType) and _ST.elements: - _Twin = _ST - if _Twin is None and isinstance(Pointee, ir.IdentifiedStructType): - try: - _Twin = Gen._FindConcreteTwin(Pointee) - except Exception: - _Twin = None - return _Twin - - def _ConcretizeGepBase(self, Gen: LlvmCodeGenerator, Ptr: Any, ClassName: str | None, - Tag: str) -> tuple[Any, Any]: - """GEP 基址收口:pointee 为 **opaque** 具名结构体时 bitcast 到已具化实例。 - - 判据与实例来源见 `_ConcretePointeeFor`;返回 `(基址指针, 该类的已具化实例)`, - 第二个返回值同时用作 `_get_member_offset` 的 StructHint —— 偏移与 GEP 必须同源 - (hint 只接受同类,见 FuncGen._member_base)。pointee 非 opaque 时原样返回, - 对既有正确路径零变化;都取不到实例时同样原样返回(不兜底,由守卫报根因)。 - """ - if not isinstance(getattr(Ptr, 'type', None), ir.PointerType): - return Ptr, None - _Pointee: Any = Ptr.type.pointee - if not isinstance(_Pointee, ir.IdentifiedStructType) or _Pointee.elements is not None: - return Ptr, _Pointee - _Twin: Any = self._ConcretePointeeFor(Gen, ClassName, _Pointee) - if _Twin is None: - return Ptr, _Pointee - return (Gen.builder.bitcast(Ptr, ir.PointerType(_Twin), name=Tag), _Twin) - - def _get_attr_ptr(self, Node: ast.Attribute) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if not isinstance(Node, ast.Attribute): - return None - if isinstance(Node.value, ast.Name): - VarName: str = Node.value.id - if VarName == 'self': - ClassName: str | None = self.Trans._CurrentCpythonObjectClass - if ClassName: - SelfVar: Any = Gen._get_var_ptr('self') - if SelfVar: - SelfPtr: Any = Gen._load(SelfVar, name="self") - if isinstance(SelfPtr.type, ir.PointerType): - # 基址收口(见 _ConcretizeGepBase):opaque 基址先补 body/取孪生体 - # 再 GEP。现场(2026-09-30):includes/ast/astaux.vp:165 - # `self.arg = _copy_str(pool, arg)`,GEP 名 'arg',基址 pointee 是 - # opaque 的 %"8312ce86a2fd82c0.Keyword"*,而同短名的 'Keyword' - # 键上那份已具化(10 槽)。 - SelfPtr, _SelfPointee = self._ConcretizeGepBase( - Gen, SelfPtr, ClassName, f"cast_self_{ClassName}") - offset: int | None = Gen._get_member_offset(Node.attr, ClassName, _SelfPointee) - if offset is None: - return None - MemberPtr: Any = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) - return MemberPtr - ClassName = Gen.var_struct_class.get(VarName) - if not ClassName and getattr(Gen, 'global_struct_class', None): - ClassName = Gen.global_struct_class.get(VarName) - if not ClassName: - imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) - resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) - if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules): - AttrName: str = Node.attr - ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) - candidates: list[str] = [VarName, resolved_mod] - for mod_name in imported_modules: - if mod_name.endswith(f".{VarName}") or mod_name == VarName: - if mod_name not in candidates: - candidates.append(mod_name) - for cand in candidates: - sha1: str | None = ModuleSha1Map.get(cand) - if sha1: - prefixed: str = f"{sha1}.{AttrName}" - if prefixed in Gen.module.globals: - return Gen.module.globals[prefixed] - if AttrName in Gen.module.globals: - gv: Any = Gen.module.globals[AttrName] - if isinstance(gv, ir.GlobalVariable): - return gv - if ClassName and ClassName in Gen.structs: - VarPtr: Any = Gen.variables.get(VarName) - if VarPtr: - ObjVal: Any = VarPtr - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") - if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]: - # 基址收口(见 _ConcretizeGepBase):类键那份可能仍 opaque ⇒ 补 body / - # 取同短名孪生体后再 GEP(偏移同源)。 - ObjVal, ST = self._ConcretizeGepBase(Gen, ObjVal, ClassName, f"cast_{ClassName}") - offset = Gen._get_member_offset(Node.attr, ClassName, ST) - if offset is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: - MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) - return MemberPtr - elif isinstance(Node.value, ast.Attribute): - InnerPtr: Any = self._get_attr_ptr(Node.value) - if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): - pointee: Any = InnerPtr.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found: Any = Gen.find_struct_by_pointee(pointee) - if found: - CN: str - ST: Any - CN, ST = found - # 基址收口(见 _ConcretizeGepBase):链尾 pointee 可能仍是类键上那份 - # opaque 实例 ⇒ 补 body / 取同短名孪生体后再 GEP(偏移同源)。 - InnerPtr, ST = self._ConcretizeGepBase(Gen, InnerPtr, CN, f"cast_{CN}") - offset = Gen._get_member_offset(Node.attr, CN, ST) - if offset is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: - MemberPtr = Gen.builder.gep(InnerPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) - return MemberPtr - elif isinstance(pointee, ir.IntType) and pointee.width == 8: - AttrClassName: str | None = self._get_attr_class(Node.value, Gen) - if AttrClassName and AttrClassName in Gen.structs: - CastedObj: Any = Gen.builder.bitcast(InnerPtr, ir.PointerType(Gen.structs[AttrClassName]), name=f"cast_{AttrClassName}") - # 类键那份可能仍 opaque ⇒ 统一收口(见 _ConcretizeGepBase) - CastedObj, ST2 = self._ConcretizeGepBase( - Gen, CastedObj, AttrClassName, f"cast_twin_{AttrClassName}") - offset = Gen._get_member_offset(Node.attr, AttrClassName, ST2) - if offset is not None: - MemberPtr = Gen.builder.gep(CastedObj, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) - return MemberPtr - elif isinstance(Node.value, ast.Subscript): - SubPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) - if SubPtr and isinstance(SubPtr.type, ir.PointerType): - pointee = SubPtr.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found = Gen.find_struct_by_pointee(pointee) - if found: - CN, ST = found - # 基址收口(本分支此前完全没有这一步:opaque 时 GEP 直接抛裸 TypeError) - SubPtr, ST = self._ConcretizeGepBase(Gen, SubPtr, CN, f"cast_{CN}") - offset = Gen._get_member_offset(Node.attr, CN, ST) - if offset is not None: - MemberPtr = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) - return MemberPtr - elif isinstance(Node.value, ast.Call): - # 指针强转基座(`(ast.Call*(vinner)).IsVolatile = 1`):`T*(x)` 被 vast 的 - # _ConsumePtrSuffixCall 解析为 `(T | t.CPtr)(x)` 强转调用,结果类型是联合 - # 简化后的 i8*(struct 身份丢失),故须从强转注解(func 是 BitOr 联合) - # 提真实类名,bitcast/inttoptr 回 Ptr(类) 后 GEP 成员。 - # 与读取侧 _handle_attr_call_result 的联合分支(_extract_union_base_class) - # 逐字同口径;此前写入侧只认 Name/Attribute/Subscript,强转基座的字段 - # 写入一律静默返回 None。 - ObjVal: Any = self.HandleExprLlvm(Node.value) - if ObjVal is not None and isinstance(Node.value.func, ast.BinOp) \ - and isinstance(Node.value.func.op, ast.BitOr): - UnionCls: str | None = self._extract_union_base_class(Node.value.func) - if UnionCls and UnionCls in Gen.structs: - ST: Any = Gen.structs[UnionCls] - if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(UnionCls, Gen) - ST = Gen.structs.get(UnionCls, ST) - if isinstance(ST, ir.IdentifiedStructType) and not ST.elements: - # 类键那份仍 opaque(body 未 set_body / 另一份实例已具化)⇒ 取已具化 - # 实例作目标 pointee,否则 bitcast/inttoptr 出来的基址仍是 opaque、 - # GEP 在编译期算不出元素类型(判据见 _ConcretePointeeFor)。 - _TwU: Any = self._ConcretePointeeFor(Gen, UnionCls, ST) - if _TwU is not None: - ST = _TwU - TargetPtrTy: Any = ir.PointerType(ST) - if isinstance(ObjVal.type, ir.PointerType): - if ObjVal.type != TargetPtrTy: - ObjVal = Gen.builder.bitcast(ObjVal, TargetPtrTy, name=f"cast_{UnionCls}") - elif isinstance(ObjVal.type, ir.IntType): - ObjVal = Gen.builder.inttoptr(ObjVal, TargetPtrTy, name=f"int2ptr_{UnionCls}") - else: - return None - offset: int | None = Gen._get_member_offset(Node.attr, UnionCls) - if offset is not None: - MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) - return MemberPtr - return None - - def _HandleSliceLlvm(self, base_val: ir.Value, slice_node: ast.Slice | ast.Index, base_ast: ast.AST) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - lower_val: Any = None - upper_val: Any = None - step_val: Any = None - if isinstance(slice_node, ast.Slice): - if slice_node.lower: - lower_val = self.HandleExprLlvm(slice_node.lower) - if slice_node.upper: - upper_val = self.HandleExprLlvm(slice_node.upper) - if slice_node.step: - step_val = self.HandleExprLlvm(slice_node.step) - if isinstance(slice_node, ast.Index): - return self.HandleExprLlvm(slice_node.value) - return None - - def _infer_element_struct_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: - if isinstance(node, ast.Attribute): - attr_name: str = node.attr - if isinstance(node.value, ast.Name) and node.value.id == 'self': - current_class: str | None = self.Trans._CurrentCpythonObjectClass - if current_class: - if current_class in Gen.class_member_element_class: - elem_class: str | None = Gen.class_member_element_class[current_class].get(attr_name) - if elem_class and elem_class in Gen.structs: - return elem_class - if current_class in Gen.class_members: - for member_name, member_type in Gen.class_members[current_class]: - if member_name == attr_name: - if isinstance(member_type, ir.IdentifiedStructType): - found: Any = Gen.find_struct_by_pointee(member_type) - if found: - return found[0] - break - class_name: str | None = Gen.var_struct_class.get(attr_name) - if class_name: - return class_name - elif isinstance(node, ast.Name): - var_name: str = node.id - class_name = Gen.var_struct_class.get(var_name) - if class_name: - return class_name - return None - - def _get_int_ptr(self, target: ast.AST) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if isinstance(target, ast.Name): - VarName: str = target.id - if VarName in Gen.variables and Gen.variables[VarName] is not None: - VarPtr: Any = Gen.variables[VarName] - if isinstance(VarPtr.type, ir.PointerType): - pointee: Any = VarPtr.type.pointee - if isinstance(pointee, ir.ArrayType): - return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") - return VarPtr - elif isinstance(target, ast.Attribute): - if isinstance(target.value, ast.Name): - obj_name: str = target.value.id - if obj_name in Gen.variables and Gen.variables[obj_name] is not None: - obj_ptr: Any = Gen.variables[obj_name] - if isinstance(obj_ptr.type, ir.PointerType): - pointee = obj_ptr.type.pointee - if isinstance(pointee, ir.PointerType): - obj_ptr = Gen._load(obj_ptr, name=f"Load_{obj_name}") - pointee = obj_ptr.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - StructName: str | None = None - if isinstance(pointee, ir.IdentifiedStructType): - StructName = pointee.name - if not StructName: - found: Any = Gen.find_struct_by_pointee(pointee) - if found: - StructName = found[0] - if StructName and StructName in Gen.structs: - offset: int | None = Gen._get_member_offset(target.attr, StructName) - if offset is not None: - return Gen.builder.gep(obj_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") - obj_val: Any = self.HandleExprLlvm(target.value) - if obj_val and isinstance(obj_val.type, ir.PointerType): - if isinstance(obj_val.type.pointee, ir.PointerType): - obj_val = Gen._load(obj_val, name="Load_attr_ptr") - pointee = obj_val.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - StructName = None - if isinstance(pointee, ir.IdentifiedStructType): - StructName = pointee.name - if not StructName: - found = Gen.find_struct_by_pointee(pointee) - if found: - StructName = found[0] - if StructName and StructName in Gen.structs: - offset = Gen._get_member_offset(target.attr, StructName) - if offset is not None: - return Gen.builder.gep(obj_val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") - return None - - def _resolve_struct_class_for_attr(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: - if isinstance(Node, ast.Name): - VarName: str = Node.id - if VarName in Gen.var_struct_class: - return Gen.var_struct_class[VarName] - elif isinstance(Node, ast.Attribute): - ClassName: str | None = self._resolve_struct_class_for_attr(Node.value, Gen) - if ClassName and ClassName in Gen.class_members: - for member_name, _ in Gen.class_members[ClassName]: - if member_name == Node.attr: - return ClassName - return None - - def _infer_array_member_type(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> ir.ArrayType | None: - if isinstance(Node, ast.Attribute): - parent_class: str | None = self._get_attr_class(Node, Gen) - if parent_class and parent_class in Gen.class_members: - for m_name, m_type in Gen.class_members[parent_class]: - if m_name == Node.attr and isinstance(m_type, ir.ArrayType): - return m_type - if parent_class and parent_class in Gen.structs: - st: Any = Gen.structs[parent_class] - if isinstance(st, ir.IdentifiedStructType) and st.elements is not None: - offset: int | None = Gen._get_member_offset(Node.attr, parent_class) - if offset is not None and offset < len(st.elements): - elem: Any = st.elements[offset] - if isinstance(elem, ir.ArrayType): - return elem - return None - - def _build_attr_path(self, node: ast.AST) -> list[str]: - parts: list[str] = [] - current: ast.AST = node - while isinstance(current, ast.Attribute): - parts.append(current.attr) - current = current.value - if isinstance(current, ast.Name): - parts.append(current.id) - parts.reverse() - return parts - - def _try_resolve_nested_cdefine(self, Node: ast.Attribute) -> ir.Value | None: - """尝试解析嵌套属性路径上的 CDefine 常量 - - 注意:不再强制依赖 _ImportedModules。即使该集合为空(如自举编译 - 早期阶段),也始终构建属性路径并检查符号表,确保 ast.CONST_INT - 这类跨模块 CDefine 常量可被解析为立即数。 - """ - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - parts: list[str] = self._build_attr_path(Node) - if len(parts) < 2: - return None - attr_name: str = parts[-1] - module_parts: list[str] = parts[:-1] - possible_keys: list[str] = [attr_name] - full_path: str = '.'.join(parts) - possible_keys.append(full_path) - imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) - if module_parts: - ModulePath: str = '.'.join(module_parts) - resolved_first: str = self.Trans.SymbolTable.resolve_alias(module_parts[0]) - if resolved_first != module_parts[0]: - resolved_parts: list[str] = [resolved_first] + module_parts[1:] - resolved_path: str = '.'.join(resolved_parts) - possible_keys.append(f"{resolved_path}.{attr_name}") - if imported_modules: - for mod_name in imported_modules: - if mod_name.endswith('.' + ModulePath) or mod_name == ModulePath: - key: str = f"{mod_name}.{attr_name}" - if key not in possible_keys: - possible_keys.append(key) - if module_parts and (mod_name.endswith('.' + module_parts[-1]) or mod_name == module_parts[-1]): - key = f"{mod_name}.{attr_name}" - if key not in possible_keys: - possible_keys.append(key) - for lookup_key in possible_keys: - SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) - if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: - val: Any = SymInfo.DefineValue - if isinstance(val, int): - # 复用 _make_define_constant:带声明类型按声明位宽,否则按值推断 - # (含无符号 32 位高半区),与本文件其它 CDefine 解析点同口径。 - return self._make_define_constant(Gen, val, attr_name) - elif isinstance(val, float): - return ir.Constant(ir.FloatType(), val) - # 回退:检查 Gen._define_constants(LlvmGenerator 预扫描阶段填充) - define_constants: Any = getattr(Gen, '_define_constants', {}) - for key in (attr_name, full_path): - if key in define_constants: - val: Any = define_constants[key] - if isinstance(val, int): - # 复用 _make_define_constant:带声明类型按声明位宽,否则按值推断 - # (含无符号 32 位高半区),与本文件其它 CDefine 解析点同口径。 - return self._make_define_constant(Gen, val, attr_name) - elif isinstance(val, float): - return ir.Constant(ir.FloatType(), val) - # 回退:_all_define_constants(Phase2Translator 从所有 .pyi 预提取的跨模块 CDefine, - # 与 _HandleNameLlvm 的裸名回退同源)。此前只覆盖裸名/from-import,**模块属性访问** - # (如 `win32file.STD_OUTPUT_HANDLE`)走到这里就落空 → 转去基座求值 + 字段/全局加载, - # 命中 stub 里那条错误的 `@STD_OUTPUT_HANDLE = external global`,最终链接期未定义符号。 - # 与裸名路径同口径:仅当本模块没有同名全局时才用常量(避免遮蔽真正的模块全局)。 - _mod_globals: Any = getattr(Gen, 'module', None) - _has_mod_global: bool = _mod_globals is not None and attr_name in getattr(_mod_globals, 'globals', {}) - if not _has_mod_global: - all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} - for key2 in (attr_name, full_path): - if key2 in all_dc: - val2: Any = all_dc[key2] - if isinstance(val2, int): - return self._make_define_constant(Gen, val2, attr_name) - elif isinstance(val2, float): - return ir.Constant(ir.FloatType(), val2) - return None - - def _try_resolve_cross_module_enum(self, module_alias: str, enum_class_name: str, member_name: str) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) - if not imported_modules: - return None - resolved_module: str = self.Trans.SymbolTable.resolve_alias(module_alias) - if resolved_module not in imported_modules and module_alias not in imported_modules: - return None - enum_keys: list[str] = [enum_class_name, f"{resolved_module}.{enum_class_name}"] - for enum_key in enum_keys: - SymInfo: Any = self.Trans.SymbolTable.lookup(enum_key) - if SymInfo: - if SymInfo.IsEnum: - for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"): - info: Any = self.Trans.SymbolTable.lookup(qname) - if info and info.IsEnumMember and isinstance(info.value, int): - return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) - for key, info in self.Trans.SymbolTable.items(): - if info.IsEnumMember and info.EnumName == enum_key and key == member_name: - if isinstance(info.value, int): - return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) - for key, info in self.Trans.SymbolTable.items(): - if info.IsEnumMember and key == member_name: - if isinstance(info.value, int): - for enum_key in enum_keys: - if info.EnumName == enum_key: - return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) - return None - - def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None: - """在 class_member_element_class 中按"属主类 + 字段名"查找元素/特化类。 - - class_member_element_class 的 key 与调用方持有的类名(var_struct_class / - _CurrentCpythonObjectClass 等)可能存在"裸名 vs 带 sha1 前缀"的不一致 - (如 key='Function',而持有者是 '732a8c24abb9b516.Function')。先精确 - 匹配,再按短名(剥离 16 位 hex 前缀)遍历所有 key 匹配,确保能恢复 - 字段声明的特化类(如 rawptr[GSList[BasicBlock]] → 'GSList[BasicBlock]')。 - """ - if not OwnerClass or not FieldName: - return None - elem_map: Any = getattr(Gen, 'class_member_element_class', {}) or {} - if OwnerClass in elem_map: - sub_map: Any = elem_map[OwnerClass] - if FieldName in sub_map: - return sub_map[FieldName] - owner_short: str = Gen._extract_short_name(OwnerClass) - for key, sub_map in elem_map.items(): - if Gen._extract_short_name(key) != owner_short: - continue - if FieldName in sub_map: - return sub_map[FieldName] - # 继承链回退(对齐 ExprUtils._get_var_class 的同一张表查询):字段声明在 - # 基类时 elem_map 的键是**声明类**,而收件人类可能是子类 - # (TypeGenBase.Gen: rawptr[Generator.Generator] → 收件人 TypeGenResolve / - # TypeGenCType…)→ 上面两路全 miss。rawptr[X] 字段的 LLVM 成员类型被折叠 - # 为 i8*(struct 身份丢失,class_members / struct elements 都推不出目标类), - # elem_map 是唯一能恢复目标类的来源;miss 即任意级链解析 - # (_field_element_key)返回 None → 带注解赋值 fail-fast - # (TypeGenResolve.vp:807 self.Gen.TypeGenCType.ImportAliasBuf 根因)。 - try: - _mro_chain: list[str] = Gen._get_mro_chain(OwnerClass) - except Exception: - _mro_chain = [OwnerClass] - for _mro_cls in _mro_chain: - _sub_mro: Any = elem_map.get(_mro_cls) - if _sub_mro is None: - _sub_mro = elem_map.get(Gen._extract_short_name(_mro_cls)) - if _sub_mro and FieldName in _sub_mro: - return _sub_mro[FieldName] - return None - - def _resolve_attr_parent_hint(self, Node: ast.Attribute, Gen: LlvmCodeGenerator) -> str | None: - """从父对象上下文解析字段的元素/容器特化提示(parent_hint)。 - - 例:func.Blocks.Head —— Node.value 是 func.Blocks,父变量 func 的 - var_struct_class/global_struct_class 是 'Function',Blocks 字段声明为 - rawptr[GSList[BasicBlock]],class_member_element_class 记录元素提示 - 'GSList[BasicBlock]'。该提示用于把退化为 i8* 的字段指针恢复成目标 - 特化类(char* 分支),或在所有 GSList[T] 都有 Head/Tail/Count 时 - 选择正确特化版本(struct 分支)。兼容裸名/带 sha1 前缀键。 - """ - if not (isinstance(Node, ast.Attribute) and isinstance(Node.value, ast.Name)): - return None - p_var: str = Node.value.id - p_attr: str = Node.attr - p_CN: str | None = Gen.var_struct_class.get(p_var) - if not p_CN and getattr(Gen, 'global_struct_class', None): - p_CN = Gen.global_struct_class.get(p_var) - if not p_CN: - return None - return self._lookup_member_elem_class(Gen, p_CN, p_attr) - - def _find_struct_key_for_class_hint(self, Gen: LlvmCodeGenerator, ClassHint: str | None) -> str | None: - """把 class_member_element_class 记录的类/特化名解析为 Gen.structs 的真实键。 - - 记录值可能是裸名('GSList[BasicBlock]')或带 sha1 前缀 - ('8e92345fd262a343.GSList[BasicBlock]'),且**方括号内元素也可能带模块 - 前缀**('GSList[29fdb5e25dc949f4.BasicBlock]',跨模块泛型特化登记值)。 - Gen.structs 的键可能是另一种形态。比较前统一剥除所有 '<16hex>.' 片段: - 依次尝试:精确键 → 归一短名键 → class_sha1_map 重建前缀键 → - 遍历 Gen.structs 按归一短名匹配。 - """ - if not ClassHint: - return None - if ClassHint in Gen.structs: - return ClassHint - import re as _re_sha - def _strip_sha(s: str) -> str: - return _re_sha.sub(r'(?i)[0-9a-f]{16}\.', '', s) - short: str = _strip_sha(Gen._extract_short_name(ClassHint)) - if short in Gen.structs: - return short - expected_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(short) - if expected_sha1: - prefixed: str = f"{expected_sha1}.{short}" - if prefixed in Gen.structs: - return prefixed - for key in Gen.structs: - if _strip_sha(Gen._extract_short_name(key)) == short: - return key - return None - - def _get_attr_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: - if isinstance(Node, ast.Attribute): - # 注: 不再硬编码 ZLIB_VERSION / C_OK,它们应从符号表查找 - if isinstance(Node.value, ast.Name) and Node.value.id == 'self': - ClassName: str | None = self.Trans._CurrentCpythonObjectClass - return self._lookup_member_elem_class(Gen, ClassName, Node.attr) - if isinstance(Node.value, ast.Name): - VarName: str = Node.value.id - ClassName: str | None = Gen.var_struct_class.get(VarName) - if not ClassName and getattr(Gen, 'global_struct_class', None): - ClassName = Gen.global_struct_class.get(VarName) - if ClassName: - return self._lookup_member_elem_class(Gen, ClassName, Node.attr) - if isinstance(Node.value, ast.Attribute): - ParentClass: str | None = self._get_attr_class(Node.value, Gen) - if ParentClass: - return self._lookup_member_elem_class(Gen, ParentClass, Node.attr) - return None - - # ========== 任意级属性链:逐段前向类解析 ========== - - def _pointee_llvm_to_struct_key(self, Gen: LlvmCodeGenerator, llvm_type: ir.Type) -> str | None: - """把 LLVM 成员类型(Ptr->IdentifiedStruct / IdentifiedStruct)解析为 - Gen.structs 的真实键。 - - 先精确匹配(find_struct_by_pointee),失败再按短名匹配(优先选择 - 有完整 elements 的定义),兼容 sha1 前缀冲突/泛型特化场景。 - """ - cand: ir.Type = llvm_type - if isinstance(cand, ir.PointerType): - cand = cand.pointee - if not isinstance(cand, ir.IdentifiedStructType): - return None - found: Any = Gen.find_struct_by_pointee(cand) - if found: - return found[0] - cand_short: str = Gen._extract_short_name(cand.name) - best_key: str | None = None - best_elems: int = -1 - for k in Gen.structs: - if Gen._extract_short_name(k) != cand_short: - continue - st_v: Any = Gen.structs[k] - if not isinstance(st_v, ir.IdentifiedStructType): - continue - n_elems: int = len(st_v.elements) if st_v.elements is not None else 0 - if n_elems > best_elems: - best_elems = n_elems - best_key = k - return best_key - - def _owner_member_llvm_types(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[tuple[str, ir.Type]]: - """收集 OwnerClass(含继承父类)的成员 (名, LLVM 类型) 列表。 - - class_members 的键可能是带 sha1 前缀全名,此处按短名/剥 '[' 泛型 - 参数后的基名匹配,并沿 class_parents 继承链向上补充父类字段。 - """ - owner_short: str = Gen._extract_short_name(OwnerClass) - base_short: str = owner_short.split('[')[0] if '[' in owner_short else owner_short - members_map: dict[str, list] = {} - for k, v in getattr(Gen, 'class_members', {}).items(): - k_short: str = Gen._extract_short_name(k) - k_base: str = k_short.split('[')[0] if '[' in k_short else k_short - if k_short == owner_short or k_base == base_short: - members_map[k] = list(v) - ordered: list[tuple[str, ir.Type]] = [] - seen: set[str] = set() - for m in self._class_and_parents(Gen, OwnerClass): - m_short: str = Gen._extract_short_name(m) - for k, v in members_map.items(): - if Gen._extract_short_name(k) != m_short: - continue - for name, ty in v: - if name not in seen: - seen.add(name) - ordered.append((name, ty)) - return ordered - - def _class_and_parents(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[str]: - """OwnerClass 自身 + 全部父类短名链(子类在前)""" - chain: list[str] = [] - seen: set[str] = set() - cur: str = Gen._extract_short_name(OwnerClass) - parents_map: Any = getattr(Gen, 'class_parents', {}) or {} - single_parent: Any = getattr(Gen, 'class_parent', {}) or {} - while cur and cur not in seen: - seen.add(cur) - chain.append(cur) - nxt: list[str] = parents_map.get(cur, []) - if not nxt and cur in single_parent and single_parent[cur]: - nxt = [single_parent[cur]] - if not nxt: - break - # 多继承:只取第一条链向上(深度优先声明顺序)保持与 - # _get_member_offset 的继承链回退一致 - cur = Gen._extract_short_name(nxt[0]) - return chain - - def _field_element_key(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, AttrName: str) -> str | None: - """解析 OwnerClass 上 AttrName 字段的"元素 struct 键"(可在 Gen.structs 定位)。 - - 供任意级链(ve.Alloca.Ty.Pointee)逐段恢复目标类:跨模块 stub 会把 - rawptr[X] 指针字段退化为 i8*,load 出的值丢失 struct 身份;此处综合 - class_member_element_class(登记的元素/特化类)→ class_members LLVM - 成员类型 → struct elements 字段类型 推断该字段指向的 struct 键, - 兼容裸名/sha1 前缀/泛型特化名。 - """ - if not OwnerClass or not AttrName: - return None - # 1) class_member_element_class 直查(含短名模糊匹配) - hint: str | None = self._lookup_member_elem_class(Gen, OwnerClass, AttrName) - if hint: - k: str | None = self._find_struct_key_for_class_hint(Gen, hint) - if k: - return k - # 2) class_members 成员 LLVM 类型 - for m_name, m_type in self._owner_member_llvm_types(Gen, OwnerClass): - if m_name != AttrName: - continue - k2: str | None = self._pointee_llvm_to_struct_key(Gen, m_type) - if k2: - return k2 - break - # 3) struct elements 字段类型(加载 stub 后按偏移取值) - owner_key: str | None = self._find_struct_key_for_class_hint(Gen, OwnerClass) - if owner_key and owner_key in Gen.structs: - st_v: Any - ok_v: bool - st_v, ok_v = self._try_Load_struct_from_stub(Gen, owner_key) - if isinstance(st_v, ir.IdentifiedStructType) and st_v.elements is not None: - try: - off: int | None = Gen._get_member_offset(AttrName, owner_key) - except KeyError: - off = None - if off is not None and off < len(st_v.elements): - k3: str | None = self._pointee_llvm_to_struct_key(Gen, st_v.elements[off]) - if k3: - return k3 - # 4) 继承父类 class_members 兜底(_owner_member_llvm_types 已含继承链, - # 此处仅处理父类 struct elements 的字段类型) - for parent_short in self._class_and_parents(Gen, OwnerClass)[1:]: - p_key: str | None = self._find_struct_key_for_class_hint(Gen, parent_short) - if not p_key or p_key not in Gen.structs: - continue - p_st: Any - p_ok: bool - p_st, p_ok = self._try_Load_struct_from_stub(Gen, p_key) - if isinstance(p_st, ir.IdentifiedStructType) and p_st.elements is not None: - try: - p_off: int | None = Gen._get_member_offset(AttrName, p_key) - except KeyError: - p_off = None - if p_off is not None and p_off < len(p_st.elements): - k4: str | None = self._pointee_llvm_to_struct_key(Gen, p_st.elements[p_off]) - if k4: - return k4 - return None - - def _resolve_chain_result_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: - """沿链前向解析表达式(Name 或任意级 Attribute 链)的最终 struct 键。 - - 例如 ve.Alloca.Ty.Pointee:最内层 Name ve 查 var_struct_class / - global_struct_class / 模块全局 / 变量 alloca pointee 得首个类(Value), - 之后每段用 _field_element_key 在当前类上取字段的元素类推进,直到最外层。 - 对自指/REnum(如 LLVMType.Pointee → LLVMType)或 class_member_element_class - 缺项的字段,由 _field_element_key 的 class_members / struct elements 兜底。 - """ - if isinstance(Node, ast.Name): - if Node.id == 'self': - return self.Trans._CurrentCpythonObjectClass - return self._resolve_var_classname(Gen, Node.id) - if isinstance(Node, ast.Attribute): - ParentKey: str | None = self._resolve_chain_result_class(Node.value, Gen) - if not ParentKey: - return None - if ParentKey not in Gen.structs: - resolved_parent: str | None = self._find_struct_key_for_class_hint(Gen, ParentKey) - if not resolved_parent: - return None - ParentKey = resolved_parent - return self._field_element_key(Gen, ParentKey, Node.attr) - return None - - def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: + offset = Gen._get_member_offset(AttrName, ClassName, _StructHint) + # 检查 offset 是否超出实际结构体 + if isinstance(ObjVal.type, ir.PointerType): + actual_pointee: Any = ObjVal.type.pointee + if isinstance(actual_pointee, ir.IdentifiedStructType): + actual_elems: Any = actual_pointee.elements + if actual_elems is not None and offset is not None and offset >= len(actual_elems): + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + actual_elems = actual_pointee.elements + if actual_elems is not None and offset >= len(actual_elems): + # sha1 冲突修复:ObjVal 的 pointee 可能是字段不全的版本(如 2 字段 Function), + # 而 st(来自 _try_Load_struct_from_stub)可能是字段完整的版本。 + # bitcast ObjVal 到 st 的类型,使 GEP 能正确访问完整字段。 + if st is not None and isinstance(st, ir.IdentifiedStructType) and st.elements is not None and offset < len(st.elements): + ObjVal = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_load_{ClassName}") + else: + self._NoteAttrNone( + Gen, ClassName, AttrName, + f"槽位越界(_handle_attr_name 侧):字段 '{AttrName}' 需槽位 " + f"{offset},但 ObjVal 的 pointee 只有 {len(actual_elems)} 个元素," + f"st 版结构体也不够({len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'None/opaque'})") + return None + result = self._gep_and_Load_member(Gen, ObjVal, offset, AttrName, ClassName, st) + return result + + def _recover_struct_class(self, Gen: LlvmCodeGenerator, VarName: str) -> str | None: + """由变量的 LLVM 存储类型恢复结构体类键(var_struct_class 缺失时兜底)。 + + 单级 Name 字段取值(如 tp.elts)的本地变量,若其注解 rawptr[X] 被 TypeMerge + builtins 分支折叠成 i8*、且 var_struct_class 未登记 X,属性访问层无法解析 + 类名。此处直接检查 Gen.variables / _reg_values / _direct_values / + global_vars 中该变量的 alloca(或值)指针 pointee:能辨识出 + IdentifiedStruct 就按 pointee 恢复真实 Gen.structs 键(精确匹配失败时按 + 短名找元素最多的完整版本),供调用方回填并继续 GEP 成员访问。不发射任何 + 额外 IR(纯查表)。 + """ + if not VarName: + return None + probe: ir.Type | None = None + vp: Any = Gen.variables.get(VarName) + if vp is not None and isinstance(vp.type, ir.PointerType): + probe = vp.type.pointee + if isinstance(probe, ir.PointerType): + probe = probe.pointee + if probe is None: + for reg in (Gen._reg_values.get(VarName), Gen._direct_values.get(VarName)): + if reg is not None and isinstance(reg.type, ir.PointerType): + probe = reg.type.pointee + if isinstance(probe, ir.PointerType): + probe = probe.pointee + break + if probe is None and VarName in Gen.global_vars and hasattr(Gen, 'module') and VarName in Gen.module.globals: + gv: Any = Gen.module.globals[VarName] + if isinstance(gv, ir.GlobalVariable) and isinstance(gv.type, ir.PointerType): + probe = gv.type.pointee + if isinstance(probe, ir.PointerType): + probe = probe.pointee + if probe is None or not isinstance(probe, ir.IdentifiedStructType): + return None + found: Any = Gen.find_struct_by_pointee(probe) + if found: + return found[0] + return self._pointee_llvm_to_struct_key(Gen, probe) + + def _handle_attr_nested(self, Node: ast.Attribute) -> ir.Value | None: + """处理 a.b.c 嵌套属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(Node.value.value, ast.Name): + ParentVarName: str = Node.value.value.id + ParentAttrName: str = Node.value.attr + # __doc__ 魔法字符串:ClassName.method.__doc__ 或 module.ClassName.__doc__ + if Node.attr == '__doc__': + full_symbol: str = f"{ParentVarName}.{ParentAttrName}" + doc_ptr: ir.Value | None = Gen._get_doc_ptr(full_symbol) + if doc_ptr is not None: + return doc_ptr + # 回退:module.ClassName.__doc__ → 仅用 ClassName 查找 + doc_ptr = Gen._get_doc_ptr(ParentAttrName) + if doc_ptr is not None: + return doc_ptr + enum_result: Any = self._try_resolve_cross_module_enum(ParentVarName, ParentAttrName, Node.attr) + if enum_result is not None: + return enum_result + # 尝试跨模块子模块全局变量解析: a.b.c 其中 a.b 是已知子模块, c 是该模块的全局变量 + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if imported_modules: + full_ModulePath: str = f"{ParentVarName}.{ParentAttrName}" + if full_ModulePath in imported_modules: + result: Any = self._lookup_module_global(Gen, full_ModulePath, Node.attr) + if result is not None: + return result + elif isinstance(Node.value.value, ast.Attribute) and isinstance(Node.value.value.value, ast.Name): + # 三层嵌套:module.ClassName.method.__doc__ + if Node.attr == '__doc__': + ModuleName: str = Node.value.value.value.id + ClassName2: str = Node.value.value.attr + MethodName: str = Node.value.attr + FullSymbol3: str = f"{ModuleName}.{ClassName2}.{MethodName}" + doc_ptr3: ir.Value | None = Gen._get_doc_ptr(FullSymbol3) + if doc_ptr3 is not None: + return doc_ptr3 + # 回退:ClassName.method.__doc__ + doc_ptr3 = Gen._get_doc_ptr(f"{ClassName2}.{MethodName}") + if doc_ptr3 is not None: + return doc_ptr3 + # 三层嵌套:module.submodule.EnumClass.MEMBER + ModuleAlias3: str = f"{Node.value.value.value.id}.{Node.value.value.attr}" + EnumClassName3: str = Node.value.attr + enum_result3: Any = self._try_resolve_cross_module_enum(ModuleAlias3, EnumClassName3, Node.attr) + if enum_result3 is not None: + return enum_result3 + cdefine_result: Any = self._try_resolve_nested_cdefine(Node) + if cdefine_result is not None: + return cdefine_result + ObjVal: Any = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + # 取证(纯记账):基座求值为空/非指针时,把**内层链的失败原因**一并挂上—— + # 嵌套读取的内层记录随后会被本节点的记录覆盖,报错点(本节点)与真因(内层某级) + # 相隔数层的可读性问题由此解决(现场:`GenApi._g_gen.TypeGenCType.CTypedefNames`)。 + _prev_an: Any = getattr(Gen, '_LastAttrNone', None) + _chain_an: str = '' + if (isinstance(_prev_an, tuple) and len(_prev_an) >= 3 and _prev_an[2] + and _prev_an[1] and _prev_an[1] != Node.attr): + _chain_an = f";内层字段 '{_prev_an[1]}' 的失败原因:{_prev_an[2]}" + self._NoteAttrNone( + Gen, None, Node.attr, + f"基座 {self._ExprShape(Node.value)} 求值为空或非指针" + f"(type={getattr(ObjVal, 'type', None)}){_chain_an}") + return None + pointee: Any = ObjVal.type.pointee + + # char* → struct* cast + 成员访问 + if isinstance(pointee, ir.IntType) and pointee.width == 8: + # 两级自动解引用链(变量 = 指针.字段.成员,如 blk = func.Blocks.Head) + # 根因修复:跨模块 stub 会把容器/类内指针字段(rawptr[GSList[BasicBlock]] + # 等)退化为 i8*,此处 ObjVal 是刚从结构体字段 load 出的 i8*。必须恢复 + # 该字段声明的元素/特化类(如 'GSList[BasicBlock]')才能 + # bitcast + GEP + load 出成员。否则本函数返回 None → AnnAssign/Assign + # 静默跳过 store → 目标 alloca 永不初始化(_peephole_opt 读未初始化 + # 栈变量随机崩溃,见 __function.vp L783 注释)。 + # 解析顺序:_resolve_chain_result_class 沿链前向解析父表达式(可为任意 + # 级数,如 ve.Alloca.Ty)的结果类;失败回退 _get_attr_class 直查 + # class_member_element_class;再回退 _resolve_attr_parent_hint(与下方 + # struct 分支同一 parent_hint 机制:func 的 var_struct_class + + # class_member_element_class 给出 GSList[BasicBlock] 提示);三者都兼容 + # 裸名/带 sha1 前缀的键。 + AttrClassName: str | None = self._resolve_chain_result_class(Node.value, Gen) + if not AttrClassName: + AttrClassName = self._get_attr_class(Node.value, Gen) + if not AttrClassName: + AttrClassName = self._resolve_attr_parent_hint(Node.value, Gen) + if not AttrClassName: + # 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory): + # 上面三个解析器都按「字段所属类」思路找,对「模块别名.变量」返回 None + # (attr_class=None 即此处的 key-miss 现场,StructGenMixin.vp:139 与 + # ClassGenVTable.vp:99 根因)。而该变量的类已由 HandlesImports 的 + # _EmitExternalGlobalDeclLlvm / HandlesAnnAssign 登记在 + # var_struct_class / global_struct_class(键为**裸变量名**)⇒ 直读。 + _mva: ast.AST | None = Node.value + if isinstance(_mva, ast.Attribute) and isinstance(_mva.value, ast.Name): + _mname: str = _mva.value.id + _sha1_map2 = getattr(Gen, 'ModuleSha1Map', {}) or {} + _imported2 = getattr(self.Trans, '_ImportedModules', None) or [] + _aliases2 = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {} + try: + _rmod: str = self.Trans.SymbolTable.resolve_alias(_mname) + except Exception: + _rmod = _mname + _is_mod2: bool = (_mname in _sha1_map2 or _rmod in _sha1_map2 + or _mname in _imported2 or _rmod in _imported2 + or _mname in _aliases2 or _rmod in _aliases2) + if not _is_mod2: + for _k2 in _sha1_map2: + if _k2 == _mname or _k2.endswith(f'.{_mname}'): + _is_mod2 = True + break + if _is_mod2: + AttrClassName = (getattr(Gen, 'var_struct_class', {}).get(_mva.attr) + or getattr(Gen, 'global_struct_class', {}).get(_mva.attr)) + ResolvedKey: str | None = self._find_struct_key_for_class_hint(Gen, AttrClassName) + # 左值注解靶型提示:解析出的类不声明该字段时,用注解类兜一次(两条件同时成立才改判) + ResolvedKey = self._apply_target_struct_hint(Gen, Node.attr, ResolvedKey) + if ResolvedKey is None or ResolvedKey not in Gen.structs: + self._NoteAttrNone( + Gen, AttrClassName, Node.attr, + f"基座字段的所属类未能在 Gen.structs 命中键(类名提示={AttrClassName!r}," + f"structs 键数={len(Gen.structs)})") + return None + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, ResolvedKey) + if not isinstance(st, ir.IdentifiedStructType): + self._NoteAttrNone(Gen, ResolvedKey, Node.attr, + f"类 '{ResolvedKey}' 的 struct 实例类型异常({type(st).__name__})") + return None + if st.elements is None: + # stub 仍 opaque(如跨模块泛型仅声明):走泛型特化候选访问 + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st, Node, ResolvedKey, AttrClassName) + if spec_val is not None: + return spec_val + self._NoteAttrNone( + Gen, ResolvedKey, Node.attr, + f"类 '{ResolvedKey}' 的 body 仍为 opaque(elements=None),且无泛型特化成员可访问") + return None + CastedObj: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(st), name=f"cast_{ResolvedKey}") + if Node.attr in Gen.class_member_bitoffsets.get(ResolvedKey, {}): + return self._load_bitfield_member(CastedObj, ResolvedKey, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, ResolvedKey) + if offset is None: + self._NoteAttrNone( + Gen, ResolvedKey, Node.attr, + f"字段 '{Node.attr}' 在类 '{ResolvedKey}' 中取不到偏移" + f"(成员表 {len(Gen.class_members.get(ResolvedKey) or [])} 项 / body {len(st.elements)} 槽)") + return None + if offset < len(st.elements) and isinstance(st.elements[offset], ir.VoidType): + self._NoteAttrNone( + Gen, ResolvedKey, Node.attr, + f"字段 '{Node.attr}' 的槽 {offset} 是 void(占位槽:类型信息缺失)") + return None + return self._gep_and_Load_member(Gen, CastedObj, offset, Node.attr, ResolvedKey, st) + + # 已知结构体类型 → 直接成员访问 + if isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + # 父上下文特化提示:Node.value 是 Attribute(Name, attr)(如 + # func.Blocks)时,从 var_struct_class/global_struct_class + + # class_member_element_class 取字段声明的特化名(如 'GSList[BasicBlock]'), + # 避免在所有 GSList[T] 都有 Head/Tail/Count 字段时选错特化版本。 + # 与 char* 分支共用 _resolve_attr_parent_hint(兼容裸名/sha1 前缀键); + # 父表达式为多级链(Node.value 是 Attribute)时 _resolve_attr_parent_hint + # 无法取提示,用 _resolve_chain_result_class 沿链前向解析补充。 + parent_hint: str | None = self._resolve_attr_parent_hint(Node, Gen) + if not parent_hint and isinstance(Node.value, ast.Attribute): + parent_hint = self._resolve_chain_result_class(Node.value, Gen) or parent_hint + # 同短名候选:**唯一**时才放行(两个及以上同名类无法判定是哪一个, + # 宁可留痕失败也不能选错布局——同名类冲突在本仓一律以 sha1 全名区分)。 + _UniqShortCN: str | None = None + if isinstance(pointee, ir.IdentifiedStructType): + _PShort2: str = (getattr(pointee, 'name', '') or '').rsplit('.', 1)[-1] + _ShortCNs: list[str] = [ + _c5 for _c5, _s5 in Gen.structs.items() + if isinstance(_s5, ir.IdentifiedStructType) + and (getattr(_s5, 'name', '') or '').rsplit('.', 1)[-1] == _PShort2 + ] + if len(_ShortCNs) == 1: + _UniqShortCN = _ShortCNs[0] + for CN, ST in Gen.structs.items(): + if ((pointee == ST) or self._PointeeMatchesStruct(pointee, ST) + or (_UniqShortCN is not None and CN == _UniqShortCN)): + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN, parent_hint) + if spec_val is not None: + return spec_val + break + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(ObjVal, CN, Node.attr) + offset = Gen._get_member_offset(Node.attr, CN) + if offset is None: + self._NoteAttrNone( + Gen, CN, Node.attr, + f"字段 '{Node.attr}' 在类 '{CN}' 中取不到偏移" + f"(成员表 {len(Gen.class_members.get(CN) or [])} 项 / " + f"body {len(st.elements) if isinstance(st, ir.IdentifiedStructType) and st.elements is not None else 'opaque'} 槽)") + break + # 泛型基础类型字段特化(根本修复): + # 当字段声明为特化泛型(如 GSList[BasicBlock])时,基础类型(GSList)的 + # Head/Tail/Count 字段类型是泛型占位 T*。直接按基础类型访问会返回 T*, + # 导致变量类型被污染(后续字段访问/循环体翻译静默失败,见 + # llvmlite __function.vp _find_max_ssa_number 死循环根因)。 + # 用 class_member_element_class 记录的特化类型(parent_hint)重新访问, + # 返回真实元素类型(如 BasicBlock*)。 + if (parent_hint is not None + and isinstance(st, ir.IdentifiedStructType) + and st.elements is not None and offset < len(st.elements) + and isinstance(st.elements[offset], ir.PointerType) + and isinstance(st.elements[offset].pointee, ir.IdentifiedStructType)): + _field_short: str = (st.elements[offset].pointee.name or '').split('.')[-1] + if (len(_field_short) == 1 and _field_short.isalpha() and _field_short.isupper()): + _hint_short: str = parent_hint.split('.')[-1] if '.' in parent_hint else parent_hint + for CN2, ST2 in Gen.structs.items(): + if not isinstance(ST2, ir.IdentifiedStructType): + continue + if ST2 is ST: + continue + if ST2.elements is None: + continue + _spec_short: str = CN2.split('.')[-1] if '.' in CN2 else CN2 + if _spec_short == _hint_short: + _casted: Any = Gen.builder.bitcast(ObjVal, ir.PointerType(ST2), name=f"cast_spec_{CN}") + _off2: int | None = Gen._get_member_offset(Node.attr, CN2) + if _off2 is not None: + return self._gep_and_Load_member(Gen, _casted, _off2, Node.attr, CN2, ST2) + break + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + # 结构体分支未命中任何类:pointee 与 Gen.structs 全部键都不等(同名类双实例 / + # 该类未注册)——这是"基座类型已知却读不到字段"的最后一种来源,必须留痕。 + _PShortN: str = (getattr(pointee, 'name', '') or '').rsplit('.', 1)[-1] + _SameShort: list[str] = [] + for _cn3, _st3 in Gen.structs.items(): + if not isinstance(_st3, ir.IdentifiedStructType): + continue + if (getattr(_st3, 'name', '') or '').rsplit('.', 1)[-1] == _PShortN: + _SameShort.append(_cn3) + self._NoteAttrNone( + Gen, None, Node.attr, + f"基座 pointee {pointee} 未匹配 Gen.structs 中任何 struct(键数={len(Gen.structs)})" + f";分级匹配(身份 / IR 全名 / 同 sha1 前缀)与「同短名 '{_PShortN}' 唯一候选」" + f"**全部未命中**,同短名候选={_SameShort if _SameShort else '无'}" + f"(候选 >1 时会被主动拒绝,避免选错同名类)" + f"——即该类在本 Gen 的 structs 里没有可用实例(另一 Gen/缓存的实例漏进来了," + f"或该类从未注册)" + f";基座形态={self._ExprShape(Node.value)}") + return None + + def _handle_attr_call_result(self, Node: ast.Attribute) -> ir.Value | None: + """处理 func().attr 属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # Check if this is c.Deref(...) — we need the pointer, not the Loaded value + ObjVal: Any = None + is_cderef: bool = (isinstance(Node.value, ast.Call) and + isinstance(Node.value.func, ast.Attribute) and + isinstance(Node.value.func.value, ast.Name) and + Node.value.func.value.id == 'c' and + Node.value.func.attr == 'Deref' and + Node.value.args) + if is_cderef: + deref_arg: ast.expr = Node.value.args[0] + inner_val: Any = self.HandleExprLlvm(deref_arg) + if inner_val and isinstance(inner_val.type, ir.PointerType): + if isinstance(inner_val.type.pointee, ir.PointerType): + ObjVal = Gen._load(inner_val, name="deref_load_ptr") + else: + ObjVal = inner_val + if ObjVal is None: + ObjVal = self.HandleExprLlvm(Node.value) + # 联合类型转换 (X | t.CPtr)(arg) 的属性访问(治本修复): + # TPC 联合转换对非 CType 类(如 ast.Name / ast.Call)目标类型解析失败, + # 结果可能退回 Ptr(i8) 或错误类型,导致下方 find_struct_by_pointee 失败、 + # 属性访问被静默丢弃——((ast.Name|t.CPtr)(ov)).id 编译后 store 0 次 + # (with 语句 asname 未绑定、pool 未定义根因)。从联合 BinOp 提取真实 + # 基础类并 bitcast 到 Ptr(类) 再访问成员。 + # + # 该形态的每个 return None 都改成 fail-fast(见 _FailFastAttrCallBase): + # `(T | t.CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `T*(x)` 统一 + # 产成该 Call),返回 None 会让调用方只报"表达式编译期求值为空",看不出是 + # 类名没被当成类、结构体不透明、还是成员偏移取不到(现场:ViperC + # ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`,而同一函数前三条 + # `(ast.Assign/AnnAssign/AugAssign*(node)).value` 同款写法正常)。 + _IsUnionCastBase: bool = (isinstance(Node.value, ast.Call) + and isinstance(Node.value.func, ast.BinOp) + and isinstance(Node.value.func.op, ast.BitOr)) + _UnionCastCls: str | None = None + if _IsUnionCastBase: + _UnionCastCls = self._extract_union_base_class(Node.value.func) + if ObjVal is not None and _UnionCastCls and _UnionCastCls in Gen.structs: + target_ty: Any = ir.PointerType(Gen.structs[_UnionCastCls]) + if isinstance(ObjVal.type, ir.PointerType): + if ObjVal.type != target_ty: + ObjVal = Gen.builder.bitcast(ObjVal, target_ty, name="cast_union_attr") + elif isinstance(ObjVal.type, ir.IntType): + ObjVal = Gen.builder.inttoptr(ObjVal, target_ty, name="int2ptr_union_attr") + if not ObjVal: + self._FailFastAttrCallBase("基座强转调用求值为空(HandleExprLlvm 返回 None)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, None) + return None + if not isinstance(ObjVal.type, ir.PointerType): + self._FailFastAttrCallBase("基座强转结果不是指针类型", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) + return None + pointee: Any = ObjVal.type.pointee + if isinstance(pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr") + pointee = ObjVal.type.pointee + found: Any = Gen.find_struct_by_pointee(pointee) + if not found: + self._FailFastAttrCallBase("被指结构体未在 structs 登记(find_struct_by_pointee 未命中)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) + return None + CN: str + ST: Any + CN, ST = found + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) + if spec_val is not None: + return spec_val + self._FailFastAttrCallBase("结构体不透明(stub / .vpi 未加载出字段)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) + return None + bitfield_offsets: Any = Gen.class_member_bitoffsets.get(CN, {}) + if Node.attr in bitfield_offsets: + return self._load_bitfield_member(ObjVal, CN, Node.attr) + offset: int | None = Gen._get_member_offset(Node.attr, CN) + if offset is None: + self._FailFastAttrCallBase("成员偏移取不到(class_members / 继承链均未命中)", + Node, Gen, _IsUnionCastBase, _UnionCastCls, ObjVal) + return None + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + + def _FailFastAttrCallBase(self, Stage: str, Node: ast.Attribute, Gen: Any, + IsUnionCastBase: bool, UnionCls: str | None, + ObjVal: Any) -> None: + """`(T | t.CPtr)(x).attr` 属性访问失败时的 fail-fast(非该形态时静默返回,行为不变)。 + + `(T | t.CPtr)(x)` 只可能来自指针强转(parser 把 `(T*)x` / `(T*)(x)` / `T*(x)` + 统一产成该 Call),其属性访问必须有值产出。此前一律 `return None`,调用方只能 + 报"赋值表达式 'value'(Attribute 形态)编译期求值为空",把"类名没被当成类 / + 结构体不透明 / 成员偏移取不到"三种完全不同的根因混成一条报文 + (现场:ViperC ExprGenBase.vp:360 `v = (ast.Expr*(node)).value`)。 + 此处逐项列出判定依据,直接指认失败环节。 + """ + if not IsUnionCastBase: + return + from lib.core.LLVMCG.FuncGen import FailFastError + _Parts: list[str] = [ + f"属性 '{Node.attr}' 的基座是强转调用 '{self._ExprText(Node.value)}'," + f"失败环节:{Stage}", + f"联合基类名={UnionCls!r}" + f";短名 in structs={'是' if (UnionCls and UnionCls in Gen.structs) else '否'}" + f";structs 同短名键=" + f"{[k for k in Gen.structs if UnionCls and k.split('.')[-1] == UnionCls][:6]}" + f";_struct_sha1_map={getattr(Gen, '_struct_sha1_map', {}).get(UnionCls)!r}" + f";class_sha1_map={getattr(Gen, 'class_sha1_map', {}).get(UnionCls)!r}", + ] + if ObjVal is None: + _Parts.append("基座求值结果=None") + else: + _Parts.append(f"基座求值类型={ObjVal.type!r}") + if isinstance(ObjVal.type, ir.PointerType): + _Pt: Any = ObjVal.type.pointee + _Parts.append(f"被指类型={_Pt!r};elements=" + f"{getattr(_Pt, 'elements', 'n/a')}") + _Found: Any = Gen.find_struct_by_pointee(_Pt) + _Parts.append( + f"find_struct_by_pointee={'命中 ' + str(_Found[0]) if _Found else '未命中'}") + if _Found: + _CN: str = _Found[0] + _Parts.append( + f"class_members[{_CN!r}]=" + f"{[m[0] for m in Gen.class_members.get(_CN, [])][:12]}" + f"({len(Gen.class_members.get(_CN, []))} 项)" + f";成员 '{Node.attr}' 偏移={self._SafeOffset(Gen, Node.attr, _CN)!r}") + if UnionCls: + _Parts.append( + f"class_members[{UnionCls!r}]=" + f"{[m[0] for m in Gen.class_members.get(UnionCls, [])][:12]}" + f"({len(Gen.class_members.get(UnionCls, []))} 项)" + f";class_parent={getattr(Gen, 'class_parent', {}).get(UnionCls)!r}") + raise FailFastError(";".join(_Parts)) + + @staticmethod + def _SafeOffset(Gen: Any, Attr: str, ClassName: str) -> Any: + """取证用成员偏移查询:本身绝不能再抛异常掩盖原始故障。""" + try: + return Gen._get_member_offset(Attr, ClassName) + except Exception as _E: + return f"<查询异常 {type(_E).__name__}: {_E}>" + + def _extract_union_base_class(self, NodeFunc: ast.BinOp) -> str | None: + """从 (X | t.CPtr) 联合 BinOp 提取第一个非指针/非修饰符的类名 + + X 可以是任意类(如 ast.Name / ast.Call / memhub.MemPool),不限于 + t.CType 子类——联合转换 (X | t.CPtr)(arg) 的属性访问需要真实类名 + 才能 bitcast 到 Ptr(类) 并定位成员偏移。 + """ + if not isinstance(NodeFunc, ast.BinOp) or not isinstance(NodeFunc.op, ast.BitOr): + return None + skip_names = { + 'CPtr', 'CArrayPtr', 'CConst', 'CVolatile', 'CInline', + 'CStatic', 'CExtern', 'CExport', 'CAuto', 'CRegister', + 'CDefine', 'CPass', 'CTypeDefault', 'State', + 'BigEndian', 'LittleEndian', + } + operands: list = [] + + def _collect(node: ast.AST) -> None: + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + _collect(node.left) + _collect(node.right) + else: + operands.append(node) + + _collect(NodeFunc) + for op in operands: + name: str | None = None + if isinstance(op, ast.Attribute): + name = op.attr + elif isinstance(op, ast.Name): + name = op.id + if name and name not in skip_names: + return name + return None + + def _handle_attr_subscript_result(self, Node: ast.Attribute) -> ir.Value | None: + """处理 arr[i].attr 属性访问""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ObjVal: Any = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + return None + pointee: Any = ObjVal.type.pointee + if not isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + return None + found: Any = Gen.find_struct_by_pointee(pointee) + if not found: + return None + CN: str + ST: Any + CN, ST = found + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) + if spec_val is not None: + return spec_val + return None + offset: int | None = Gen._get_member_offset(Node.attr, CN) + if offset is None: + return None + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + + def _handle_attr_fallback(self, Node: ast.Attribute) -> ir.Value | None: + """兜底:对任意值尝试指针解引用后查找结构体成员""" + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + ObjVal: Any = self.HandleExprLlvm(Node.value) + if not ObjVal or not isinstance(ObjVal.type, ir.PointerType): + return None + pointee: Any = ObjVal.type.pointee + if isinstance(pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name="load_ptr_for_attr_fallback") + pointee = ObjVal.type.pointee + if not isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + return None + found: Any = Gen.find_struct_by_pointee(pointee) + if not found: + return None + CN: str + ST: Any + CN, ST = found + st: Any + ok: bool + st, ok = self._try_Load_struct_from_stub(Gen, CN) + if not isinstance(st, ir.IdentifiedStructType) or st.elements is None: + spec_val: Any = self._try_access_opaque_generic_member(Gen, ObjVal, st if isinstance(st, ir.IdentifiedStructType) else pointee, Node, CN) + if spec_val is not None: + return spec_val + return None + offset: int | None = Gen._get_member_offset(Node.attr, CN) + if offset is None: + return None + return self._gep_and_Load_member(Gen, ObjVal, offset, Node.attr, CN, st) + + # ========== 主入口 ========== + + def _HandleAttributeLlvm(self, Node: ast.Attribute) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + # 处理 t/c import 的别名重定向 + t_c_imported: Any = getattr(self.Trans, '_t_c_imported_names', {}) + if VarName != 'self' and VarName in t_c_imported: + src_module: str + src_name: str + src_module, src_name = t_c_imported[VarName] + virtual_attr: ast.Attribute = ast.Attribute( + value=ast.Name(id=src_module, ctx=ast.Load()), + attr=src_name, ctx=ast.Load() + ) + ast.copy_location(virtual_attr, Node) + return self._HandleAttributeLlvm(ast.Attribute( + value=virtual_attr, attr=Node.attr, ctx=ast.Load() + )) + if VarName == 'self': + return self._handle_attr_self(Node, VarName) + return self._handle_attr_name(Node, VarName) + + elif isinstance(Node.value, ast.Attribute): + return self._handle_attr_nested(Node) + + elif isinstance(Node.value, ast.Call): + return self._handle_attr_call_result(Node) + + elif isinstance(Node.value, ast.Subscript): + return self._handle_attr_subscript_result(Node) + + # 兜底 + return self._handle_attr_fallback(Node) + + # ========== HandleSubscript 及其辅助 ========== + + def _HandleSubscriptLlvm(self, Node: ast.Subscript) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # pointee-volatile(rawptr[t.CVolatile | T])基址:下标 GEP 名带基址名, + # 顶层 _load/_store 经 BaseGen._IsPtrVolatile 回查登记名,使其 load/store + # 带 LLVM volatile。基址名取不到时后缀为空 ⇒ GEP 名仍是 "subscript", + # 既有代码 IR 逐字节不变。 + _PVSuffix: str = Gen._SubscriptSuffix(Node.value) + # 查询数组元素字节序 + arr_byte_order: str = '' + if isinstance(Node.value, ast.Name) and Node.value.id in Gen.local_var_byteorders: + arr_byte_order = Gen.local_var_byteorders[Node.value.id] + ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.value, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__getitem__'): + obj_val: Any = self.HandleExprLlvm(Node.value) + idx_val: Any = self.HandleExprLlvm(Node.slice) + if obj_val and idx_val: + # 处理 obj_val 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + # 需要先 inttoptr 转换为 i8*,再 bitcast 到目标 struct 指针 + if isinstance(obj_val.type, ir.IntType) and obj_val.type.width == 8: + obj_val = Gen.builder.inttoptr(obj_val, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + if isinstance(idx_val.type, ir.IntType) and idx_val.type.width < 64: + idx_val = Gen.builder.zext(idx_val, ir.IntType(64), name="zext_idx") + _getitem_func: Any = Gen._get_function(f'{ClassName}.__getitem__') + # 处理泛型特化类型不匹配:obj_val 可能是通用类型,而函数期望特化类型 + if _getitem_func.ftype.args and _getitem_func.ftype.args[0] != obj_val.type: + _expected_type = _getitem_func.ftype.args[0] + if isinstance(_expected_type, ir.PointerType) and isinstance(obj_val.type, ir.PointerType): + obj_val = Gen.builder.bitcast(obj_val, _expected_type, name=f"cast_{ClassName}_getitem") + result: Any = Gen.builder.call(_getitem_func, [obj_val, idx_val], name=f"call_{ClassName}.__getitem__") + return result + ValueVal: Any = self.HandleExprLlvm(Node.value) + if not ValueVal: + return None + IndexVal: Any = self.HandleExprLlvm(Node.slice) + if not IndexVal: + return None + if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: + Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx") + IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx") + if isinstance(ValueVal.type, ir.IntType): + var_ptr: Any = self._get_int_ptr(Node.value) + if var_ptr: + i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") + ptr: Any = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") + return Gen._load(ptr, name="int_subscript_val") + return None + # 当变量本身是二级指针(如 elem_ptr: T | t.CPtr = AST**), + # 但 HandleExprLlvm 已自动 load 返回 AST*(一级指针), + # 需要检查 Gen.variables 中的原始 alloca 类型, + # 若是二级指针且存储的是结构体指针,应 bitcast ValueVal 为 AST** 后 GEP 再 load, + # 避免 GEP AST 结构体数组返回错误的 AST*(指向存储位置而非实际对象)。 + # 仅当 var_ptr_element 标记为 True 时触发(由 AnnAssign 检测 (S|t.CPtr)|t.CPtr 注解设置), + # 防止 pts: Point | t.CPtr(简单结构体指针)错误走二级指针路径。 + if (isinstance(Node.value, ast.Name) + and isinstance(ValueVal.type, ir.PointerType) + and isinstance(ValueVal.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType))): + VarName: str = Node.value.id + _vpe_load: bool = (getattr(Gen, 'var_ptr_element', {}).get(VarName, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(VarName, False)) + if _vpe_load and VarName in Gen.variables and Gen.variables[VarName] is not None: + VarAlloca: ir.Value = Gen.variables[VarName] + if (isinstance(VarAlloca.type, ir.PointerType) + and isinstance(VarAlloca.type.pointee, ir.PointerType)): + # 原始 alloca 是二级指针,ValueVal 是已 load 的一级结构体指针 + # 该指针指向数据缓冲区,缓冲区存储的是 AST* 指针数组, + # 需 bitcast ValueVal 为 AST** 后 GEP 指针数组再 load 出 AST* + PtrToStruct: ir.Type = ValueVal.type.pointee + BaseAsPP: ir.Value = Gen.builder.bitcast(ValueVal, + ir.PointerType(ir.PointerType(PtrToStruct)), name="cast_base_pp_load") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_load") + ElemPtrLoad: ir.Value = Gen.builder.gep(BaseAsPP, [IndexVal], name="subscript_pp_load") + return Gen._load(ElemPtrLoad, name="ptr_elem_load") + if isinstance(ValueVal.type, ir.PointerType) and isinstance(ValueVal.type.pointee, ir.PointerType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") + ElemPtr: Any = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") + if isinstance(ValueVal.type.pointee.pointee, ir.IntType) and ValueVal.type.pointee.pointee.width == 8: + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + if isinstance(ValueVal.type, ir.PointerType): + pointee: Any = ValueVal.type.pointee + if isinstance(pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript{_PVSuffix}") + elem_type: Any = pointee.element + if isinstance(elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + else: + if isinstance(pointee, ir.IntType) and pointee.width == 8: + # 指针槽变量优先(8fbba 修复):T|t.CPtr 指针槽(elem_ptr 指向 + # 8 字节指针数组,如 _list.vp 泛型方法体)→ i8** load i8*。 + # 必须放在 _infer_element_struct_class 之前——泛型 T 未解析时 + # var_struct_class 误注册 llvmlite.Value,若先推断 elem_class + # 会 cast 到 Value*,与函数声明 i8* 不匹配(llc 报错)。 + if isinstance(Node.value, ast.Name): + _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) + if _ptr_elem_flag: + CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") + ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") + return Gen._load(ElemPtr, name="str_ptr_load") + elem_class: str | None = self._infer_element_struct_class(Node.value, Gen) + if elem_class and elem_class in Gen.structs: + CastedPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(Gen.structs[elem_class]), name=f"cast_{elem_class}_arr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_arr_index") + ElemPtr = Gen.builder.gep(CastedPtr, [IndexVal], name=f"subscript{_PVSuffix}") + if isinstance(pointee, ir.IntType): + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + return ElemPtr + # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr) + # elem_ptr 是 i8* 但实际指向 i8* (字符串指针), elem_ptr[0] 应加载 8 字节 + if isinstance(Node.value, ast.Name): + _ptr_elem_flag = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) + if _ptr_elem_flag: + CastedPtrPtr = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index") + ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript{_PVSuffix}") + return Gen._load(ElemPtr, name="str_ptr_load") + arr_type: Any = self._infer_array_member_type(Node.value, Gen) + if arr_type and isinstance(arr_type, ir.ArrayType): + arr_ptr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(arr_type), name=f"cast_arr_subscript") + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(arr_ptr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + arr_elem_type: Any = arr_type.element + if isinstance(arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript{_PVSuffix}") + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + elif isinstance(ValueVal.type, ir.ArrayType): + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + var_arr_elem_type: Any = VarPtr.type.pointee.element + if isinstance(var_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + if isinstance(Node.value, ast.Attribute): + AttrPtr: Any = self._get_attr_ptr(Node.value) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + attr_arr_elem_type: Any = AttrPtr.type.pointee.element + if isinstance(attr_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + if isinstance(Node.value, ast.Subscript): + InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) + if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): + inner_pointee: Any = InnerPtr.type.pointee + if isinstance(inner_pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript{_PVSuffix}") + inner_elem_type: Any = inner_pointee.element + if isinstance(inner_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") + Gen._store(ValueVal, arr_alloc) + ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript{_PVSuffix}") + alloc_arr_elem_type: Any = ValueVal.type.element + if isinstance(alloc_arr_elem_type, (ir.IdentifiedStructType, ir.LiteralStructType)): + return ElemPtr + return self._load_arr_elem_with_bswap(Gen, ElemPtr, arr_byte_order) + return None + + def HandleSubscriptPtrLlvm(self, Node: ast.Subscript) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + # 同 _HandleSubscriptLlvm:pointee-volatile 基址名后缀(空后缀 ⇒ 名不变) + _PVSuffix: str = Gen._SubscriptSuffix(Node.value) + ValueVal: Any = self.HandleExprLlvm(Node.value) + if not ValueVal: + return None + IndexVal: Any = self.HandleExprLlvm(Node.slice) + if not IndexVal: + return None + if isinstance(IndexVal.type, ir.PointerType) and isinstance(IndexVal.type.pointee, ir.IntType) and IndexVal.type.pointee.width == 8: + Loaded_byte: Any = Gen._load(IndexVal, name="Load_char_idx_ptr") + IndexVal = Gen.builder.zext(Loaded_byte, ir.IntType(32), name="char_to_int_idx_ptr") + if isinstance(ValueVal.type, ir.PointerType): + pointee: Any = ValueVal.type.pointee + if isinstance(pointee, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr: Any = Gen.builder.gep(ValueVal, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + elif isinstance(pointee, ir.PointerType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index_ptr") + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + else: + # 指针到 str/bytes 的变量 (BinOp 注解如 str | t.CPtr, bytes | t.CPtr) + # elem_ptr 是 i8* 但实际指向 i8* (指针数组), 应按 8 字节步长 GEP + if isinstance(Node.value, ast.Name): + _ptr_elem_flag: bool = (getattr(Gen, 'var_ptr_element', {}).get(Node.value.id, False) + or getattr(Gen, 'global_var_ptr_element', {}).get(Node.value.id, False)) + if _ptr_elem_flag: + CastedPtrPtr: Any = Gen.builder.bitcast(ValueVal, ir.PointerType(ir.PointerType(ir.IntType(8))), name="cast_pp_str_ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_pp_str_index_ptr") + ElemPtr = Gen.builder.gep(CastedPtrPtr, [IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_ptr_index") + ElemPtr = Gen.builder.gep(ValueVal, [IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + elif isinstance(ValueVal.type, ir.IntType): + var_ptr: Any = self._get_int_ptr(Node.value) + if var_ptr: + i8_ptr: Any = Gen.builder.bitcast(var_ptr, ir.PointerType(ir.IntType(8)), name="int_to_i8ptr") + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width < 64: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(64), name="zext_int_index") + ElemPtr = Gen.builder.gep(i8_ptr, [IndexVal], name="int_subscript_ptr") + return ElemPtr + elif isinstance(ValueVal.type, ir.ArrayType): + zero = ir.Constant(ir.IntType(32), 0) + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType) and isinstance(VarPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(VarPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + if isinstance(Node.value, ast.Subscript): + InnerPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) + if InnerPtr: + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(InnerPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + if isinstance(Node.value, ast.Attribute): + AttrPtr: Any = self._get_attr_ptr(Node.value) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType) and isinstance(AttrPtr.type.pointee, ir.ArrayType): + if isinstance(IndexVal.type, ir.IntType) and IndexVal.type.width != 32: + if IndexVal.type.width < 32: + IndexVal = Gen.builder.zext(IndexVal, ir.IntType(32), name="zext_arr_index") + else: + IndexVal = Gen.builder.trunc(IndexVal, ir.IntType(32), name="trunc_arr_index") + ElemPtr = Gen.builder.gep(AttrPtr, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + arr_alloc: Any = Gen._allocaEntry(ValueVal.type, name="arr_subscript_tmp") + Gen._store(ValueVal, arr_alloc) + ElemPtr = Gen.builder.gep(arr_alloc, [zero, IndexVal], name=f"subscript_ptr{_PVSuffix}") + return ElemPtr + return None + + # ========== _get_attr_ptr / _get_int_ptr / _get_attr_class ========== + + def _ConcretePointeeFor(self, Gen: LlvmCodeGenerator, ClassName: str | None, + Pointee: Any) -> Any: + """取该类的**已具化实例**(GEP 基址可用的那份):类键那份 → 同短名孪生体 → None。 + + 为什么需要:同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名 + '.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份" + 并不保证一致;typed pointer 下 GEP 必须在**编译期**从 pointee 算元素类型, + 撞 opaque 即抛裸 TypeError('NoneType' object is not subscriptable)。 + + 判据(全仓同族点同一套,不引入新判据): + ① 类键那份(`Gen.structs[ClassName]`)可经 `_TryLoadStructFromStub` 补出 body; + ② 否则按**同短名**取一份已具化实例(`Gen._FindConcreteTwin`,槽数最多); + ③ 都拿不到 ⇒ None(**不兜底**,交 IR 边界守卫 fail-fast 报同短名实例清单)。 + """ + _Twin: Any = None + if ClassName: + _ST: Any = Gen.structs.get(ClassName) + if isinstance(_ST, ir.IdentifiedStructType) and not _ST.elements: + self.Trans.ImportHandler._TryLoadStructFromStub(ClassName, Gen) + _ST = Gen.structs.get(ClassName, _ST) + if isinstance(_ST, ir.IdentifiedStructType) and _ST.elements: + _Twin = _ST + if _Twin is None and isinstance(Pointee, ir.IdentifiedStructType): + try: + _Twin = Gen._FindConcreteTwin(Pointee) + except Exception: + _Twin = None + return _Twin + + def _ConcretizeGepBase(self, Gen: LlvmCodeGenerator, Ptr: Any, ClassName: str | None, + Tag: str) -> tuple[Any, Any]: + """GEP 基址收口:pointee 为 **opaque** 具名结构体时 bitcast 到已具化实例。 + + 判据与实例来源见 `_ConcretePointeeFor`;返回 `(基址指针, 该类的已具化实例)`, + 第二个返回值同时用作 `_get_member_offset` 的 StructHint —— 偏移与 GEP 必须同源 + (hint 只接受同类,见 FuncGen._member_base)。pointee 非 opaque 时原样返回, + 对既有正确路径零变化;都取不到实例时同样原样返回(不兜底,由守卫报根因)。 + """ + if not isinstance(getattr(Ptr, 'type', None), ir.PointerType): + return Ptr, None + _Pointee: Any = Ptr.type.pointee + if not isinstance(_Pointee, ir.IdentifiedStructType) or _Pointee.elements is not None: + return Ptr, _Pointee + _Twin: Any = self._ConcretePointeeFor(Gen, ClassName, _Pointee) + if _Twin is None: + return Ptr, _Pointee + return (Gen.builder.bitcast(Ptr, ir.PointerType(_Twin), name=Tag), _Twin) + + def _get_attr_ptr(self, Node: ast.Attribute) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if not isinstance(Node, ast.Attribute): + return None + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + if VarName == 'self': + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + if ClassName: + SelfVar: Any = Gen._get_var_ptr('self') + if SelfVar: + SelfPtr: Any = Gen._load(SelfVar, name="self") + if isinstance(SelfPtr.type, ir.PointerType): + # 基址收口(见 _ConcretizeGepBase):opaque 基址先补 body/取孪生体 + # 再 GEP。现场(2026-09-30):includes/ast/astaux.vp:165 + # `self.arg = _copy_str(pool, arg)`,GEP 名 'arg',基址 pointee 是 + # opaque 的 %"8312ce86a2fd82c0.Keyword"*,而同短名的 'Keyword' + # 键上那份已具化(10 槽)。 + SelfPtr, _SelfPointee = self._ConcretizeGepBase( + Gen, SelfPtr, ClassName, f"cast_self_{ClassName}") + offset: int | None = Gen._get_member_offset(Node.attr, ClassName, _SelfPointee) + if offset is None: + return None + MemberPtr: Any = Gen.builder.gep(SelfPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + ClassName = Gen.var_struct_class.get(VarName) + if not ClassName and getattr(Gen, 'global_struct_class', None): + ClassName = Gen.global_struct_class.get(VarName) + if not ClassName: + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + resolved_mod: str = self.Trans.SymbolTable.resolve_alias(VarName) + if imported_modules and (VarName in imported_modules or resolved_mod in imported_modules): + AttrName: str = Node.attr + ModuleSha1Map: Any = getattr(Gen, 'ModuleSha1Map', {}) + candidates: list[str] = [VarName, resolved_mod] + for mod_name in imported_modules: + if mod_name.endswith(f".{VarName}") or mod_name == VarName: + if mod_name not in candidates: + candidates.append(mod_name) + for cand in candidates: + sha1: str | None = ModuleSha1Map.get(cand) + if sha1: + prefixed: str = f"{sha1}.{AttrName}" + if prefixed in Gen.module.globals: + return Gen.module.globals[prefixed] + if AttrName in Gen.module.globals: + gv: Any = Gen.module.globals[AttrName] + if isinstance(gv, ir.GlobalVariable): + return gv + if ClassName and ClassName in Gen.structs: + VarPtr: Any = Gen.variables.get(VarName) + if VarPtr: + ObjVal: Any = VarPtr + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + ObjVal = Gen._load(ObjVal, name=f"Load_{VarName}") + if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == Gen.structs[ClassName]: + # 基址收口(见 _ConcretizeGepBase):类键那份可能仍 opaque ⇒ 补 body / + # 取同短名孪生体后再 GEP(偏移同源)。 + ObjVal, ST = self._ConcretizeGepBase(Gen, ObjVal, ClassName, f"cast_{ClassName}") + offset = Gen._get_member_offset(Node.attr, ClassName, ST) + if offset is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: + MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(Node.value, ast.Attribute): + InnerPtr: Any = self._get_attr_ptr(Node.value) + if InnerPtr and isinstance(InnerPtr.type, ir.PointerType): + pointee: Any = InnerPtr.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: Any = Gen.find_struct_by_pointee(pointee) + if found: + CN: str + ST: Any + CN, ST = found + # 基址收口(见 _ConcretizeGepBase):链尾 pointee 可能仍是类键上那份 + # opaque 实例 ⇒ 补 body / 取同短名孪生体后再 GEP(偏移同源)。 + InnerPtr, ST = self._ConcretizeGepBase(Gen, InnerPtr, CN, f"cast_{CN}") + offset = Gen._get_member_offset(Node.attr, CN, ST) + if offset is not None and isinstance(ST, ir.IdentifiedStructType) and ST.elements is not None: + MemberPtr = Gen.builder.gep(InnerPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + AttrClassName: str | None = self._get_attr_class(Node.value, Gen) + if AttrClassName and AttrClassName in Gen.structs: + CastedObj: Any = Gen.builder.bitcast(InnerPtr, ir.PointerType(Gen.structs[AttrClassName]), name=f"cast_{AttrClassName}") + # 类键那份可能仍 opaque ⇒ 统一收口(见 _ConcretizeGepBase) + CastedObj, ST2 = self._ConcretizeGepBase( + Gen, CastedObj, AttrClassName, f"cast_twin_{AttrClassName}") + offset = Gen._get_member_offset(Node.attr, AttrClassName, ST2) + if offset is not None: + MemberPtr = Gen.builder.gep(CastedObj, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(Node.value, ast.Subscript): + SubPtr: Any = self.HandleSubscriptPtrLlvm(Node.value) + if SubPtr and isinstance(SubPtr.type, ir.PointerType): + pointee = SubPtr.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found = Gen.find_struct_by_pointee(pointee) + if found: + CN, ST = found + # 基址收口(本分支此前完全没有这一步:opaque 时 GEP 直接抛裸 TypeError) + SubPtr, ST = self._ConcretizeGepBase(Gen, SubPtr, CN, f"cast_{CN}") + offset = Gen._get_member_offset(Node.attr, CN, ST) + if offset is not None: + MemberPtr = Gen.builder.gep(SubPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + elif isinstance(Node.value, ast.Call): + # 指针强转基座(`(ast.Call*(vinner)).IsVolatile = 1`):`T*(x)` 被 vast 的 + # _ConsumePtrSuffixCall 解析为 `(T | t.CPtr)(x)` 强转调用,结果类型是联合 + # 简化后的 i8*(struct 身份丢失),故须从强转注解(func 是 BitOr 联合) + # 提真实类名,bitcast/inttoptr 回 Ptr(类) 后 GEP 成员。 + # 与读取侧 _handle_attr_call_result 的联合分支(_extract_union_base_class) + # 逐字同口径;此前写入侧只认 Name/Attribute/Subscript,强转基座的字段 + # 写入一律静默返回 None。 + ObjVal: Any = self.HandleExprLlvm(Node.value) + if ObjVal is not None and isinstance(Node.value.func, ast.BinOp) \ + and isinstance(Node.value.func.op, ast.BitOr): + UnionCls: str | None = self._extract_union_base_class(Node.value.func) + if UnionCls and UnionCls in Gen.structs: + ST: Any = Gen.structs[UnionCls] + if isinstance(ST, ir.IdentifiedStructType) and (ST.elements is None or len(ST.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(UnionCls, Gen) + ST = Gen.structs.get(UnionCls, ST) + if isinstance(ST, ir.IdentifiedStructType) and not ST.elements: + # 类键那份仍 opaque(body 未 set_body / 另一份实例已具化)⇒ 取已具化 + # 实例作目标 pointee,否则 bitcast/inttoptr 出来的基址仍是 opaque、 + # GEP 在编译期算不出元素类型(判据见 _ConcretePointeeFor)。 + _TwU: Any = self._ConcretePointeeFor(Gen, UnionCls, ST) + if _TwU is not None: + ST = _TwU + TargetPtrTy: Any = ir.PointerType(ST) + if isinstance(ObjVal.type, ir.PointerType): + if ObjVal.type != TargetPtrTy: + ObjVal = Gen.builder.bitcast(ObjVal, TargetPtrTy, name=f"cast_{UnionCls}") + elif isinstance(ObjVal.type, ir.IntType): + ObjVal = Gen.builder.inttoptr(ObjVal, TargetPtrTy, name=f"int2ptr_{UnionCls}") + else: + return None + offset: int | None = Gen._get_member_offset(Node.attr, UnionCls) + if offset is not None: + MemberPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=Node.attr) + return MemberPtr + return None + + def _HandleSliceLlvm(self, base_val: ir.Value, slice_node: ast.Slice | ast.Index, base_ast: ast.AST) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + lower_val: Any = None + upper_val: Any = None + step_val: Any = None + if isinstance(slice_node, ast.Slice): + if slice_node.lower: + lower_val = self.HandleExprLlvm(slice_node.lower) + if slice_node.upper: + upper_val = self.HandleExprLlvm(slice_node.upper) + if slice_node.step: + step_val = self.HandleExprLlvm(slice_node.step) + if isinstance(slice_node, ast.Index): + return self.HandleExprLlvm(slice_node.value) + return None + + def _infer_element_struct_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(node, ast.Attribute): + attr_name: str = node.attr + if isinstance(node.value, ast.Name) and node.value.id == 'self': + current_class: str | None = self.Trans._CurrentCpythonObjectClass + if current_class: + if current_class in Gen.class_member_element_class: + elem_class: str | None = Gen.class_member_element_class[current_class].get(attr_name) + if elem_class and elem_class in Gen.structs: + return elem_class + if current_class in Gen.class_members: + for member_name, member_type in Gen.class_members[current_class]: + if member_name == attr_name: + if isinstance(member_type, ir.IdentifiedStructType): + found: Any = Gen.find_struct_by_pointee(member_type) + if found: + return found[0] + break + class_name: str | None = Gen.var_struct_class.get(attr_name) + if class_name: + return class_name + elif isinstance(node, ast.Name): + var_name: str = node.id + class_name = Gen.var_struct_class.get(var_name) + if class_name: + return class_name + return None + + def _get_int_ptr(self, target: ast.AST) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(target, ast.Name): + VarName: str = target.id + if VarName in Gen.variables and Gen.variables[VarName] is not None: + VarPtr: Any = Gen.variables[VarName] + if isinstance(VarPtr.type, ir.PointerType): + pointee: Any = VarPtr.type.pointee + if isinstance(pointee, ir.ArrayType): + return Gen.builder.gep(VarPtr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"arr_start_{VarName}") + return VarPtr + elif isinstance(target, ast.Attribute): + if isinstance(target.value, ast.Name): + obj_name: str = target.value.id + if obj_name in Gen.variables and Gen.variables[obj_name] is not None: + obj_ptr: Any = Gen.variables[obj_name] + if isinstance(obj_ptr.type, ir.PointerType): + pointee = obj_ptr.type.pointee + if isinstance(pointee, ir.PointerType): + obj_ptr = Gen._load(obj_ptr, name=f"Load_{obj_name}") + pointee = obj_ptr.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + StructName: str | None = None + if isinstance(pointee, ir.IdentifiedStructType): + StructName = pointee.name + if not StructName: + found: Any = Gen.find_struct_by_pointee(pointee) + if found: + StructName = found[0] + if StructName and StructName in Gen.structs: + offset: int | None = Gen._get_member_offset(target.attr, StructName) + if offset is not None: + return Gen.builder.gep(obj_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") + obj_val: Any = self.HandleExprLlvm(target.value) + if obj_val and isinstance(obj_val.type, ir.PointerType): + if isinstance(obj_val.type.pointee, ir.PointerType): + obj_val = Gen._load(obj_val, name="Load_attr_ptr") + pointee = obj_val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + StructName = None + if isinstance(pointee, ir.IdentifiedStructType): + StructName = pointee.name + if not StructName: + found = Gen.find_struct_by_pointee(pointee) + if found: + StructName = found[0] + if StructName and StructName in Gen.structs: + offset = Gen._get_member_offset(target.attr, StructName) + if offset is not None: + return Gen.builder.gep(obj_val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{target.attr}_ptr") + return None + + def _resolve_struct_class_for_attr(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(Node, ast.Name): + VarName: str = Node.id + if VarName in Gen.var_struct_class: + return Gen.var_struct_class[VarName] + elif isinstance(Node, ast.Attribute): + ClassName: str | None = self._resolve_struct_class_for_attr(Node.value, Gen) + if ClassName and ClassName in Gen.class_members: + for member_name, _ in Gen.class_members[ClassName]: + if member_name == Node.attr: + return ClassName + return None + + def _infer_array_member_type(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> ir.ArrayType | None: + if isinstance(Node, ast.Attribute): + parent_class: str | None = self._get_attr_class(Node, Gen) + if parent_class and parent_class in Gen.class_members: + for m_name, m_type in Gen.class_members[parent_class]: + if m_name == Node.attr and isinstance(m_type, ir.ArrayType): + return m_type + if parent_class and parent_class in Gen.structs: + st: Any = Gen.structs[parent_class] + if isinstance(st, ir.IdentifiedStructType) and st.elements is not None: + offset: int | None = Gen._get_member_offset(Node.attr, parent_class) + if offset is not None and offset < len(st.elements): + elem: Any = st.elements[offset] + if isinstance(elem, ir.ArrayType): + return elem + return None + + def _build_attr_path(self, node: ast.AST) -> list[str]: + parts: list[str] = [] + current: ast.AST = node + while isinstance(current, ast.Attribute): + parts.append(current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.append(current.id) + parts.reverse() + return parts + + def _try_resolve_nested_cdefine(self, Node: ast.Attribute) -> ir.Value | None: + """尝试解析嵌套属性路径上的 CDefine 常量 + + 注意:不再强制依赖 _ImportedModules。即使该集合为空(如自举编译 + 早期阶段),也始终构建属性路径并检查符号表,确保 ast.CONST_INT + 这类跨模块 CDefine 常量可被解析为立即数。 + """ + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + parts: list[str] = self._build_attr_path(Node) + if len(parts) < 2: + return None + attr_name: str = parts[-1] + module_parts: list[str] = parts[:-1] + possible_keys: list[str] = [attr_name] + full_path: str = '.'.join(parts) + possible_keys.append(full_path) + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if module_parts: + ModulePath: str = '.'.join(module_parts) + resolved_first: str = self.Trans.SymbolTable.resolve_alias(module_parts[0]) + if resolved_first != module_parts[0]: + resolved_parts: list[str] = [resolved_first] + module_parts[1:] + resolved_path: str = '.'.join(resolved_parts) + possible_keys.append(f"{resolved_path}.{attr_name}") + if imported_modules: + for mod_name in imported_modules: + if mod_name.endswith('.' + ModulePath) or mod_name == ModulePath: + key: str = f"{mod_name}.{attr_name}" + if key not in possible_keys: + possible_keys.append(key) + if module_parts and (mod_name.endswith('.' + module_parts[-1]) or mod_name == module_parts[-1]): + key = f"{mod_name}.{attr_name}" + if key not in possible_keys: + possible_keys.append(key) + for lookup_key in possible_keys: + SymInfo: Any = self.Trans.SymbolTable.lookup(lookup_key) + if SymInfo and SymInfo.IsDefine and SymInfo.DefineValue is not None: + val: Any = SymInfo.DefineValue + if isinstance(val, int): + # 复用 _make_define_constant:带声明类型按声明位宽,否则按值推断 + # (含无符号 32 位高半区),与本文件其它 CDefine 解析点同口径。 + return self._make_define_constant(Gen, val, attr_name) + elif isinstance(val, float): + return ir.Constant(ir.FloatType(), val) + # 回退:检查 Gen._define_constants(LlvmGenerator 预扫描阶段填充) + define_constants: Any = getattr(Gen, '_define_constants', {}) + for key in (attr_name, full_path): + if key in define_constants: + val: Any = define_constants[key] + if isinstance(val, int): + # 复用 _make_define_constant:带声明类型按声明位宽,否则按值推断 + # (含无符号 32 位高半区),与本文件其它 CDefine 解析点同口径。 + return self._make_define_constant(Gen, val, attr_name) + elif isinstance(val, float): + return ir.Constant(ir.FloatType(), val) + # 回退:_all_define_constants(Phase2Translator 从所有 .pyi 预提取的跨模块 CDefine, + # 与 _HandleNameLlvm 的裸名回退同源)。此前只覆盖裸名/from-import,**模块属性访问** + # (如 `win32file.STD_OUTPUT_HANDLE`)走到这里就落空 → 转去基座求值 + 字段/全局加载, + # 命中 stub 里那条错误的 `@STD_OUTPUT_HANDLE = external global`,最终链接期未定义符号。 + # 与裸名路径同口径:仅当本模块没有同名全局时才用常量(避免遮蔽真正的模块全局)。 + _mod_globals: Any = getattr(Gen, 'module', None) + _has_mod_global: bool = _mod_globals is not None and attr_name in getattr(_mod_globals, 'globals', {}) + if not _has_mod_global: + all_dc: dict[str, Any] = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} + for key2 in (attr_name, full_path): + if key2 in all_dc: + val2: Any = all_dc[key2] + if isinstance(val2, int): + return self._make_define_constant(Gen, val2, attr_name) + elif isinstance(val2, float): + return ir.Constant(ir.FloatType(), val2) + return None + + def _try_resolve_cross_module_enum(self, module_alias: str, enum_class_name: str, member_name: str) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + imported_modules: Any = getattr(self.Trans, '_ImportedModules', None) + if not imported_modules: + return None + resolved_module: str = self.Trans.SymbolTable.resolve_alias(module_alias) + if resolved_module not in imported_modules and module_alias not in imported_modules: + return None + enum_keys: list[str] = [enum_class_name, f"{resolved_module}.{enum_class_name}"] + for enum_key in enum_keys: + SymInfo: Any = self.Trans.SymbolTable.lookup(enum_key) + if SymInfo: + if SymInfo.IsEnum: + for qname in (f"{enum_key}.{member_name}", f"{enum_key}_{member_name}"): + info: Any = self.Trans.SymbolTable.lookup(qname) + if info and info.IsEnumMember and isinstance(info.value, int): + return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) + for key, info in self.Trans.SymbolTable.items(): + if info.IsEnumMember and info.EnumName == enum_key and key == member_name: + if isinstance(info.value, int): + return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) + for key, info in self.Trans.SymbolTable.items(): + if info.IsEnumMember and key == member_name: + if isinstance(info.value, int): + for enum_key in enum_keys: + if info.EnumName == enum_key: + return ir.Constant(Gen._enum_int_type(enum_key, info), info.value) + return None + + def _lookup_member_elem_class(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, FieldName: str) -> str | None: + """在 class_member_element_class 中按"属主类 + 字段名"查找元素/特化类。 + + class_member_element_class 的 key 与调用方持有的类名(var_struct_class / + _CurrentCpythonObjectClass 等)可能存在"裸名 vs 带 sha1 前缀"的不一致 + (如 key='Function',而持有者是 '732a8c24abb9b516.Function')。先精确 + 匹配,再按短名(剥离 16 位 hex 前缀)遍历所有 key 匹配,确保能恢复 + 字段声明的特化类(如 rawptr[GSList[BasicBlock]] → 'GSList[BasicBlock]')。 + """ + if not OwnerClass or not FieldName: + return None + elem_map: Any = getattr(Gen, 'class_member_element_class', {}) or {} + if OwnerClass in elem_map: + sub_map: Any = elem_map[OwnerClass] + if FieldName in sub_map: + return sub_map[FieldName] + owner_short: str = Gen._extract_short_name(OwnerClass) + for key, sub_map in elem_map.items(): + if Gen._extract_short_name(key) != owner_short: + continue + if FieldName in sub_map: + return sub_map[FieldName] + # 继承链回退(对齐 ExprUtils._get_var_class 的同一张表查询):字段声明在 + # 基类时 elem_map 的键是**声明类**,而收件人类可能是子类 + # (TypeGenBase.Gen: rawptr[Generator.Generator] → 收件人 TypeGenResolve / + # TypeGenCType…)→ 上面两路全 miss。rawptr[X] 字段的 LLVM 成员类型被折叠 + # 为 i8*(struct 身份丢失,class_members / struct elements 都推不出目标类), + # elem_map 是唯一能恢复目标类的来源;miss 即任意级链解析 + # (_field_element_key)返回 None → 带注解赋值 fail-fast + # (TypeGenResolve.vp:807 self.Gen.TypeGenCType.ImportAliasBuf 根因)。 + try: + _mro_chain: list[str] = Gen._get_mro_chain(OwnerClass) + except Exception: + _mro_chain = [OwnerClass] + for _mro_cls in _mro_chain: + _sub_mro: Any = elem_map.get(_mro_cls) + if _sub_mro is None: + _sub_mro = elem_map.get(Gen._extract_short_name(_mro_cls)) + if _sub_mro and FieldName in _sub_mro: + return _sub_mro[FieldName] + return None + + def _resolve_attr_parent_hint(self, Node: ast.Attribute, Gen: LlvmCodeGenerator) -> str | None: + """从父对象上下文解析字段的元素/容器特化提示(parent_hint)。 + + 例:func.Blocks.Head —— Node.value 是 func.Blocks,父变量 func 的 + var_struct_class/global_struct_class 是 'Function',Blocks 字段声明为 + rawptr[GSList[BasicBlock]],class_member_element_class 记录元素提示 + 'GSList[BasicBlock]'。该提示用于把退化为 i8* 的字段指针恢复成目标 + 特化类(char* 分支),或在所有 GSList[T] 都有 Head/Tail/Count 时 + 选择正确特化版本(struct 分支)。兼容裸名/带 sha1 前缀键。 + """ + if not (isinstance(Node, ast.Attribute) and isinstance(Node.value, ast.Name)): + return None + p_var: str = Node.value.id + p_attr: str = Node.attr + p_CN: str | None = Gen.var_struct_class.get(p_var) + if not p_CN and getattr(Gen, 'global_struct_class', None): + p_CN = Gen.global_struct_class.get(p_var) + if not p_CN: + return None + return self._lookup_member_elem_class(Gen, p_CN, p_attr) + + def _find_struct_key_for_class_hint(self, Gen: LlvmCodeGenerator, ClassHint: str | None) -> str | None: + """把 class_member_element_class 记录的类/特化名解析为 Gen.structs 的真实键。 + + 记录值可能是裸名('GSList[BasicBlock]')或带 sha1 前缀 + ('8e92345fd262a343.GSList[BasicBlock]'),且**方括号内元素也可能带模块 + 前缀**('GSList[29fdb5e25dc949f4.BasicBlock]',跨模块泛型特化登记值)。 + Gen.structs 的键可能是另一种形态。比较前统一剥除所有 '<16hex>.' 片段: + 依次尝试:精确键 → 归一短名键 → class_sha1_map 重建前缀键 → + 遍历 Gen.structs 按归一短名匹配。 + """ + if not ClassHint: + return None + if ClassHint in Gen.structs: + return ClassHint + import re as _re_sha + def _strip_sha(s: str) -> str: + return _re_sha.sub(r'(?i)[0-9a-f]{16}\.', '', s) + short: str = _strip_sha(Gen._extract_short_name(ClassHint)) + if short in Gen.structs: + return short + expected_sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(short) + if expected_sha1: + prefixed: str = f"{expected_sha1}.{short}" + if prefixed in Gen.structs: + return prefixed + for key in Gen.structs: + if _strip_sha(Gen._extract_short_name(key)) == short: + return key + return None + + def _get_attr_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(Node, ast.Attribute): + # 注: 不再硬编码 ZLIB_VERSION / C_OK,它们应从符号表查找 + if isinstance(Node.value, ast.Name) and Node.value.id == 'self': + ClassName: str | None = self.Trans._CurrentCpythonObjectClass + return self._lookup_member_elem_class(Gen, ClassName, Node.attr) + if isinstance(Node.value, ast.Name): + VarName: str = Node.value.id + ClassName: str | None = Gen.var_struct_class.get(VarName) + if not ClassName and getattr(Gen, 'global_struct_class', None): + ClassName = Gen.global_struct_class.get(VarName) + if ClassName: + return self._lookup_member_elem_class(Gen, ClassName, Node.attr) + if isinstance(Node.value, ast.Attribute): + ParentClass: str | None = self._get_attr_class(Node.value, Gen) + if ParentClass: + return self._lookup_member_elem_class(Gen, ParentClass, Node.attr) + return None + + # ========== 任意级属性链:逐段前向类解析 ========== + + def _pointee_llvm_to_struct_key(self, Gen: LlvmCodeGenerator, llvm_type: ir.Type) -> str | None: + """把 LLVM 成员类型(Ptr->IdentifiedStruct / IdentifiedStruct)解析为 + Gen.structs 的真实键。 + + 先精确匹配(find_struct_by_pointee),失败再按短名匹配(优先选择 + 有完整 elements 的定义),兼容 sha1 前缀冲突/泛型特化场景。 + """ + cand: ir.Type = llvm_type + if isinstance(cand, ir.PointerType): + cand = cand.pointee + if not isinstance(cand, ir.IdentifiedStructType): + return None + found: Any = Gen.find_struct_by_pointee(cand) + if found: + return found[0] + cand_short: str = Gen._extract_short_name(cand.name) + best_key: str | None = None + best_elems: int = -1 + for k in Gen.structs: + if Gen._extract_short_name(k) != cand_short: + continue + st_v: Any = Gen.structs[k] + if not isinstance(st_v, ir.IdentifiedStructType): + continue + n_elems: int = len(st_v.elements) if st_v.elements is not None else 0 + if n_elems > best_elems: + best_elems = n_elems + best_key = k + return best_key + + def _owner_member_llvm_types(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[tuple[str, ir.Type]]: + """收集 OwnerClass(含继承父类)的成员 (名, LLVM 类型) 列表。 + + class_members 的键可能是带 sha1 前缀全名,此处按短名/剥 '[' 泛型 + 参数后的基名匹配,并沿 class_parents 继承链向上补充父类字段。 + """ + owner_short: str = Gen._extract_short_name(OwnerClass) + base_short: str = owner_short.split('[')[0] if '[' in owner_short else owner_short + members_map: dict[str, list] = {} + for k, v in getattr(Gen, 'class_members', {}).items(): + k_short: str = Gen._extract_short_name(k) + k_base: str = k_short.split('[')[0] if '[' in k_short else k_short + if k_short == owner_short or k_base == base_short: + members_map[k] = list(v) + ordered: list[tuple[str, ir.Type]] = [] + seen: set[str] = set() + for m in self._class_and_parents(Gen, OwnerClass): + m_short: str = Gen._extract_short_name(m) + for k, v in members_map.items(): + if Gen._extract_short_name(k) != m_short: + continue + for name, ty in v: + if name not in seen: + seen.add(name) + ordered.append((name, ty)) + return ordered + + def _class_and_parents(self, Gen: LlvmCodeGenerator, OwnerClass: str) -> list[str]: + """OwnerClass 自身 + 全部父类短名链(子类在前)""" + chain: list[str] = [] + seen: set[str] = set() + cur: str = Gen._extract_short_name(OwnerClass) + parents_map: Any = getattr(Gen, 'class_parents', {}) or {} + single_parent: Any = getattr(Gen, 'class_parent', {}) or {} + while cur and cur not in seen: + seen.add(cur) + chain.append(cur) + nxt: list[str] = parents_map.get(cur, []) + if not nxt and cur in single_parent and single_parent[cur]: + nxt = [single_parent[cur]] + if not nxt: + break + # 多继承:只取第一条链向上(深度优先声明顺序)保持与 + # _get_member_offset 的继承链回退一致 + cur = Gen._extract_short_name(nxt[0]) + return chain + + def _field_element_key(self, Gen: LlvmCodeGenerator, OwnerClass: str | None, AttrName: str) -> str | None: + """解析 OwnerClass 上 AttrName 字段的"元素 struct 键"(可在 Gen.structs 定位)。 + + 供任意级链(ve.Alloca.Ty.Pointee)逐段恢复目标类:跨模块 stub 会把 + rawptr[X] 指针字段退化为 i8*,load 出的值丢失 struct 身份;此处综合 + class_member_element_class(登记的元素/特化类)→ class_members LLVM + 成员类型 → struct elements 字段类型 推断该字段指向的 struct 键, + 兼容裸名/sha1 前缀/泛型特化名。 + """ + if not OwnerClass or not AttrName: + return None + # 1) class_member_element_class 直查(含短名模糊匹配) + hint: str | None = self._lookup_member_elem_class(Gen, OwnerClass, AttrName) + if hint: + k: str | None = self._find_struct_key_for_class_hint(Gen, hint) + if k: + return k + # 2) class_members 成员 LLVM 类型 + for m_name, m_type in self._owner_member_llvm_types(Gen, OwnerClass): + if m_name != AttrName: + continue + k2: str | None = self._pointee_llvm_to_struct_key(Gen, m_type) + if k2: + return k2 + break + # 3) struct elements 字段类型(加载 stub 后按偏移取值) + owner_key: str | None = self._find_struct_key_for_class_hint(Gen, OwnerClass) + if owner_key and owner_key in Gen.structs: + st_v: Any + ok_v: bool + st_v, ok_v = self._try_Load_struct_from_stub(Gen, owner_key) + if isinstance(st_v, ir.IdentifiedStructType) and st_v.elements is not None: + try: + off: int | None = Gen._get_member_offset(AttrName, owner_key) + except KeyError: + off = None + if off is not None and off < len(st_v.elements): + k3: str | None = self._pointee_llvm_to_struct_key(Gen, st_v.elements[off]) + if k3: + return k3 + # 4) 继承父类 class_members 兜底(_owner_member_llvm_types 已含继承链, + # 此处仅处理父类 struct elements 的字段类型) + for parent_short in self._class_and_parents(Gen, OwnerClass)[1:]: + p_key: str | None = self._find_struct_key_for_class_hint(Gen, parent_short) + if not p_key or p_key not in Gen.structs: + continue + p_st: Any + p_ok: bool + p_st, p_ok = self._try_Load_struct_from_stub(Gen, p_key) + if isinstance(p_st, ir.IdentifiedStructType) and p_st.elements is not None: + try: + p_off: int | None = Gen._get_member_offset(AttrName, p_key) + except KeyError: + p_off = None + if p_off is not None and p_off < len(p_st.elements): + k4: str | None = self._pointee_llvm_to_struct_key(Gen, p_st.elements[p_off]) + if k4: + return k4 + return None + + def _resolve_chain_result_class(self, Node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + """沿链前向解析表达式(Name 或任意级 Attribute 链)的最终 struct 键。 + + 例如 ve.Alloca.Ty.Pointee:最内层 Name ve 查 var_struct_class / + global_struct_class / 模块全局 / 变量 alloca pointee 得首个类(Value), + 之后每段用 _field_element_key 在当前类上取字段的元素类推进,直到最外层。 + 对自指/REnum(如 LLVMType.Pointee → LLVMType)或 class_member_element_class + 缺项的字段,由 _field_element_key 的 class_members / struct elements 兜底。 + """ + if isinstance(Node, ast.Name): + if Node.id == 'self': + return self.Trans._CurrentCpythonObjectClass + return self._resolve_var_classname(Gen, Node.id) + if isinstance(Node, ast.Attribute): + ParentKey: str | None = self._resolve_chain_result_class(Node.value, Gen) + if not ParentKey: + return None + if ParentKey not in Gen.structs: + resolved_parent: str | None = self._find_struct_key_for_class_hint(Gen, ParentKey) + if not resolved_parent: + return None + ParentKey = resolved_parent + return self._field_element_key(Gen, ParentKey, Node.attr) + return None + + def _get_llvm_member_offset(self, field_name: str, ClassName: str, Gen: LlvmCodeGenerator) -> int | None: return Gen._get_member_offset(field_name, ClassName) \ No newline at end of file diff --git a/lib/core/Handles/HandlesExprBuiltin.py b/lib/core/Handles/HandlesExprBuiltin.py index aa068fa..6a71e1a 100644 --- a/lib/core/Handles/HandlesExprBuiltin.py +++ b/lib/core/Handles/HandlesExprBuiltin.py @@ -1,600 +1,600 @@ -from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin -import vast as ast -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.includes.t import CTypeRegistry -from lib.includes import t as _t -from lib.core.SymbolUtils import IsTModule - - -# len() 参数形态不支持时的共享提示语(与 TransPyV App/lib/core/Gen/Call/ -# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。 -_LEN_SUPPORTED_HINT: str = ( - "len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;" - "非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度," - "或改用 t.CPtr[元素] 并显式维护元素计数" -) - - -class ExprBuiltinHandle(BaseHandle): - _C_LIB_FUNCS: dict[str, object] = { - 'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), - 'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), - 'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]), - 'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), - 'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), - 'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), - 'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), - 'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), - 'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), - 'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), - 'abs': lambda: (ir.IntType(32), [ir.IntType(32)]), - 'labs': lambda: (ir.IntType(64), [ir.IntType(64)]), - } - - def _HandleBuiltinCallLlvm(self, Node: ast.Call) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if not isinstance(Node.func, ast.Name): - return None - FuncName: str = Node.func.id - - # Two-arg scalar alias form (int(v, t.CPtr) / char / bool / float / double) - # is a pointer wrap, not a scalar cast: route it to the same exit as the - # attribute form t.CInt(v, t.CPtr). Ignoring the CPtr mark would degrade - # it to a bare trunc, e.g. includes/argparse.vp get_bool() reading a - # pointer's low 32 bits instead of the stored value. - if (len(Node.args) >= 2 and FuncName in ('int', 'char', 'bool', 'float', 'double') - and self.Trans.ExprCallHandle._IsCPtrMark(Node.args[1])): - PtrCastAttr: str = 'C' + FuncName[0].upper() + FuncName[1:] - return self.Trans.TSpecialCallHandle._HandleTPtrCastLlvm(Node, PtrCastAttr) - - if FuncName == 'print': - return self._HandlePrintLlvm(Node) - elif FuncName == 'len': - if Node.args: - return self._HandleLenLlvm(Node.args[0]) - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'sizeof': - if Node.args: - return self._HandleSizeofLlvm(Node.args[0]) - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'abs': - if Node.args: - return self._HandleAbsLlvm(Node.args[0]) - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'arg': - return self._HandleArgLlvm(Node.args) - elif FuncName == 'bool': - if Node.args: - Val = self.HandleExprLlvm(Node.args[0]) - if Val: - if isinstance(Val.type, ir.IntType): - if Val.type.width == 1: - return Val - return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result") - elif isinstance(Val.type, ir.PointerType): - return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result") - return Val - return ir.Constant(ir.IntType(1), 0) - elif FuncName == 'int': - if Node.args: - Val = self.HandleExprLlvm(Node.args[0]) - if Val: - if isinstance(Val.type, ir.IntType): - if Val.type.width < 32: - return Gen.builder.zext(Val, ir.IntType(32), name="cast_int") - elif Val.type.width > 32: - return Gen.builder.trunc(Val, ir.IntType(32), name="trunc_int") - return Val - elif isinstance(Val.type, ir.PointerType): - if isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8: - result = self._HandleAtoiLlvm(Val) - if result: - return result - elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)): - return Gen.builder.fptosi(Val, ir.IntType(32), name="float_to_int") - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'char': - # char(x) → i8(与 int()/bool()/float()/double() 并列的内建标量强转; - # char = t.CChar = i8) - if Node.args: - Val = self.HandleExprLlvm(Node.args[0]) - if Val: - t8_char: ir.Type = ir.IntType(8) - if isinstance(Val.type, ir.IntType): - if Val.type.width < 8: - return Gen.builder.zext(Val, t8_char, name="zext_char") - elif Val.type.width > 8: - return Gen.builder.trunc(Val, t8_char, name="trunc_char") - return Val - elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)): - return Gen.builder.fptosi(Val, t8_char, name="float_to_char") - return ir.Constant(ir.IntType(8), 0) - elif FuncName == 'float' or FuncName == 'double': - if Node.args: - Val = self.HandleExprLlvm(Node.args[0]) - if Val: - target_type = ir.DoubleType() if FuncName == 'double' else ir.FloatType() - if isinstance(Val.type, (ir.FloatType, ir.DoubleType)): - if Val.type != target_type: - if isinstance(target_type, ir.DoubleType): - return Gen.builder.fpext(Val, target_type, name="fpext_double") - return Gen.builder.fptrunc(Val, target_type, name="fptrunc_float") - return Val - elif isinstance(Val.type, ir.IntType): - if Val.type.width == 1: - return Gen.builder.uitofp(Val, target_type, name="uitofp") - return Gen.builder.sitofp(Val, target_type, name="sitofp") - return ir.Constant(ir.DoubleType() if FuncName == 'double' else ir.FloatType(), 0.0) - elif FuncName == 'min': - if len(Node.args) >= 2: - a = self.HandleExprLlvm(Node.args[0]) - b = self.HandleExprLlvm(Node.args[1]) - if a and b: - is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType)) - if is_float: - fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="min_int2float") - fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="min_int2float") - cmp = Gen.builder.fcmp_ordered('olt', fa, fb, name="min_cmp") - return Gen.builder.select(cmp, fa, fb, name="min_val") - else: - cmp = Gen.builder.icmp_signed('<', a, b, name="min_cmp") - return Gen.builder.select(cmp, a, b, name="min_val") - elif len(Node.args) == 1: - return self.HandleExprLlvm(Node.args[0]) - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'max': - if len(Node.args) >= 2: - a = self.HandleExprLlvm(Node.args[0]) - b = self.HandleExprLlvm(Node.args[1]) - if a and b: - is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType)) - if is_float: - fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="max_int2float") - fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="max_int2float") - cmp = Gen.builder.fcmp_ordered('ogt', fa, fb, name="max_cmp") - return Gen.builder.select(cmp, fa, fb, name="max_val") - else: - cmp = Gen.builder.icmp_signed('>', a, b, name="max_cmp") - return Gen.builder.select(cmp, a, b, name="max_val") - elif len(Node.args) == 1: - return self.HandleExprLlvm(Node.args[0]) - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'str': - if Node.args: - arg_node = Node.args[0] - arg_val = self.HandleExprLlvm(arg_node) - if arg_val: - if isinstance(arg_val.type, ir.PointerType): - ClassName = self.Trans.ExprHandler._get_var_class(arg_node, Gen) - if ClassName and Gen._has_function(f'{ClassName}.__str__'): - if isinstance(arg_val.type, ir.PointerType) and isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8: - if ClassName in Gen.structs: - arg_val = Gen.builder.bitcast(arg_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_str") - str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [arg_val], name=f"call_{ClassName}.__str__") - return str_val - return arg_val - # str 是类型名(= t.CChar* = i8*),str(x) ≡ (str)(x) 类型转换: - # 整型(u64/sizet 地址、char 码)→ inttoptr,与 TPV - # CallGenDispatch._tc_h3_stdarg_cast 的 str 分支同语义 - # (CallGenCtor._translate_t_type_cast 的「整数 → 指针: inttoptr」)。 - # 曾用 _EmitStringFromValue 把整数 snprintf 进栈缓冲并返回栈地址 - # → free/写入即 bad-free(StubMergerBuild.vp 崩溃根因)。 - if isinstance(arg_val.type, ir.IntType): - return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="str_int2ptr") - return ir.Constant(ir.IntType(8).as_pointer(), None) - elif FuncName == 'bytes': - # bytes() — 类型转换为 i8* 指针 (等同于 t.CPtr / str) - # bytes(int_addr) → inttoptr; bytes(ptr) → bitcast - if Node.args: - arg_val = self.HandleExprLlvm(Node.args[0]) - if arg_val: - if isinstance(arg_val.type, ir.PointerType): - if isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8: - return arg_val - return Gen.builder.bitcast(arg_val, ir.IntType(8).as_pointer(), name="bytes_cast") - if isinstance(arg_val.type, ir.IntType): - return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="bytes_int2ptr") - return ir.Constant(ir.IntType(8).as_pointer(), None) - elif FuncName == 'input': - return self._HandleInputLlvm(Node) - elif FuncName == 'isinstance': - # isinstance(obj, Type) — 运行时 vtable 继承链比较(2026-08-10) - # CVTable 对象头部 vtable 指针唯一标识类;isinstance = 对象 vtable - # ∈ 目标类及其 MRO 祖先的 vtable 集合(运行时真多态判断)。 - # NoVTable/基础类型/无法解析时回退编译时静态判断。 - return self._HandleIsinstanceLlvm(Node) - elif FuncName == 'ord': - if Node.args: - return self._HandleOrdLlvm(Node.args[0]) - return ir.Constant(ir.IntType(32), 0) - elif FuncName == 'chr': - if Node.args: - return self._HandleChrLlvm(Node.args[0]) - return ir.Constant(ir.IntType(8), 0) - elif FuncName == 'malloc': - return self._HandleMallocLlvm(Node.args) - elif FuncName == 'realloc': - return self._HandleReallocLlvm(Node.args) - elif FuncName == 'free': - return self._HandleFreeLlvm(Node.args) - elif FuncName == 'memcpy': - return self._HandleMemcpyLlvm(Node.args) - elif FuncName == 'memset': - return self._HandleMemsetLlvm(Node.args) - elif FuncName == 'va_start': - return self._HandleVaStartLlvm(Node.args) - elif FuncName == 'va_end': - return self._HandleVaEndLlvm(Node.args) - elif FuncName == 'va_arg': - return self._HandleVaArgLlvm(Node.args) - elif FuncName == 'dir': - return self._HandleDirLlvm(Node) - elif FuncName == 'type': - return self._HandleTypeLlvm(Node) - return None - - # ============================================================ - # isinstance - 运行时 vtable 子类集合比较(2026-08-10) - # - # CVTable 对象头部 vtable 指针唯一标识对象**实际类**(创建对象时写入 - # 实际类的 vtable)。isinstance(obj, Cls) 语义 = Cls 是实际类的祖先 - # (实际类 == Cls 或继承自 Cls)。运行时只能拿到实际类 vtable 地址, - # 无法向上推导祖先链,因此在编译期沿 class_parents 反向 BFS 枚举 - # Cls 的全部已知子类(含自身),比较 obj->vtable ∈ {Cls 子类 vtable}。 - # class_parents 由 Phase1 声明阶段对全部可达文件填充,子类集合完整。 - # NoVTable / 基础类型 / 无 vtable 可解析 → 回退编译时静态判断。 - # ============================================================ - def _HandleIsinstanceLlvm(self, Node: ast.Call) -> ir.Value: - """isinstance(obj, Cls):运行时 vtable 继承链比较(元组 Cls 支持 or 合并)""" - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if len(Node.args) < 2: - return ir.Constant(ir.IntType(1), 0) - ObjVal: ir.Value | None = self.HandleExprLlvm(Node.args[0]) - if ObjVal is None: - return ir.Constant(ir.IntType(1), 0) - ClsNames: list[str] = self._ExtractIsinstanceClassNames(Node.args[1]) - if not ClsNames: - return ir.Constant(ir.IntType(1), 0) - Res: ir.Value | None = None - for CN in ClsNames: - R: ir.Value = self._IsinstanceOne(Gen, ObjVal, CN) - if Res is None: - Res = R - else: - Res = Gen.builder.or_(Res, R, name="isinstance_or") - return Res if Res is not None else ir.Constant(ir.IntType(1), 0) - - def _ExtractIsinstanceClassNames(self, TypeNode: ast.expr) -> list[str]: - """从 isinstance 第二参数提取类名列表(支持单类 / (A, B) 元组 / 泛型特化)""" - if isinstance(TypeNode, ast.Tuple): - names: list[str] = [] - for elt in TypeNode.elts: - names.extend(self._ExtractIsinstanceClassNames(elt)) - return names - if isinstance(TypeNode, ast.Name): - return [TypeNode.id] - if isinstance(TypeNode, ast.Attribute): - return [TypeNode.attr] - if isinstance(TypeNode, ast.Subscript): - spec = self.Trans.ExprCallHandle._ResolveGenericSlice(TypeNode) - if spec: - return [spec] - return [] - - def _IsinstanceOne(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value: - """单类判断:优先运行时 vtable 比较,失败回退编译时静态判断""" - VtableAddrs: list = self._CollectSubclassVtables(Gen, ClassName) - if VtableAddrs: - try: - return self._EmitVtableCompare(Gen, ObjVal, VtableAddrs) - except Exception: - pass - return self._IsinstanceCompileTime(Gen, ObjVal, ClassName) - - def _CollectSubclassVtables(self, Gen, ClassName: str) -> list: - """收集目标类及**所有已知子类**的 vtable 全局地址(沿 class_parents 反向 BFS)。 - - isinstance(obj, Cls) 的正确语义:obj 头部 vtable 标识对象实际类 C, - 判断 Cls 是否 ∈ C 的祖先链。运行时无法从 C_vtable 向上推导祖先, - 因此在编译期反向枚举 Cls 的子树(含 Cls 自身)的 vtable 地址, - 运行时比较 obj->vtable ∈ 该集合(C 是 Cls 子类 → 命中)。 - """ - addrs: list = [] - # 反向子类表:父类名 -> [子类名...](class_parents 由 Phase1 全量填充) - children: dict[str, list[str]] = {} - for cn, parents in getattr(Gen, 'class_parents', {}).items(): - for p in parents: - children.setdefault(p, []).append(cn) - queue: list[str] = [ClassName] - seen: set[str] = set() - while queue: - cn: str = queue.pop(0) - if cn in seen: - continue - seen.add(cn) - vt = self._ResolveVtableAddr(Gen, cn) - if vt is not None: - addrs.append(vt) - for ch in children.get(cn, []): - if ch not in seen: - queue.append(ch) - return addrs - - def _ResolveVtableAddr(self, Gen, ClassName: str): - """解析类 ClassName 的 vtable 全局地址(本地 Vtables / 跨模块外部引用)。 - - 仅对 CVTable 类返回地址;NoVTable 类(class_vtable 与 - _cross_module_vtable_classes 均不含)没有 vtable 全局变量, - 返回 None 让调用方回退编译时静态判断(避免生成对不存在的 - sha1.Class_Vtable 符号的引用导致链接失败)。 - """ - has_vtable: bool = (ClassName in Gen.class_vtable - or ClassName in Gen._cross_module_vtable_classes) - if not has_vtable: - return None - vt = Gen.Vtables.get(ClassName) if hasattr(Gen, 'Vtables') else None - if vt is not None: - return vt - sha1_map: dict = getattr(Gen, 'class_sha1_map', {}) - sha1 = sha1_map.get(ClassName) - if not sha1: - return None - vname: str = f"{sha1}.{ClassName}_Vtable" - if vname in Gen.module.globals: - return Gen.module.globals[vname] - try: - # 外部引用声明为 i8*(而非 [N x i8*]),避免与定义模块 - # 不同长度的 array 类型在链接时 type mismatch - ext = ir.GlobalVariable(Gen.module, ir.PointerType(ir.IntType(8)), name=vname) - ext.linkage = 'external' - return ext - except Exception: - return None - - def _EmitVtableCompare(self, Gen, ObjVal: ir.Value, VtableAddrs: list) -> ir.Value: - """运行时:读对象头部 vtable 指针,与集合中每个 vtable 地址比较""" - ObjPtr: ir.Value = ObjVal - if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): - # 二级指针(变量 alloca / 联合 Class|t.CPtr)→ 先 load 出对象指针 - ObjPtr = Gen._load(ObjVal, name="isinstance_load") - if not isinstance(ObjPtr.type, ir.PointerType): - return self._IsinstanceCompileTime(Gen, ObjVal, '') - ObjAsVt: ir.Value = Gen.builder.bitcast(ObjPtr, ir.PointerType(ir.PointerType(ir.IntType(8))), name="isinstance_cast") - VtPtr: ir.Value = Gen._load(ObjAsVt, name="isinstance_vt") - Res: ir.Value | None = None - for addr in VtableAddrs: - if addr.type == ir.PointerType(ir.IntType(8)): - AddrI8: ir.Value = addr - else: - AddrI8 = Gen.builder.bitcast(addr, ir.PointerType(ir.IntType(8)), name="vt_addr") - Cmp: ir.Value = Gen.builder.icmp_unsigned('==', VtPtr, AddrI8, name="isinstance_cmp") - if Res is None: - Res = Cmp - else: - Res = Gen.builder.or_(Res, Cmp, name="isinstance_or") - return Res if Res is not None else ir.Constant(ir.IntType(1), 0) - - def _IsinstanceCompileTime(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value: - """编译时静态判断:obj 静态类型 == ClassName 或其 MRO 后代 → 1,否则 0""" - if ClassName: - ObjType: ir.Type = ObjVal.type - if isinstance(ObjType, ir.PointerType): - ObjType = ObjType.pointee - if isinstance(ObjType, ir.IdentifiedStructType): - ShortName: str = Gen._extract_short_name(ObjType.name) - mro: list = Gen._get_mro_chain(ClassName) if hasattr(Gen, '_get_mro_chain') else [ClassName] - if ShortName == ClassName or ShortName in mro: - return ir.Constant(ir.IntType(1), 1) - # obj 是 ClassName 的子类(obj 的 MRO 含 ClassName) - obj_mro: list = Gen._get_mro_chain(ShortName) if hasattr(Gen, '_get_mro_chain') else [] - if ClassName in obj_mro: - return ir.Constant(ir.IntType(1), 1) - # 无法判断 → 保守 false - return ir.Constant(ir.IntType(1), 0) - - def _HandlePrintLlvm(self, Node: ast.Call) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) - args: list[ast.expr] = Node.args - end_arg: ast.expr | None = None - sep_arg: ast.expr | None = None - if Node.keywords: - for kw in Node.keywords: - if kw.arg == 'end': - end_arg = kw.value - elif kw.arg == 'sep': - sep_arg = kw.value - sep_str: str = ' ' - if sep_arg and isinstance(sep_arg, ast.Constant) and isinstance(sep_arg.value, str): - sep_str = sep_arg.value - end_str: str = '\n' - if end_arg: - if isinstance(end_arg, ast.Constant) and isinstance(end_arg.value, str): - end_str = end_arg.value - elif isinstance(end_arg, ast.Constant) and end_arg.value is None: - end_str = '' - if not args: - if end_str: - nl_fmt = Gen.emit_constant(end_str, 'string') - Gen.builder.call(printf_func, [nl_fmt], name="print_nl") - return ir.Constant(ir.IntType(32), 0) - has_fstring = any(isinstance(arg, ast.JoinedStr) for arg in args) - if has_fstring: - return self._HandlePrintWithFString(Node, printf_func, args, sep_str, end_str) - fmt_parts = [] - fmt_args = [] - for i, arg in enumerate(args): - if i > 0: - fmt_parts.append(sep_str) - val = self.HandleExprLlvm(arg) - if not val: - fmt_parts.append('(null)') - continue - if isinstance(val, ir.Constant) and isinstance(val.type, ir.PointerType) and val.constant is None: - fmt_parts.append('nil') - continue - if isinstance(val.type, ir.PointerType): - pointee = val.type.pointee - if isinstance(pointee, ir.IntType) and pointee.width == 8: - fmt_parts.append('%s') - fmt_args.append(val) - elif isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8: - elem_ptr = Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="print_arr_to_ptr") - fmt_parts.append('%s') - fmt_args.append(elem_ptr) - elif isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen) - if not ClassName and isinstance(arg, ast.Name): - ClassName = Gen.var_struct_class.get(arg.id) - if ClassName and Gen._has_function(f'{ClassName}.__str__'): - if isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8: - if ClassName in Gen.structs: - val = Gen.builder.bitcast(val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [val], name=f"call_{ClassName}.__str__") - fmt_parts.append('%s') - fmt_args.append(str_val) - else: - int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int") - fmt_parts.append('0x%llx') - fmt_args.append(int_val) - else: - int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int") - fmt_parts.append('%lld') - fmt_args.append(int_val) - elif isinstance(val.type, ir.IntType): - is_unsigned = self._is_val_unsigned(arg, val) - if val.type.width == 1: - fmt_parts.append('%d') - val = Gen.builder.zext(val, ir.IntType(32), name="zext_bool_print") - elif val.type.width == 8: - if self._is_char_type(arg): - char_var = Gen._allocaEntry(ir.IntType(8), name="print_char_buf") - Gen.builder.store(val, char_var) - fmt_parts.append('%c') - fmt_args.append(char_var) - continue - fmt_parts.append('%d') - if is_unsigned: - val = Gen.builder.zext(val, ir.IntType(32), name="zext_i8_print") - else: - val = Gen.builder.sext(val, ir.IntType(32), name="sext_i8_print") - elif val.type.width == 16: - fmt_parts.append('%u' if is_unsigned else '%d') - if is_unsigned: - val = Gen.builder.zext(val, ir.IntType(32), name="zext_i16_print") - else: - val = Gen.builder.sext(val, ir.IntType(32), name="sext_i16_print") - elif val.type.width == 32: - fmt_parts.append('%u' if is_unsigned else '%d') - elif val.type.width == 64: - fmt_parts.append('%llu' if is_unsigned else '%lld') - else: - fmt_parts.append('%d') - val = Gen.builder.zext(val, ir.IntType(32), name="zext_int_print") - fmt_args.append(val) - elif isinstance(val.type, ir.FloatType): - fmt_parts.append('%f') - fmt_args.append(Gen.builder.fpext(val, ir.DoubleType(), name="fpext_printf_float")) - elif isinstance(val.type, ir.DoubleType): - fmt_parts.append('%f') - fmt_args.append(val) - else: - fmt_parts.append('(unknown)') - fmt_parts.append(end_str) - fmt_str = ''.join(fmt_parts) - fmt_ptr = Gen._create_string_global(fmt_str) - call_args = [fmt_ptr] + fmt_args - return Gen.builder.call(printf_func, call_args, name="print_call") - - def _HandlePrintWithFString(self, Node: ast.Call, printf_func: ir.Function, args: list[ast.expr], sep_str: str, end_str: str) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - for i, arg in enumerate(args): - if i > 0: - sep_const = Gen.emit_constant(sep_str, 'string') - Gen.builder.call(printf_func, [sep_const], name="print_sep") - if isinstance(arg, ast.JoinedStr): - str_val = self.HandleExprLlvm(arg) - if str_val: - Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None) - else: - ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen) - if ClassName and Gen._has_function(f'{ClassName}.__str__'): - obj_val = self.HandleExprLlvm(arg) - if obj_val: - if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: - if ClassName in Gen.structs: - obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [obj_val], name=f"call_{ClassName}.__str__") - Gen._RegisterTempPtr(str_val) - Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None) - else: - val = self.HandleExprLlvm(arg) - if val: - if isinstance(val.type, ir.PointerType): - pointee = val.type.pointee - if not (isinstance(pointee, ir.IntType) and pointee.width == 8): - try: - val = Gen._load(val, name="print_Load") - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - is_u = Gen._check_node_unsigned(arg) - if not is_u and id(val) in Gen._unsigned_results: - is_u = True - Gen._emit_printf([val], unsigned_flags=[is_u], sep=None, end=None) - if end_str: - end_const = Gen.emit_constant(end_str, 'string') - Gen.builder.call(printf_func, [end_const], name="print_end") - return ir.Constant(ir.IntType(32), 0) - - def _is_char_type(self, arg_node: ast.AST) -> bool: - if isinstance(arg_node, ast.Call): - if isinstance(arg_node.func, ast.Name): - return arg_node.func.id in ('CChar', 'CUInt8T', 'CInt8T') - if isinstance(arg_node.func, ast.Attribute): - return arg_node.func.attr in ('CChar', 'CUInt8T', 'CInt8T') - return False - - def _is_val_unsigned(self, arg_node: ast.AST, val: ir.Value) -> bool: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if isinstance(arg_node, ast.Name): - var_name: str = arg_node.id - signedness: object = Gen.var_signedness.get(var_name) - if signedness: - if isinstance(signedness, bool): - return signedness - return 'unsigned' in signedness - if isinstance(val.type, ir.IntType): - return False - return False - - def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None: - if func_name not in self._C_LIB_FUNCS: - return None - ret_type: ir.Type - param_types: list[ir.Type] - ret_type, param_types = self._C_LIB_FUNCS[func_name]() - func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types) - # C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号: - # 裸建会在第二次抛 llvmlite DuplicatedNameError。 - return Gen._CreateOrReuseFuncDecl(func_name, func_type) - +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +import vast as ast +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes.t import CTypeRegistry +from lib.includes import t as _t +from lib.core.SymbolUtils import IsTModule + + +# len() 参数形态不支持时的共享提示语(与 ViperC App/lib/core/Gen/Call/ +# CallGenDispatch.vp 的 len() 分支同口径,措辞逐字一致,便于用户对照两侧报文)。 +_LEN_SUPPORTED_HINT: str = ( + "len() 只支持定长数组/数组指针(t.CArray[T, N])、i8*(str/bytes)或带 __len__ 的结构体;" + "非 i8 裸指针/标量指针没有长度语义:字符串请用 str/bytes,需要元素个数请自行记录长度," + "或改用 t.CPtr[元素] 并显式维护元素计数" +) + + +class ExprBuiltinHandle(BaseHandle): + _C_LIB_FUNCS: dict[str, object] = { + 'strlen': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'strlength': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'memset': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)]), + 'memcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'memmove': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'memcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'strncmp': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'strncpy': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64)]), + 'strcat': lambda: (ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8))]), + 'puts': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + 'atoi': lambda: (ir.IntType(32), [ir.PointerType(ir.IntType(8))]), + 'atol': lambda: (ir.IntType(64), [ir.PointerType(ir.IntType(8))]), + 'abs': lambda: (ir.IntType(32), [ir.IntType(32)]), + 'labs': lambda: (ir.IntType(64), [ir.IntType(64)]), + } + + def _HandleBuiltinCallLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not isinstance(Node.func, ast.Name): + return None + FuncName: str = Node.func.id + + # Two-arg scalar alias form (int(v, t.CPtr) / char / bool / float / double) + # is a pointer wrap, not a scalar cast: route it to the same exit as the + # attribute form t.CInt(v, t.CPtr). Ignoring the CPtr mark would degrade + # it to a bare trunc, e.g. includes/argparse.vp get_bool() reading a + # pointer's low 32 bits instead of the stored value. + if (len(Node.args) >= 2 and FuncName in ('int', 'char', 'bool', 'float', 'double') + and self.Trans.ExprCallHandle._IsCPtrMark(Node.args[1])): + PtrCastAttr: str = 'C' + FuncName[0].upper() + FuncName[1:] + return self.Trans.TSpecialCallHandle._HandleTPtrCastLlvm(Node, PtrCastAttr) + + if FuncName == 'print': + return self._HandlePrintLlvm(Node) + elif FuncName == 'len': + if Node.args: + return self._HandleLenLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'sizeof': + if Node.args: + return self._HandleSizeofLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'abs': + if Node.args: + return self._HandleAbsLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'arg': + return self._HandleArgLlvm(Node.args) + elif FuncName == 'bool': + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + if isinstance(Val.type, ir.IntType): + if Val.type.width == 1: + return Val + return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result") + elif isinstance(Val.type, ir.PointerType): + return Gen.builder.icmp_signed('!=', Val, Gen._ZeroConst(Val.type), name="bool_result") + return Val + return ir.Constant(ir.IntType(1), 0) + elif FuncName == 'int': + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + if isinstance(Val.type, ir.IntType): + if Val.type.width < 32: + return Gen.builder.zext(Val, ir.IntType(32), name="cast_int") + elif Val.type.width > 32: + return Gen.builder.trunc(Val, ir.IntType(32), name="trunc_int") + return Val + elif isinstance(Val.type, ir.PointerType): + if isinstance(Val.type.pointee, ir.IntType) and Val.type.pointee.width == 8: + result = self._HandleAtoiLlvm(Val) + if result: + return result + elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fptosi(Val, ir.IntType(32), name="float_to_int") + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'char': + # char(x) → i8(与 int()/bool()/float()/double() 并列的内建标量强转; + # char = t.CChar = i8) + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + t8_char: ir.Type = ir.IntType(8) + if isinstance(Val.type, ir.IntType): + if Val.type.width < 8: + return Gen.builder.zext(Val, t8_char, name="zext_char") + elif Val.type.width > 8: + return Gen.builder.trunc(Val, t8_char, name="trunc_char") + return Val + elif isinstance(Val.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fptosi(Val, t8_char, name="float_to_char") + return ir.Constant(ir.IntType(8), 0) + elif FuncName == 'float' or FuncName == 'double': + if Node.args: + Val = self.HandleExprLlvm(Node.args[0]) + if Val: + target_type = ir.DoubleType() if FuncName == 'double' else ir.FloatType() + if isinstance(Val.type, (ir.FloatType, ir.DoubleType)): + if Val.type != target_type: + if isinstance(target_type, ir.DoubleType): + return Gen.builder.fpext(Val, target_type, name="fpext_double") + return Gen.builder.fptrunc(Val, target_type, name="fptrunc_float") + return Val + elif isinstance(Val.type, ir.IntType): + if Val.type.width == 1: + return Gen.builder.uitofp(Val, target_type, name="uitofp") + return Gen.builder.sitofp(Val, target_type, name="sitofp") + return ir.Constant(ir.DoubleType() if FuncName == 'double' else ir.FloatType(), 0.0) + elif FuncName == 'min': + if len(Node.args) >= 2: + a = self.HandleExprLlvm(Node.args[0]) + b = self.HandleExprLlvm(Node.args[1]) + if a and b: + is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType)) + if is_float: + fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="min_int2float") + fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="min_int2float") + cmp = Gen.builder.fcmp_ordered('olt', fa, fb, name="min_cmp") + return Gen.builder.select(cmp, fa, fb, name="min_val") + else: + cmp = Gen.builder.icmp_signed('<', a, b, name="min_cmp") + return Gen.builder.select(cmp, a, b, name="min_val") + elif len(Node.args) == 1: + return self.HandleExprLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'max': + if len(Node.args) >= 2: + a = self.HandleExprLlvm(Node.args[0]) + b = self.HandleExprLlvm(Node.args[1]) + if a and b: + is_float = isinstance(a.type, (ir.FloatType, ir.DoubleType)) or isinstance(b.type, (ir.FloatType, ir.DoubleType)) + if is_float: + fa = Gen._to_float(a, ir.FloatType()) if isinstance(a.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(a, ir.FloatType(), name="max_int2float") + fb = Gen._to_float(b, ir.FloatType()) if isinstance(b.type, (ir.FloatType, ir.DoubleType)) else Gen.builder.sitofp(b, ir.FloatType(), name="max_int2float") + cmp = Gen.builder.fcmp_ordered('ogt', fa, fb, name="max_cmp") + return Gen.builder.select(cmp, fa, fb, name="max_val") + else: + cmp = Gen.builder.icmp_signed('>', a, b, name="max_cmp") + return Gen.builder.select(cmp, a, b, name="max_val") + elif len(Node.args) == 1: + return self.HandleExprLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'str': + if Node.args: + arg_node = Node.args[0] + arg_val = self.HandleExprLlvm(arg_node) + if arg_val: + if isinstance(arg_val.type, ir.PointerType): + ClassName = self.Trans.ExprHandler._get_var_class(arg_node, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__str__'): + if isinstance(arg_val.type, ir.PointerType) and isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8: + if ClassName in Gen.structs: + arg_val = Gen.builder.bitcast(arg_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}_str") + str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [arg_val], name=f"call_{ClassName}.__str__") + return str_val + return arg_val + # str 是类型名(= t.CChar* = i8*),str(x) ≡ (str)(x) 类型转换: + # 整型(u64/sizet 地址、char 码)→ inttoptr,与 VPC + # CallGenDispatch._tc_h3_stdarg_cast 的 str 分支同语义 + # (CallGenCtor._translate_t_type_cast 的「整数 → 指针: inttoptr」)。 + # 曾用 _EmitStringFromValue 把整数 snprintf 进栈缓冲并返回栈地址 + # → free/写入即 bad-free(StubMergerBuild.vp 崩溃根因)。 + if isinstance(arg_val.type, ir.IntType): + return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="str_int2ptr") + return ir.Constant(ir.IntType(8).as_pointer(), None) + elif FuncName == 'bytes': + # bytes() — 类型转换为 i8* 指针 (等同于 t.CPtr / str) + # bytes(int_addr) → inttoptr; bytes(ptr) → bitcast + if Node.args: + arg_val = self.HandleExprLlvm(Node.args[0]) + if arg_val: + if isinstance(arg_val.type, ir.PointerType): + if isinstance(arg_val.type.pointee, ir.IntType) and arg_val.type.pointee.width == 8: + return arg_val + return Gen.builder.bitcast(arg_val, ir.IntType(8).as_pointer(), name="bytes_cast") + if isinstance(arg_val.type, ir.IntType): + return Gen.builder.inttoptr(arg_val, ir.IntType(8).as_pointer(), name="bytes_int2ptr") + return ir.Constant(ir.IntType(8).as_pointer(), None) + elif FuncName == 'input': + return self._HandleInputLlvm(Node) + elif FuncName == 'isinstance': + # isinstance(obj, Type) — 运行时 vtable 继承链比较(2026-08-10) + # CVTable 对象头部 vtable 指针唯一标识类;isinstance = 对象 vtable + # ∈ 目标类及其 MRO 祖先的 vtable 集合(运行时真多态判断)。 + # NoVTable/基础类型/无法解析时回退编译时静态判断。 + return self._HandleIsinstanceLlvm(Node) + elif FuncName == 'ord': + if Node.args: + return self._HandleOrdLlvm(Node.args[0]) + return ir.Constant(ir.IntType(32), 0) + elif FuncName == 'chr': + if Node.args: + return self._HandleChrLlvm(Node.args[0]) + return ir.Constant(ir.IntType(8), 0) + elif FuncName == 'malloc': + return self._HandleMallocLlvm(Node.args) + elif FuncName == 'realloc': + return self._HandleReallocLlvm(Node.args) + elif FuncName == 'free': + return self._HandleFreeLlvm(Node.args) + elif FuncName == 'memcpy': + return self._HandleMemcpyLlvm(Node.args) + elif FuncName == 'memset': + return self._HandleMemsetLlvm(Node.args) + elif FuncName == 'va_start': + return self._HandleVaStartLlvm(Node.args) + elif FuncName == 'va_end': + return self._HandleVaEndLlvm(Node.args) + elif FuncName == 'va_arg': + return self._HandleVaArgLlvm(Node.args) + elif FuncName == 'dir': + return self._HandleDirLlvm(Node) + elif FuncName == 'type': + return self._HandleTypeLlvm(Node) + return None + + # ============================================================ + # isinstance - 运行时 vtable 子类集合比较(2026-08-10) + # + # CVTable 对象头部 vtable 指针唯一标识对象**实际类**(创建对象时写入 + # 实际类的 vtable)。isinstance(obj, Cls) 语义 = Cls 是实际类的祖先 + # (实际类 == Cls 或继承自 Cls)。运行时只能拿到实际类 vtable 地址, + # 无法向上推导祖先链,因此在编译期沿 class_parents 反向 BFS 枚举 + # Cls 的全部已知子类(含自身),比较 obj->vtable ∈ {Cls 子类 vtable}。 + # class_parents 由 Phase1 声明阶段对全部可达文件填充,子类集合完整。 + # NoVTable / 基础类型 / 无 vtable 可解析 → 回退编译时静态判断。 + # ============================================================ + def _HandleIsinstanceLlvm(self, Node: ast.Call) -> ir.Value: + """isinstance(obj, Cls):运行时 vtable 继承链比较(元组 Cls 支持 or 合并)""" + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if len(Node.args) < 2: + return ir.Constant(ir.IntType(1), 0) + ObjVal: ir.Value | None = self.HandleExprLlvm(Node.args[0]) + if ObjVal is None: + return ir.Constant(ir.IntType(1), 0) + ClsNames: list[str] = self._ExtractIsinstanceClassNames(Node.args[1]) + if not ClsNames: + return ir.Constant(ir.IntType(1), 0) + Res: ir.Value | None = None + for CN in ClsNames: + R: ir.Value = self._IsinstanceOne(Gen, ObjVal, CN) + if Res is None: + Res = R + else: + Res = Gen.builder.or_(Res, R, name="isinstance_or") + return Res if Res is not None else ir.Constant(ir.IntType(1), 0) + + def _ExtractIsinstanceClassNames(self, TypeNode: ast.expr) -> list[str]: + """从 isinstance 第二参数提取类名列表(支持单类 / (A, B) 元组 / 泛型特化)""" + if isinstance(TypeNode, ast.Tuple): + names: list[str] = [] + for elt in TypeNode.elts: + names.extend(self._ExtractIsinstanceClassNames(elt)) + return names + if isinstance(TypeNode, ast.Name): + return [TypeNode.id] + if isinstance(TypeNode, ast.Attribute): + return [TypeNode.attr] + if isinstance(TypeNode, ast.Subscript): + spec = self.Trans.ExprCallHandle._ResolveGenericSlice(TypeNode) + if spec: + return [spec] + return [] + + def _IsinstanceOne(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value: + """单类判断:优先运行时 vtable 比较,失败回退编译时静态判断""" + VtableAddrs: list = self._CollectSubclassVtables(Gen, ClassName) + if VtableAddrs: + try: + return self._EmitVtableCompare(Gen, ObjVal, VtableAddrs) + except Exception: + pass + return self._IsinstanceCompileTime(Gen, ObjVal, ClassName) + + def _CollectSubclassVtables(self, Gen, ClassName: str) -> list: + """收集目标类及**所有已知子类**的 vtable 全局地址(沿 class_parents 反向 BFS)。 + + isinstance(obj, Cls) 的正确语义:obj 头部 vtable 标识对象实际类 C, + 判断 Cls 是否 ∈ C 的祖先链。运行时无法从 C_vtable 向上推导祖先, + 因此在编译期反向枚举 Cls 的子树(含 Cls 自身)的 vtable 地址, + 运行时比较 obj->vtable ∈ 该集合(C 是 Cls 子类 → 命中)。 + """ + addrs: list = [] + # 反向子类表:父类名 -> [子类名...](class_parents 由 Phase1 全量填充) + children: dict[str, list[str]] = {} + for cn, parents in getattr(Gen, 'class_parents', {}).items(): + for p in parents: + children.setdefault(p, []).append(cn) + queue: list[str] = [ClassName] + seen: set[str] = set() + while queue: + cn: str = queue.pop(0) + if cn in seen: + continue + seen.add(cn) + vt = self._ResolveVtableAddr(Gen, cn) + if vt is not None: + addrs.append(vt) + for ch in children.get(cn, []): + if ch not in seen: + queue.append(ch) + return addrs + + def _ResolveVtableAddr(self, Gen, ClassName: str): + """解析类 ClassName 的 vtable 全局地址(本地 Vtables / 跨模块外部引用)。 + + 仅对 CVTable 类返回地址;NoVTable 类(class_vtable 与 + _cross_module_vtable_classes 均不含)没有 vtable 全局变量, + 返回 None 让调用方回退编译时静态判断(避免生成对不存在的 + sha1.Class_Vtable 符号的引用导致链接失败)。 + """ + has_vtable: bool = (ClassName in Gen.class_vtable + or ClassName in Gen._cross_module_vtable_classes) + if not has_vtable: + return None + vt = Gen.Vtables.get(ClassName) if hasattr(Gen, 'Vtables') else None + if vt is not None: + return vt + sha1_map: dict = getattr(Gen, 'class_sha1_map', {}) + sha1 = sha1_map.get(ClassName) + if not sha1: + return None + vname: str = f"{sha1}.{ClassName}_Vtable" + if vname in Gen.module.globals: + return Gen.module.globals[vname] + try: + # 外部引用声明为 i8*(而非 [N x i8*]),避免与定义模块 + # 不同长度的 array 类型在链接时 type mismatch + ext = ir.GlobalVariable(Gen.module, ir.PointerType(ir.IntType(8)), name=vname) + ext.linkage = 'external' + return ext + except Exception: + return None + + def _EmitVtableCompare(self, Gen, ObjVal: ir.Value, VtableAddrs: list) -> ir.Value: + """运行时:读对象头部 vtable 指针,与集合中每个 vtable 地址比较""" + ObjPtr: ir.Value = ObjVal + if isinstance(ObjVal.type, ir.PointerType) and isinstance(ObjVal.type.pointee, ir.PointerType): + # 二级指针(变量 alloca / 联合 Class|t.CPtr)→ 先 load 出对象指针 + ObjPtr = Gen._load(ObjVal, name="isinstance_load") + if not isinstance(ObjPtr.type, ir.PointerType): + return self._IsinstanceCompileTime(Gen, ObjVal, '') + ObjAsVt: ir.Value = Gen.builder.bitcast(ObjPtr, ir.PointerType(ir.PointerType(ir.IntType(8))), name="isinstance_cast") + VtPtr: ir.Value = Gen._load(ObjAsVt, name="isinstance_vt") + Res: ir.Value | None = None + for addr in VtableAddrs: + if addr.type == ir.PointerType(ir.IntType(8)): + AddrI8: ir.Value = addr + else: + AddrI8 = Gen.builder.bitcast(addr, ir.PointerType(ir.IntType(8)), name="vt_addr") + Cmp: ir.Value = Gen.builder.icmp_unsigned('==', VtPtr, AddrI8, name="isinstance_cmp") + if Res is None: + Res = Cmp + else: + Res = Gen.builder.or_(Res, Cmp, name="isinstance_or") + return Res if Res is not None else ir.Constant(ir.IntType(1), 0) + + def _IsinstanceCompileTime(self, Gen, ObjVal: ir.Value, ClassName: str) -> ir.Value: + """编译时静态判断:obj 静态类型 == ClassName 或其 MRO 后代 → 1,否则 0""" + if ClassName: + ObjType: ir.Type = ObjVal.type + if isinstance(ObjType, ir.PointerType): + ObjType = ObjType.pointee + if isinstance(ObjType, ir.IdentifiedStructType): + ShortName: str = Gen._extract_short_name(ObjType.name) + mro: list = Gen._get_mro_chain(ClassName) if hasattr(Gen, '_get_mro_chain') else [ClassName] + if ShortName == ClassName or ShortName in mro: + return ir.Constant(ir.IntType(1), 1) + # obj 是 ClassName 的子类(obj 的 MRO 含 ClassName) + obj_mro: list = Gen._get_mro_chain(ShortName) if hasattr(Gen, '_get_mro_chain') else [] + if ClassName in obj_mro: + return ir.Constant(ir.IntType(1), 1) + # 无法判断 → 保守 false + return ir.Constant(ir.IntType(1), 0) + + def _HandlePrintLlvm(self, Node: ast.Call) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + printf_func: ir.Function = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + args: list[ast.expr] = Node.args + end_arg: ast.expr | None = None + sep_arg: ast.expr | None = None + if Node.keywords: + for kw in Node.keywords: + if kw.arg == 'end': + end_arg = kw.value + elif kw.arg == 'sep': + sep_arg = kw.value + sep_str: str = ' ' + if sep_arg and isinstance(sep_arg, ast.Constant) and isinstance(sep_arg.value, str): + sep_str = sep_arg.value + end_str: str = '\n' + if end_arg: + if isinstance(end_arg, ast.Constant) and isinstance(end_arg.value, str): + end_str = end_arg.value + elif isinstance(end_arg, ast.Constant) and end_arg.value is None: + end_str = '' + if not args: + if end_str: + nl_fmt = Gen.emit_constant(end_str, 'string') + Gen.builder.call(printf_func, [nl_fmt], name="print_nl") + return ir.Constant(ir.IntType(32), 0) + has_fstring = any(isinstance(arg, ast.JoinedStr) for arg in args) + if has_fstring: + return self._HandlePrintWithFString(Node, printf_func, args, sep_str, end_str) + fmt_parts = [] + fmt_args = [] + for i, arg in enumerate(args): + if i > 0: + fmt_parts.append(sep_str) + val = self.HandleExprLlvm(arg) + if not val: + fmt_parts.append('(null)') + continue + if isinstance(val, ir.Constant) and isinstance(val.type, ir.PointerType) and val.constant is None: + fmt_parts.append('nil') + continue + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, ir.IntType) and pointee.width == 8: + fmt_parts.append('%s') + fmt_args.append(val) + elif isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8: + elem_ptr = Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="print_arr_to_ptr") + fmt_parts.append('%s') + fmt_args.append(elem_ptr) + elif isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen) + if not ClassName and isinstance(arg, ast.Name): + ClassName = Gen.var_struct_class.get(arg.id) + if ClassName and Gen._has_function(f'{ClassName}.__str__'): + if isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8: + if ClassName in Gen.structs: + val = Gen.builder.bitcast(val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [val], name=f"call_{ClassName}.__str__") + fmt_parts.append('%s') + fmt_args.append(str_val) + else: + int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int") + fmt_parts.append('0x%llx') + fmt_args.append(int_val) + else: + int_val = Gen.builder.ptrtoint(val, ir.IntType(64), name="print_ptr_to_int") + fmt_parts.append('%lld') + fmt_args.append(int_val) + elif isinstance(val.type, ir.IntType): + is_unsigned = self._is_val_unsigned(arg, val) + if val.type.width == 1: + fmt_parts.append('%d') + val = Gen.builder.zext(val, ir.IntType(32), name="zext_bool_print") + elif val.type.width == 8: + if self._is_char_type(arg): + char_var = Gen._allocaEntry(ir.IntType(8), name="print_char_buf") + Gen.builder.store(val, char_var) + fmt_parts.append('%c') + fmt_args.append(char_var) + continue + fmt_parts.append('%d') + if is_unsigned: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_i8_print") + else: + val = Gen.builder.sext(val, ir.IntType(32), name="sext_i8_print") + elif val.type.width == 16: + fmt_parts.append('%u' if is_unsigned else '%d') + if is_unsigned: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_i16_print") + else: + val = Gen.builder.sext(val, ir.IntType(32), name="sext_i16_print") + elif val.type.width == 32: + fmt_parts.append('%u' if is_unsigned else '%d') + elif val.type.width == 64: + fmt_parts.append('%llu' if is_unsigned else '%lld') + else: + fmt_parts.append('%d') + val = Gen.builder.zext(val, ir.IntType(32), name="zext_int_print") + fmt_args.append(val) + elif isinstance(val.type, ir.FloatType): + fmt_parts.append('%f') + fmt_args.append(Gen.builder.fpext(val, ir.DoubleType(), name="fpext_printf_float")) + elif isinstance(val.type, ir.DoubleType): + fmt_parts.append('%f') + fmt_args.append(val) + else: + fmt_parts.append('(unknown)') + fmt_parts.append(end_str) + fmt_str = ''.join(fmt_parts) + fmt_ptr = Gen._create_string_global(fmt_str) + call_args = [fmt_ptr] + fmt_args + return Gen.builder.call(printf_func, call_args, name="print_call") + + def _HandlePrintWithFString(self, Node: ast.Call, printf_func: ir.Function, args: list[ast.expr], sep_str: str, end_str: str) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + for i, arg in enumerate(args): + if i > 0: + sep_const = Gen.emit_constant(sep_str, 'string') + Gen.builder.call(printf_func, [sep_const], name="print_sep") + if isinstance(arg, ast.JoinedStr): + str_val = self.HandleExprLlvm(arg) + if str_val: + Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None) + else: + ClassName = self.Trans.ExprHandler._get_var_class(arg, Gen) + if ClassName and Gen._has_function(f'{ClassName}.__str__'): + obj_val = self.HandleExprLlvm(arg) + if obj_val: + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + if ClassName in Gen.structs: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + str_val = Gen.builder.call(Gen._get_function(f'{ClassName}.__str__'), [obj_val], name=f"call_{ClassName}.__str__") + Gen._RegisterTempPtr(str_val) + Gen._emit_printf([str_val], unsigned_flags=[False], sep=None, end=None) + else: + val = self.HandleExprLlvm(arg) + if val: + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if not (isinstance(pointee, ir.IntType) and pointee.width == 8): + try: + val = Gen._load(val, name="print_Load") + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + is_u = Gen._check_node_unsigned(arg) + if not is_u and id(val) in Gen._unsigned_results: + is_u = True + Gen._emit_printf([val], unsigned_flags=[is_u], sep=None, end=None) + if end_str: + end_const = Gen.emit_constant(end_str, 'string') + Gen.builder.call(printf_func, [end_const], name="print_end") + return ir.Constant(ir.IntType(32), 0) + + def _is_char_type(self, arg_node: ast.AST) -> bool: + if isinstance(arg_node, ast.Call): + if isinstance(arg_node.func, ast.Name): + return arg_node.func.id in ('CChar', 'CUInt8T', 'CInt8T') + if isinstance(arg_node.func, ast.Attribute): + return arg_node.func.attr in ('CChar', 'CUInt8T', 'CInt8T') + return False + + def _is_val_unsigned(self, arg_node: ast.AST, val: ir.Value) -> bool: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if isinstance(arg_node, ast.Name): + var_name: str = arg_node.id + signedness: object = Gen.var_signedness.get(var_name) + if signedness: + if isinstance(signedness, bool): + return signedness + return 'unsigned' in signedness + if isinstance(val.type, ir.IntType): + return False + return False + + def _try_declare_c_lib_func(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.Function | None: + if func_name not in self._C_LIB_FUNCS: + return None + ret_type: ir.Type + param_types: list[ir.Type] + ret_type, param_types = self._C_LIB_FUNCS[func_name]() + func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types) + # C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号: + # 裸建会在第二次抛 llvmlite DuplicatedNameError。 + return Gen._CreateOrReuseFuncDecl(func_name, func_type) + def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin, val: ir.Value | None, reason: str) -> None: - """len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 TransPyV 同口径)。 + """len(x) 参数形态不支持时的编译期 fail-fast 出口(TPC 与 ViperC 同口径)。 - 规则(对齐 TransPyV App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支): + 规则(对齐 ViperC App/lib/core/Gen/Call/CallGenDispatch.vp 的 len() 分支): 定长数组/数组指针 → 常量 N;i8*(str/bytes)→ strlen; 带 __len__ 的结构体 → 调用 __len__;其余一律编译期报错。 非 i8 裸指针(如 `[]T` 形参实为 `T*`)与标量指针**没有长度语义**,旧实现会 @@ -625,676 +625,676 @@ class ExprBuiltinHandle(BaseHandle): + (f",源码: {code_line}" if code_line else '')) raise TypeError(msg) - def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - # len(obj.attr):obj 可为 self 或任意局部变量(VpiReader 的 - # len(simp.names) 即非 self 场景)。字段静态类型若退化为 i8* - # (跨模块 stub 的 rawptr[list[...]],stub 不注入字段类型),通用 - # strlen 分支会把 list 容器头当字符串扫 → 垃圾长度 → 越界 get → - # 读任意地址(VpiReader.vp _ParseVpiIntoModuleImpl 读 -1 根因)。 - # 此处按字段的 class_member_element_class 侧信道(记录 list 特化名) - # 分派到 list.__len__;attr__len 计数成员特例(TypeGenCType)保留。 - if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name): - obj_name = arg_node.value.id - attr_name = arg_node.attr - len_member = f'{attr_name}__len' - current_class = self.Trans._CurrentCpythonObjectClass if obj_name == 'self' else None - if current_class is None: - _vsc: dict = Gen.var_struct_class if Gen.var_struct_class else {} - _gsc: dict = getattr(Gen, 'global_struct_class', {}) or {} - current_class = _vsc.get(obj_name) or _gsc.get(obj_name) - # 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如 - # 'a351387236c6ce1c.TypeGenCType'),而 class_members / - # class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离 - # 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss, - # 整个 obj.attr 分支(含 list.__len__ 修复)被跳过,fall through - # 到 strlen(stdint.vp 0xC0000005 崩溃根因)。 - bare_cls: str | None = current_class - if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.': - bare_cls = bare_cls[17:] - if bare_cls and bare_cls in Gen.class_members: - for m_name, m_type in Gen.class_members[bare_cls]: - if m_name == len_member: - self_val = Gen._get_var_ptr(obj_name) - if self_val: - self_ptr = Gen._load(self_val, name="obj_for_len") - # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):接收者的 - # pointee 可能是**另一份 opaque 同名实例**,GEP 需要在编译期算出 - # 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。 - _STl: Any - self_ptr, _STl = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, self_ptr, bare_cls, f"cast_{bare_cls}") - offset = Gen._get_member_offset(len_member, bare_cls, _STl) - len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{len_member}_ptr") - return Gen._load(len_ptr, name=f"{obj_name}_{len_member}") - # 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段 - # 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through - # 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get - # → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class - # 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。 - field_cls: str | None = None - _elem_map: dict = getattr(Gen, 'class_member_element_class', {}) - for _mro_cls in Gen._get_mro_chain(bare_cls): - # class_member_element_class 的 key 是裸类名(如 'TypeGenBase'), - # _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。 - _mro_key: str = _mro_cls - if len(_mro_cls) > 17 and _mro_cls[16] == '.': - _mro_key = _mro_cls[17:] - _sub: dict = _elem_map.get(_mro_key, {}) - if attr_name in _sub: - field_cls = _sub[attr_name] - break - if field_cls and (field_cls == 'list' or field_cls.startswith('list[')): - self_val = Gen._get_var_ptr(obj_name) - if self_val: - self_ptr = Gen._load(self_val, name="obj_for_len") - # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase) - _STl2: Any - self_ptr, _STl2 = self.Trans.ExprAttrHandle._ConcretizeGepBase( - Gen, self_ptr, bare_cls, f"cast_{bare_cls}") - offset = Gen._get_member_offset(attr_name, bare_cls, _STl2) - if offset is not None: - list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{attr_name}_ptr") - list_val = Gen._load(list_ptr, name=f"{obj_name}_{attr_name}") - # 诊断增强(不改变任何成功路径):字段身份(class_member_element_class) - # 说是 list 容器,但按 _get_member_offset 取出的字段静态类型不是 - # list 特化接收者(典型为 i32,即偏移落在 lineno/col_offset 等 i32 字段) - # 时,llvmlite 只会抛出无上下文的 - # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 - # 此处仅在"该调用本就会抛 TypeError 终止"时生效,把类/字段/偏移/ - # 接收者类型一并报出,用于判定 class_members 偏移与 struct 布局不一致。 - try: - result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) - except TypeError as _e_len: - # 来源明细(声明侧 class_members 名序/项数 vs 布局侧 elements) - # 追加进报文:本处失败的本质是"偏移算错",只看 偏移=6 无法区分 - # 是"表多一项(→ base 被 delta==0 判 0)"还是"取值处实例与类键实例 - # 不同源"。判据/读数见 AssignHandle._DescribeMemberSlot。 - _extra_len: str = '' - try: - _extra_len = self.Trans.AssignHandler._DescribeMemberSlot( - Gen, bare_cls, attr_name, offset, self_ptr, _STl2) - except Exception as _ea: - _extra_len = f"\n (来源明细生成失败: {type(_ea).__name__}: {_ea})" - raise TypeError( - f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:" - f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};" - f"(成员偏移与 struct 布局不一致)原错误: {_e_len}{_extra_len}") - if result is not None: - return result - val = self.HandleExprLlvm(arg_node) - if not val: - return None - if isinstance(val.type, ir.PointerType): - pointee = val.type.pointee - if isinstance(pointee, ir.PointerType): - val = Gen._load(val, name="len_deref") - pointee = val.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - # struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变); - # 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 TPV 同口径)。 - _matched_cn: str | None = None - for CN, ST in Gen.structs.items(): - if pointee == ST: - _matched_cn = CN - break - if _matched_cn is not None: - result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen) - if result is not None: - return result - self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法") - else: - # 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。 - self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") - if isinstance(pointee, ir.ArrayType): - return ir.Constant(ir.IntType(64), pointee.count) - # i8*(含 str/bytes 指针形态)→ strlen(保持不变)。 - if isinstance(pointee, ir.IntType) and pointee.width == 8: - if val.type != ir.PointerType(ir.IntType(8)): - val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast") - correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))]) - strlen_func = None - if 'strlen' in Gen.functions: - existing = Gen.functions['strlen'] - if existing.ftype == correct_strlen_type: - strlen_func = existing - if not strlen_func: - try: - strlen_func = Gen._CreateOrReuseFuncDecl('strlen', correct_strlen_type) - Gen.functions['strlen'] = strlen_func - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - raise - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - if strlen_func: - result = Gen.builder.call(strlen_func, [val], name="strlen_call") - return result - return None - # 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast - # (旧实现静默返回常量 0 或回落 strlen,与 TPV 口径不符)。 - self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") - elif isinstance(val.type, ir.ArrayType): - return ir.Constant(ir.IntType(64), val.type.count) - # 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。 - self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") - - def _resolve_nested_opaque_structs(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, visited: set | None = None) -> None: - """递归解析嵌套的 opaque struct,从 stub 文件中加载其定义""" - if visited is None: - visited = set() - if isinstance(struct_type, ir.PointerType): - self._resolve_nested_opaque_structs(struct_type.pointee, Gen, visited) - return - if not isinstance(struct_type, ir.IdentifiedStructType): - return - if struct_type in visited: - return - visited.add(struct_type) - if struct_type.elements is None or len(struct_type.elements) == 0: - # 尝试从 stub 加载 opaque struct - try: - st_name = struct_type.name - except Exception: - st_name = None - if st_name: - # 提取短名称(去掉模块前缀) - if '.' in st_name: - short_name = st_name.split('.', 1)[1] - else: - short_name = st_name - self.Trans.ImportHandler._TryLoadStructFromStub(short_name, Gen) - return - # 递归解析嵌套的 struct 成员 - for elem in struct_type.elements: - self._resolve_nested_opaque_structs(elem, Gen, visited) - - def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - type_name: str | None - if isinstance(Node, ast.Name): - type_name = Node.id - elif isinstance(Node, ast.Attribute): - type_name = Node.attr - else: - # 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])): - # 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。 - raise ValueError( - f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}" - f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 0" - ) - - if type_name not in Gen.structs: - # str/bytes 是指针类型 (char*/void*),sizeof = 指针大小 - if type_name in ('str', 'bytes'): - return ir.Constant(ir.IntType(64), 8) - ctype_cls = CTypeRegistry.GetClassByName(type_name) - if ctype_cls is not None: - try: - ctype_inst = ctype_cls() - size_bits = getattr(ctype_inst, 'Size', 0) or 0 - size_bytes = size_bits // 8 - return ir.Constant(ir.IntType(64), size_bytes) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型失败: {_e}", "Exception") - resolved = CTypeRegistry.ResolveName(type_name) - if resolved: - ctype_cls, ptr_level = resolved - try: - ctype_inst = ctype_cls() - size_bits = getattr(ctype_inst, 'Size', 0) or 0 - size_bytes = size_bits // 8 - if ptr_level > 0: - size_bytes = 8 - return ir.Constant(ir.IntType(64), size_bytes) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型失败: {_e}", "Exception") - # 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。 - raise ValueError( - f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析" - f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 0" - ) - - struct_type = Gen.structs[type_name] - if isinstance(struct_type, ir.PointerType): - struct_type = struct_type.pointee - if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0): - self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen) - struct_type = Gen.structs.get(type_name, struct_type) - # 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确 - self._resolve_nested_opaque_structs(struct_type, Gen) - # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。 - return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type)) - - def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - val: ir.Value | None = self.HandleExprLlvm(arg_node) - if not val: - return None - if isinstance(val.type, ir.PointerType): - pointee = val.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen) - if result is not None: - return result - break - if isinstance(val.type, ir.IntType): - neg_val = Gen.builder.neg(val, name="abs_neg") - is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg") - return Gen.builder.select(is_neg, neg_val, val, name="abs_result") - elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): - zero = ir.Constant(val.type, 0.0) - neg_val = Gen.builder.fneg(val, name="fabs_neg") - is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg") - return Gen.builder.select(is_neg, neg_val, val, name="fabs_result") - return None - - def _HandleDirLlvm(self, Node: ast.Call) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - return ir.Constant(ir.IntType(8).as_pointer(), None) - - def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - type_str: str - if not Node.args: - type_str = "" - else: - arg = Node.args[0] - enum_type_name = None - - def _get_attr_path(node: ast.AST) -> str | None: - if isinstance(node, ast.Name): - return node.id - elif isinstance(node, ast.Attribute): - return f"{_get_attr_path(node.value)}.{node.attr}" - return None - - if isinstance(arg, ast.Name): - arg_name = arg.id - Gen = self.Trans.LlvmGen - if arg_name in Gen.var_type_info: - type_info = Gen.var_type_info[arg_name] - if isinstance(type_info, dict) and type_info.get('type') == 'enum': - enum_type_name = type_info.get('name') - if not enum_type_name and self.Trans.SymbolTable.has(arg_name): - TypeInfo = self.Trans.SymbolTable[arg_name] - if TypeInfo.IsEnum: - enum_type_name = arg_name - elif isinstance(arg, ast.Attribute): - LastPart = None - enum_class_name = None - if isinstance(arg.value, ast.Name): - LastPart = arg.attr - if self.Trans.SymbolTable.has(LastPart): - AttrInfo = self.Trans.SymbolTable[LastPart] - if AttrInfo.IsEnumMember and AttrInfo.EnumName: - enum_type_name = AttrInfo.EnumName - elif isinstance(arg.value, ast.Attribute): - attr_path = _get_attr_path(arg) - if attr_path: - parts = attr_path.split('.') - if len(parts) >= 2: - enum_class_name = parts[-2] - LastPart = parts[-1] - qualified_name_dot = f"{enum_class_name}.{LastPart}" - qualified_name_under = f"{enum_class_name}_{LastPart}" - enum_member_found = None - for qname in (qualified_name_dot, qualified_name_under): - if self.Trans.SymbolTable.has(qname): - info = self.Trans.SymbolTable[qname] - if info.IsEnumMember and info.EnumName == enum_class_name: - enum_member_found = info - break - if not enum_member_found: - for key in self.Trans.SymbolTable: - if key == LastPart: - info = self.Trans.SymbolTable[key] - if info.IsEnumMember and info.EnumName == enum_class_name: - enum_member_found = info - break - if enum_member_found: - enum_type_name = enum_class_name - if enum_type_name: - _enum_bits: int = Gen._enum_int_type(enum_type_name).width - type_str = f"" - else: - llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg) - if isinstance(llvm_type, ir.PointerType): - pointee = llvm_type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - llvm_type = pointee - type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type) - type_str = f"" - return Gen.emit_constant(type_str, 'string') - - def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str): - prompt = Node.args[0].value - prompt_ptr = Gen.emit_constant(prompt, 'string') - printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) - Gen.builder.call(printf_func, [prompt_ptr]) - scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) - input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer") - result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer]) - return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result") - - def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - atoi_func: ir.Function = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))])) - return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result") - - def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - val: ir.Value | None = self.HandleExprLlvm(expr_node) - if val and isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8: - return Gen._load(val, name="ord_result") - return ir.Constant(ir.IntType(32), 0) - - def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - val: ir.Value | None = self.HandleExprLlvm(expr_node) - if val: - char_ptr = Gen._alloca(ir.IntType(8), name="chr_char") - Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr) - return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result") - return ir.Constant(ir.IntType(8).as_pointer(), None) - - def _HandleMallocLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) - malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type) - param_type: ir.Type = malloc_func.type.pointee.args[0] - size_val: ir.Value | None = None - if args: - size_val = self.HandleExprLlvm(args[0]) - if size_val and isinstance(size_val.type, ir.IntType): - if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width: - if size_val.type.width < param_type.width: - size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size") - else: - size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size") - if not size_val: - size_val = ir.Constant(param_type, 1) - result: ir.Value = Gen.builder.call(malloc_func, [size_val], name="malloc_result") - return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast") - - def _HandleReallocLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - realloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]) - realloc_func: ir.Function = Gen._get_or_declare_func('realloc', realloc_type) - size_param_type: ir.Type = realloc_func.type.pointee.args[1] - ptr_val: ir.Value | None = None - size_val: ir.Value | None = None - if len(args) >= 2: - ptr_val = self.HandleExprLlvm(args[0]) - size_val = self.HandleExprLlvm(args[1]) - elif len(args) == 1: - size_val = self.HandleExprLlvm(args[0]) - if not ptr_val: - ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None) - if isinstance(ptr_val.type, ir.PointerType) and not (isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8): - ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr") - if size_val and isinstance(size_val.type, ir.IntType): - if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width: - if size_val.type.width < size_param_type.width: - size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size") - else: - size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size") - if not size_val: - size_val = ir.Constant(size_param_type, 0) - result: ir.Value = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result") - return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast") - - def _HandleFreeLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) - free_func: ir.Function = Gen._get_or_declare_func('free', free_type) - if args: - var_name: str | None = None - if isinstance(args[0], ast.Name): - var_name = args[0].id - ptr_val: ir.Value | None = self.HandleExprLlvm(args[0]) - if ptr_val is not None: - if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr: - registered_val = Gen._var_to_heap_ptr[var_name] - Gen._unregister_local_heap_ptr(registered_val) - del Gen._var_to_heap_ptr[var_name] - if isinstance(ptr_val.type, ir.PointerType): - if isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8: - pass - else: - ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr") - elif isinstance(ptr_val.type, ir.IntType): - ptr_val = Gen.builder.inttoptr(ptr_val, ir.PointerType(ir.IntType(8)), name="free_inttoptr") - Gen.builder.call(free_func, [ptr_val], name="free_call") - return ir.Constant(ir.IntType(32), 0) - - def _HandleMemcpyLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if 'llvm.memcpy' not in Gen.functions: - memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) - Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type) - if len(args) >= 3: - dst: ir.Value | None = self.HandleExprLlvm(args[0]) - src: ir.Value | None = self.HandleExprLlvm(args[1]) - size: ir.Value | None = self.HandleExprLlvm(args[2]) - if dst and src and size: - i8ptr: ir.PointerType = ir.PointerType(ir.IntType(8)) - if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr: - dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast") - elif isinstance(dst.type, ir.IntType): - if dst.type.width < 64: - dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr") - dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr") - if isinstance(src.type, ir.PointerType) and src.type != i8ptr: - src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast") - elif isinstance(src.type, ir.IntType): - if src.type.width < 64: - src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr") - src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr") - if isinstance(size.type, ir.IntType) and size.type.width < 64: - size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size") - isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0) - Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call") - return ir.Constant(ir.IntType(32), 0) - - def _HandleMemsetLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - memset_func: ir.Function = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)])) - if len(args) >= 3: - dst: ir.Value | None = self.HandleExprLlvm(args[0]) - val: ir.Value | None = self.HandleExprLlvm(args[1]) - size: ir.Value | None = self.HandleExprLlvm(args[2]) - if dst and val is not None and size: - if isinstance(dst.type, ir.PointerType) and not (isinstance(dst.type.pointee, ir.IntType) and dst.type.pointee.width == 8): - dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast") - elif isinstance(dst.type, ir.IntType): - if dst.type.width < 64: - dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_memset_dst") - elif dst.type.width > 64: - dst = Gen.builder.trunc(dst, ir.IntType(64), name="trunc_memset_dst") - dst = Gen.builder.inttoptr(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_int2ptr") - if isinstance(val.type, ir.PointerType): - # 0 字面量被推断为 null 指针(i8*),memset 第二参数需要 i32 - val = ir.Constant(ir.IntType(32), 0) - elif isinstance(val.type, ir.IntType) and val.type.width != 32: - if val.type.width < 32: - val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val") - else: - val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val") - if isinstance(size.type, ir.IntType) and size.type.width < 64: - size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size") - elif isinstance(size.type, ir.IntType) and size.type.width > 64: - size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size") - Gen.builder.call(memset_func, [dst, val, size], name="memset_call") - return ir.Constant(ir.IntType(32), 0) - - def _HandleVaStartLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - return ir.Constant(ir.IntType(32), 0) - - def _HandleVaEndLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - return ir.Constant(ir.IntType(32), 0) - - def _HandleArgLlvm(self, args: list[ast.expr], CallNode: ast.Call | None = None) -> ir.Value | None: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - va_list_ptr: ir.Value | None = None - variadic_info: dict | None = getattr(Gen, '_va_arg_info', None) - if not variadic_info: - variadic_info = getattr(Gen, '_variadic_info', None) - if variadic_info: - va_list_ptr = variadic_info.get('va_list_ptr') - if not va_list_ptr: - vararg_name: str = variadic_info.get('vararg_name', 'args') if variadic_info else 'args' - # va_list 的两种形态(回退分支只会命中其中之一,处理方式**不同**): - # 1) 自身变参形态(`def f(a, *args)` 内部调用 arg(T)):HandlesFunctions - # 在函数入口自动发射 llvm.va_start,并把 **va_list 缓冲的地址** 以 - # args 为名登记进 Gen._direct_values。此处的值本身就是 va_arg 需要的 - # 那个 va_list 指针,**绝不能 load**——load 会把缓冲首字节当成指针再 - # 解引用,读到垃圾。 - # 2) 转发形态(如 `vsnprintf(buf, size, fmt, args)`,被调函数自身**非** - # 变参):第 4 个形参 args 在 LLVM 里是 alloca 出来的 i8** 槽,槽里 - # 存着**调用方传下来的 va_list 指针值**(sprintf/snprintf 把自己的 - # *args 指针转发下来)。va_arg 需要的是槽里存的那个 i8* 指针值,所以 - # 必须 load 形参槽把指针取出来;直接传 alloca(i8**)会多一层间接, - # 把"指向指针的槽"当成 va_list 缓冲,读到的位置完全错误。 - # 两种形态的区别是"登记进 _direct_values 的是缓冲地址 / 形参槽的 alloca": - # 前者直接用,后者必须 load 出槽内容。 - if vararg_name in Gen._direct_values: - va_list_ptr = Gen._direct_values[vararg_name] - elif vararg_name in Gen.variables: - va_list_ptr = Gen._load(Gen.variables[vararg_name], name="varg_forward_load") - if not va_list_ptr: - return ir.Constant(ir.IntType(32), 0) - # 类型参数解析与 va_arg(ap, T) 共用同一入口:显式类型走 _resolve_vararg_type - # (typedef/T*/struct 同一规则源,解析不出即 fail-fast),无参数走 - # _infer_vararg_type_from_context(无注解可推断时保持 i32 默认)。 - target_type: ir.Type = self._resolve_vararg_type(args[0], Gen) if args \ - else self._infer_vararg_type_from_context(Gen) - if not getattr(Gen, '_va_arg_counter', None): - Gen._va_arg_counter = 0 - Gen._va_arg_counter += 1 - result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type) - if result is not None and getattr(result, 'name', None): - result.name = f"va_arg_result_{Gen._va_arg_counter}" - return result - - def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type: - """将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。 - - 解析顺序(与注解解析同一规则源,避免各处手抄一份而漏项): - 1) str/bytes/CPtr → i8*;类名(Gen.structs)→ 该类指针 - 2) CTypeRegistry.GetClassByName/ResolveName(t.* 类型类与 C 基本类型名) - 3) CTypeInfo.FromNode:builtins/库/符号表 typedef(uint64/int64/sizet/ - size_t…)、`T*` 指针后缀(parser 已展开为 `T | t.CPtr`)等 - - fail-fast:以上均解析不出 LLVM 类型时直接报错终止,**绝不回退 i32**。 - 回退 i32 的后果不是"精度差一点"而是**读错变参槽**:Win64 变参槽固定 8 字节, - emit_va_arg 对 <64 位整数会生成 "va_arg i64 → trunc to i32",于是 - `va_arg(ap, uint64)` 只保留低 32 位、高 32 位被丢弃。viperlib vsnprintf 的 - `%016llx` 分支(`u_num_l: uint64 = va_arg(ap, uint64)`)因此输出 - 0x00000000XXXXXXXX —— llvmlite.ConstFloat 用该文本作"浮点常量位模式名", - 所有 double 字面量遂退化为「真值低 32 位零扩展」(1.0/2.5/3.0 的低 32 位 - 全 0 ⇒ 全变成 0.0,除法/比较结果错乱)。 - """ - target_type: ir.Type | None = None - if isinstance(type_arg, ast.Name): - type_name: str = type_arg.id - if type_name in ('str', 'bytes'): - target_type = ir.PointerType(ir.IntType(8)) - elif type_name == 'CPtr': - target_type = ir.PointerType(ir.IntType(8)) - elif type_name in Gen.structs: - target_type = ir.PointerType(Gen.structs[type_name]) - else: - ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name) - if ctype_cls is not None: - llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - else: - resolved_tuple: tuple | None = CTypeRegistry.ResolveName(type_name) - if resolved_tuple is not None: - ctype_cls, ptr_level = resolved_tuple - llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if llvm_str: - base: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if base is not None: - for _ in range(ptr_level): - if isinstance(base, ir.VoidType): - base = ir.IntType(8).as_pointer() - else: - base = ir.PointerType(base) - target_type = base - elif isinstance(type_arg, ast.Attribute): - if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): - attr_name: str = type_arg.attr - ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name) - if ctype_cls is not None: - llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) - resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) - if resolved: - target_type = resolved - # 常规分支未命中:走注解解析同一规则源(typedef / `T*` 指针后缀 / t.* 组合写法)。 - # 该规则源对未收录的名字会退化成 `%"{mod}.名"*` 假结构体(CStruct),故显式判掉。 - if target_type is None: - type_info: CTypeInfo | None = CTypeInfo.FromNode(type_arg, self.Trans.SymbolTable) - if type_info is not None and type_info.BaseType is not None \ - and not isinstance(type_info.BaseType, _t.CStruct): - target_type = Gen._ctype_to_llvm(type_info) - if target_type is None: - type_name_dbg: str = type(type_arg).__name__ - if isinstance(type_arg, ast.Name): - type_name_dbg = type_arg.id - elif isinstance(type_arg, ast.Attribute): - type_name_dbg = type_arg.attr - raise TypeError( - f"[fast-fail] va_arg/arg 的类型参数 '{type_name_dbg}' 解析不出 LLVM 类型:" - f"拒绝回退 i32(Win64 变参槽 8 字节,i32 会 trunc 掉高 32 位," - f"如 va_arg(ap, uint64) 只留低 32 位)。请检查该类型名是否已声明/import。") - return target_type - - def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type: - """从当前赋值上下文推断 va_arg/arg 的目标类型(无显式类型参数时用) - - 有赋值注解时与显式类型参数走同一解析入口(含 typedef/T*/struct;解析不出即 - fail-fast,避免 i32 回退读错 8 字节变参槽);无注解可推断时保持 i32 默认 - (`va_arg(ap)` 读 i32 的既有语义,见 viperlib vsnprintf)。 - """ - assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) - if assign_node: - ann: ast.AST | None = getattr(assign_node, 'annotation', None) - if ann is not None: - return self._resolve_vararg_type(ann, Gen) - return ir.IntType(32) - - def _HandleVaArgLlvm(self, args: list[ast.expr]) -> ir.Value: - Gen: LlvmGeneratorMixin = self.Trans.LlvmGen - if not args: - return ir.Constant(ir.IntType(32), 0) - # 求值 va_list 指针(args[0]) - va_list_ptr: ir.Value = self.HandleExprLlvm(args[0]) - if va_list_ptr is None: - return ir.Constant(ir.IntType(32), 0) - # 解析目标类型 - if len(args) >= 2: - target_type: ir.Type = self._resolve_vararg_type(args[1], Gen) - else: - target_type: ir.Type = self._infer_vararg_type_from_context(Gen) - if not getattr(Gen, '_va_arg_counter', None): - Gen._va_arg_counter = 0 - Gen._va_arg_counter += 1 - result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type) - if result is not None and getattr(result, 'name', None): - result.name = f"va_arg_explicit_{Gen._va_arg_counter}" - return result if result is not None else ir.Constant(ir.IntType(32), 0) + def _HandleLenLlvm(self, arg_node: ast.AST) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + # len(obj.attr):obj 可为 self 或任意局部变量(VpiReader 的 + # len(simp.names) 即非 self 场景)。字段静态类型若退化为 i8* + # (跨模块 stub 的 rawptr[list[...]],stub 不注入字段类型),通用 + # strlen 分支会把 list 容器头当字符串扫 → 垃圾长度 → 越界 get → + # 读任意地址(VpiReader.vp _ParseVpiIntoModuleImpl 读 -1 根因)。 + # 此处按字段的 class_member_element_class 侧信道(记录 list 特化名) + # 分派到 list.__len__;attr__len 计数成员特例(TypeGenCType)保留。 + if isinstance(arg_node, ast.Attribute) and isinstance(arg_node.value, ast.Name): + obj_name = arg_node.value.id + attr_name = arg_node.attr + len_member = f'{attr_name}__len' + current_class = self.Trans._CurrentCpythonObjectClass if obj_name == 'self' else None + if current_class is None: + _vsc: dict = Gen.var_struct_class if Gen.var_struct_class else {} + _gsc: dict = getattr(Gen, 'global_struct_class', {}) or {} + current_class = _vsc.get(obj_name) or _gsc.get(obj_name) + # 关键:_CurrentCpythonObjectClass 可能带 sha1 前缀(如 + # 'a351387236c6ce1c.TypeGenCType'),而 class_members / + # class_member_element_class 的 key 全部是裸类名。必须先把前缀剥离 + # 再做 `in Gen.class_members` 检查——否则带前缀时 gate 直接 miss, + # 整个 obj.attr 分支(含 list.__len__ 修复)被跳过,fall through + # 到 strlen(stdint.vp 0xC0000005 崩溃根因)。 + bare_cls: str | None = current_class + if bare_cls and len(bare_cls) > 17 and bare_cls[16] == '.': + bare_cls = bare_cls[17:] + if bare_cls and bare_cls in Gen.class_members: + for m_name, m_type in Gen.class_members[bare_cls]: + if m_name == len_member: + self_val = Gen._get_var_ptr(obj_name) + if self_val: + self_ptr = Gen._load(self_val, name="obj_for_len") + # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase):接收者的 + # pointee 可能是**另一份 opaque 同名实例**,GEP 需要在编译期算出 + # 元素类型 ⇒ 先补 body / 取同短名孪生体,并以该实例算偏移(同源)。 + _STl: Any + self_ptr, _STl = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, self_ptr, bare_cls, f"cast_{bare_cls}") + offset = Gen._get_member_offset(len_member, bare_cls, _STl) + len_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{len_member}_ptr") + return Gen._load(len_ptr, name=f"{obj_name}_{len_member}") + # 修复 stdint.vp 崩溃(0xC0000005)根因:`list[X] | t.CPtr` 联合字段 + # 的 LLVM 类型退化为 i8*,_HandleLenLlvm 无法识别为 list,fall through + # 到 strlen 分支 → 对 list 对象指针执行 strlen → 垃圾长度 → 越界 get + # → 垃圾指针 → strcmp 崩。此处按字段静态类型(class_member_element_class + # 记录的特化名,如 'list[str]',沿 MRO 链查找含父类)分派到 list.__len__。 + field_cls: str | None = None + _elem_map: dict = getattr(Gen, 'class_member_element_class', {}) + for _mro_cls in Gen._get_mro_chain(bare_cls): + # class_member_element_class 的 key 是裸类名(如 'TypeGenBase'), + # _get_mro_chain 在输入裸名时也返回裸名链;再保险剥一次前缀。 + _mro_key: str = _mro_cls + if len(_mro_cls) > 17 and _mro_cls[16] == '.': + _mro_key = _mro_cls[17:] + _sub: dict = _elem_map.get(_mro_key, {}) + if attr_name in _sub: + field_cls = _sub[attr_name] + break + if field_cls and (field_cls == 'list' or field_cls.startswith('list[')): + self_val = Gen._get_var_ptr(obj_name) + if self_val: + self_ptr = Gen._load(self_val, name="obj_for_len") + # 基址收口(判据见 ExprAttrHandle._ConcretizeGepBase) + _STl2: Any + self_ptr, _STl2 = self.Trans.ExprAttrHandle._ConcretizeGepBase( + Gen, self_ptr, bare_cls, f"cast_{bare_cls}") + offset = Gen._get_member_offset(attr_name, bare_cls, _STl2) + if offset is not None: + list_ptr = Gen.builder.gep(self_ptr, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), offset)], name=f"{obj_name}_{attr_name}_ptr") + list_val = Gen._load(list_ptr, name=f"{obj_name}_{attr_name}") + # 诊断增强(不改变任何成功路径):字段身份(class_member_element_class) + # 说是 list 容器,但按 _get_member_offset 取出的字段静态类型不是 + # list 特化接收者(典型为 i32,即偏移落在 lineno/col_offset 等 i32 字段) + # 时,llvmlite 只会抛出无上下文的 + # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 + # 此处仅在"该调用本就会抛 TypeError 终止"时生效,把类/字段/偏移/ + # 接收者类型一并报出,用于判定 class_members 偏移与 struct 布局不一致。 + try: + result = self.Trans.ExprUtils._try_operator_overLoad(field_cls, '__len__', list_val, Gen) + except TypeError as _e_len: + # 来源明细(声明侧 class_members 名序/项数 vs 布局侧 elements) + # 追加进报文:本处失败的本质是"偏移算错",只看 偏移=6 无法区分 + # 是"表多一项(→ base 被 delta==0 判 0)"还是"取值处实例与类键实例 + # 不同源"。判据/读数见 AssignHandle._DescribeMemberSlot。 + _extra_len: str = '' + try: + _extra_len = self.Trans.AssignHandler._DescribeMemberSlot( + Gen, bare_cls, attr_name, offset, self_ptr, _STl2) + except Exception as _ea: + _extra_len = f"\n (来源明细生成失败: {type(_ea).__name__}: {_ea})" + raise TypeError( + f"[fast-fail] len({obj_name}.{attr_name}) 分派 '{field_cls}.__len__' 失败:" + f"类='{bare_cls}' 偏移={offset} 接收者类型={list_val.type};" + f"(成员偏移与 struct 布局不一致)原错误: {_e_len}{_extra_len}") + if result is not None: + return result + val = self.HandleExprLlvm(arg_node) + if not val: + return None + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, ir.PointerType): + val = Gen._load(val, name="len_deref") + pointee = val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + # struct(或任何非基本类型)→ 仍先走既有 __len__ 分派(保持不变); + # 没有 __len__ 时由旧实现的「回落 strlen」改为 fail-fast(与 VPC 同口径)。 + _matched_cn: str | None = None + for CN, ST in Gen.structs.items(): + if pointee == ST: + _matched_cn = CN + break + if _matched_cn is not None: + result = self.Trans.ExprUtils._try_operator_overLoad(_matched_cn, '__len__', val, Gen) + if result is not None: + return result + self._FailFastLenUnsupported(arg_node, Gen, val, f"len() 参数类型 '{_matched_cn}' 没有 __len__ 方法") + else: + # 未在 Gen.structs 登记的 struct(opaque/匿名)→ fail-fast。 + self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") + if isinstance(pointee, ir.ArrayType): + return ir.Constant(ir.IntType(64), pointee.count) + # i8*(含 str/bytes 指针形态)→ strlen(保持不变)。 + if isinstance(pointee, ir.IntType) and pointee.width == 8: + if val.type != ir.PointerType(ir.IntType(8)): + val = Gen.builder.bitcast(val, ir.PointerType(ir.IntType(8)), name="str_ptr_cast") + correct_strlen_type = ir.FunctionType(ir.IntType(64), [ir.PointerType(ir.IntType(8))]) + strlen_func = None + if 'strlen' in Gen.functions: + existing = Gen.functions['strlen'] + if existing.ftype == correct_strlen_type: + strlen_func = existing + if not strlen_func: + try: + strlen_func = Gen._CreateOrReuseFuncDecl('strlen', correct_strlen_type) + Gen.functions['strlen'] = strlen_func + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if strlen_func: + result = Gen.builder.call(strlen_func, [val], name="strlen_call") + return result + return None + # 非 i8 裸指针 / 标量指针(i32*/i64*/float*/double*)→ fail-fast + # (旧实现静默返回常量 0 或回落 strlen,与 VPC 口径不符)。 + self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") + elif isinstance(val.type, ir.ArrayType): + return ir.Constant(ir.IntType(64), val.type.count) + # 其它不支持形态(标量值 / 结构体值等)→ fail-fast(旧实现静默返回常量 0)。 + self._FailFastLenUnsupported(arg_node, Gen, val, "len() 参数类型不支持(非字符串/数组/对象)") + + def _resolve_nested_opaque_structs(self, struct_type: ir.Type, Gen: LlvmGeneratorMixin, visited: set | None = None) -> None: + """递归解析嵌套的 opaque struct,从 stub 文件中加载其定义""" + if visited is None: + visited = set() + if isinstance(struct_type, ir.PointerType): + self._resolve_nested_opaque_structs(struct_type.pointee, Gen, visited) + return + if not isinstance(struct_type, ir.IdentifiedStructType): + return + if struct_type in visited: + return + visited.add(struct_type) + if struct_type.elements is None or len(struct_type.elements) == 0: + # 尝试从 stub 加载 opaque struct + try: + st_name = struct_type.name + except Exception: + st_name = None + if st_name: + # 提取短名称(去掉模块前缀) + if '.' in st_name: + short_name = st_name.split('.', 1)[1] + else: + short_name = st_name + self.Trans.ImportHandler._TryLoadStructFromStub(short_name, Gen) + return + # 递归解析嵌套的 struct 成员 + for elem in struct_type.elements: + self._resolve_nested_opaque_structs(elem, Gen, visited) + + def _HandleSizeofLlvm(self, Node: ast.AST) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + type_name: str | None + if isinstance(Node, ast.Name): + type_name = Node.id + elif isinstance(Node, ast.Attribute): + type_name = Node.attr + else: + # 非 名称/属性 的 sizeof 目标(如 sizeof(a + b)、sizeof(t.CArray[int,4])): + # 曾静默返回 0 → 下游 alloc(0) 返回 NULL。fail-fast。 + raise ValueError( + f"[FATAL][__sizeof__] sizeof 的目标 {type(Node).__name__}" + f"({ast.unparse(Node)})不是类型名(Name/Attribute),拒绝静默返回 0" + ) + + if type_name not in Gen.structs: + # str/bytes 是指针类型 (char*/void*),sizeof = 指针大小 + if type_name in ('str', 'bytes'): + return ir.Constant(ir.IntType(64), 8) + ctype_cls = CTypeRegistry.GetClassByName(type_name) + if ctype_cls is not None: + try: + ctype_inst = ctype_cls() + size_bits = getattr(ctype_inst, 'Size', 0) or 0 + size_bytes = size_bits // 8 + return ir.Constant(ir.IntType(64), size_bytes) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + resolved = CTypeRegistry.ResolveName(type_name) + if resolved: + ctype_cls, ptr_level = resolved + try: + ctype_inst = ctype_cls() + size_bits = getattr(ctype_inst, 'Size', 0) or 0 + size_bytes = size_bits // 8 + if ptr_level > 0: + size_bytes = 8 + return ir.Constant(ir.IntType(64), size_bytes) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型失败: {_e}", "Exception") + # 所有查找策略均失败:曾静默返回 0 → 下游 elem_size=0 / alloc(0)。fail-fast。 + raise ValueError( + f"[FATAL][__sizeof__] 类型 '{type_name}' 无法解析" + f"(不在 Gen.structs、CTypeRegistry 也找不到),拒绝静默返回 0" + ) + + struct_type = Gen.structs[type_name] + if isinstance(struct_type, ir.PointerType): + struct_type = struct_type.pointee + if isinstance(struct_type, ir.IdentifiedStructType) and (struct_type.elements is None or len(struct_type.elements) == 0): + self.Trans.ImportHandler._TryLoadStructFromStub(type_name, Gen) + struct_type = Gen.structs.get(type_name, struct_type) + # 递归解析所有嵌套的 opaque struct,确保 sizeof 计算正确 + self._resolve_nested_opaque_structs(struct_type, Gen) + # _get_struct_size 已在尺寸不可得时 fail-fast(不返回 0),此处直接采用。 + return ir.Constant(ir.IntType(64), Gen._get_struct_size(struct_type)) + + def _HandleAbsLlvm(self, arg_node: ast.AST) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + val: ir.Value | None = self.HandleExprLlvm(arg_node) + if not val: + return None + if isinstance(val.type, ir.PointerType): + pointee = val.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + result = self.Trans.ExprUtils._try_operator_overLoad(CN, '__abs__', val, Gen) + if result is not None: + return result + break + if isinstance(val.type, ir.IntType): + neg_val = Gen.builder.neg(val, name="abs_neg") + is_neg = Gen.builder.icmp_signed('<', val, ir.Constant(val.type, 0), name="is_neg") + return Gen.builder.select(is_neg, neg_val, val, name="abs_result") + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + zero = ir.Constant(val.type, 0.0) + neg_val = Gen.builder.fneg(val, name="fabs_neg") + is_neg = Gen.builder.fcmp_ordered('<', val, zero, name="f_is_neg") + return Gen.builder.select(is_neg, neg_val, val, name="fabs_result") + return None + + def _HandleDirLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleTypeLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + type_str: str + if not Node.args: + type_str = "" + else: + arg = Node.args[0] + enum_type_name = None + + def _get_attr_path(node: ast.AST) -> str | None: + if isinstance(node, ast.Name): + return node.id + elif isinstance(node, ast.Attribute): + return f"{_get_attr_path(node.value)}.{node.attr}" + return None + + if isinstance(arg, ast.Name): + arg_name = arg.id + Gen = self.Trans.LlvmGen + if arg_name in Gen.var_type_info: + type_info = Gen.var_type_info[arg_name] + if isinstance(type_info, dict) and type_info.get('type') == 'enum': + enum_type_name = type_info.get('name') + if not enum_type_name and self.Trans.SymbolTable.has(arg_name): + TypeInfo = self.Trans.SymbolTable[arg_name] + if TypeInfo.IsEnum: + enum_type_name = arg_name + elif isinstance(arg, ast.Attribute): + LastPart = None + enum_class_name = None + if isinstance(arg.value, ast.Name): + LastPart = arg.attr + if self.Trans.SymbolTable.has(LastPart): + AttrInfo = self.Trans.SymbolTable[LastPart] + if AttrInfo.IsEnumMember and AttrInfo.EnumName: + enum_type_name = AttrInfo.EnumName + elif isinstance(arg.value, ast.Attribute): + attr_path = _get_attr_path(arg) + if attr_path: + parts = attr_path.split('.') + if len(parts) >= 2: + enum_class_name = parts[-2] + LastPart = parts[-1] + qualified_name_dot = f"{enum_class_name}.{LastPart}" + qualified_name_under = f"{enum_class_name}_{LastPart}" + enum_member_found = None + for qname in (qualified_name_dot, qualified_name_under): + if self.Trans.SymbolTable.has(qname): + info = self.Trans.SymbolTable[qname] + if info.IsEnumMember and info.EnumName == enum_class_name: + enum_member_found = info + break + if not enum_member_found: + for key in self.Trans.SymbolTable: + if key == LastPart: + info = self.Trans.SymbolTable[key] + if info.IsEnumMember and info.EnumName == enum_class_name: + enum_member_found = info + break + if enum_member_found: + enum_type_name = enum_class_name + if enum_type_name: + _enum_bits: int = Gen._enum_int_type(enum_type_name).width + type_str = f"" + else: + llvm_type = self.Trans.ExprUtils._infer_expr_llvm_type_full(arg) + if isinstance(llvm_type, ir.PointerType): + pointee = llvm_type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + llvm_type = pointee + type_info = self.Trans.ExprUtils._llvm_type_to_detailed_string(llvm_type) + type_str = f"" + return Gen.emit_constant(type_str, 'string') + + def _HandleInputLlvm(self, Node: ast.Call) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if Node.args and isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, str): + prompt = Node.args[0].value + prompt_ptr = Gen.emit_constant(prompt, 'string') + printf_func = Gen.get_or_declare_c_func('printf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + Gen.builder.call(printf_func, [prompt_ptr]) + scanf_func = Gen.get_or_declare_c_func('scanf', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))], var_arg=True)) + input_buffer = Gen._alloca(ir.ArrayType(ir.IntType(8), 256), name="input_buffer") + result = Gen.builder.call(scanf_func, [Gen.emit_constant("%255s", 'string'), input_buffer]) + return Gen.builder.gep(input_buffer, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="input_result") + + def _HandleAtoiLlvm(self, str_ptr: ir.Value) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + atoi_func: ir.Function = Gen.get_or_declare_c_func('atoi', ir.FunctionType(ir.IntType(32), [ir.PointerType(ir.IntType(8))])) + return Gen.builder.call(atoi_func, [str_ptr], name="atoi_result") + + def _HandleOrdLlvm(self, expr_node: ast.AST) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + val: ir.Value | None = self.HandleExprLlvm(expr_node) + if val and isinstance(val.type, ir.PointerType) and isinstance(val.type.pointee, ir.IntType) and val.type.pointee.width == 8: + return Gen._load(val, name="ord_result") + return ir.Constant(ir.IntType(32), 0) + + def _HandleChrLlvm(self, expr_node: ast.AST) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + val: ir.Value | None = self.HandleExprLlvm(expr_node) + if val: + char_ptr = Gen._alloca(ir.IntType(8), name="chr_char") + Gen.builder.store(Gen.builder.trunc(val, ir.IntType(8), name="trunc_chr"), char_ptr) + return Gen.builder.gep(char_ptr, [ir.Constant(ir.IntType(32), 0)], name="chr_result") + return ir.Constant(ir.IntType(8).as_pointer(), None) + + def _HandleMallocLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) + malloc_func: ir.Function = Gen._get_or_declare_func('malloc', malloc_type) + param_type: ir.Type = malloc_func.type.pointee.args[0] + size_val: ir.Value | None = None + if args: + size_val = self.HandleExprLlvm(args[0]) + if size_val and isinstance(size_val.type, ir.IntType): + if isinstance(param_type, ir.IntType) and size_val.type.width != param_type.width: + if size_val.type.width < param_type.width: + size_val = Gen.builder.zext(size_val, param_type, name="zext_malloc_size") + else: + size_val = Gen.builder.trunc(size_val, param_type, name="trunc_malloc_size") + if not size_val: + size_val = ir.Constant(param_type, 1) + result: ir.Value = Gen.builder.call(malloc_func, [size_val], name="malloc_result") + return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="malloc_cast") + + def _HandleReallocLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + realloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(64)]) + realloc_func: ir.Function = Gen._get_or_declare_func('realloc', realloc_type) + size_param_type: ir.Type = realloc_func.type.pointee.args[1] + ptr_val: ir.Value | None = None + size_val: ir.Value | None = None + if len(args) >= 2: + ptr_val = self.HandleExprLlvm(args[0]) + size_val = self.HandleExprLlvm(args[1]) + elif len(args) == 1: + size_val = self.HandleExprLlvm(args[0]) + if not ptr_val: + ptr_val = ir.Constant(ir.PointerType(ir.IntType(8)), None) + if isinstance(ptr_val.type, ir.PointerType) and not (isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8): + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="realloc_cast_ptr") + if size_val and isinstance(size_val.type, ir.IntType): + if isinstance(size_param_type, ir.IntType) and size_val.type.width != size_param_type.width: + if size_val.type.width < size_param_type.width: + size_val = Gen.builder.zext(size_val, size_param_type, name="zext_realloc_size") + else: + size_val = Gen.builder.trunc(size_val, size_param_type, name="trunc_realloc_size") + if not size_val: + size_val = ir.Constant(size_param_type, 0) + result: ir.Value = Gen.builder.call(realloc_func, [ptr_val, size_val], name="realloc_result") + return Gen.builder.bitcast(result, ir.IntType(8).as_pointer(), name="realloc_cast") + + def _HandleFreeLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + free_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8))]) + free_func: ir.Function = Gen._get_or_declare_func('free', free_type) + if args: + var_name: str | None = None + if isinstance(args[0], ast.Name): + var_name = args[0].id + ptr_val: ir.Value | None = self.HandleExprLlvm(args[0]) + if ptr_val is not None: + if var_name and hasattr(Gen, '_var_to_heap_ptr') and var_name in Gen._var_to_heap_ptr: + registered_val = Gen._var_to_heap_ptr[var_name] + Gen._unregister_local_heap_ptr(registered_val) + del Gen._var_to_heap_ptr[var_name] + if isinstance(ptr_val.type, ir.PointerType): + if isinstance(ptr_val.type.pointee, ir.IntType) and ptr_val.type.pointee.width == 8: + pass + else: + ptr_val = Gen.builder.bitcast(ptr_val, ir.PointerType(ir.IntType(8)), name="free_cast_ptr") + elif isinstance(ptr_val.type, ir.IntType): + ptr_val = Gen.builder.inttoptr(ptr_val, ir.PointerType(ir.IntType(8)), name="free_inttoptr") + Gen.builder.call(free_func, [ptr_val], name="free_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleMemcpyLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if 'llvm.memcpy' not in Gen.functions: + memcpy_type: ir.FunctionType = ir.FunctionType(ir.VoidType(), [ir.PointerType(ir.IntType(8)), ir.PointerType(ir.IntType(8)), ir.IntType(64), ir.IntType(1)]) + Gen.functions['llvm.memcpy'] = Gen._CreateOrReuseFuncDecl('llvm.memcpy', memcpy_type) + if len(args) >= 3: + dst: ir.Value | None = self.HandleExprLlvm(args[0]) + src: ir.Value | None = self.HandleExprLlvm(args[1]) + size: ir.Value | None = self.HandleExprLlvm(args[2]) + if dst and src and size: + i8ptr: ir.PointerType = ir.PointerType(ir.IntType(8)) + if isinstance(dst.type, ir.PointerType) and dst.type != i8ptr: + dst = Gen.builder.bitcast(dst, i8ptr, name="memcpy_dst_cast") + elif isinstance(dst.type, ir.IntType): + if dst.type.width < 64: + dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_dst_ptr") + dst = Gen.builder.inttoptr(dst, i8ptr, name="dst_int2ptr") + if isinstance(src.type, ir.PointerType) and src.type != i8ptr: + src = Gen.builder.bitcast(src, i8ptr, name="memcpy_src_cast") + elif isinstance(src.type, ir.IntType): + if src.type.width < 64: + src = Gen.builder.zext(src, ir.IntType(64), name="zext_src_ptr") + src = Gen.builder.inttoptr(src, i8ptr, name="src_int2ptr") + if isinstance(size.type, ir.IntType) and size.type.width < 64: + size = Gen.builder.zext(size, ir.IntType(64), name="zext_memcpy_size") + isvolatile: ir.Constant = ir.Constant(ir.IntType(1), 0) + Gen.builder.call(Gen.functions['llvm.memcpy'], [dst, src, size, isvolatile], name="memcpy_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleMemsetLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + memset_func: ir.Function = Gen.get_or_declare_c_func('memset', ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.PointerType(ir.IntType(8)), ir.IntType(32), ir.IntType(64)])) + if len(args) >= 3: + dst: ir.Value | None = self.HandleExprLlvm(args[0]) + val: ir.Value | None = self.HandleExprLlvm(args[1]) + size: ir.Value | None = self.HandleExprLlvm(args[2]) + if dst and val is not None and size: + if isinstance(dst.type, ir.PointerType) and not (isinstance(dst.type.pointee, ir.IntType) and dst.type.pointee.width == 8): + dst = Gen.builder.bitcast(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_cast") + elif isinstance(dst.type, ir.IntType): + if dst.type.width < 64: + dst = Gen.builder.zext(dst, ir.IntType(64), name="zext_memset_dst") + elif dst.type.width > 64: + dst = Gen.builder.trunc(dst, ir.IntType(64), name="trunc_memset_dst") + dst = Gen.builder.inttoptr(dst, ir.PointerType(ir.IntType(8)), name="memset_dst_int2ptr") + if isinstance(val.type, ir.PointerType): + # 0 字面量被推断为 null 指针(i8*),memset 第二参数需要 i32 + val = ir.Constant(ir.IntType(32), 0) + elif isinstance(val.type, ir.IntType) and val.type.width != 32: + if val.type.width < 32: + val = Gen.builder.zext(val, ir.IntType(32), name="zext_memset_val") + else: + val = Gen.builder.trunc(val, ir.IntType(32), name="trunc_memset_val") + if isinstance(size.type, ir.IntType) and size.type.width < 64: + size = Gen.builder.zext(size, ir.IntType(64), name="zext_memset_size") + elif isinstance(size.type, ir.IntType) and size.type.width > 64: + size = Gen.builder.trunc(size, ir.IntType(64), name="trunc_memset_size") + Gen.builder.call(memset_func, [dst, val, size], name="memset_call") + return ir.Constant(ir.IntType(32), 0) + + def _HandleVaStartLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(32), 0) + + def _HandleVaEndLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + return ir.Constant(ir.IntType(32), 0) + + def _HandleArgLlvm(self, args: list[ast.expr], CallNode: ast.Call | None = None) -> ir.Value | None: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + va_list_ptr: ir.Value | None = None + variadic_info: dict | None = getattr(Gen, '_va_arg_info', None) + if not variadic_info: + variadic_info = getattr(Gen, '_variadic_info', None) + if variadic_info: + va_list_ptr = variadic_info.get('va_list_ptr') + if not va_list_ptr: + vararg_name: str = variadic_info.get('vararg_name', 'args') if variadic_info else 'args' + # va_list 的两种形态(回退分支只会命中其中之一,处理方式**不同**): + # 1) 自身变参形态(`def f(a, *args)` 内部调用 arg(T)):HandlesFunctions + # 在函数入口自动发射 llvm.va_start,并把 **va_list 缓冲的地址** 以 + # args 为名登记进 Gen._direct_values。此处的值本身就是 va_arg 需要的 + # 那个 va_list 指针,**绝不能 load**——load 会把缓冲首字节当成指针再 + # 解引用,读到垃圾。 + # 2) 转发形态(如 `vsnprintf(buf, size, fmt, args)`,被调函数自身**非** + # 变参):第 4 个形参 args 在 LLVM 里是 alloca 出来的 i8** 槽,槽里 + # 存着**调用方传下来的 va_list 指针值**(sprintf/snprintf 把自己的 + # *args 指针转发下来)。va_arg 需要的是槽里存的那个 i8* 指针值,所以 + # 必须 load 形参槽把指针取出来;直接传 alloca(i8**)会多一层间接, + # 把"指向指针的槽"当成 va_list 缓冲,读到的位置完全错误。 + # 两种形态的区别是"登记进 _direct_values 的是缓冲地址 / 形参槽的 alloca": + # 前者直接用,后者必须 load 出槽内容。 + if vararg_name in Gen._direct_values: + va_list_ptr = Gen._direct_values[vararg_name] + elif vararg_name in Gen.variables: + va_list_ptr = Gen._load(Gen.variables[vararg_name], name="varg_forward_load") + if not va_list_ptr: + return ir.Constant(ir.IntType(32), 0) + # 类型参数解析与 va_arg(ap, T) 共用同一入口:显式类型走 _resolve_vararg_type + # (typedef/T*/struct 同一规则源,解析不出即 fail-fast),无参数走 + # _infer_vararg_type_from_context(无注解可推断时保持 i32 默认)。 + target_type: ir.Type = self._resolve_vararg_type(args[0], Gen) if args \ + else self._infer_vararg_type_from_context(Gen) + if not getattr(Gen, '_va_arg_counter', None): + Gen._va_arg_counter = 0 + Gen._va_arg_counter += 1 + result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type) + if result is not None and getattr(result, 'name', None): + result.name = f"va_arg_result_{Gen._va_arg_counter}" + return result + + def _resolve_vararg_type(self, type_arg: ast.expr, Gen: LlvmGeneratorMixin) -> ir.Type: + """将类型表达式解析为 LLVM 类型(供 arg/va_arg 共用)。 + + 解析顺序(与注解解析同一规则源,避免各处手抄一份而漏项): + 1) str/bytes/CPtr → i8*;类名(Gen.structs)→ 该类指针 + 2) CTypeRegistry.GetClassByName/ResolveName(t.* 类型类与 C 基本类型名) + 3) CTypeInfo.FromNode:builtins/库/符号表 typedef(uint64/int64/sizet/ + size_t…)、`T*` 指针后缀(parser 已展开为 `T | t.CPtr`)等 + + fail-fast:以上均解析不出 LLVM 类型时直接报错终止,**绝不回退 i32**。 + 回退 i32 的后果不是"精度差一点"而是**读错变参槽**:Win64 变参槽固定 8 字节, + emit_va_arg 对 <64 位整数会生成 "va_arg i64 → trunc to i32",于是 + `va_arg(ap, uint64)` 只保留低 32 位、高 32 位被丢弃。viperlib vsnprintf 的 + `%016llx` 分支(`u_num_l: uint64 = va_arg(ap, uint64)`)因此输出 + 0x00000000XXXXXXXX —— llvmlite.ConstFloat 用该文本作"浮点常量位模式名", + 所有 double 字面量遂退化为「真值低 32 位零扩展」(1.0/2.5/3.0 的低 32 位 + 全 0 ⇒ 全变成 0.0,除法/比较结果错乱)。 + """ + target_type: ir.Type | None = None + if isinstance(type_arg, ast.Name): + type_name: str = type_arg.id + if type_name in ('str', 'bytes'): + target_type = ir.PointerType(ir.IntType(8)) + elif type_name == 'CPtr': + target_type = ir.PointerType(ir.IntType(8)) + elif type_name in Gen.structs: + target_type = ir.PointerType(Gen.structs[type_name]) + else: + ctype_cls: type | None = CTypeRegistry.GetClassByName(type_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + else: + resolved_tuple: tuple | None = CTypeRegistry.ResolveName(type_name) + if resolved_tuple is not None: + ctype_cls, ptr_level = resolved_tuple + llvm_str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str: + base: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if base is not None: + for _ in range(ptr_level): + if isinstance(base, ir.VoidType): + base = ir.IntType(8).as_pointer() + else: + base = ir.PointerType(base) + target_type = base + elif isinstance(type_arg, ast.Attribute): + if isinstance(type_arg.value, ast.Name) and IsTModule(type_arg.value.id, self.Trans.SymbolTable): + attr_name: str = type_arg.attr + ctype_cls: type | None = CTypeRegistry.GetClassByName(attr_name) + if ctype_cls is not None: + llvm_str: str | None = CTypeRegistry.CTypeToLLVM(ctype_cls) + resolved: ir.Type | None = Gen._type_str_to_llvm(llvm_str) + if resolved: + target_type = resolved + # 常规分支未命中:走注解解析同一规则源(typedef / `T*` 指针后缀 / t.* 组合写法)。 + # 该规则源对未收录的名字会退化成 `%"{mod}.名"*` 假结构体(CStruct),故显式判掉。 + if target_type is None: + type_info: CTypeInfo | None = CTypeInfo.FromNode(type_arg, self.Trans.SymbolTable) + if type_info is not None and type_info.BaseType is not None \ + and not isinstance(type_info.BaseType, _t.CStruct): + target_type = Gen._ctype_to_llvm(type_info) + if target_type is None: + type_name_dbg: str = type(type_arg).__name__ + if isinstance(type_arg, ast.Name): + type_name_dbg = type_arg.id + elif isinstance(type_arg, ast.Attribute): + type_name_dbg = type_arg.attr + raise TypeError( + f"[fast-fail] va_arg/arg 的类型参数 '{type_name_dbg}' 解析不出 LLVM 类型:" + f"拒绝回退 i32(Win64 变参槽 8 字节,i32 会 trunc 掉高 32 位," + f"如 va_arg(ap, uint64) 只留低 32 位)。请检查该类型名是否已声明/import。") + return target_type + + def _infer_vararg_type_from_context(self, Gen: LlvmGeneratorMixin) -> ir.Type: + """从当前赋值上下文推断 va_arg/arg 的目标类型(无显式类型参数时用) + + 有赋值注解时与显式类型参数走同一解析入口(含 typedef/T*/struct;解析不出即 + fail-fast,避免 i32 回退读错 8 字节变参槽);无注解可推断时保持 i32 默认 + (`va_arg(ap)` 读 i32 的既有语义,见 viperlib vsnprintf)。 + """ + assign_node: ast.AST | None = getattr(self.Trans, '_current_assign_node', None) + if assign_node: + ann: ast.AST | None = getattr(assign_node, 'annotation', None) + if ann is not None: + return self._resolve_vararg_type(ann, Gen) + return ir.IntType(32) + + def _HandleVaArgLlvm(self, args: list[ast.expr]) -> ir.Value: + Gen: LlvmGeneratorMixin = self.Trans.LlvmGen + if not args: + return ir.Constant(ir.IntType(32), 0) + # 求值 va_list 指针(args[0]) + va_list_ptr: ir.Value = self.HandleExprLlvm(args[0]) + if va_list_ptr is None: + return ir.Constant(ir.IntType(32), 0) + # 解析目标类型 + if len(args) >= 2: + target_type: ir.Type = self._resolve_vararg_type(args[1], Gen) + else: + target_type: ir.Type = self._infer_vararg_type_from_context(Gen) + if not getattr(Gen, '_va_arg_counter', None): + Gen._va_arg_counter = 0 + Gen._va_arg_counter += 1 + result: ir.Value | None = Gen.emit_va_arg(va_list_ptr, target_type) + if result is not None and getattr(result, 'name', None): + result.name = f"va_arg_explicit_{Gen._va_arg_counter}" + return result if result is not None else ir.Constant(ir.IntType(32), 0) diff --git a/lib/core/Handles/HandlesExprFormat.py b/lib/core/Handles/HandlesExprFormat.py index 4b71e1a..f10b3d9 100644 --- a/lib/core/Handles/HandlesExprFormat.py +++ b/lib/core/Handles/HandlesExprFormat.py @@ -1,280 +1,280 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -from lib.core.Handles.HandlesBase import BaseHandle -import vast as ast -import re -import llvmlite.ir as ir - - -# --------------------------------------------------------------- -# `_parse_format_spec` 的格式说明符解析正则**预编译**(性能:避免每次 -# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 -# 语义与逐字写法完全一致,模式串一字未改)。 -# --------------------------------------------------------------- -# Python 格式说明符 `[[fill]align][sign][#][0][width][,][.precision][type]` -_RE_FORMAT_SPEC: re.Pattern[str] = re.compile(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$') - - -class ExprFormatHandle(BaseHandle): - def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if not Gen or not Gen.builder: - return ir.Constant(ir.IntType(8).as_pointer(), None) - format_str: str = "" - cast_args: list[Any] = [] - for part in Node.values: - if isinstance(part, ast.Constant) and isinstance(part.value, str): - format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0') - elif isinstance(part, ast.FormattedValue): - fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen) - if fmt_class and Gen._has_function(f'{fmt_class}.__str__'): - obj_val: Any = self.HandleExprLlvm(part.value) - if obj_val: - if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: - if fmt_class in Gen.structs: - obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}") - val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__") - Gen._RegisterTempPtr(val) - format_str += "%s" - cast_args.append(val) - else: - format_str += "%s" - cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) - continue - val = self.HandleExprLlvm(part.value) - if not val: - format_str += "%d" - cast_args.append(ir.Constant(ir.IntType(32), 0)) - continue - # 检查 format_spec(Python f-string 格式说明符) - if part.format_spec is not None: - c_fmt: str | None = self._parse_format_spec(part.format_spec, val) - if c_fmt is not None: - cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value) - format_str += c_fmt - cast_args.append(cast_val) - continue - if isinstance(val.type, ir.PointerType): - pointee: ir.Type = val.type.pointee - if isinstance(pointee, ir.IntType) and pointee.width == 8: - format_str += "%s" - cast_args.append(val) - elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int") - trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc") - format_str += "%d" - cast_args.append(trunc_val) - else: - Loaded: Any = Gen._load(val, name="fstr_deref") - if isinstance(Loaded.type, ir.IntType): - if Loaded.type.width == 8: - format_str += "%c" - ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int") - cast_args.append(ext) - else: - format_str += "%d" - cast_args.append(Loaded) - elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)): - format_str += "%f" - cast_args.append(Loaded) - elif isinstance(Loaded.type, ir.PointerType): - format_str += "%s" - cast_args.append(Loaded) - else: - format_str += "%d" - cast_args.append(Loaded) - elif isinstance(val.type, ir.IntType): - if val.type.width == 8: - format_str += "%c" - ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int") - cast_args.append(ext) - elif val.type.width == 1: - zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int") - format_str += "%d" - cast_args.append(zext) - else: - is_u: bool = Gen._check_node_unsigned(part.value) - format_str += "%u" if is_u else "%d" - cast_args.append(val) - elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): - format_str += "%f" - cast_args.append(val) - else: - format_str += "%d" - cast_args.append(val) - if not format_str: - return Gen.emit_constant("", 'string') - format_ptr: Any = Gen._create_string_global(format_str) - # 统一使用 snprintf 生成字符串,返回 i8* - # __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放) - in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__')) - snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [ - ir.PointerType(ir.IntType(8)), - ir.IntType(64), - ir.PointerType(ir.IntType(8)) - ], var_arg=True) - snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type) - null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) - # 第一遍:获取所需长度(NULL 缓冲,size=0) - len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len") - # 加 1 用于 null 终止符 - size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size") - size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext") - if in_str_method: - # 堆分配(__str__ 返回值需跨函数生命周期) - malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) - malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type) - buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf") - Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf") - Gen._RegisterTempPtr(buf_ptr) - return buf_ptr - # 栈分配(局部使用,自动释放) - buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val) - Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf") - return buf_ptr - - def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None: - """将 Python f-string format_spec 转换为 C printf 格式说明符。 - - 支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。 - 返回 None 表示无法解析,调用方应回退到类型推断。 - """ - if isinstance(format_spec_node, ast.JoinedStr): - parts: list[str] = [] - for v in format_spec_node.values: - if isinstance(v, ast.Constant) and isinstance(v.value, str): - parts.append(v.value) - else: - return None - spec_str: str = ''.join(parts) - elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str): - spec_str = format_spec_node.value - else: - return None - - if not spec_str: - return None - - m: Any = _RE_FORMAT_SPEC.match(spec_str) - if not m: - return None - - fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups() - - c_flags: str = '' - if sign == '+': - c_flags += '+' - elif sign == ' ': - c_flags += ' ' - if alt: - c_flags += '#' - - if fill_align: - align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0] - if align_char in ('<', '^'): - c_flags += '-' - if zero: - c_flags += '0' - - c_width: str = width or '' - c_precision: str = precision or '' - - length_mod: str = '' - if isinstance(val.type, ir.IntType): - # 32 位**不写**长度修饰符(N14):与 TPV 侧 ExprGenFstr 保持同步。 - # 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。 - if val.type.width == 64: - length_mod = 'll' - elif val.type.width == 16: - length_mod = 'h' - elif val.type.width == 8: - length_mod = 'hh' - - c_specifier: str = '' - if fmt_type in ('d', 'n'): - c_specifier = f'{length_mod}d' - elif fmt_type == 'x': - c_specifier = f'{length_mod}x' - elif fmt_type == 'X': - c_specifier = f'{length_mod}X' - elif fmt_type == 'o': - c_specifier = f'{length_mod}o' - elif fmt_type == 'b': - return None - elif fmt_type == 'c': - c_specifier = 'c' - elif fmt_type in ('f', 'F'): - c_specifier = 'f' - elif fmt_type in ('e', 'E', 'g', 'G'): - c_specifier = fmt_type - elif fmt_type == 's': - c_specifier = 's' - elif fmt_type == '%': - c_specifier = 'f%%' - else: - if isinstance(val.type, ir.IntType): - if val.type.width == 8: - c_specifier = 'c' - elif val.type.width == 1: - c_specifier = 'd' - else: - c_specifier = f'{length_mod}d' - elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): - c_specifier = 'f' - elif isinstance(val.type, ir.PointerType): - c_specifier = 's' - else: - c_specifier = 'd' - - return f'%{c_flags}{c_width}{c_precision}{c_specifier}' - - def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value: - """根据 C printf 格式说明符转换值类型。""" - fmt_type_char: str = '' - for ch in reversed(c_fmt): - if ch.isalpha() and ch not in ('l', 'h'): - fmt_type_char = ch - break - elif ch == '%': - break - - if fmt_type_char == 's': - if isinstance(val.type, ir.PointerType): - pointee: ir.Type = val.type.pointee - if isinstance(pointee, ir.IntType) and pointee.width == 8: - return val - if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8: - return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr") - return val - elif fmt_type_char == 'c': - if isinstance(val.type, ir.IntType) and val.type.width < 32: - return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c") - return val - elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'): - if isinstance(val.type, ir.IntType): - if val.type.width == 8: - is_u: bool = Gen._check_node_unsigned(arg_node) - if is_u: - return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8") - return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8") - elif val.type.width == 16: - is_u = Gen._check_node_unsigned(arg_node) - if is_u: - return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16") - return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16") - elif val.type.width == 1: - return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool") - return val - elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): - return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i") - return val - elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'): - if isinstance(val.type, ir.FloatType): - return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext") - elif isinstance(val.type, ir.IntType): - return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f") - return val +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle +import vast as ast +import re +import llvmlite.ir as ir + + +# --------------------------------------------------------------- +# `_parse_format_spec` 的格式说明符解析正则**预编译**(性能:避免每次 +# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 +# 语义与逐字写法完全一致,模式串一字未改)。 +# --------------------------------------------------------------- +# Python 格式说明符 `[[fill]align][sign][#][0][width][,][.precision][type]` +_RE_FORMAT_SPEC: re.Pattern[str] = re.compile(r'^(.?[<>=^])?([+\- ])?(#)?(0)?(\d+)?(,)?(\.\d+)?([bcdeEfFgGnosxX%])?$') + + +class ExprFormatHandle(BaseHandle): + def _HandleJoinedStrLlvm(self, Node: ast.JoinedStr) -> ir.Value: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if not Gen or not Gen.builder: + return ir.Constant(ir.IntType(8).as_pointer(), None) + format_str: str = "" + cast_args: list[Any] = [] + for part in Node.values: + if isinstance(part, ast.Constant) and isinstance(part.value, str): + format_str += part.value.replace('\\', '\\\\').replace('%', '%%').replace('\n', '\\n').replace('\t', '\\t').replace('\0', '\\0') + elif isinstance(part, ast.FormattedValue): + fmt_class: str | None = self.Trans.ExprHandler._get_var_class(part.value, Gen) + if fmt_class and Gen._has_function(f'{fmt_class}.__str__'): + obj_val: Any = self.HandleExprLlvm(part.value) + if obj_val: + if isinstance(obj_val.type, ir.PointerType) and isinstance(obj_val.type.pointee, ir.IntType) and obj_val.type.pointee.width == 8: + if fmt_class in Gen.structs: + obj_val = Gen.builder.bitcast(obj_val, ir.PointerType(Gen.structs[fmt_class]), name=f"cast_{fmt_class}") + val: Any = Gen.builder.call(Gen._get_function(f'{fmt_class}.__str__'), [obj_val], name=f"call_{fmt_class}.__str__") + Gen._RegisterTempPtr(val) + format_str += "%s" + cast_args.append(val) + else: + format_str += "%s" + cast_args.append(ir.Constant(ir.PointerType(ir.IntType(8)), None)) + continue + val = self.HandleExprLlvm(part.value) + if not val: + format_str += "%d" + cast_args.append(ir.Constant(ir.IntType(32), 0)) + continue + # 检查 format_spec(Python f-string 格式说明符) + if part.format_spec is not None: + c_fmt: str | None = self._parse_format_spec(part.format_spec, val) + if c_fmt is not None: + cast_val: Any = self._cast_value_for_format(val, c_fmt, Gen, part.value) + format_str += c_fmt + cast_args.append(cast_val) + continue + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, ir.IntType) and pointee.width == 8: + format_str += "%s" + cast_args.append(val) + elif isinstance(pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + int_val: Any = Gen.builder.ptrtoint(val, ir.IntType(64), name="fstr_ptr2int") + trunc_val: Any = Gen.builder.trunc(int_val, ir.IntType(32), name="fstr_trunc") + format_str += "%d" + cast_args.append(trunc_val) + else: + Loaded: Any = Gen._load(val, name="fstr_deref") + if isinstance(Loaded.type, ir.IntType): + if Loaded.type.width == 8: + format_str += "%c" + ext: Any = Gen.builder.zext(Loaded, ir.IntType(32), name="fstr_char2int") + cast_args.append(ext) + else: + format_str += "%d" + cast_args.append(Loaded) + elif isinstance(Loaded.type, (ir.FloatType, ir.DoubleType)): + format_str += "%f" + cast_args.append(Loaded) + elif isinstance(Loaded.type, ir.PointerType): + format_str += "%s" + cast_args.append(Loaded) + else: + format_str += "%d" + cast_args.append(Loaded) + elif isinstance(val.type, ir.IntType): + if val.type.width == 8: + format_str += "%c" + ext = Gen.builder.zext(val, ir.IntType(32), name="fstr_char2int") + cast_args.append(ext) + elif val.type.width == 1: + zext: Any = Gen.builder.zext(val, ir.IntType(32), name="fstr_bool2int") + format_str += "%d" + cast_args.append(zext) + else: + is_u: bool = Gen._check_node_unsigned(part.value) + format_str += "%u" if is_u else "%d" + cast_args.append(val) + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + format_str += "%f" + cast_args.append(val) + else: + format_str += "%d" + cast_args.append(val) + if not format_str: + return Gen.emit_constant("", 'string') + format_ptr: Any = Gen._create_string_global(format_str) + # 统一使用 snprintf 生成字符串,返回 i8* + # __str__ 方法内使用堆分配(返回值需持久化),其他场景使用栈分配(自动释放) + in_str_method: bool = bool(getattr(Gen, 'func', None) and getattr(Gen.func, 'name', '').endswith('.__str__')) + snprintf_type: ir.FunctionType = ir.FunctionType(ir.IntType(32), [ + ir.PointerType(ir.IntType(8)), + ir.IntType(64), + ir.PointerType(ir.IntType(8)) + ], var_arg=True) + snprintf_func: Any = Gen.get_or_declare_c_func('snprintf', snprintf_type) + null_ptr: ir.Constant = ir.Constant(ir.PointerType(ir.IntType(8)), None) + # 第一遍:获取所需长度(NULL 缓冲,size=0) + len_val: ir.Value = Gen.builder.call(snprintf_func, [null_ptr, ir.Constant(ir.IntType(64), 0), format_ptr] + cast_args, name="fstr_len") + # 加 1 用于 null 终止符 + size_val: ir.Value = Gen.builder.add(len_val, ir.Constant(ir.IntType(32), 1), name="fstr_size") + size_ext: ir.Value = Gen.builder.zext(size_val, ir.IntType(64), name="fstr_size_ext") + if in_str_method: + # 堆分配(__str__ 返回值需跨函数生命周期) + malloc_type: ir.FunctionType = ir.FunctionType(ir.PointerType(ir.IntType(8)), [ir.IntType(64)]) + malloc_func: Any = Gen._get_or_declare_func('malloc', malloc_type) + buf_ptr: ir.Value = Gen.builder.call(malloc_func, [size_ext], name="fstr_heap_buf") + Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf") + Gen._RegisterTempPtr(buf_ptr) + return buf_ptr + # 栈分配(局部使用,自动释放) + buf_ptr: ir.Value = Gen._alloca(ir.IntType(8), name="fstr_stack_buf", size=size_val) + Gen.builder.call(snprintf_func, [buf_ptr, size_ext, format_ptr] + cast_args, name="fstr_snprintf") + return buf_ptr + + def _parse_format_spec(self, format_spec_node: ast.AST, val: ir.Value) -> str | None: + """将 Python f-string format_spec 转换为 C printf 格式说明符。 + + 支持: d/x/X/o/c/f/s 及 [[fill]align][sign][#][0][width][.precision][type] 语法。 + 返回 None 表示无法解析,调用方应回退到类型推断。 + """ + if isinstance(format_spec_node, ast.JoinedStr): + parts: list[str] = [] + for v in format_spec_node.values: + if isinstance(v, ast.Constant) and isinstance(v.value, str): + parts.append(v.value) + else: + return None + spec_str: str = ''.join(parts) + elif isinstance(format_spec_node, ast.Constant) and isinstance(format_spec_node.value, str): + spec_str = format_spec_node.value + else: + return None + + if not spec_str: + return None + + m: Any = _RE_FORMAT_SPEC.match(spec_str) + if not m: + return None + + fill_align, sign, alt, zero, width, comma, precision, fmt_type = m.groups() + + c_flags: str = '' + if sign == '+': + c_flags += '+' + elif sign == ' ': + c_flags += ' ' + if alt: + c_flags += '#' + + if fill_align: + align_char: str = fill_align[-1] if len(fill_align) > 1 else fill_align[0] + if align_char in ('<', '^'): + c_flags += '-' + if zero: + c_flags += '0' + + c_width: str = width or '' + c_precision: str = precision or '' + + length_mod: str = '' + if isinstance(val.type, ir.IntType): + # 32 位**不写**长度修饰符(N14):与 VPC 侧 ExprGenFstr 保持同步。 + # 写 'l' 会在 LP64(long=64 位)下让 printf 按 64 位读取 i32 实参。 + if val.type.width == 64: + length_mod = 'll' + elif val.type.width == 16: + length_mod = 'h' + elif val.type.width == 8: + length_mod = 'hh' + + c_specifier: str = '' + if fmt_type in ('d', 'n'): + c_specifier = f'{length_mod}d' + elif fmt_type == 'x': + c_specifier = f'{length_mod}x' + elif fmt_type == 'X': + c_specifier = f'{length_mod}X' + elif fmt_type == 'o': + c_specifier = f'{length_mod}o' + elif fmt_type == 'b': + return None + elif fmt_type == 'c': + c_specifier = 'c' + elif fmt_type in ('f', 'F'): + c_specifier = 'f' + elif fmt_type in ('e', 'E', 'g', 'G'): + c_specifier = fmt_type + elif fmt_type == 's': + c_specifier = 's' + elif fmt_type == '%': + c_specifier = 'f%%' + else: + if isinstance(val.type, ir.IntType): + if val.type.width == 8: + c_specifier = 'c' + elif val.type.width == 1: + c_specifier = 'd' + else: + c_specifier = f'{length_mod}d' + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + c_specifier = 'f' + elif isinstance(val.type, ir.PointerType): + c_specifier = 's' + else: + c_specifier = 'd' + + return f'%{c_flags}{c_width}{c_precision}{c_specifier}' + + def _cast_value_for_format(self, val: ir.Value, c_fmt: str, Gen: Any, arg_node: ast.AST) -> ir.Value: + """根据 C printf 格式说明符转换值类型。""" + fmt_type_char: str = '' + for ch in reversed(c_fmt): + if ch.isalpha() and ch not in ('l', 'h'): + fmt_type_char = ch + break + elif ch == '%': + break + + if fmt_type_char == 's': + if isinstance(val.type, ir.PointerType): + pointee: ir.Type = val.type.pointee + if isinstance(pointee, ir.IntType) and pointee.width == 8: + return val + if isinstance(pointee, ir.ArrayType) and isinstance(pointee.element, ir.IntType) and pointee.element.width == 8: + return Gen.builder.gep(val, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name="fstr_arr2ptr") + return val + elif fmt_type_char == 'c': + if isinstance(val.type, ir.IntType) and val.type.width < 32: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_c") + return val + elif fmt_type_char in ('d', 'x', 'X', 'o', 'i'): + if isinstance(val.type, ir.IntType): + if val.type.width == 8: + is_u: bool = Gen._check_node_unsigned(arg_node) + if is_u: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i8") + return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i8") + elif val.type.width == 16: + is_u = Gen._check_node_unsigned(arg_node) + if is_u: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_i16") + return Gen.builder.sext(val, ir.IntType(32), name="fstr_sext_i16") + elif val.type.width == 1: + return Gen.builder.zext(val, ir.IntType(32), name="fstr_zext_bool") + return val + elif isinstance(val.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fptosi(val, ir.IntType(32), name="fstr_f2i") + return val + elif fmt_type_char in ('f', 'e', 'E', 'g', 'G'): + if isinstance(val.type, ir.FloatType): + return Gen.builder.fpext(val, ir.DoubleType(), name="fstr_fpext") + elif isinstance(val.type, ir.IntType): + return Gen.builder.sitofp(val, ir.DoubleType(), name="fstr_i2f") + return val return val \ No newline at end of file diff --git a/lib/core/Handles/HandlesExprOps.py b/lib/core/Handles/HandlesExprOps.py index f06e36a..5a090c9 100644 --- a/lib/core/Handles/HandlesExprOps.py +++ b/lib/core/Handles/HandlesExprOps.py @@ -1,585 +1,585 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -from lib.core.Handles.HandlesBase import BaseHandle -import vast as ast -import llvmlite.ir as ir -from lib.core.SymbolUtils import RequireIntLiteralStorable - - -class ExprOpsHandle(BaseHandle): - def _HandleBinOpLlvm(self, Node: ast.BinOp, VarType: ir.Type | str | None = None) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - Gen._set_node_info(Node, "BinOp") - LeftVal: Any = self.HandleExprLlvm(Node.left, VarType) - if not LeftVal: - return None - RightVal: Any = self.HandleExprLlvm(Node.right, VarType) - if not RightVal: - return None - Op: str | None = self.Trans.ExprHandler.GetOpSymbol(Node.op) - if not Op: - return None - OP_OVERLOAD_MAP: dict[str, list[str]] = { - '+': ['__add__'], '-': ['__sub__'], '*': ['__mul__'], - '/': ['__truediv__', '__div__'], '//': ['__floordiv__'], '%': ['__mod__'], - '**': ['__pow__'], - '&': ['__and__'], '|': ['__or__'], '^': ['__xor__'], - '<<': ['__lshift__'], '>>': ['__rshift__'], - } - # 反向运算符:左操作数无对应 dunder 时,调度右操作数的 __rX__ - # (Python 语义:a + b → 先 a.__add__(b),缺失再 b.__radd__(a)) - REVERSE_OVERLOAD_MAP: dict[str, list[str]] = { - '+': ['__radd__'], '-': ['__rsub__'], '*': ['__rmul__'], - '/': ['__rtruediv__'], '//': ['__rfloordiv__'], '%': ['__rmod__'], - '**': ['__rpow__'], - '&': ['__rand__'], '|': ['__ror__'], '^': ['__rxor__'], - '<<': ['__rlshift__'], '>>': ['__rrshift__'], - } - if Op in OP_OVERLOAD_MAP: - LeftClassName: str | None = None - if isinstance(LeftVal.type, ir.PointerType): - pointee: ir.Type = LeftVal.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - LeftClassName = CN - break - elif isinstance(pointee, ir.IntType) and pointee.width == 8: - LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen) - if LeftClassName and LeftClassName in Gen.structs: - TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName]) - LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") - if LeftClassName is None and isinstance(Node.left, ast.Name): - LeftClassName = Gen.var_struct_class.get(Node.left.id) - if LeftClassName and LeftClassName in Gen.structs: - if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: - TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName]) - LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") - if LeftClassName: - op_names: list[str] = OP_OVERLOAD_MAP[Op] - for op_name in op_names: - result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal) - if result is not None: - return result - # 反向运算符重载:左操作数无对应 dunder 时调度右操作数的 __rX__(左值) - if Op in REVERSE_OVERLOAD_MAP: - RightClassName: str | None = None - if isinstance(RightVal.type, ir.PointerType): - pointee_r: ir.Type = RightVal.type.pointee - if isinstance(pointee_r, (ir.IdentifiedStructType, ir.LiteralStructType)): - for CN, ST in Gen.structs.items(): - if pointee_r == ST: - RightClassName = CN - break - elif isinstance(pointee_r, ir.IntType) and pointee_r.width == 8: - RightClassName = self.Trans.ExprUtils._get_var_class(Node.right, Gen) - if RightClassName and RightClassName in Gen.structs: - TargetStructPtrR: ir.PointerType = ir.PointerType(Gen.structs[RightClassName]) - RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}") - if RightClassName is None and isinstance(Node.right, ast.Name): - RightClassName = Gen.var_struct_class.get(Node.right.id) - if RightClassName and RightClassName in Gen.structs: - if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8: - TargetStructPtrR = ir.PointerType(Gen.structs[RightClassName]) - RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}") - if RightClassName: - for op_name_r in REVERSE_OVERLOAD_MAP[Op]: - result_r: Any = self.Trans.ExprUtils._try_operator_overLoad(RightClassName, op_name_r, RightVal, Gen, LeftVal) - if result_r is not None: - return result_r - if Op in ('+', 'Add') and isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): - if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: - if isinstance(Node.left, ast.Constant) and isinstance(Node.left.value, str) and len(Node.left.value) == 1: - CharVal: Any = Gen._load(LeftVal, name="Load_char_const") - CharAsInt: Any = Gen.builder.zext(CharVal, RightVal.type, name="char_to_int") - result = Gen.builder.add(CharAsInt, RightVal, name="char_add") - TruncResult: Any = Gen.builder.trunc(result, ir.IntType(8), name="add_to_char") - return TruncResult - is_unsigned: bool = Gen._check_node_unsigned(Node.left) or Gen._check_node_unsigned(Node.right) - if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): - if LeftVal.type.width < RightVal.type.width: - need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1 - if not need_zext and isinstance(RightVal, ir.Constant): - signed_max: int = (1 << (LeftVal.type.width - 1)) - 1 - try: - if RightVal.constant > signed_max: - need_zext = True - except (TypeError, ValueError): - pass - if need_zext: - LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left") - else: - LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left") - elif LeftVal.type.width > RightVal.type.width: - need_zext = is_unsigned or Gen._check_node_unsigned(Node.right) or RightVal.type.width == 8 or RightVal.type.width == 1 - if not need_zext and isinstance(LeftVal, ir.Constant): - signed_max = (1 << (RightVal.type.width - 1)) - 1 - try: - if LeftVal.constant > signed_max: - need_zext = True - except (TypeError, ValueError): - pass - if need_zext: - RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right") - else: - RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right") - elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): - if Op not in ('+', '-', 'Add', 'Sub') and isinstance(LeftVal.type.pointee, ir.IntType): - LeftVal = Gen._load(LeftVal, name="deref_ptr_binop") - if LeftVal.type.width < RightVal.type.width: - LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_deref_left") - elif LeftVal.type.width > RightVal.type.width: - RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_deref") - elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType): - if Op not in ('+', '-', 'Add', 'Sub') and isinstance(RightVal.type.pointee, ir.IntType): - RightVal = Gen._load(RightVal, name="deref_ptr_binop_r") - if RightVal.type.width < LeftVal.type.width: - RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_deref_right") - elif RightVal.type.width > LeftVal.type.width: - LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_deref") - if Op in ('>>', 'RShift') and isinstance(Node.left, ast.Name): - vname: str = Node.left.id - if vname in Gen.var_signedness and Gen.var_signedness[vname]: - is_unsigned = True - # 双指针算术 fail-fast 已**下沉到单一收敛点** `Gen.emit_binary_op` - # (LLVMCG/ExprGen.py):BinOp 与 AugAssign(+=、-=、*= …)都经该函数, - # 判据(运算符属算术/位运算/移位集合 且 两操作数都是 LLVM 指针)与报文 - # 在那里一次收口。此处不再保留副本,避免两处判据/报文分叉。 - result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned) - return result - - def _HandleBoolOpLlvm(self, Node: ast.BoolOp) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - - def _to_bool(val: Any, node: ast.expr) -> Any: - if isinstance(val.type, ir.IntType) and val.type.width == 1: - return val - return self._CoerceCondToBool(Gen, node, val) - - values: list[ast.expr] = Node.values - if len(values) < 2: - return self.HandleExprLlvm(values[0]) if values else None - if isinstance(Node.op, ast.And): - phi_incomings: list[tuple[ir.Constant, ir.Block]] = [] - end_bb: ir.Block = Gen.func.append_basic_block(name="and.end") - for idx in range(len(values) - 1): - val: Any = self.HandleExprLlvm(values[idx]) - if not val: - if not end_bb.is_terminated: - saved_block: ir.Block = Gen.builder.block - Gen.builder.position_at_start(end_bb) - Gen.builder.unreachable() - Gen.builder.position_at_end(saved_block) - return None - if not isinstance(val.type, ir.IntType) or val.type.width != 1: - val = _to_bool(val, values[idx]) - cur_bb: ir.Block = Gen.builder.block - next_bb: ir.Block = Gen.func.append_basic_block(name=f"and.rhs{idx}") - Gen.builder.cbranch(val, next_bb, end_bb) - phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb)) - Gen.builder.position_at_start(next_bb) - last_val: Any = self.HandleExprLlvm(values[-1]) - if not last_val: - last_val = ir.Constant(ir.IntType(1), 0) - if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1: - last_val = _to_bool(last_val, values[-1]) - last_bb: ir.Block = Gen.builder.block - Gen.builder.branch(end_bb) - Gen.builder.position_at_start(end_bb) - result_phi: Any = Gen.builder.phi(ir.IntType(1), name="and.result") - for const_val, bb in phi_incomings: - result_phi.add_incoming(const_val, bb) - result_phi.add_incoming(last_val, last_bb) - return result_phi - elif isinstance(Node.op, ast.Or): - phi_incomings = [] - end_bb = Gen.func.append_basic_block(name="or.end") - for idx in range(len(values) - 1): - val = self.HandleExprLlvm(values[idx]) - if not val: - if not end_bb.is_terminated: - saved_block = Gen.builder.block - Gen.builder.position_at_start(end_bb) - Gen.builder.unreachable() - Gen.builder.position_at_end(saved_block) - return None - if not isinstance(val.type, ir.IntType) or val.type.width != 1: - val = _to_bool(val, values[idx]) - cur_bb = Gen.builder.block - next_bb = Gen.func.append_basic_block(name=f"or.rhs{idx}") - Gen.builder.cbranch(val, end_bb, next_bb) - phi_incomings.append((ir.Constant(ir.IntType(1), 1), cur_bb)) - Gen.builder.position_at_start(next_bb) - last_val = self.HandleExprLlvm(values[-1]) - if not last_val: - last_val = ir.Constant(ir.IntType(1), 1) - if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1: - last_val = _to_bool(last_val, values[-1]) - last_bb = Gen.builder.block - Gen.builder.branch(end_bb) - Gen.builder.position_at_start(end_bb) - result_phi = Gen.builder.phi(ir.IntType(1), name="or.result") - for const_val, bb in phi_incomings: - result_phi.add_incoming(const_val, bb) - result_phi.add_incoming(last_val, last_bb) - return result_phi - return None - - def _HandleUnaryOpLlvm(self, Node: ast.UnaryOp) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - OperandVal: Any = self.HandleExprLlvm(Node.operand) - if not OperandVal: - return None - OpSymbol: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.op) - if not OpSymbol: - return None - UNARY_OVERLOAD_MAP: dict[str, str] = { - '-': '__neg__', - '+': '__pos__', - '~': '__invert__', - } - if OpSymbol in UNARY_OVERLOAD_MAP: - ClassName: str | None = None - if isinstance(OperandVal.type, ir.PointerType): - pointee: ir.Type = OperandVal.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - ClassName = CN - break - elif isinstance(pointee, ir.IntType) and pointee.width == 8: - ClassName = self.Trans.ExprUtils._get_var_class(Node.operand, Gen) - if ClassName and ClassName in Gen.structs: - TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[ClassName]) - OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}") - if ClassName is None and isinstance(Node.operand, ast.Name): - ClassName = Gen.var_struct_class.get(Node.operand.id) - if ClassName and ClassName in Gen.structs: - if isinstance(OperandVal.type, ir.PointerType) and isinstance(OperandVal.type.pointee, ir.IntType) and OperandVal.type.pointee.width == 8: - TargetStructPtr = ir.PointerType(Gen.structs[ClassName]) - OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}") - if ClassName: - op_name: str = UNARY_OVERLOAD_MAP[OpSymbol] - result: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, OperandVal, Gen) - if result is not None: - return result - if OpSymbol == 'not': - # not x = 逻辑取反(优先调度 __bool__,与 TPV 对齐) - bool_val: Any = self._CoerceCondToBool(Gen, Node.operand, OperandVal) - return Gen.builder.not_(bool_val, name="not_result") - elif OpSymbol == '~': - if isinstance(OperandVal.type, ir.IntType): - mask: int = (1 << OperandVal.type.width) - 1 - return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, mask), name="bnot_result") - return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, -1), name="bnot_result") - elif OpSymbol == '+': - return OperandVal - elif OpSymbol == '-': - # 一元减 + 整数字面量的**联合越界判定**(与 TPV `_parse_factor` 同口径): - # `-` 不属于字面量,幅值合法(≤ 2^64-1)的字面量取负后可能 < -2^63。 - if isinstance(Node.operand, ast.Constant) \ - and isinstance(Node.operand.value, int) \ - and not isinstance(Node.operand.value, bool): - RequireIntLiteralStorable(Gen, self.Trans, Node, -Node.operand.value) - if isinstance(OperandVal.type, ir.IntType): - return Gen.builder.neg(OperandVal, name="neg_result") - elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)): - return Gen.builder.fneg(OperandVal, name="fneg_result") - elif OpSymbol == '*': - # 单目解引用:*p = load(p),要求操作数是指针 - if not isinstance(OperandVal.type, ir.PointerType): - raise TypeError( - f"[fast-fail] 单目 '*' 解引用的操作数必须是指针类型," - f"实际为 {OperandVal.type}" - ) - return Gen._load(OperandVal, name="deref") - elif OpSymbol == '&': - # 单目取地址:&x 返回变量/字段/下标的地址(T*) - if isinstance(Node.operand, ast.Name): - VarName: str = Node.operand.id - if VarName in Gen.functions: - return Gen.functions[VarName] - if VarName in Gen.variables and Gen.variables[VarName] is not None: - var_ptr: ir.Value = Gen.variables[VarName] - if isinstance(var_ptr.type, ir.PointerType) and isinstance(var_ptr.type.pointee, ir.PointerType): - # T**:若 T 是 struct 类(变量语义即对象),返回对象指针 - if isinstance(var_ptr.type.pointee.pointee, ir.IdentifiedStructType): - return self.HandleExprLlvm(Node.operand) - return var_ptr - elif isinstance(Node.operand, ast.Attribute): - AttrPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Node.operand) - if AttrPtr and isinstance(AttrPtr.type, ir.PointerType): - return AttrPtr - elif isinstance(Node.operand, ast.Subscript): - SubPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Node.operand) - if SubPtr and isinstance(SubPtr.type, ir.PointerType): - return SubPtr - elif isinstance(Node.operand, ast.Call): - # &() → rvalue 取址。复用上面已求值的 OperandVal,绝不二次求值 - # (构造调用会再跑一遍 __before_init__/__init__)。 - # · 构造调用(_HandleClassNewLlvm)返回的就是临时对象的栈存储指针 - # Ptr(Struct) —— 该指针即"栈构造结构体的地址"本身,原样返回; - # 不能再套 alloca+store(会得到 Ptr(Ptr(Struct)),赋值端还得 - # load 一次回落,语义也从"对象地址"变"槽地址")。 - # · 其它返回值(标量、按值返回的结构体值):入口临时槽承载—— - # allocaEntry 同类型槽 → store → 返回槽地址(与 TransPyV 侧 - # ExprGenOps._addrof_of_call 同口径)。 - OperandType: ir.Type = OperandVal.type - if isinstance(OperandType, ir.PointerType) \ - and isinstance(OperandType.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - return OperandVal - TmpSlot: ir.Value = Gen._allocaEntry(OperandType, name="addrof_tmp") - Gen.builder.store(OperandVal, TmpSlot) - return TmpSlot - raise TypeError( - f"[fast-fail] 单目 '&' 取地址的操作数必须是左值" - f"(Name/Attribute/Subscript)或构造调用/函数调用结果,实际为 {type(Node.operand).__name__}" - ) - return None - - def _HandleCompareLlvm(self, Node: ast.Compare) -> ir.Value | None: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - LeftVal: Any = self.HandleExprLlvm(Node.left) - if not LeftVal: - return None - is_unsigned: bool = Gen._check_node_unsigned(Node.left) or (Gen._check_node_unsigned(Node.comparators[0]) if Node.comparators else False) - if len(Node.ops) == 1: - Op: ast.cmpop = Node.ops[0] - # in/not in: 调用 __contains__ - if isinstance(Op, (ast.In, ast.NotIn)): - RightVal: Any = self.HandleExprLlvm(Node.comparators[0]) - if RightVal: - ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.comparators[0], Gen) - if ClassName and Gen._has_function(f'{ClassName}.__contains__'): - # 处理 RightVal 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 - if isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8: - RightVal = Gen.builder.inttoptr(RightVal, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") - if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8: - RightVal = Gen.builder.bitcast(RightVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") - contains_func: ir.Function = Gen._get_function(f'{ClassName}.__contains__') - result: ir.Value = Gen.builder.call(contains_func, [RightVal, LeftVal], name=f"call_{ClassName}.__contains__") - # 归一化为 i1 - if isinstance(result.type, ir.IntType) and result.type.width > 1: - zero: ir.Constant = ir.Constant(result.type, 0) - result = Gen.builder.icmp_signed('!=', result, zero, name="contains_bool") - if isinstance(Op, ast.NotIn): - result = Gen.builder.not_(result, name="not_in_result") - return result - return ir.Constant(ir.IntType(1), 0) - ComparatorSymbol: str | None = self.Trans.ExprHandler.GetComparatorSymbol(Op) - if not ComparatorSymbol: - return None - RightVal: Any = self.HandleExprLlvm(Node.comparators[0]) - if not RightVal: - return None - # 比较运算符重载检测:当左侧是结构体指针时,尝试调用 __eq__/__ne__/__lt__/__le__/__gt__/__ge__ - CMP_OVERLOAD_MAP: dict[str, str] = { - '==': '__eq__', '!=': '__ne__', '<': '__lt__', - '<=': '__le__', '>': '__gt__', '>=': '__ge__', - } - if ComparatorSymbol in CMP_OVERLOAD_MAP: - LeftClassName: str | None = None - if isinstance(LeftVal.type, ir.PointerType): - pointee: ir.Type = LeftVal.type.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - for CN, ST in Gen.structs.items(): - if pointee == ST: - LeftClassName = CN - break - elif isinstance(pointee, ir.IntType) and pointee.width == 8: - LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen) - if LeftClassName and LeftClassName in Gen.structs: - TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName]) - LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") - if LeftClassName is None and isinstance(Node.left, ast.Name): - LeftClassName = Gen.var_struct_class.get(Node.left.id) - if LeftClassName and LeftClassName in Gen.structs: - if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: - TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName]) - LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") - if LeftClassName: - op_name: str = CMP_OVERLOAD_MAP[ComparatorSymbol] - result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal) - if result is not None: - return result - if isinstance(LeftVal, ir.Constant) and isinstance(RightVal, ir.Constant): - if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): - lv: int = LeftVal.constant - rv: int = RightVal.constant - lw: int = LeftVal.type.width - rw: int = RightVal.type.width - if lw < rw: - lv = lv if lv >= 0 else lv + (1 << lw) - elif rw < lw: - rv = rv if rv >= 0 else rv + (1 << rw) - if is_unsigned: - mask_l: int = (1 << max(lw, rw)) - 1 - lv = lv & mask_l - rv = rv & mask_l - cmp_map: dict[str, bool] = { - '==': lv == rv, '!=': lv != rv, - '<': lv < rv, '>': lv > rv, - '<=': lv <= rv, '>=': lv >= rv, - } - result: bool = cmp_map.get(ComparatorSymbol, False) - return ir.Constant(ir.IntType(1), 1 if result else 0) - # is / is not / == / != : 整数与 None(空指针常量)比较 - # 整数上的 None 隐式表示 0 ⇒ `x is None` / `x == None` 等价 `x == 0`, - # `x is not None` / `x != None` 等价 `x != 0`(与 TPV 的 ExprGenOps 统一) - # 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃; - # 也必须在下方"指针 vs 整型"特例分支之前,否则 i8 会命中 `Gen._load(null)` - if ComparatorSymbol in ('is', 'is not', '==', '!='): - LeftIsNullPtr: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None and isinstance(LeftVal.type, ir.PointerType) - RightIsNullPtr: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None and isinstance(RightVal.type, ir.PointerType) - LeftIsValueType: bool = isinstance(LeftVal.type, ir.IntType) - RightIsValueType: bool = isinstance(RightVal.type, ir.IntType) - IsNoneNegated: bool = ComparatorSymbol in ('is not', '!=') - if LeftIsValueType and RightIsNullPtr: - IsNoneCmpL: ir.Value = Gen.builder.icmp_signed('==', LeftVal, ir.Constant(LeftVal.type, 0), name="is_none_int") - if IsNoneNegated: - IsNoneCmpL = Gen.builder.not_(IsNoneCmpL, name="is_not_result") - return IsNoneCmpL - if RightIsValueType and LeftIsNullPtr: - IsNoneCmpR: ir.Value = Gen.builder.icmp_signed('==', RightVal, ir.Constant(RightVal.type, 0), name="is_none_int") - if IsNoneNegated: - IsNoneCmpR = Gen.builder.not_(IsNoneCmpR, name="is_not_result") - return IsNoneCmpR - if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): - if LeftVal.type.width < RightVal.type.width: - # 如果宽类型常量值超出窄类型有符号范围,则必须用zext - # i8 类型默认使用 zext(系统编程中 i8 几乎总是无符号字节) - need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1 - if not need_zext and isinstance(RightVal, ir.Constant): - signed_max: int = (1 << (LeftVal.type.width - 1)) - 1 - try: - if RightVal.constant > signed_max or RightVal.constant < 0: - need_zext = True - except (TypeError, ValueError): - pass - if need_zext: - LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_cmp") - else: - LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left_cmp") - elif LeftVal.type.width > RightVal.type.width: - need_zext = is_unsigned or Gen._check_node_unsigned(Node.comparators[0]) or RightVal.type.width == 8 or RightVal.type.width == 1 - if not need_zext and isinstance(LeftVal, ir.Constant): - signed_max = (1 << (RightVal.type.width - 1)) - 1 - try: - if LeftVal.constant > signed_max or LeftVal.constant < 0: - need_zext = True - except (TypeError, ValueError): - pass - if need_zext: - RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_cmp") - else: - RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right_cmp") - elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): - if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8 and isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8: - LeftVal = Gen._load(LeftVal, name="Load_char_ptr_cmp") - else: - if isinstance(RightVal, ir.Constant) and RightVal.constant == 0: - RightVal = ir.Constant(LeftVal.type, None) - else: - # 指针 vs 非零整数: 将指针转为整数走正常 ==(地址值比较) - LeftVal = Gen.builder.ptrtoint(LeftVal, RightVal.type, name="ptr2int_cmp") - elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType): - if isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8 and isinstance(LeftVal.type, ir.IntType) and LeftVal.type.width == 8: - RightVal = Gen._load(RightVal, name="Load_char_ptr_cmp") - else: - if isinstance(LeftVal, ir.Constant) and LeftVal.constant == 0: - LeftVal = ir.Constant(RightVal.type, None) - else: - # 非零整数 vs 指针: 将指针转为整数走正常 ==(地址值比较) - RightVal = Gen.builder.ptrtoint(RightVal, LeftVal.type, name="ptr2int_cmp") - # is / is not: 地址比较(指针)或值比较(int) - if ComparatorSymbol in ('is', 'is not'): - if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType): - addr_cmp = Gen.builder.icmp_unsigned('==', LeftVal, RightVal, name="is_cmp") - if ComparatorSymbol == 'is not': - addr_cmp = Gen.builder.not_(addr_cmp, name="is_not_result") - return addr_cmp - if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): - val_cmp = Gen.builder.icmp_signed('==', LeftVal, RightVal, name="is_cmp") - if ComparatorSymbol == 'is not': - val_cmp = Gen.builder.not_(val_cmp, name="is_not_result") - return val_cmp - # == / !=: 指针间做地址比较(C-Like 语义,不隐式调用 strcmp) - # 如需字符串内容比较,应显式调用 string.strcmp() - if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType): - result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") - elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)): - if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType): - RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name="int_to_float_cmp") - elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType): - LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name="int_to_float_cmp") - result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") - elif is_unsigned: - result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") - else: - result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") - return result - EndBB: ir.Block = Gen.func.append_basic_block(name="cmp.end") - phi_incomings: list[tuple[ir.Value, ir.Block]] = [] - for i, (Op, Comparator) in enumerate(zip(Node.ops, Node.comparators)): - ComparatorSymbol = self.Trans.ExprHandler.GetComparatorSymbol(Op) - if not ComparatorSymbol: - continue - RightVal = self.HandleExprLlvm(Comparator) - if not RightVal: - continue - if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): - if LeftVal.type.width < RightVal.type.width: - need_zext = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1 - if not need_zext and isinstance(RightVal, ir.Constant): - signed_max = (1 << (LeftVal.type.width - 1)) - 1 - try: - if RightVal.constant > signed_max or RightVal.constant < 0: - need_zext = True - except (TypeError, ValueError): - pass - if need_zext: - LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name=f"zext_left_{i}") - else: - LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name=f"sext_left_{i}") - elif LeftVal.type.width > RightVal.type.width: - need_zext = is_unsigned or Gen._check_node_unsigned(Comparator) or RightVal.type.width == 8 or RightVal.type.width == 1 - if not need_zext and isinstance(LeftVal, ir.Constant): - signed_max = (1 << (RightVal.type.width - 1)) - 1 - try: - if LeftVal.constant > signed_max or LeftVal.constant < 0: - need_zext = True - except (TypeError, ValueError): - pass - if need_zext: - RightVal = Gen.builder.zext(RightVal, LeftVal.type, name=f"zext_right_{i}") - else: - RightVal = Gen.builder.sext(RightVal, LeftVal.type, name=f"sext_right_{i}") - if is_unsigned: - cmp_result: Any = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}") - elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)): - if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType): - RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name=f"int_to_float_cmp_{i}") - elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType): - LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name=f"int_to_float_cmp_{i}") - cmp_result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}") - else: - cmp_result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}") - if i < len(Node.ops) - 1: - NextBB: ir.Block = Gen.func.append_basic_block(name=f"cmp.next.{i}") - cur_bb: ir.Block = Gen.builder.block - Gen.builder.cbranch(cmp_result, NextBB, EndBB) - phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb)) - Gen.builder.position_at_start(NextBB) - LeftVal = RightVal - else: - cur_bb = Gen.builder.block - Gen.builder.branch(EndBB) - phi_incomings.append((cmp_result, cur_bb)) - Gen.builder.position_at_start(EndBB) - final_result: Any = Gen.builder.phi(ir.IntType(1), name="final_cmp") - for val, bb in phi_incomings: - final_result.add_incoming(val, bb) +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import BaseHandle +import vast as ast +import llvmlite.ir as ir +from lib.core.SymbolUtils import RequireIntLiteralStorable + + +class ExprOpsHandle(BaseHandle): + def _HandleBinOpLlvm(self, Node: ast.BinOp, VarType: ir.Type | str | None = None) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + Gen._set_node_info(Node, "BinOp") + LeftVal: Any = self.HandleExprLlvm(Node.left, VarType) + if not LeftVal: + return None + RightVal: Any = self.HandleExprLlvm(Node.right, VarType) + if not RightVal: + return None + Op: str | None = self.Trans.ExprHandler.GetOpSymbol(Node.op) + if not Op: + return None + OP_OVERLOAD_MAP: dict[str, list[str]] = { + '+': ['__add__'], '-': ['__sub__'], '*': ['__mul__'], + '/': ['__truediv__', '__div__'], '//': ['__floordiv__'], '%': ['__mod__'], + '**': ['__pow__'], + '&': ['__and__'], '|': ['__or__'], '^': ['__xor__'], + '<<': ['__lshift__'], '>>': ['__rshift__'], + } + # 反向运算符:左操作数无对应 dunder 时,调度右操作数的 __rX__ + # (Python 语义:a + b → 先 a.__add__(b),缺失再 b.__radd__(a)) + REVERSE_OVERLOAD_MAP: dict[str, list[str]] = { + '+': ['__radd__'], '-': ['__rsub__'], '*': ['__rmul__'], + '/': ['__rtruediv__'], '//': ['__rfloordiv__'], '%': ['__rmod__'], + '**': ['__rpow__'], + '&': ['__rand__'], '|': ['__ror__'], '^': ['__rxor__'], + '<<': ['__rlshift__'], '>>': ['__rrshift__'], + } + if Op in OP_OVERLOAD_MAP: + LeftClassName: str | None = None + if isinstance(LeftVal.type, ir.PointerType): + pointee: ir.Type = LeftVal.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + LeftClassName = CN + break + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen) + if LeftClassName and LeftClassName in Gen.structs: + TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName]) + LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") + if LeftClassName is None and isinstance(Node.left, ast.Name): + LeftClassName = Gen.var_struct_class.get(Node.left.id) + if LeftClassName and LeftClassName in Gen.structs: + if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: + TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName]) + LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") + if LeftClassName: + op_names: list[str] = OP_OVERLOAD_MAP[Op] + for op_name in op_names: + result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal) + if result is not None: + return result + # 反向运算符重载:左操作数无对应 dunder 时调度右操作数的 __rX__(左值) + if Op in REVERSE_OVERLOAD_MAP: + RightClassName: str | None = None + if isinstance(RightVal.type, ir.PointerType): + pointee_r: ir.Type = RightVal.type.pointee + if isinstance(pointee_r, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee_r == ST: + RightClassName = CN + break + elif isinstance(pointee_r, ir.IntType) and pointee_r.width == 8: + RightClassName = self.Trans.ExprUtils._get_var_class(Node.right, Gen) + if RightClassName and RightClassName in Gen.structs: + TargetStructPtrR: ir.PointerType = ir.PointerType(Gen.structs[RightClassName]) + RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}") + if RightClassName is None and isinstance(Node.right, ast.Name): + RightClassName = Gen.var_struct_class.get(Node.right.id) + if RightClassName and RightClassName in Gen.structs: + if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8: + TargetStructPtrR = ir.PointerType(Gen.structs[RightClassName]) + RightVal = Gen.builder.bitcast(RightVal, TargetStructPtrR, name=f"bitcast_r_to_{RightClassName}") + if RightClassName: + for op_name_r in REVERSE_OVERLOAD_MAP[Op]: + result_r: Any = self.Trans.ExprUtils._try_operator_overLoad(RightClassName, op_name_r, RightVal, Gen, LeftVal) + if result_r is not None: + return result_r + if Op in ('+', 'Add') and isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): + if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: + if isinstance(Node.left, ast.Constant) and isinstance(Node.left.value, str) and len(Node.left.value) == 1: + CharVal: Any = Gen._load(LeftVal, name="Load_char_const") + CharAsInt: Any = Gen.builder.zext(CharVal, RightVal.type, name="char_to_int") + result = Gen.builder.add(CharAsInt, RightVal, name="char_add") + TruncResult: Any = Gen.builder.trunc(result, ir.IntType(8), name="add_to_char") + return TruncResult + is_unsigned: bool = Gen._check_node_unsigned(Node.left) or Gen._check_node_unsigned(Node.right) + if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): + if LeftVal.type.width < RightVal.type.width: + need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1 + if not need_zext and isinstance(RightVal, ir.Constant): + signed_max: int = (1 << (LeftVal.type.width - 1)) - 1 + try: + if RightVal.constant > signed_max: + need_zext = True + except (TypeError, ValueError): + pass + if need_zext: + LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left") + else: + LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left") + elif LeftVal.type.width > RightVal.type.width: + need_zext = is_unsigned or Gen._check_node_unsigned(Node.right) or RightVal.type.width == 8 or RightVal.type.width == 1 + if not need_zext and isinstance(LeftVal, ir.Constant): + signed_max = (1 << (RightVal.type.width - 1)) - 1 + try: + if LeftVal.constant > signed_max: + need_zext = True + except (TypeError, ValueError): + pass + if need_zext: + RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right") + else: + RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right") + elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): + if Op not in ('+', '-', 'Add', 'Sub') and isinstance(LeftVal.type.pointee, ir.IntType): + LeftVal = Gen._load(LeftVal, name="deref_ptr_binop") + if LeftVal.type.width < RightVal.type.width: + LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_deref_left") + elif LeftVal.type.width > RightVal.type.width: + RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_deref") + elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType): + if Op not in ('+', '-', 'Add', 'Sub') and isinstance(RightVal.type.pointee, ir.IntType): + RightVal = Gen._load(RightVal, name="deref_ptr_binop_r") + if RightVal.type.width < LeftVal.type.width: + RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_deref_right") + elif RightVal.type.width > LeftVal.type.width: + LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_deref") + if Op in ('>>', 'RShift') and isinstance(Node.left, ast.Name): + vname: str = Node.left.id + if vname in Gen.var_signedness and Gen.var_signedness[vname]: + is_unsigned = True + # 双指针算术 fail-fast 已**下沉到单一收敛点** `Gen.emit_binary_op` + # (LLVMCG/ExprGen.py):BinOp 与 AugAssign(+=、-=、*= …)都经该函数, + # 判据(运算符属算术/位运算/移位集合 且 两操作数都是 LLVM 指针)与报文 + # 在那里一次收口。此处不再保留副本,避免两处判据/报文分叉。 + result = Gen.emit_binary_op(Op, LeftVal, RightVal, is_unsigned=is_unsigned) + return result + + def _HandleBoolOpLlvm(self, Node: ast.BoolOp) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + + def _to_bool(val: Any, node: ast.expr) -> Any: + if isinstance(val.type, ir.IntType) and val.type.width == 1: + return val + return self._CoerceCondToBool(Gen, node, val) + + values: list[ast.expr] = Node.values + if len(values) < 2: + return self.HandleExprLlvm(values[0]) if values else None + if isinstance(Node.op, ast.And): + phi_incomings: list[tuple[ir.Constant, ir.Block]] = [] + end_bb: ir.Block = Gen.func.append_basic_block(name="and.end") + for idx in range(len(values) - 1): + val: Any = self.HandleExprLlvm(values[idx]) + if not val: + if not end_bb.is_terminated: + saved_block: ir.Block = Gen.builder.block + Gen.builder.position_at_start(end_bb) + Gen.builder.unreachable() + Gen.builder.position_at_end(saved_block) + return None + if not isinstance(val.type, ir.IntType) or val.type.width != 1: + val = _to_bool(val, values[idx]) + cur_bb: ir.Block = Gen.builder.block + next_bb: ir.Block = Gen.func.append_basic_block(name=f"and.rhs{idx}") + Gen.builder.cbranch(val, next_bb, end_bb) + phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb)) + Gen.builder.position_at_start(next_bb) + last_val: Any = self.HandleExprLlvm(values[-1]) + if not last_val: + last_val = ir.Constant(ir.IntType(1), 0) + if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1: + last_val = _to_bool(last_val, values[-1]) + last_bb: ir.Block = Gen.builder.block + Gen.builder.branch(end_bb) + Gen.builder.position_at_start(end_bb) + result_phi: Any = Gen.builder.phi(ir.IntType(1), name="and.result") + for const_val, bb in phi_incomings: + result_phi.add_incoming(const_val, bb) + result_phi.add_incoming(last_val, last_bb) + return result_phi + elif isinstance(Node.op, ast.Or): + phi_incomings = [] + end_bb = Gen.func.append_basic_block(name="or.end") + for idx in range(len(values) - 1): + val = self.HandleExprLlvm(values[idx]) + if not val: + if not end_bb.is_terminated: + saved_block = Gen.builder.block + Gen.builder.position_at_start(end_bb) + Gen.builder.unreachable() + Gen.builder.position_at_end(saved_block) + return None + if not isinstance(val.type, ir.IntType) or val.type.width != 1: + val = _to_bool(val, values[idx]) + cur_bb = Gen.builder.block + next_bb = Gen.func.append_basic_block(name=f"or.rhs{idx}") + Gen.builder.cbranch(val, end_bb, next_bb) + phi_incomings.append((ir.Constant(ir.IntType(1), 1), cur_bb)) + Gen.builder.position_at_start(next_bb) + last_val = self.HandleExprLlvm(values[-1]) + if not last_val: + last_val = ir.Constant(ir.IntType(1), 1) + if not isinstance(last_val.type, ir.IntType) or last_val.type.width != 1: + last_val = _to_bool(last_val, values[-1]) + last_bb = Gen.builder.block + Gen.builder.branch(end_bb) + Gen.builder.position_at_start(end_bb) + result_phi = Gen.builder.phi(ir.IntType(1), name="or.result") + for const_val, bb in phi_incomings: + result_phi.add_incoming(const_val, bb) + result_phi.add_incoming(last_val, last_bb) + return result_phi + return None + + def _HandleUnaryOpLlvm(self, Node: ast.UnaryOp) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + OperandVal: Any = self.HandleExprLlvm(Node.operand) + if not OperandVal: + return None + OpSymbol: str | None = self.Trans.ExprHandler.GetUnaryOpSymbol(Node.op) + if not OpSymbol: + return None + UNARY_OVERLOAD_MAP: dict[str, str] = { + '-': '__neg__', + '+': '__pos__', + '~': '__invert__', + } + if OpSymbol in UNARY_OVERLOAD_MAP: + ClassName: str | None = None + if isinstance(OperandVal.type, ir.PointerType): + pointee: ir.Type = OperandVal.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + ClassName = CN + break + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + ClassName = self.Trans.ExprUtils._get_var_class(Node.operand, Gen) + if ClassName and ClassName in Gen.structs: + TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[ClassName]) + OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}") + if ClassName is None and isinstance(Node.operand, ast.Name): + ClassName = Gen.var_struct_class.get(Node.operand.id) + if ClassName and ClassName in Gen.structs: + if isinstance(OperandVal.type, ir.PointerType) and isinstance(OperandVal.type.pointee, ir.IntType) and OperandVal.type.pointee.width == 8: + TargetStructPtr = ir.PointerType(Gen.structs[ClassName]) + OperandVal = Gen.builder.bitcast(OperandVal, TargetStructPtr, name=f"bitcast_to_{ClassName}") + if ClassName: + op_name: str = UNARY_OVERLOAD_MAP[OpSymbol] + result: Any = self.Trans.ExprUtils._try_operator_overLoad(ClassName, op_name, OperandVal, Gen) + if result is not None: + return result + if OpSymbol == 'not': + # not x = 逻辑取反(优先调度 __bool__,与 VPC 对齐) + bool_val: Any = self._CoerceCondToBool(Gen, Node.operand, OperandVal) + return Gen.builder.not_(bool_val, name="not_result") + elif OpSymbol == '~': + if isinstance(OperandVal.type, ir.IntType): + mask: int = (1 << OperandVal.type.width) - 1 + return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, mask), name="bnot_result") + return Gen.builder.xor(OperandVal, ir.Constant(OperandVal.type, -1), name="bnot_result") + elif OpSymbol == '+': + return OperandVal + elif OpSymbol == '-': + # 一元减 + 整数字面量的**联合越界判定**(与 VPC `_parse_factor` 同口径): + # `-` 不属于字面量,幅值合法(≤ 2^64-1)的字面量取负后可能 < -2^63。 + if isinstance(Node.operand, ast.Constant) \ + and isinstance(Node.operand.value, int) \ + and not isinstance(Node.operand.value, bool): + RequireIntLiteralStorable(Gen, self.Trans, Node, -Node.operand.value) + if isinstance(OperandVal.type, ir.IntType): + return Gen.builder.neg(OperandVal, name="neg_result") + elif isinstance(OperandVal.type, (ir.FloatType, ir.DoubleType)): + return Gen.builder.fneg(OperandVal, name="fneg_result") + elif OpSymbol == '*': + # 单目解引用:*p = load(p),要求操作数是指针 + if not isinstance(OperandVal.type, ir.PointerType): + raise TypeError( + f"[fast-fail] 单目 '*' 解引用的操作数必须是指针类型," + f"实际为 {OperandVal.type}" + ) + return Gen._load(OperandVal, name="deref") + elif OpSymbol == '&': + # 单目取地址:&x 返回变量/字段/下标的地址(T*) + if isinstance(Node.operand, ast.Name): + VarName: str = Node.operand.id + if VarName in Gen.functions: + return Gen.functions[VarName] + if VarName in Gen.variables and Gen.variables[VarName] is not None: + var_ptr: ir.Value = Gen.variables[VarName] + if isinstance(var_ptr.type, ir.PointerType) and isinstance(var_ptr.type.pointee, ir.PointerType): + # T**:若 T 是 struct 类(变量语义即对象),返回对象指针 + if isinstance(var_ptr.type.pointee.pointee, ir.IdentifiedStructType): + return self.HandleExprLlvm(Node.operand) + return var_ptr + elif isinstance(Node.operand, ast.Attribute): + AttrPtr: ir.Value | None = self.Trans.ExprAttrHandle._get_attr_ptr(Node.operand) + if AttrPtr and isinstance(AttrPtr.type, ir.PointerType): + return AttrPtr + elif isinstance(Node.operand, ast.Subscript): + SubPtr: ir.Value | None = self.Trans.ExprAttrHandle.HandleSubscriptPtrLlvm(Node.operand) + if SubPtr and isinstance(SubPtr.type, ir.PointerType): + return SubPtr + elif isinstance(Node.operand, ast.Call): + # &() → rvalue 取址。复用上面已求值的 OperandVal,绝不二次求值 + # (构造调用会再跑一遍 __before_init__/__init__)。 + # · 构造调用(_HandleClassNewLlvm)返回的就是临时对象的栈存储指针 + # Ptr(Struct) —— 该指针即"栈构造结构体的地址"本身,原样返回; + # 不能再套 alloca+store(会得到 Ptr(Ptr(Struct)),赋值端还得 + # load 一次回落,语义也从"对象地址"变"槽地址")。 + # · 其它返回值(标量、按值返回的结构体值):入口临时槽承载—— + # allocaEntry 同类型槽 → store → 返回槽地址(与 ViperC 侧 + # ExprGenOps._addrof_of_call 同口径)。 + OperandType: ir.Type = OperandVal.type + if isinstance(OperandType, ir.PointerType) \ + and isinstance(OperandType.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + return OperandVal + TmpSlot: ir.Value = Gen._allocaEntry(OperandType, name="addrof_tmp") + Gen.builder.store(OperandVal, TmpSlot) + return TmpSlot + raise TypeError( + f"[fast-fail] 单目 '&' 取地址的操作数必须是左值" + f"(Name/Attribute/Subscript)或构造调用/函数调用结果,实际为 {type(Node.operand).__name__}" + ) + return None + + def _HandleCompareLlvm(self, Node: ast.Compare) -> ir.Value | None: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + LeftVal: Any = self.HandleExprLlvm(Node.left) + if not LeftVal: + return None + is_unsigned: bool = Gen._check_node_unsigned(Node.left) or (Gen._check_node_unsigned(Node.comparators[0]) if Node.comparators else False) + if len(Node.ops) == 1: + Op: ast.cmpop = Node.ops[0] + # in/not in: 调用 __contains__ + if isinstance(Op, (ast.In, ast.NotIn)): + RightVal: Any = self.HandleExprLlvm(Node.comparators[0]) + if RightVal: + ClassName: str | None = self.Trans.ExprHandler._get_var_class(Node.comparators[0], Gen) + if ClassName and Gen._has_function(f'{ClassName}.__contains__'): + # 处理 RightVal 为 i8(整数)的情况:联合类型 | t.CPtr 降级后可能被 load 为 i8 + if isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8: + RightVal = Gen.builder.inttoptr(RightVal, ir.PointerType(ir.IntType(8)), name=f"i8_to_ptr_{ClassName}") + if isinstance(RightVal.type, ir.PointerType) and isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8: + RightVal = Gen.builder.bitcast(RightVal, ir.PointerType(Gen.structs[ClassName]), name=f"cast_{ClassName}") + contains_func: ir.Function = Gen._get_function(f'{ClassName}.__contains__') + result: ir.Value = Gen.builder.call(contains_func, [RightVal, LeftVal], name=f"call_{ClassName}.__contains__") + # 归一化为 i1 + if isinstance(result.type, ir.IntType) and result.type.width > 1: + zero: ir.Constant = ir.Constant(result.type, 0) + result = Gen.builder.icmp_signed('!=', result, zero, name="contains_bool") + if isinstance(Op, ast.NotIn): + result = Gen.builder.not_(result, name="not_in_result") + return result + return ir.Constant(ir.IntType(1), 0) + ComparatorSymbol: str | None = self.Trans.ExprHandler.GetComparatorSymbol(Op) + if not ComparatorSymbol: + return None + RightVal: Any = self.HandleExprLlvm(Node.comparators[0]) + if not RightVal: + return None + # 比较运算符重载检测:当左侧是结构体指针时,尝试调用 __eq__/__ne__/__lt__/__le__/__gt__/__ge__ + CMP_OVERLOAD_MAP: dict[str, str] = { + '==': '__eq__', '!=': '__ne__', '<': '__lt__', + '<=': '__le__', '>': '__gt__', '>=': '__ge__', + } + if ComparatorSymbol in CMP_OVERLOAD_MAP: + LeftClassName: str | None = None + if isinstance(LeftVal.type, ir.PointerType): + pointee: ir.Type = LeftVal.type.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + for CN, ST in Gen.structs.items(): + if pointee == ST: + LeftClassName = CN + break + elif isinstance(pointee, ir.IntType) and pointee.width == 8: + LeftClassName = self.Trans.ExprUtils._get_var_class(Node.left, Gen) + if LeftClassName and LeftClassName in Gen.structs: + TargetStructPtr: ir.PointerType = ir.PointerType(Gen.structs[LeftClassName]) + LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") + if LeftClassName is None and isinstance(Node.left, ast.Name): + LeftClassName = Gen.var_struct_class.get(Node.left.id) + if LeftClassName and LeftClassName in Gen.structs: + if isinstance(LeftVal.type, ir.PointerType) and isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8: + TargetStructPtr = ir.PointerType(Gen.structs[LeftClassName]) + LeftVal = Gen.builder.bitcast(LeftVal, TargetStructPtr, name=f"bitcast_to_{LeftClassName}") + if LeftClassName: + op_name: str = CMP_OVERLOAD_MAP[ComparatorSymbol] + result: Any = self.Trans.ExprUtils._try_operator_overLoad(LeftClassName, op_name, LeftVal, Gen, RightVal) + if result is not None: + return result + if isinstance(LeftVal, ir.Constant) and isinstance(RightVal, ir.Constant): + if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): + lv: int = LeftVal.constant + rv: int = RightVal.constant + lw: int = LeftVal.type.width + rw: int = RightVal.type.width + if lw < rw: + lv = lv if lv >= 0 else lv + (1 << lw) + elif rw < lw: + rv = rv if rv >= 0 else rv + (1 << rw) + if is_unsigned: + mask_l: int = (1 << max(lw, rw)) - 1 + lv = lv & mask_l + rv = rv & mask_l + cmp_map: dict[str, bool] = { + '==': lv == rv, '!=': lv != rv, + '<': lv < rv, '>': lv > rv, + '<=': lv <= rv, '>=': lv >= rv, + } + result: bool = cmp_map.get(ComparatorSymbol, False) + return ir.Constant(ir.IntType(1), 1 if result else 0) + # is / is not / == / != : 整数与 None(空指针常量)比较 + # 整数上的 None 隐式表示 0 ⇒ `x is None` / `x == None` 等价 `x == 0`, + # `x is not None` / `x != None` 等价 `x != 0`(与 VPC 的 ExprGenOps 统一) + # 此检查必须在类型对齐之前,否则类型对齐会对 i8* null 执行 load(空指针解引用)导致崩溃; + # 也必须在下方"指针 vs 整型"特例分支之前,否则 i8 会命中 `Gen._load(null)` + if ComparatorSymbol in ('is', 'is not', '==', '!='): + LeftIsNullPtr: bool = isinstance(LeftVal, ir.Constant) and LeftVal.constant is None and isinstance(LeftVal.type, ir.PointerType) + RightIsNullPtr: bool = isinstance(RightVal, ir.Constant) and RightVal.constant is None and isinstance(RightVal.type, ir.PointerType) + LeftIsValueType: bool = isinstance(LeftVal.type, ir.IntType) + RightIsValueType: bool = isinstance(RightVal.type, ir.IntType) + IsNoneNegated: bool = ComparatorSymbol in ('is not', '!=') + if LeftIsValueType and RightIsNullPtr: + IsNoneCmpL: ir.Value = Gen.builder.icmp_signed('==', LeftVal, ir.Constant(LeftVal.type, 0), name="is_none_int") + if IsNoneNegated: + IsNoneCmpL = Gen.builder.not_(IsNoneCmpL, name="is_not_result") + return IsNoneCmpL + if RightIsValueType and LeftIsNullPtr: + IsNoneCmpR: ir.Value = Gen.builder.icmp_signed('==', RightVal, ir.Constant(RightVal.type, 0), name="is_none_int") + if IsNoneNegated: + IsNoneCmpR = Gen.builder.not_(IsNoneCmpR, name="is_not_result") + return IsNoneCmpR + if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): + if LeftVal.type.width < RightVal.type.width: + # 如果宽类型常量值超出窄类型有符号范围,则必须用zext + # i8 类型默认使用 zext(系统编程中 i8 几乎总是无符号字节) + need_zext: bool = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1 + if not need_zext and isinstance(RightVal, ir.Constant): + signed_max: int = (1 << (LeftVal.type.width - 1)) - 1 + try: + if RightVal.constant > signed_max or RightVal.constant < 0: + need_zext = True + except (TypeError, ValueError): + pass + if need_zext: + LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name="zext_left_cmp") + else: + LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name="sext_left_cmp") + elif LeftVal.type.width > RightVal.type.width: + need_zext = is_unsigned or Gen._check_node_unsigned(Node.comparators[0]) or RightVal.type.width == 8 or RightVal.type.width == 1 + if not need_zext and isinstance(LeftVal, ir.Constant): + signed_max = (1 << (RightVal.type.width - 1)) - 1 + try: + if LeftVal.constant > signed_max or LeftVal.constant < 0: + need_zext = True + except (TypeError, ValueError): + pass + if need_zext: + RightVal = Gen.builder.zext(RightVal, LeftVal.type, name="zext_right_cmp") + else: + RightVal = Gen.builder.sext(RightVal, LeftVal.type, name="sext_right_cmp") + elif isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.IntType): + if isinstance(LeftVal.type.pointee, ir.IntType) and LeftVal.type.pointee.width == 8 and isinstance(RightVal.type, ir.IntType) and RightVal.type.width == 8: + LeftVal = Gen._load(LeftVal, name="Load_char_ptr_cmp") + else: + if isinstance(RightVal, ir.Constant) and RightVal.constant == 0: + RightVal = ir.Constant(LeftVal.type, None) + else: + # 指针 vs 非零整数: 将指针转为整数走正常 ==(地址值比较) + LeftVal = Gen.builder.ptrtoint(LeftVal, RightVal.type, name="ptr2int_cmp") + elif isinstance(RightVal.type, ir.PointerType) and isinstance(LeftVal.type, ir.IntType): + if isinstance(RightVal.type.pointee, ir.IntType) and RightVal.type.pointee.width == 8 and isinstance(LeftVal.type, ir.IntType) and LeftVal.type.width == 8: + RightVal = Gen._load(RightVal, name="Load_char_ptr_cmp") + else: + if isinstance(LeftVal, ir.Constant) and LeftVal.constant == 0: + LeftVal = ir.Constant(RightVal.type, None) + else: + # 非零整数 vs 指针: 将指针转为整数走正常 ==(地址值比较) + RightVal = Gen.builder.ptrtoint(RightVal, LeftVal.type, name="ptr2int_cmp") + # is / is not: 地址比较(指针)或值比较(int) + if ComparatorSymbol in ('is', 'is not'): + if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType): + addr_cmp = Gen.builder.icmp_unsigned('==', LeftVal, RightVal, name="is_cmp") + if ComparatorSymbol == 'is not': + addr_cmp = Gen.builder.not_(addr_cmp, name="is_not_result") + return addr_cmp + if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): + val_cmp = Gen.builder.icmp_signed('==', LeftVal, RightVal, name="is_cmp") + if ComparatorSymbol == 'is not': + val_cmp = Gen.builder.not_(val_cmp, name="is_not_result") + return val_cmp + # == / !=: 指针间做地址比较(C-Like 语义,不隐式调用 strcmp) + # 如需字符串内容比较,应显式调用 string.strcmp() + if isinstance(LeftVal.type, ir.PointerType) and isinstance(RightVal.type, ir.PointerType): + result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") + elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)): + if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType): + RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name="int_to_float_cmp") + elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType): + LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name="int_to_float_cmp") + result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") + elif is_unsigned: + result = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") + else: + result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name="cmpresult") + return result + EndBB: ir.Block = Gen.func.append_basic_block(name="cmp.end") + phi_incomings: list[tuple[ir.Value, ir.Block]] = [] + for i, (Op, Comparator) in enumerate(zip(Node.ops, Node.comparators)): + ComparatorSymbol = self.Trans.ExprHandler.GetComparatorSymbol(Op) + if not ComparatorSymbol: + continue + RightVal = self.HandleExprLlvm(Comparator) + if not RightVal: + continue + if isinstance(LeftVal.type, ir.IntType) and isinstance(RightVal.type, ir.IntType): + if LeftVal.type.width < RightVal.type.width: + need_zext = is_unsigned or Gen._check_node_unsigned(Node.left) or LeftVal.type.width == 8 or LeftVal.type.width == 1 + if not need_zext and isinstance(RightVal, ir.Constant): + signed_max = (1 << (LeftVal.type.width - 1)) - 1 + try: + if RightVal.constant > signed_max or RightVal.constant < 0: + need_zext = True + except (TypeError, ValueError): + pass + if need_zext: + LeftVal = Gen.builder.zext(LeftVal, RightVal.type, name=f"zext_left_{i}") + else: + LeftVal = Gen.builder.sext(LeftVal, RightVal.type, name=f"sext_left_{i}") + elif LeftVal.type.width > RightVal.type.width: + need_zext = is_unsigned or Gen._check_node_unsigned(Comparator) or RightVal.type.width == 8 or RightVal.type.width == 1 + if not need_zext and isinstance(LeftVal, ir.Constant): + signed_max = (1 << (RightVal.type.width - 1)) - 1 + try: + if LeftVal.constant > signed_max or LeftVal.constant < 0: + need_zext = True + except (TypeError, ValueError): + pass + if need_zext: + RightVal = Gen.builder.zext(RightVal, LeftVal.type, name=f"zext_right_{i}") + else: + RightVal = Gen.builder.sext(RightVal, LeftVal.type, name=f"sext_right_{i}") + if is_unsigned: + cmp_result: Any = Gen.builder.icmp_unsigned(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}") + elif isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) or isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)): + if isinstance(LeftVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(RightVal.type, ir.IntType): + RightVal = Gen.builder.sitofp(RightVal, LeftVal.type, name=f"int_to_float_cmp_{i}") + elif isinstance(RightVal.type, (ir.FloatType, ir.DoubleType)) and isinstance(LeftVal.type, ir.IntType): + LeftVal = Gen.builder.sitofp(LeftVal, RightVal.type, name=f"int_to_float_cmp_{i}") + cmp_result = Gen.builder.fcmp_unordered(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}") + else: + cmp_result = Gen.builder.icmp_signed(ComparatorSymbol, LeftVal, RightVal, name=f"cmp_{i}") + if i < len(Node.ops) - 1: + NextBB: ir.Block = Gen.func.append_basic_block(name=f"cmp.next.{i}") + cur_bb: ir.Block = Gen.builder.block + Gen.builder.cbranch(cmp_result, NextBB, EndBB) + phi_incomings.append((ir.Constant(ir.IntType(1), 0), cur_bb)) + Gen.builder.position_at_start(NextBB) + LeftVal = RightVal + else: + cur_bb = Gen.builder.block + Gen.builder.branch(EndBB) + phi_incomings.append((cmp_result, cur_bb)) + Gen.builder.position_at_start(EndBB) + final_result: Any = Gen.builder.phi(ir.IntType(1), name="final_cmp") + for val, bb in phi_incomings: + final_result.add_incoming(val, bb) return final_result \ No newline at end of file diff --git a/lib/core/Handles/HandlesExprUtils.py b/lib/core/Handles/HandlesExprUtils.py index 5aed459..1b63814 100644 --- a/lib/core/Handles/HandlesExprUtils.py +++ b/lib/core/Handles/HandlesExprUtils.py @@ -1,443 +1,443 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -import vast as ast -import llvmlite.ir as ir -from lib.core.SymbolUtils import IsTModule - - -class ExprUtils: - def __init__(self, translator: "Translator") -> None: - self.Trans: Translator = translator - - def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None: - OpMap: dict[type, str] = { - ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', - ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&', - ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>', - ast.MatMult: '@', - } - return OpMap.get(type(Op), None) - - def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None: - OpMap: dict[type, str] = { - ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-', - ast.Deref: '*', ast.AddrOf: '&', - } - return OpMap.get(type(Op), None) - - def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None: - OpMap: dict[type, str] = { - ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=', - ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not', - ast.In: 'in', ast.NotIn: 'not in', - } - return OpMap.get(type(Op), None) - - def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: - if isinstance(node, ast.Name): - VarName: str = node.id - return Gen.var_struct_class.get(VarName) - if isinstance(node, ast.Attribute): - AttrName: str = node.attr - # 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable) - # 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派 - # (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST') - BaseClass: str | None = self._get_var_class(node.value, Gen) - if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self': - BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None) - if not BaseClass: - # 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory): - # var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此 - # 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast - # ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。 - BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName) - if BaseClass: - # 对齐 _HandleLenLlvm L3445-3454:class_member_element_class / - # class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀 - # (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段 - # (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败 - # → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查 - # element_class(list 特化名,如 list[...ASTptr])优先。 - _bare: str = BaseClass - if len(_bare) > 17 and _bare[16] == '.': - _bare = _bare[17:] - element_class_map = getattr(Gen, 'class_member_element_class', {}) - try: - _mro_chain: list = Gen._get_mro_chain(_bare) - except Exception: - _mro_chain = [_bare] - for _mro_cls in _mro_chain: - _mk: str = _mro_cls - if len(_mk) > 17 and _mk[16] == '.': - _mk = _mk[17:] - _sub = element_class_map.get(_mk) - if _sub and AttrName in _sub: - return _sub[AttrName] - if _bare in Gen.class_members: - for m_name, m_type in Gen.class_members[_bare]: - if m_name == AttrName: - if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found = Gen.find_struct_by_pointee(m_type.pointee) - if found: - return found[0] - break - # Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后, - # 避免字段名和变量名冲突导致错误分派) - cls: str | None = Gen.var_struct_class.get(AttrName) - if cls: - return cls - return None - return None - - def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST, - AttrName: str) -> str | None: - """`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory) - - 背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在 - `var_struct_class`(只含当前模块变量)也不在 `global_struct_class` - (只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None → - 整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。 - 这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体 - 类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} / - {sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。 - """ - if not isinstance(ModNode, ast.Name): - return None - Mod: str = ModNode.id - imported: Any = getattr(self.Trans, '_ImportedModules', None) or [] - sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {} - try: - resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod) - except Exception: - resolved_mod = Mod - aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {} - _is_mod: bool = (Mod in imported or resolved_mod in imported - or Mod in sha1_map or resolved_mod in sha1_map - or Mod in aliases or resolved_mod in aliases) - if not _is_mod: - # 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory), - # 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。 - for _k in sha1_map: - if _k == Mod or _k.endswith(f'.{Mod}'): - _is_mod = True - break - if not _is_mod: - return None - # 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记) - gsc: Any = getattr(Gen, 'global_struct_class', {}) or {} - vsc: Any = getattr(Gen, 'var_struct_class', {}) or {} - for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName): - if key in gsc: - return gsc[key] - if key in vsc: - return vsc[key] - # 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态) - try: - _via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName) - except Exception: - _via_attr = None - if _via_attr: - return _via_attr - # 3) 从 LLVM 全局量类型反推结构体类名 - cands: list[str] = [f"{Mod}.{AttrName}", AttrName] - for m in imported: - if m == Mod or m.endswith(f".{Mod}"): - k: str = f"{m}.{AttrName}" - if k not in cands: - cands.append(k) - for cand in (Mod, resolved_mod): - sha1: str | None = sha1_map.get(cand) - if sha1: - k2: str = f"{sha1}.{AttrName}" - if k2 not in cands: - cands.append(k2) - globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {} - # 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到 - for _gk in globals_map: - if _gk.endswith(f".{AttrName}") and _gk not in cands: - cands.append(_gk) - for key in cands: - gv: Any = globals_map.get(key) - if gv is None: - continue - ty: Any = getattr(gv, 'type', None) - if not isinstance(ty, ir.PointerType): - continue - pointee: Any = ty.pointee - # 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层 - if isinstance(pointee, ir.PointerType): - pointee = pointee.pointee - if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - found: Any = Gen.find_struct_by_pointee(pointee) - if found: - return found[0] - return None - - def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None: - FullMethodName: str = f'{ClassName}.{op_name}' - if not Gen._has_function(FullMethodName): - FullMethodName = f'{ClassName}.{op_name}__' - if not Gen._has_function(FullMethodName): - # 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 TPV 一致) - for anc in Gen._get_mro_chain(ClassName)[1:]: - anc_method: str = f'{anc}.{op_name}' - if Gen._has_function(anc_method): - FullMethodName = anc_method - break - anc_method_ss = f'{anc}.{op_name}__' - if Gen._has_function(anc_method_ss): - FullMethodName = anc_method_ss - break - if not Gen._has_function(FullMethodName): - # 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且 - # 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本 - # (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时, - # var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。 - # 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。 - if '[' not in ClassName: - SpecClass: str | None = None - # 先收集所有候选特化(有方法 declare 或 define 的) - best_candidates: list[str] = [] - for CN in Gen.structs.keys(): - if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')): - best_candidates.append(CN) - # 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中 - # 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined - # symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。 - for CN in best_candidates: - _fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__' - _f: Any = Gen._get_function(_fn) - if _f is not None and getattr(_f, 'blocks', []): - SpecClass = CN - break - if SpecClass is None and best_candidates: - # 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class - # 缓存机制会从 declare_only 升级为完整特化),补发后重新检查 - try: - self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen) - except Exception: - pass - _fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__' - _f2: Any = Gen._get_function(_fn2) - if _f2 is not None and getattr(_f2, 'blocks', []): - SpecClass = best_candidates[0] - if SpecClass: - FullMethodName = f'{SpecClass}.{op_name}' - if not Gen._has_function(FullMethodName): - FullMethodName = f'{SpecClass}.{op_name}__' - if Gen._has_function(FullMethodName): - func: Any = Gen._get_function(FullMethodName) - # 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时, - # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 - method_anc: str | None = None - _fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName - if '.' in FullMethodName: - _fm_cls: str = FullMethodName.split('.')[0] - if _fm_cls != ClassName: - method_anc = _fm_cls - _call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc) - call_args: list[Any] = [_call_self] - if other_val is not None: - call_args.append(other_val) - if len(func.ftype.args) == len(call_args): - for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)): - if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType): - if expected_type != actual_val.type: - if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): - call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}") - result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}") - return result - return None - - def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]: - info: dict[str, Any] = { - 'name': "unknown", - 'size': 0, - 'signed': False, - 'ptr': False - } - if isinstance(llvm_type, ir.IntType): - info['size'] = llvm_type.width // 8 - info['signed'] = True - if llvm_type.width == 8: - info['name'] = "char" - elif llvm_type.width == 16: - info['name'] = "short" - elif llvm_type.width == 32: - info['name'] = "int" - elif llvm_type.width == 64: - info['name'] = "long long" - else: - info['name'] = f"int{llvm_type.width}" - elif isinstance(llvm_type, ir.DoubleType): - info['size'] = 8 - info['signed'] = True - info['name'] = "double" - elif isinstance(llvm_type, ir.FloatType): - info['size'] = 4 - info['signed'] = True - info['name'] = "float" - elif isinstance(llvm_type, ir.PointerType): - info['size'] = 8 - info['signed'] = False - info['ptr'] = True - pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee) - info['name'] = f"{pointee_info['name']}*" - elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)): - size: int = 0 - for elem in llvm_type.elements: - elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem) - size += elem_info['size'] - info['size'] = size - info['signed'] = False - if isinstance(llvm_type, ir.IdentifiedStructType): - info['name'] = llvm_type.name - else: - info['name'] = "struct" - elif isinstance(llvm_type, ir.ArrayType): - elem_info = self._llvm_type_to_detailed_string(llvm_type.element) - info['size'] = elem_info['size'] * llvm_type.count - info['signed'] = elem_info['signed'] - info['name'] = f"{elem_info['name']}[]" - elif isinstance(llvm_type, ir.VoidType): - info['size'] = 0 - info['signed'] = False - info['name'] = "void" - else: - info['size'] = 0 - info['signed'] = False - info['name'] = str(llvm_type) - return info - - def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - if isinstance(node, ast.Constant): - if isinstance(node.value, bool): - return ir.IntType(32) - elif isinstance(node.value, int): - return ir.IntType(32) - elif isinstance(node.value, float): - return ir.DoubleType() - elif isinstance(node.value, str): - if getattr(node, 'kind', None) == 'u': - return ir.PointerType(ir.IntType(16)) - return ir.PointerType(ir.IntType(8)) - return ir.IntType(32) - elif isinstance(node, ast.Name): - if node.id in Gen.variables: - var_ptr: Any = Gen.variables[node.id] - if isinstance(var_ptr.type, ir.PointerType): - return var_ptr.type.pointee - return var_ptr.type - if node.id in Gen._reg_values: - return Gen._reg_values[node.id].type - return ir.IntType(32) - elif isinstance(node, ast.Attribute): - attr_name: str = node.attr - AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name) - if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None): - # 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型) - return self.Trans.LlvmGen._enum_int_type( - getattr(AttrInfo, 'EnumName', None), AttrInfo) - return ir.IntType(32) - elif isinstance(node, ast.BinOp): - left_type: ir.Type = self._infer_expr_llvm_type_full(node.left) - right_type: ir.Type = self._infer_expr_llvm_type_full(node.right) - if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)): - return ir.DoubleType() - if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType): - return ir.PointerType(ir.IntType(8)) - return ir.IntType(32) - elif isinstance(node, ast.UnaryOp): - op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand) - if isinstance(node.op, ast.Deref): - # *p → pointee 类型 - if isinstance(op_inner, ir.PointerType): - return op_inner.pointee - return op_inner - if isinstance(node.op, ast.AddrOf): - # &x → T*(取地址即指针) - return ir.PointerType(op_inner) - return op_inner - elif isinstance(node, ast.Subscript): - val_type: ir.Type = self._infer_expr_llvm_type_full(node.value) - if isinstance(val_type, ir.PointerType): - if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8: - return ir.IntType(8) - if isinstance(val_type.pointee, ir.ArrayType): - return ir.PointerType(val_type.pointee.element) - return val_type.pointee - if isinstance(val_type, ir.ArrayType): - return val_type.element - if isinstance(val_type, ir.IntType): - return ir.IntType(8) - return ir.IntType(32) - elif isinstance(node, ast.Call): - if isinstance(node.func, ast.Name): - func_name: str = node.func.id - if Gen._has_function(func_name): - fn: Any = Gen._get_function(func_name) - return fn.function_type.return_type - if func_name == 'len': - return ir.IntType(32) - if func_name == 'sizeof': - return ir.IntType(32) - if func_name == 'ord': - return ir.IntType(32) - if func_name == 'chr': - return ir.IntType(8) - if func_name == 'int': - return ir.IntType(32) - if func_name == 'float' or func_name == 'double': - return ir.DoubleType() - elif isinstance(node.func, ast.Attribute): - if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c': - if node.func.attr == 'Deref': - return ir.IntType(64) - if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable): - if node.func.attr == 'CChar': - return ir.PointerType(ir.IntType(8)) - return ir.IntType(32) - elif isinstance(node, ast.Compare): - return ir.IntType(1) - elif isinstance(node, ast.BoolOp): - return ir.IntType(1) - elif isinstance(node, ast.IfExp): - return self._infer_expr_llvm_type_full(node.body) - elif isinstance(node, ast.Attribute): - return ir.PointerType(ir.IntType(8)) - return ir.IntType(32) - - def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None: - """收集 AST 节点中的绑定变量和自由变量 - - Args: - node: AST 节点 - bound: 集合,收集绑定变量(ast.Store 上下文) - free: 集合,收集自由变量(ast.Load 上下文),可为 None - """ - if free is None: - free = set() - if isinstance(node, ast.Name): - if isinstance(node.ctx, ast.Store): - bound.add(node.id) - elif isinstance(node.ctx, ast.Load): - free.add(node.id) - elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)): - # 这些节点有自己的作用域,不递归进入 - # 但需要收集参数名作为 bound - if hasattr(node, 'args') and node.args: - for arg in node.args.args: - bound.add(arg.arg) - elif getattr(node, '_fields', None): - for field in node._fields: - value: Any = getattr(node, field, None) - if isinstance(value, list): - for item in value: - if isinstance(item, (ast.stmt, ast.expr)): - self._collect_names(item, bound, free) - elif isinstance(value, (ast.stmt, ast.expr)): +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +import vast as ast +import llvmlite.ir as ir +from lib.core.SymbolUtils import IsTModule + + +class ExprUtils: + def __init__(self, translator: "Translator") -> None: + self.Trans: Translator = translator + + def GetOpSymbol(self, Op: ast.cmpop | ast.operator | ast.unaryoperator) -> str | None: + OpMap: dict[type, str] = { + ast.Add: '+', ast.Sub: '-', ast.Mult: '*', ast.Div: '/', + ast.FloorDiv: '//', ast.Mod: '%', ast.Pow: '**', ast.BitAnd: '&', + ast.BitOr: '|', ast.BitXor: '^', ast.LShift: '<<', ast.RShift: '>>', + ast.MatMult: '@', + } + return OpMap.get(type(Op), None) + + def GetUnaryOpSymbol(self, Op: ast.unaryoperator) -> str | None: + OpMap: dict[type, str] = { + ast.Invert: '~', ast.Not: 'not', ast.UAdd: '+', ast.USub: '-', + ast.Deref: '*', ast.AddrOf: '&', + } + return OpMap.get(type(Op), None) + + def GetComparatorSymbol(self, Op: ast.cmpop) -> str | None: + OpMap: dict[type, str] = { + ast.Eq: '==', ast.NotEq: '!=', ast.Lt: '<', ast.LtE: '<=', + ast.Gt: '>', ast.GtE: '>=', ast.Is: 'is', ast.IsNot: 'is not', + ast.In: 'in', ast.NotIn: 'not in', + } + return OpMap.get(type(Op), None) + + def _get_var_class(self, node: ast.AST, Gen: LlvmCodeGenerator) -> str | None: + if isinstance(node, ast.Name): + VarName: str = node.id + return Gen.var_struct_class.get(VarName) + if isinstance(node, ast.Attribute): + AttrName: str = node.attr + # 从父对象的类成员类型推导(如 self._ht → ParsedArgs._ht → HashTable) + # 必须先查 BaseClass,避免字段名和变量名冲突导致错误分派 + # (如 old_args.args 匹配到函数参数 args 的 var_struct_class='AST') + BaseClass: str | None = self._get_var_class(node.value, Gen) + if not BaseClass and isinstance(node.value, ast.Name) and node.value.id == 'self': + BaseClass = getattr(self.Trans, '_CurrentCpythonObjectClass', None) + if not BaseClass: + # 【模块别名 + 模块级变量】`Mod.Var` 形态(如 CompilerFactory.factory): + # var_struct_class 只登记**当前模块**变量,跨模块量不在其中 → 递归到此 + # 返回 None → 整条链 `Mod.Var.Field` 解析为空 → 赋值/调用 fail-fast + # ("属性访问未能解析出目标类型/成员",StructGenMixin.vp:139 根因)。 + BaseClass = self._resolve_module_var_class(Gen, node.value, AttrName) + if BaseClass: + # 对齐 _HandleLenLlvm L3445-3454:class_member_element_class / + # class_members 的键是裸类名,而 var_struct_class 可能带 sha1 前缀 + # (如 'd81b22c853de40a2.Call')。直查会 miss → 容器字段 + # (list[...] / rawptr[list[...]],如 ast.Call.args)方法分派失败 + # → "Undefined method 'get' in class 'rawptr'"。剥前缀 + MRO 链查 + # element_class(list 特化名,如 list[...ASTptr])优先。 + _bare: str = BaseClass + if len(_bare) > 17 and _bare[16] == '.': + _bare = _bare[17:] + element_class_map = getattr(Gen, 'class_member_element_class', {}) + try: + _mro_chain: list = Gen._get_mro_chain(_bare) + except Exception: + _mro_chain = [_bare] + for _mro_cls in _mro_chain: + _mk: str = _mro_cls + if len(_mk) > 17 and _mk[16] == '.': + _mk = _mk[17:] + _sub = element_class_map.get(_mk) + if _sub and AttrName in _sub: + return _sub[AttrName] + if _bare in Gen.class_members: + for m_name, m_type in Gen.class_members[_bare]: + if m_name == AttrName: + if isinstance(m_type, ir.PointerType) and isinstance(m_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found = Gen.find_struct_by_pointee(m_type.pointee) + if found: + return found[0] + break + # Fallback: 直接查找 var_struct_class(放在 BaseClass 查找之后, + # 避免字段名和变量名冲突导致错误分派) + cls: str | None = Gen.var_struct_class.get(AttrName) + if cls: + return cls + return None + return None + + def _resolve_module_var_class(self, Gen: LlvmCodeGenerator, ModNode: ast.AST, + AttrName: str) -> str | None: + """`模块别名.模块级变量` 的类名解析(如 CompilerFactory.factory) + + 背景:`CompilerFactory.factory.PermPool` 这类链的第一跳既不在 + `var_struct_class`(只含当前模块变量)也不在 `global_struct_class` + (只含按变量名登记的项),于是 `_get_var_class` 递归到底返回 None → + 整条链解析为空 → 赋值/调用 fail-fast("属性访问未能解析出目标类型/成员")。 + 这里按模块别名把 `Mod.Var` 落到其 LLVM 全局量,再从全局量类型反推结构体 + 类名;候选键形态对齐 `_lookup_module_global`(裸名 / {mod}.{var} / + {sha1}.{var})。仅当 ModNode 确为 import 模块(含别名)时生效。 + """ + if not isinstance(ModNode, ast.Name): + return None + Mod: str = ModNode.id + imported: Any = getattr(self.Trans, '_ImportedModules', None) or [] + sha1_map: Any = getattr(Gen, 'ModuleSha1Map', {}) or {} + try: + resolved_mod: str = self.Trans.SymbolTable.resolve_alias(Mod) + except Exception: + resolved_mod = Mod + aliases: Any = getattr(self.Trans.SymbolTable, 'import_aliases', {}) or {} + _is_mod: bool = (Mod in imported or resolved_mod in imported + or Mod in sha1_map or resolved_mod in sha1_map + or Mod in aliases or resolved_mod in aliases) + if not _is_mod: + # 兜底:ModuleSha1Map 的键多为全路径(lib.core.CompilerFactory), + # 用后缀扫描判定「Mod 是否确实是某个已导入模块(含别名)」。 + for _k in sha1_map: + if _k == Mod or _k.endswith(f'.{Mod}'): + _is_mod = True + break + if not _is_mod: + return None + # 1) 变量名侧已有登记(stub 解析时跨模块量常以**裸变量名**登记) + gsc: Any = getattr(Gen, 'global_struct_class', {}) or {} + vsc: Any = getattr(Gen, 'var_struct_class', {}) or {} + for key in (f"{Mod}.{AttrName}", f"{resolved_mod}.{AttrName}", AttrName): + if key in gsc: + return gsc[key] + if key in vsc: + return vsc[key] + # 2) 借属性侧既有解析器(覆盖 module.globals/variables 与前缀形态) + try: + _via_attr: str | None = self.Trans.ExprAttrHandle._resolve_var_classname(Gen, AttrName) + except Exception: + _via_attr = None + if _via_attr: + return _via_attr + # 3) 从 LLVM 全局量类型反推结构体类名 + cands: list[str] = [f"{Mod}.{AttrName}", AttrName] + for m in imported: + if m == Mod or m.endswith(f".{Mod}"): + k: str = f"{m}.{AttrName}" + if k not in cands: + cands.append(k) + for cand in (Mod, resolved_mod): + sha1: str | None = sha1_map.get(cand) + if sha1: + k2: str = f"{sha1}.{AttrName}" + if k2 not in cands: + cands.append(k2) + globals_map: Any = getattr(getattr(Gen, 'module', None), 'globals', {}) or {} + # 3b) 后缀扫描:全局量键常为 "{sha1}.{var}",sha1 不一定能从 Mod 反查到 + for _gk in globals_map: + if _gk.endswith(f".{AttrName}") and _gk not in cands: + cands.append(_gk) + for key in cands: + gv: Any = globals_map.get(key) + if gv is None: + continue + ty: Any = getattr(gv, 'type', None) + if not isinstance(ty, ir.PointerType): + continue + pointee: Any = ty.pointee + # 全局量本身是指针变量(值类型 X*,LLVM 全局类型 X**)→ 再剥一层 + if isinstance(pointee, ir.PointerType): + pointee = pointee.pointee + if isinstance(pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + found: Any = Gen.find_struct_by_pointee(pointee) + if found: + return found[0] + return None + + def _try_operator_overLoad(self, ClassName: str, op_name: str, obj_val: ir.Value, Gen: LlvmCodeGenerator, other_val: ir.Value | None = None) -> ir.Value | None: + FullMethodName: str = f'{ClassName}.{op_name}' + if not Gen._has_function(FullMethodName): + FullMethodName = f'{ClassName}.{op_name}__' + if not Gen._has_function(FullMethodName): + # 多继承:沿 MRO 祖先链查找 dunder 方法(先声明父类优先,与 VPC 一致) + for anc in Gen._get_mro_chain(ClassName)[1:]: + anc_method: str = f'{anc}.{op_name}' + if Gen._has_function(anc_method): + FullMethodName = anc_method + break + anc_method_ss = f'{anc}.{op_name}__' + if Gen._has_function(anc_method_ss): + FullMethodName = anc_method_ss + break + if not Gen._has_function(FullMethodName): + # 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray')且 + # 无直接匹配方法时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本 + # (如 'ndarray[double]')。注解 'numpy.ndarray | t.CPtr' 未指定类型参数时, + # var_struct_class 仅记录裸模板名,但方法只在特化版本中定义。 + # 选择策略:优先选 obj_val.type.pointee 实际匹配的特化;否则选第一个有方法的特化。 + if '[' not in ClassName: + SpecClass: str | None = None + # 先收集所有候选特化(有方法 declare 或 define 的) + best_candidates: list[str] = [] + for CN in Gen.structs.keys(): + if CN.startswith(f'{ClassName}[') and (Gen._has_function(f'{CN}.{op_name}') or Gen._has_function(f'{CN}.{op_name}__')): + best_candidates.append(CN) + # 优先选已编译方法体(define)的特化:dict 插入序选第一个会命中 + # 只有 declare 无 define 的错误特化(如 list[AST]),生成 undefined + # symbol(ld.lld undefined symbol: list[AST].__len__ 根因)。 + for CN in best_candidates: + _fn: str = f'{CN}.{op_name}' if Gen._has_function(f'{CN}.{op_name}') else f'{CN}.{op_name}__' + _f: Any = Gen._get_function(_fn) + if _f is not None and getattr(_f, 'blocks', []): + SpecClass = CN + break + if SpecClass is None and best_candidates: + # 全部只有 declare:按需补发方法体(幂等,_specialize_generic_class + # 缓存机制会从 declare_only 升级为完整特化),补发后重新检查 + try: + self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen) + except Exception: + pass + _fn2: str = f'{best_candidates[0]}.{op_name}' if Gen._has_function(f'{best_candidates[0]}.{op_name}') else f'{best_candidates[0]}.{op_name}__' + _f2: Any = Gen._get_function(_fn2) + if _f2 is not None and getattr(_f2, 'blocks', []): + SpecClass = best_candidates[0] + if SpecClass: + FullMethodName = f'{SpecClass}.{op_name}' + if not Gen._has_function(FullMethodName): + FullMethodName = f'{SpecClass}.{op_name}__' + if Gen._has_function(FullMethodName): + func: Any = Gen._get_function(FullMethodName) + # 多继承 this 调整:运算符重载方法定义在"无 vtable 父类"且字段区偏移 > 0 时, + # 把 self 定位到父类第一个用户字段在子类中的位置并 bitcast 成父类指针。 + method_anc: str | None = None + _fm_short: str = FullMethodName.split('.')[-1] if '.' in FullMethodName else FullMethodName + if '.' in FullMethodName: + _fm_cls: str = FullMethodName.split('.')[0] + if _fm_cls != ClassName: + method_anc = _fm_cls + _call_self: Any = self.Trans.ExprCallHandle._adjust_this_for_inherited_method(Gen, obj_val, ClassName, method_anc) + call_args: list[Any] = [_call_self] + if other_val is not None: + call_args.append(other_val) + if len(func.ftype.args) == len(call_args): + for i, (expected_type, actual_val) in enumerate(zip(func.ftype.args, call_args)): + if isinstance(expected_type, ir.PointerType) and isinstance(actual_val.type, ir.PointerType): + if expected_type != actual_val.type: + if isinstance(expected_type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType)): + call_args[i] = Gen.builder.bitcast(actual_val, expected_type, name=f"bitcast_arg_{i}") + result: Any = Gen.builder.call(func, call_args, name=f"call_{FullMethodName}") + return result + return None + + def _llvm_type_to_detailed_string(self, llvm_type: ir.Type, var_name: str = "") -> dict[str, Any]: + info: dict[str, Any] = { + 'name': "unknown", + 'size': 0, + 'signed': False, + 'ptr': False + } + if isinstance(llvm_type, ir.IntType): + info['size'] = llvm_type.width // 8 + info['signed'] = True + if llvm_type.width == 8: + info['name'] = "char" + elif llvm_type.width == 16: + info['name'] = "short" + elif llvm_type.width == 32: + info['name'] = "int" + elif llvm_type.width == 64: + info['name'] = "long long" + else: + info['name'] = f"int{llvm_type.width}" + elif isinstance(llvm_type, ir.DoubleType): + info['size'] = 8 + info['signed'] = True + info['name'] = "double" + elif isinstance(llvm_type, ir.FloatType): + info['size'] = 4 + info['signed'] = True + info['name'] = "float" + elif isinstance(llvm_type, ir.PointerType): + info['size'] = 8 + info['signed'] = False + info['ptr'] = True + pointee_info: dict[str, Any] = self._llvm_type_to_detailed_string(llvm_type.pointee) + info['name'] = f"{pointee_info['name']}*" + elif isinstance(llvm_type, (ir.LiteralStructType, ir.IdentifiedStructType)): + size: int = 0 + for elem in llvm_type.elements: + elem_info: dict[str, Any] = self._llvm_type_to_detailed_string(elem) + size += elem_info['size'] + info['size'] = size + info['signed'] = False + if isinstance(llvm_type, ir.IdentifiedStructType): + info['name'] = llvm_type.name + else: + info['name'] = "struct" + elif isinstance(llvm_type, ir.ArrayType): + elem_info = self._llvm_type_to_detailed_string(llvm_type.element) + info['size'] = elem_info['size'] * llvm_type.count + info['signed'] = elem_info['signed'] + info['name'] = f"{elem_info['name']}[]" + elif isinstance(llvm_type, ir.VoidType): + info['size'] = 0 + info['signed'] = False + info['name'] = "void" + else: + info['size'] = 0 + info['signed'] = False + info['name'] = str(llvm_type) + return info + + def _infer_expr_llvm_type_full(self, node: ast.AST) -> ir.Type: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + if isinstance(node, ast.Constant): + if isinstance(node.value, bool): + return ir.IntType(32) + elif isinstance(node.value, int): + return ir.IntType(32) + elif isinstance(node.value, float): + return ir.DoubleType() + elif isinstance(node.value, str): + if getattr(node, 'kind', None) == 'u': + return ir.PointerType(ir.IntType(16)) + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + elif isinstance(node, ast.Name): + if node.id in Gen.variables: + var_ptr: Any = Gen.variables[node.id] + if isinstance(var_ptr.type, ir.PointerType): + return var_ptr.type.pointee + return var_ptr.type + if node.id in Gen._reg_values: + return Gen._reg_values[node.id].type + return ir.IntType(32) + elif isinstance(node, ast.Attribute): + attr_name: str = node.attr + AttrInfo: Any = self.Trans.SymbolTable.lookup(attr_name) + if AttrInfo and getattr(AttrInfo, 'IsEnumMember', None): + # 枚举成员表达式的 LLVM 类型 = 该枚举的存储宽度(最宽成员类型) + return self.Trans.LlvmGen._enum_int_type( + getattr(AttrInfo, 'EnumName', None), AttrInfo) + return ir.IntType(32) + elif isinstance(node, ast.BinOp): + left_type: ir.Type = self._infer_expr_llvm_type_full(node.left) + right_type: ir.Type = self._infer_expr_llvm_type_full(node.right) + if isinstance(left_type, (ir.FloatType, ir.DoubleType)) or isinstance(right_type, (ir.FloatType, ir.DoubleType)): + return ir.DoubleType() + if isinstance(left_type, ir.PointerType) or isinstance(right_type, ir.PointerType): + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + elif isinstance(node, ast.UnaryOp): + op_inner: ir.Type = self._infer_expr_llvm_type_full(node.operand) + if isinstance(node.op, ast.Deref): + # *p → pointee 类型 + if isinstance(op_inner, ir.PointerType): + return op_inner.pointee + return op_inner + if isinstance(node.op, ast.AddrOf): + # &x → T*(取地址即指针) + return ir.PointerType(op_inner) + return op_inner + elif isinstance(node, ast.Subscript): + val_type: ir.Type = self._infer_expr_llvm_type_full(node.value) + if isinstance(val_type, ir.PointerType): + if isinstance(val_type.pointee, ir.IntType) and val_type.pointee.width == 8: + return ir.IntType(8) + if isinstance(val_type.pointee, ir.ArrayType): + return ir.PointerType(val_type.pointee.element) + return val_type.pointee + if isinstance(val_type, ir.ArrayType): + return val_type.element + if isinstance(val_type, ir.IntType): + return ir.IntType(8) + return ir.IntType(32) + elif isinstance(node, ast.Call): + if isinstance(node.func, ast.Name): + func_name: str = node.func.id + if Gen._has_function(func_name): + fn: Any = Gen._get_function(func_name) + return fn.function_type.return_type + if func_name == 'len': + return ir.IntType(32) + if func_name == 'sizeof': + return ir.IntType(32) + if func_name == 'ord': + return ir.IntType(32) + if func_name == 'chr': + return ir.IntType(8) + if func_name == 'int': + return ir.IntType(32) + if func_name == 'float' or func_name == 'double': + return ir.DoubleType() + elif isinstance(node.func, ast.Attribute): + if isinstance(node.func.value, ast.Name) and node.func.value.id == 'c': + if node.func.attr == 'Deref': + return ir.IntType(64) + if isinstance(node.func.value, ast.Name) and IsTModule(node.func.value.id, self.Trans.SymbolTable): + if node.func.attr == 'CChar': + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + elif isinstance(node, ast.Compare): + return ir.IntType(1) + elif isinstance(node, ast.BoolOp): + return ir.IntType(1) + elif isinstance(node, ast.IfExp): + return self._infer_expr_llvm_type_full(node.body) + elif isinstance(node, ast.Attribute): + return ir.PointerType(ir.IntType(8)) + return ir.IntType(32) + + def _collect_names(self, node: ast.AST, bound: set[str], free: set[str] | None = None) -> None: + """收集 AST 节点中的绑定变量和自由变量 + + Args: + node: AST 节点 + bound: 集合,收集绑定变量(ast.Store 上下文) + free: 集合,收集自由变量(ast.Load 上下文),可为 None + """ + if free is None: + free = set() + if isinstance(node, ast.Name): + if isinstance(node.ctx, ast.Store): + bound.add(node.id) + elif isinstance(node.ctx, ast.Load): + free.add(node.id) + elif isinstance(node, (ast.Lambda, ast.FunctionDef, ast.AsyncFunctionDef)): + # 这些节点有自己的作用域,不递归进入 + # 但需要收集参数名作为 bound + if hasattr(node, 'args') and node.args: + for arg in node.args.args: + bound.add(arg.arg) + elif getattr(node, '_fields', None): + for field in node._fields: + value: Any = getattr(node, field, None) + if isinstance(value, list): + for item in value: + if isinstance(item, (ast.stmt, ast.expr)): + self._collect_names(item, bound, free) + elif isinstance(value, (ast.stmt, ast.expr)): self._collect_names(value, bound, free) \ No newline at end of file diff --git a/lib/core/Handles/HandlesFunctions.py b/lib/core/Handles/HandlesFunctions.py index 761fb07..72234da 100644 --- a/lib/core/Handles/HandlesFunctions.py +++ b/lib/core/Handles/HandlesFunctions.py @@ -1,2300 +1,2300 @@ -from __future__ import annotations -from typing import TYPE_CHECKING, Any -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -import vast as ast -import copy -import traceback -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta +from __future__ import annotations +from typing import TYPE_CHECKING, Any +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +import vast as ast +import copy +import traceback +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta from lib.core.SymbolUtils import ( IsTModule, FindStructNameInAnnotationWithContext, AnnotationContainsName, - IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, FindAutoAddrMode) -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.includes import t -from lib.includes.t import CTypeRegistry - - -# t 模块存储类标记装饰器名称集合 -_T_STORAGE_DECORATORS = frozenset({'CExport', 'CExtern', 'CStatic', 'State', 'CInline', 'TLS'}) - -# `@c.CReturn` 装饰器已移除;任何地方遇到都必须 fail-fast,不得静默忽略或改写。 -_CRETURN_REMOVED_HINT: str = ( - "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" -) - - -def _RejectCReturnDecorator(node: ast.AST, decorators: list[ast.AST]) -> None: - """扫到 `@c.CReturn` 装饰器即 fail-fast 报错(该装饰器已移除)。""" - for decorator in decorators: - if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): - if decorator.func.attr == 'CReturn': - func_name: str = getattr(node, 'name', '') - raise SyntaxError(f"函数 '{func_name}':{_CRETURN_REMOVED_HINT}") - - -def _RejectRefReturnAnnotation(node: ast.AST) -> None: - """返回注解出现值引用标记(`&T` / `&temp T`)即 fail-fast。 - - 值引用仅用于函数参数位;返回位一律拒绝,禁止静默按指针降级。 - """ - if getattr(node, 'returns', None) is None: - return - if FindAutoAddrMode(node.returns) is not None: - from lib.core.LLVMCG.FuncGen import FailFastError - func_name: str = getattr(node, 'name', '') - raise FailFastError( - f"返回注解不支持值引用 `&T` / `&temp T`(函数 '{func_name}'):" - f"值引用仅用于函数参数位;返回位请改用显式指针类型(如 `T*`)。") - - -def _IsVoidOrDeclAnnotation(Annot: ast.AST) -> bool: - """判断函数返回注解是否为 void 或 extern/export 声明型辅助符号。 - - void:None / t.CVoid,含 `|` 联合形式(注解中 `|` 表示 and), - 如 `-> t.CVoid | t.CExport`(sprintf 先例)→ 掉出末尾 ret void 合法。 - 声明型辅助符号:t.State(extern)/ t.CExport(export)/ t.CExtern—— - 此类函数返回注解被改写为 `<原类型> | 辅助符号` 且 body 为空(extern 声明), - 掉出末尾是正常形态,必须豁免 fail-fast。 - """ - if isinstance(Annot, ast.Constant): - return Annot.value is None - if isinstance(Annot, ast.Attribute): - return getattr(Annot, 'attr', '') in ('CVoid', 'State', 'CExport', 'CExtern') - if isinstance(Annot, ast.BinOp) and isinstance(Annot.op, ast.BitOr): - return _IsVoidOrDeclAnnotation(Annot.left) or _IsVoidOrDeclAnnotation(Annot.right) - return False - - -def _IsVoidOrDeclAnnotation(Annot: ast.AST) -> bool: - """判断函数返回注解是否为 void 或 extern/export 声明型辅助符号。 - - void:None / t.CVoid,含 `|` 联合形式(注解中 `|` 表示 and), - 如 `-> t.CVoid | t.CExport`(sprintf 先例)→ 掉出末尾 ret void 合法。 - 声明型辅助符号:t.State(extern)/ t.CExport(export)/ t.CExtern—— - 此类函数返回注解被改写为 `<原类型> | 辅助符号` 且 body 为空(extern 声明), - 掉出末尾是正常形态,必须豁免 fail-fast。 - """ - if isinstance(Annot, ast.Constant): - return Annot.value is None - if isinstance(Annot, ast.Attribute): - return getattr(Annot, 'attr', '') in ('CVoid', 'State', 'CExport', 'CExtern') - if isinstance(Annot, ast.BinOp) and isinstance(Annot.op, ast.BitOr): - return _IsVoidOrDeclAnnotation(Annot.left) or _IsVoidOrDeclAnnotation(Annot.right) - return False - - -class FunctionHandle(BaseHandle): - @staticmethod - def _ExtractFuncMeta(decorator_list: list[ast.AST]) -> FuncMeta: - meta: FuncMeta = FuncMeta.NONE - if not decorator_list: - return meta - for d in decorator_list: - if isinstance(d, ast.Name): - if d.id == 'staticmethod': - meta |= FuncMeta.STATIC_METHOD - elif d.id == 'property': - meta |= FuncMeta.PROPERTY_GETTER - elif d.id == 'classmethod': - meta |= FuncMeta.CLASS_METHOD - elif isinstance(d, ast.Attribute): - if isinstance(d.value, ast.Name): - # 支持 @property.setter 和 @propname.setter 两种形式 - # @propname.setter 中 propname 是之前用 @property 定义的同名属性 - if d.value.id == 'property' or d.attr in ('setter', 'getter', 'deleter'): - if d.attr == 'setter': - meta |= FuncMeta.PROPERTY_SETTER - elif d.attr == 'getter': - meta |= FuncMeta.PROPERTY_GETTER - elif d.attr == 'deleter': - meta |= FuncMeta.PROPERTY_DELETER - return meta - - def _extract_decorator_storage_flags(self, Node: ast.FunctionDef) -> dict[str, bool]: - """从装饰器列表中提取 t 模块存储类标记 - - 支持 @t.CExport, @t.CExtern, @t.CStatic, @t.State, @t.CInline - 也支持 from t import CExport 等别名形式 - - Returns: - dict with keys: 'is_export', 'is_extern', 'is_static', 'is_state', 'is_inline' - """ - flags: dict[str, bool] = { - 'is_export': False, 'is_extern': False, - 'is_static': False, 'is_state': False, 'is_inline': False, - } - if not Node.decorator_list: - return flags - symtab: object = self.Trans.SymbolTable - for decorator in Node.decorator_list: - attr_name: str | None = None - # @t.CExport 形式 - if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): - if IsTModule(decorator.value.id, symtab): - attr_name = decorator.attr - # @CExport 形式 (from t import CExport) - elif isinstance(decorator, ast.Name): - cls: type | None = getattr(t, decorator.id, None) - if cls is None and hasattr(symtab, '_t_type_symbols'): - cls = symtab._t_type_symbols.get(decorator.id) - if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): - attr_name = cls.__name__ - if attr_name and attr_name in _T_STORAGE_DECORATORS: - if attr_name == 'CExport': - flags['is_export'] = True - elif attr_name == 'CExtern': - flags['is_extern'] = True - elif attr_name == 'CStatic': - flags['is_static'] = True - elif attr_name == 'State': - flags['is_state'] = True - elif attr_name == 'CInline': - flags['is_inline'] = True - return flags - - @staticmethod - def _is_t_storage_decorator(decorator: ast.AST, symtab: object) -> bool: - """检查装饰器是否是 t 模块存储类标记(用于排除 behavior_decorators)""" - attr_name: str | None = None - if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): - if IsTModule(decorator.value.id, symtab): - attr_name = decorator.attr - elif isinstance(decorator, ast.Name): - cls: type | None = getattr(t, decorator.id, None) - if cls is None and hasattr(symtab, '_t_type_symbols'): - cls = symtab._t_type_symbols.get(decorator.id) - if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): - attr_name = cls.__name__ - elif decorator.id in _T_STORAGE_DECORATORS and hasattr(t, decorator.id): - # TLS 是普通函数标记,不是 CType 子类 - attr_name = decorator.id - return attr_name in _T_STORAGE_DECORATORS if attr_name else False - - # ------------------------------------------------------------------ - # 函数签名公共辅助:以下三个方法供 _GetFunctionSignatureLlvm 与 - # _EmitFunctionLlvm 共享,避免 ~80 行重复代码。 - # ------------------------------------------------------------------ - - def _compute_func_name_meta(self, Node: ast.FunctionDef, ClassName: str | None) -> tuple[str, str, FuncMeta]: - """计算函数全名(含 setter/deleter 后缀)和 func_meta - - Returns: - (FuncName, RawFuncName, func_meta) - """ - RawFuncName: str = Node.name - FuncName: str = f"{ClassName}.{RawFuncName}" if ClassName else RawFuncName - func_meta: FuncMeta = self._ExtractFuncMeta(Node.decorator_list) - # property setter/deleter 使用不同的函数名后缀,避免与 getter 冲突 - if FuncMeta.PROPERTY_SETTER in func_meta: - FuncName = FuncName + '$set' - elif FuncMeta.PROPERTY_DELETER in func_meta: - FuncName = FuncName + '$del' - return FuncName, RawFuncName, func_meta - - def _resolve_method_class(self, Node: ast.FunctionDef, ClassName: str | None, func_meta: FuncMeta, FuncName: str, Gen: LlvmCodeGenerator) -> tuple[bool, bool, str | None]: - """判定函数是否是实例方法 / 类方法,并解析所属类名 - - Returns: - (IsMethod, IsClassMethod, ResolvedClassName) - """ - RawFuncName: str = Node.name - IsMethod: bool = False - IsClassMethod: bool = False - ResolvedClassName: str | None = ClassName - if not ResolvedClassName: - for potential_class in Gen.class_methods: - if FuncName.startswith(f"{potential_class}."): - IsMethod = True - ResolvedClassName = potential_class - break - else: - IsMethod = FuncMeta.STATIC_METHOD not in func_meta and FuncMeta.CLASS_METHOD not in func_meta - IsClassMethod = FuncMeta.CLASS_METHOD in func_meta - if RawFuncName == '__init__': - IsMethod = False - return IsMethod, IsClassMethod, ResolvedClassName - - def _apply_struct_self_cls_types(self, ParamTypes: list[ir.Type], ParamNames: list[str], IsMethod: bool, IsClassMethod: bool, ResolvedClassName: str | None, Gen: LlvmCodeGenerator) -> bool: - """根据 IsMethod / IsClassMethod 调整 self/cls 参数的 LLVM 类型 - - - 若是实例方法且无 self 参数,自动插入一个 struct 指针类型; - 此时调用方需要同步插入对应的 ParamTypeStrs(本方法不维护)。 - - 若是 @classmethod,将 cls 参数覆盖为 struct 指针类型。 - - Returns: - True 表示为实例方法自动插入了 self 参数;False 表示未插入。 - """ - if not ResolvedClassName or ResolvedClassName not in Gen.structs: - return False - # 治本修复:同名类 sha1 冲突检测。 - # 当多个模块定义同名类(如 stmts.py:Module 和 __module.py:Module)时, - # Gen.structs[short_name] 可能指向错误模块的 struct(set_body 已锁定错误布局)。 - # 通过 class_sha1_map 获取当前类的源模块 sha1,查找以 sha1 为前缀的完整 struct。 - TargetStruct: ir.IdentifiedStructType = Gen.structs[ResolvedClassName] - ClassSha1Map: dict[str, str] = getattr(Gen, 'class_sha1_map', {}) - ExpectedSha1: str | None = ClassSha1Map.get(ResolvedClassName) - if ExpectedSha1 and isinstance(TargetStruct, ir.IdentifiedStructType): - ExistingSha1: str = Gen._extract_struct_sha1(TargetStruct) - if ExistingSha1 and ExistingSha1 != ExpectedSha1: - # sha1 冲突:查找或创建正确的 struct - FullKey: str = f"{ExpectedSha1}.{ResolvedClassName}" - if FullKey in Gen.structs: - TargetStruct = Gen.structs[FullKey] - else: - # 创建新 struct 并从 stub 加载 body - TargetStruct = Gen._get_or_create_struct(ResolvedClassName, source_sha1=ExpectedSha1) - if hasattr(self.Trans, 'ImportHandler'): - self.Trans.ImportHandler._TryLoadStructFromStub(ResolvedClassName, Gen, source_sha1=ExpectedSha1) - if FullKey in Gen.structs: - TargetStruct = Gen.structs[FullKey] - # 安全回退:如果新 struct 仍为 opaque(body 未加载),回退到原始短名 struct, - # 避免方法编译生成无效 GEP(访问 opaque struct 元素)导致运行时崩溃。 - if isinstance(TargetStruct, ir.IdentifiedStructType): - try: - _is_opaque: bool = TargetStruct.is_opaque - except (AssertionError, AttributeError): - _is_opaque = False - if _is_opaque: - TargetStruct = Gen.structs[ResolvedClassName] - if IsMethod: - StructPtrType: ir.PointerType = ir.PointerType(TargetStruct) - SelfIdx: int = next((i for i, n in enumerate(ParamNames) if n == "self"), -1) - if SelfIdx >= 0: - ParamTypes[SelfIdx] = StructPtrType - return False - ParamTypes.insert(0, StructPtrType) - ParamNames.insert(0, "self") - return True - # 非方法但属于类(如 staticmethod / 顶层函数位于类下) - SelfIdx = next((i for i, n in enumerate(ParamNames) if n == "self"), -1) - if SelfIdx >= 0: - StructPtrType = ir.PointerType(TargetStruct) - ParamTypes[SelfIdx] = StructPtrType - if IsClassMethod: - ClsIdx: int = next((i for i, n in enumerate(ParamNames) if n == "cls"), -1) - if ClsIdx >= 0: - StructPtrType = ir.PointerType(TargetStruct) - ParamTypes[ClsIdx] = StructPtrType - return False - - def _body_contains_raise(self, body: list[ast.AST]) -> bool: - for node in ast.walk(ast.Module(body=body, type_ignores=[])): - if isinstance(node, ast.Raise): - if node.exc: - if isinstance(node.exc, ast.Name) and node.exc.id == 'StopIteration': - continue - if isinstance(node.exc, ast.Call) and isinstance(node.exc.func, ast.Name) and node.exc.func.id == 'StopIteration': - continue - return True - if isinstance(node, ast.Call): - called_name: str | None = None - if isinstance(node.func, ast.Name): - called_name = node.func.id - elif isinstance(node.func, ast.Attribute): - called_name = node.func.attr - if called_name: - Gen: LlvmCodeGenerator = self.Trans.LlvmGen - for prefix in ['', f'{called_name}.', f'__']: - for suffix in ['', f'.{called_name}']: - fname: str = f'{prefix}{called_name}{suffix}' - if fname in Gen.functions: - func: Any = Gen.functions[fname] - if len(func.args) > 0: - for arg in func.args: - if hasattr(arg, 'name') and arg.name in ('__eh_msg_out__', '__eh_code_out__'): - return True - break - return False - - _LLVM_ATTR_NAMES = frozenset({ - 'nobuiltin', 'nounwind', 'noredzone', 'willreturn', 'mustprogress', - 'optnone', 'noinline', 'alwaysinline', 'readnone', 'readonly', - 'writeonly', 'inaccessiblememonly', 'inaccessiblemem_or_argmemonly', - }) - - def _ExtractLLVMAttrs(self, node: ast.AST) -> list[str]: - attrs: list[str] = [] - if isinstance(node, ast.BinOp): - attrs.extend(self._ExtractLLVMAttrs(node.left)) - attrs.extend(self._ExtractLLVMAttrs(node.right)) - elif isinstance(node, ast.Attribute): - attr_name: str | None = self._GetAttrLLVMName(node) - if attr_name: - attrs.append(attr_name) - return attrs - - def _GetAttrLLVMName(self, node: ast.AST) -> str | None: - if not isinstance(node, ast.Attribute): - return None - if node.attr not in self._LLVM_ATTR_NAMES: - return None - value: ast.AST = node.value - if not isinstance(value, ast.Attribute) or value.attr != 'llvm': - return None - inner: ast.AST = value.value - if not isinstance(inner, ast.Attribute) or inner.attr != 'attr': - return None - if not isinstance(inner.value, ast.Name) or inner.value.id != 't': - return None - return node.attr - - def _infer_return_type_from_body(self, Node: ast.AST, Gen: LlvmCodeGenerator | None = None, ClassName: str | None = None) -> tuple[str, bool] | None: - local_var_types: dict[str, tuple[str, bool]] = {} - for child in ast.walk(Node): - if isinstance(child, ast.Assign) and len(child.targets) == 1: - target: ast.AST = child.targets[0] - if isinstance(target, ast.Name) and isinstance(child.value, ast.Subscript): - sub: ast.Subscript = child.value - if (isinstance(sub.value, ast.Attribute) - and isinstance(sub.value.value, ast.Name) - and sub.value.value.id == 'self' - and ClassName and ClassName in Gen.class_members): - member_name: str = sub.value.attr - for m_name, m_type in Gen.class_members[ClassName]: - if m_name == member_name: - if isinstance(m_type, ir.ArrayType): - elem_type: ir.Type = m_type.element - if isinstance(elem_type, ir.FloatType): - local_var_types[target.id] = ('float', False) - elif isinstance(elem_type, ir.DoubleType): - local_var_types[target.id] = ('double', False) - elif isinstance(elem_type, ir.IntType): - if elem_type.width == 8: - local_var_types[target.id] = ('char', False) - else: - local_var_types[target.id] = ('int', False) - elif isinstance(elem_type, ir.PointerType): - if isinstance(elem_type.pointee, ir.IntType) and elem_type.pointee.width == 8: - local_var_types[target.id] = ('char', True) - else: - local_var_types[target.id] = ('void', True) - elif isinstance(elem_type, ir.IdentifiedStructType): - struct_name: str = elem_type.name - if struct_name: - for sn, st in Gen.structs.items(): - if st.name == struct_name or sn == struct_name: - local_var_types[target.id] = (sn, False) - break - else: - local_var_types[target.id] = (elem_type.name, False) - elif isinstance(m_type, ir.PointerType): - pointee: ir.Type = m_type.pointee - if isinstance(pointee, ir.FloatType): - local_var_types[target.id] = ('float', False) - elif isinstance(pointee, ir.DoubleType): - local_var_types[target.id] = ('double', False) - elif isinstance(pointee, ir.IntType): - if pointee.width == 8: - local_var_types[target.id] = ('char', False) - else: - local_var_types[target.id] = ('int', False) - elif isinstance(pointee, ir.PointerType): - local_var_types[target.id] = ('void', True) - elif isinstance(pointee, ir.IdentifiedStructType): - for sn, st in Gen.structs.items(): - if st == pointee or st.name == pointee.name: - local_var_types[target.id] = (sn, True) - break - break - for child in ast.walk(Node): - if isinstance(child, ast.Return) and child.value: - val: ast.AST = child.value - if isinstance(val, ast.Name) and val.id == 'self' and ClassName: - return (ClassName, True) - if isinstance(val, ast.Call): - if isinstance(val.func, ast.Name): - callee: str = val.func.id - if callee == 'malloc': - return ('char', True) - if callee == 'chr': - return ('char', False) - if Gen and callee in Gen.functions: - func: Any = Gen.functions[callee] - if hasattr(func, 'ftype'): - ret_type: ir.Type = func.ftype.return_type - if isinstance(ret_type, ir.PointerType): - if isinstance(ret_type.pointee, ir.IntType) and ret_type.pointee.width == 8: - return ('char', True) - return ('void', True) - elif isinstance(ret_type, ir.IntType): - if ret_type.width == 8: - return ('char', False) - return ('int', False) - elif isinstance(ret_type, ir.VoidType): - return ('void', False) - elif isinstance(val.func, ast.Attribute): - pass - callee_name: str | None = None - if isinstance(val.func, ast.Name): - callee_name = val.func.id - elif isinstance(val.func, ast.Attribute): - callee_name = getattr(val.func, 'attr', None) - if callee_name: - sym: Any = self.Trans.SymbolTable.lookup(callee_name) - if sym and isinstance(sym, dict): - ret_type_str: str = sym.get('return_type', '') - if ret_type_str and ret_type_str != 'int': - IsPtr: bool = sym.get('IsPtr', False) - return (ret_type_str, IsPtr) - elif isinstance(val, ast.Constant): - if val.value is None: - return ('char', True) - elif isinstance(val.value, str) and len(val.value) <= 1: - return ('char', True) - elif isinstance(val, ast.Name): - var_name: str = val.id - if var_name in local_var_types: - return local_var_types[var_name] - var_info: Any = self.Trans.SymbolTable.lookup(var_name) - if var_info and isinstance(var_info, dict): - var_type: str = var_info.get('type', '') - if var_type and var_type != 'int': - IsPtr: bool = var_info.get('IsPtr', False) - return (var_type, IsPtr) - elif isinstance(val, ast.Subscript): - if (isinstance(val.value, ast.Attribute) - and isinstance(val.value.value, ast.Name) - and val.value.value.id == 'self' - and ClassName): - member_name: str = val.value.attr - if ClassName in Gen.class_members: - for m_name, m_type in Gen.class_members[ClassName]: - if m_name == member_name: - if isinstance(m_type, ir.ArrayType): - elem_type: ir.Type = m_type.element - if isinstance(elem_type, ir.FloatType): - return ('float', False) - elif isinstance(elem_type, ir.DoubleType): - return ('double', False) - elif isinstance(elem_type, ir.IntType): - if elem_type.width == 8: - return ('char', False) - return ('int', False) - elif isinstance(elem_type, ir.PointerType): - if isinstance(elem_type.pointee, ir.IntType) and elem_type.pointee.width == 8: - return ('char', True) - return ('void', True) - elif isinstance(elem_type, ir.IdentifiedStructType): - struct_name: str = elem_type.name - if struct_name: - for sn, st in Gen.structs.items(): - if st.name == struct_name or sn == struct_name: - return (sn, False) - return (elem_type.name, False) - elif isinstance(m_type, ir.PointerType): - pointee: ir.Type = m_type.pointee - if isinstance(pointee, ir.FloatType): - return ('float', False) - elif isinstance(pointee, ir.DoubleType): - return ('double', False) - elif isinstance(pointee, ir.IntType): - if pointee.width == 8: - return ('char', False) - return ('int', False) - elif isinstance(pointee, ir.PointerType): - return ('void', True) - elif isinstance(pointee, ir.IdentifiedStructType): - for sn, st in Gen.structs.items(): - if st == pointee or st.name == pointee.name: - return (sn, True) - return (pointee.name, True) - break - if isinstance(val.value, ast.Name): - var_name: str = val.value.id - var_info: Any = self.Trans.SymbolTable.lookup(var_name) - if var_info and isinstance(var_info, dict): - var_type: str = var_info.get('type', '') - if var_type.startswith('list[') or var_type.startswith('List[') or var_type.startswith('t.CArray['): - inner: str = var_type[var_type.index('[') + 1:var_type.rindex(']')] - parts: list[str] = inner.split(',') - elem_type_str: str = parts[0].strip() - if elem_type_str and elem_type_str != 'int': - IsPtr: bool = var_info.get('IsPtr', False) - return (elem_type_str, IsPtr) - return None - - def _GetFunctionSignatureLlvm(self, Node: ast.FunctionDef, Gen: LlvmCodeGenerator, ClassName: str | None = None, extra_params: list[tuple[str, Any]] | None = None) -> tuple[str, ir.FunctionType, Any, list[Any], list[ast.AST], bool, str | None, bool]: - FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName) - if Node.decorator_list: - _RejectCReturnDecorator(Node, Node.decorator_list) - _RejectRefReturnAnnotation(Node) - MultiRetTypes: list[ast.AST] = [] - if Node.returns: - llvm_attrs: list[str] = self._ExtractLLVMAttrs(Node.returns) - if llvm_attrs: - if not hasattr(self, '_pending_llvm_attrs'): - self._pending_llvm_attrs = {} - self._pending_llvm_attrs[FuncName] = llvm_attrs - if isinstance(Node.returns, ast.Subscript) and isinstance(Node.returns.value, ast.Name) and Node.returns.value.id == 'tuple': - slice_node: ast.AST = Node.returns.slice - if isinstance(slice_node, ast.Tuple): - for elt in slice_node.elts: - MultiRetTypes.append(elt) - else: - MultiRetTypes.append(slice_node) - IsPtr: bool = False - ReturnTypeInfo: Any = None - if Node.returns: - if MultiRetTypes: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() - else: - try: - if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): - ReturnTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.returns) - if not isinstance(ReturnTypeInfo, CTypeInfo): - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() + IsPtrSubscriptNode, DesugarPtrSubscriptToUnion, FindAutoAddrMode) +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes import t +from lib.includes.t import CTypeRegistry + + +# t 模块存储类标记装饰器名称集合 +_T_STORAGE_DECORATORS = frozenset({'CExport', 'CExtern', 'CStatic', 'State', 'CInline', 'TLS'}) + +# `@c.CReturn` 装饰器已移除;任何地方遇到都必须 fail-fast,不得静默忽略或改写。 +_CRETURN_REMOVED_HINT: str = ( + "`@c.CReturn` 已移除:请改用返回注解 `-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" +) + + +def _RejectCReturnDecorator(node: ast.AST, decorators: list[ast.AST]) -> None: + """扫到 `@c.CReturn` 装饰器即 fail-fast 报错(该装饰器已移除)。""" + for decorator in decorators: + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): + if decorator.func.attr == 'CReturn': + func_name: str = getattr(node, 'name', '') + raise SyntaxError(f"函数 '{func_name}':{_CRETURN_REMOVED_HINT}") + + +def _RejectRefReturnAnnotation(node: ast.AST) -> None: + """返回注解出现值引用标记(`&T` / `&temp T`)即 fail-fast。 + + 值引用仅用于函数参数位;返回位一律拒绝,禁止静默按指针降级。 + """ + if getattr(node, 'returns', None) is None: + return + if FindAutoAddrMode(node.returns) is not None: + from lib.core.LLVMCG.FuncGen import FailFastError + func_name: str = getattr(node, 'name', '') + raise FailFastError( + f"返回注解不支持值引用 `&T` / `&temp T`(函数 '{func_name}'):" + f"值引用仅用于函数参数位;返回位请改用显式指针类型(如 `T*`)。") + + +def _IsVoidOrDeclAnnotation(Annot: ast.AST) -> bool: + """判断函数返回注解是否为 void 或 extern/export 声明型辅助符号。 + + void:None / t.CVoid,含 `|` 联合形式(注解中 `|` 表示 and), + 如 `-> t.CVoid | t.CExport`(sprintf 先例)→ 掉出末尾 ret void 合法。 + 声明型辅助符号:t.State(extern)/ t.CExport(export)/ t.CExtern—— + 此类函数返回注解被改写为 `<原类型> | 辅助符号` 且 body 为空(extern 声明), + 掉出末尾是正常形态,必须豁免 fail-fast。 + """ + if isinstance(Annot, ast.Constant): + return Annot.value is None + if isinstance(Annot, ast.Attribute): + return getattr(Annot, 'attr', '') in ('CVoid', 'State', 'CExport', 'CExtern') + if isinstance(Annot, ast.BinOp) and isinstance(Annot.op, ast.BitOr): + return _IsVoidOrDeclAnnotation(Annot.left) or _IsVoidOrDeclAnnotation(Annot.right) + return False + + +def _IsVoidOrDeclAnnotation(Annot: ast.AST) -> bool: + """判断函数返回注解是否为 void 或 extern/export 声明型辅助符号。 + + void:None / t.CVoid,含 `|` 联合形式(注解中 `|` 表示 and), + 如 `-> t.CVoid | t.CExport`(sprintf 先例)→ 掉出末尾 ret void 合法。 + 声明型辅助符号:t.State(extern)/ t.CExport(export)/ t.CExtern—— + 此类函数返回注解被改写为 `<原类型> | 辅助符号` 且 body 为空(extern 声明), + 掉出末尾是正常形态,必须豁免 fail-fast。 + """ + if isinstance(Annot, ast.Constant): + return Annot.value is None + if isinstance(Annot, ast.Attribute): + return getattr(Annot, 'attr', '') in ('CVoid', 'State', 'CExport', 'CExtern') + if isinstance(Annot, ast.BinOp) and isinstance(Annot.op, ast.BitOr): + return _IsVoidOrDeclAnnotation(Annot.left) or _IsVoidOrDeclAnnotation(Annot.right) + return False + + +class FunctionHandle(BaseHandle): + @staticmethod + def _ExtractFuncMeta(decorator_list: list[ast.AST]) -> FuncMeta: + meta: FuncMeta = FuncMeta.NONE + if not decorator_list: + return meta + for d in decorator_list: + if isinstance(d, ast.Name): + if d.id == 'staticmethod': + meta |= FuncMeta.STATIC_METHOD + elif d.id == 'property': + meta |= FuncMeta.PROPERTY_GETTER + elif d.id == 'classmethod': + meta |= FuncMeta.CLASS_METHOD + elif isinstance(d, ast.Attribute): + if isinstance(d.value, ast.Name): + # 支持 @property.setter 和 @propname.setter 两种形式 + # @propname.setter 中 propname 是之前用 @property 定义的同名属性 + if d.value.id == 'property' or d.attr in ('setter', 'getter', 'deleter'): + if d.attr == 'setter': + meta |= FuncMeta.PROPERTY_SETTER + elif d.attr == 'getter': + meta |= FuncMeta.PROPERTY_GETTER + elif d.attr == 'deleter': + meta |= FuncMeta.PROPERTY_DELETER + return meta + + def _extract_decorator_storage_flags(self, Node: ast.FunctionDef) -> dict[str, bool]: + """从装饰器列表中提取 t 模块存储类标记 + + 支持 @t.CExport, @t.CExtern, @t.CStatic, @t.State, @t.CInline + 也支持 from t import CExport 等别名形式 + + Returns: + dict with keys: 'is_export', 'is_extern', 'is_static', 'is_state', 'is_inline' + """ + flags: dict[str, bool] = { + 'is_export': False, 'is_extern': False, + 'is_static': False, 'is_state': False, 'is_inline': False, + } + if not Node.decorator_list: + return flags + symtab: object = self.Trans.SymbolTable + for decorator in Node.decorator_list: + attr_name: str | None = None + # @t.CExport 形式 + if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): + if IsTModule(decorator.value.id, symtab): + attr_name = decorator.attr + # @CExport 形式 (from t import CExport) + elif isinstance(decorator, ast.Name): + cls: type | None = getattr(t, decorator.id, None) + if cls is None and hasattr(symtab, '_t_type_symbols'): + cls = symtab._t_type_symbols.get(decorator.id) + if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): + attr_name = cls.__name__ + if attr_name and attr_name in _T_STORAGE_DECORATORS: + if attr_name == 'CExport': + flags['is_export'] = True + elif attr_name == 'CExtern': + flags['is_extern'] = True + elif attr_name == 'CStatic': + flags['is_static'] = True + elif attr_name == 'State': + flags['is_state'] = True + elif attr_name == 'CInline': + flags['is_inline'] = True + return flags + + @staticmethod + def _is_t_storage_decorator(decorator: ast.AST, symtab: object) -> bool: + """检查装饰器是否是 t 模块存储类标记(用于排除 behavior_decorators)""" + attr_name: str | None = None + if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): + if IsTModule(decorator.value.id, symtab): + attr_name = decorator.attr + elif isinstance(decorator, ast.Name): + cls: type | None = getattr(t, decorator.id, None) + if cls is None and hasattr(symtab, '_t_type_symbols'): + cls = symtab._t_type_symbols.get(decorator.id) + if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): + attr_name = cls.__name__ + elif decorator.id in _T_STORAGE_DECORATORS and hasattr(t, decorator.id): + # TLS 是普通函数标记,不是 CType 子类 + attr_name = decorator.id + return attr_name in _T_STORAGE_DECORATORS if attr_name else False + + # ------------------------------------------------------------------ + # 函数签名公共辅助:以下三个方法供 _GetFunctionSignatureLlvm 与 + # _EmitFunctionLlvm 共享,避免 ~80 行重复代码。 + # ------------------------------------------------------------------ + + def _compute_func_name_meta(self, Node: ast.FunctionDef, ClassName: str | None) -> tuple[str, str, FuncMeta]: + """计算函数全名(含 setter/deleter 后缀)和 func_meta + + Returns: + (FuncName, RawFuncName, func_meta) + """ + RawFuncName: str = Node.name + FuncName: str = f"{ClassName}.{RawFuncName}" if ClassName else RawFuncName + func_meta: FuncMeta = self._ExtractFuncMeta(Node.decorator_list) + # property setter/deleter 使用不同的函数名后缀,避免与 getter 冲突 + if FuncMeta.PROPERTY_SETTER in func_meta: + FuncName = FuncName + '$set' + elif FuncMeta.PROPERTY_DELETER in func_meta: + FuncName = FuncName + '$del' + return FuncName, RawFuncName, func_meta + + def _resolve_method_class(self, Node: ast.FunctionDef, ClassName: str | None, func_meta: FuncMeta, FuncName: str, Gen: LlvmCodeGenerator) -> tuple[bool, bool, str | None]: + """判定函数是否是实例方法 / 类方法,并解析所属类名 + + Returns: + (IsMethod, IsClassMethod, ResolvedClassName) + """ + RawFuncName: str = Node.name + IsMethod: bool = False + IsClassMethod: bool = False + ResolvedClassName: str | None = ClassName + if not ResolvedClassName: + for potential_class in Gen.class_methods: + if FuncName.startswith(f"{potential_class}."): + IsMethod = True + ResolvedClassName = potential_class + break + else: + IsMethod = FuncMeta.STATIC_METHOD not in func_meta and FuncMeta.CLASS_METHOD not in func_meta + IsClassMethod = FuncMeta.CLASS_METHOD in func_meta + if RawFuncName == '__init__': + IsMethod = False + return IsMethod, IsClassMethod, ResolvedClassName + + def _apply_struct_self_cls_types(self, ParamTypes: list[ir.Type], ParamNames: list[str], IsMethod: bool, IsClassMethod: bool, ResolvedClassName: str | None, Gen: LlvmCodeGenerator) -> bool: + """根据 IsMethod / IsClassMethod 调整 self/cls 参数的 LLVM 类型 + + - 若是实例方法且无 self 参数,自动插入一个 struct 指针类型; + 此时调用方需要同步插入对应的 ParamTypeStrs(本方法不维护)。 + - 若是 @classmethod,将 cls 参数覆盖为 struct 指针类型。 + + Returns: + True 表示为实例方法自动插入了 self 参数;False 表示未插入。 + """ + if not ResolvedClassName or ResolvedClassName not in Gen.structs: + return False + # 治本修复:同名类 sha1 冲突检测。 + # 当多个模块定义同名类(如 stmts.py:Module 和 __module.py:Module)时, + # Gen.structs[short_name] 可能指向错误模块的 struct(set_body 已锁定错误布局)。 + # 通过 class_sha1_map 获取当前类的源模块 sha1,查找以 sha1 为前缀的完整 struct。 + TargetStruct: ir.IdentifiedStructType = Gen.structs[ResolvedClassName] + ClassSha1Map: dict[str, str] = getattr(Gen, 'class_sha1_map', {}) + ExpectedSha1: str | None = ClassSha1Map.get(ResolvedClassName) + if ExpectedSha1 and isinstance(TargetStruct, ir.IdentifiedStructType): + ExistingSha1: str = Gen._extract_struct_sha1(TargetStruct) + if ExistingSha1 and ExistingSha1 != ExpectedSha1: + # sha1 冲突:查找或创建正确的 struct + FullKey: str = f"{ExpectedSha1}.{ResolvedClassName}" + if FullKey in Gen.structs: + TargetStruct = Gen.structs[FullKey] + else: + # 创建新 struct 并从 stub 加载 body + TargetStruct = Gen._get_or_create_struct(ResolvedClassName, source_sha1=ExpectedSha1) + if hasattr(self.Trans, 'ImportHandler'): + self.Trans.ImportHandler._TryLoadStructFromStub(ResolvedClassName, Gen, source_sha1=ExpectedSha1) + if FullKey in Gen.structs: + TargetStruct = Gen.structs[FullKey] + # 安全回退:如果新 struct 仍为 opaque(body 未加载),回退到原始短名 struct, + # 避免方法编译生成无效 GEP(访问 opaque struct 元素)导致运行时崩溃。 + if isinstance(TargetStruct, ir.IdentifiedStructType): + try: + _is_opaque: bool = TargetStruct.is_opaque + except (AssertionError, AttributeError): + _is_opaque = False + if _is_opaque: + TargetStruct = Gen.structs[ResolvedClassName] + if IsMethod: + StructPtrType: ir.PointerType = ir.PointerType(TargetStruct) + SelfIdx: int = next((i for i, n in enumerate(ParamNames) if n == "self"), -1) + if SelfIdx >= 0: + ParamTypes[SelfIdx] = StructPtrType + return False + ParamTypes.insert(0, StructPtrType) + ParamNames.insert(0, "self") + return True + # 非方法但属于类(如 staticmethod / 顶层函数位于类下) + SelfIdx = next((i for i, n in enumerate(ParamNames) if n == "self"), -1) + if SelfIdx >= 0: + StructPtrType = ir.PointerType(TargetStruct) + ParamTypes[SelfIdx] = StructPtrType + if IsClassMethod: + ClsIdx: int = next((i for i, n in enumerate(ParamNames) if n == "cls"), -1) + if ClsIdx >= 0: + StructPtrType = ir.PointerType(TargetStruct) + ParamTypes[ClsIdx] = StructPtrType + return False + + def _body_contains_raise(self, body: list[ast.AST]) -> bool: + for node in ast.walk(ast.Module(body=body, type_ignores=[])): + if isinstance(node, ast.Raise): + if node.exc: + if isinstance(node.exc, ast.Name) and node.exc.id == 'StopIteration': + continue + if isinstance(node.exc, ast.Call) and isinstance(node.exc.func, ast.Name) and node.exc.func.id == 'StopIteration': + continue + return True + if isinstance(node, ast.Call): + called_name: str | None = None + if isinstance(node.func, ast.Name): + called_name = node.func.id + elif isinstance(node.func, ast.Attribute): + called_name = node.func.attr + if called_name: + Gen: LlvmCodeGenerator = self.Trans.LlvmGen + for prefix in ['', f'{called_name}.', f'__']: + for suffix in ['', f'.{called_name}']: + fname: str = f'{prefix}{called_name}{suffix}' + if fname in Gen.functions: + func: Any = Gen.functions[fname] + if len(func.args) > 0: + for arg in func.args: + if hasattr(arg, 'name') and arg.name in ('__eh_msg_out__', '__eh_code_out__'): + return True + break + return False + + _LLVM_ATTR_NAMES = frozenset({ + 'nobuiltin', 'nounwind', 'noredzone', 'willreturn', 'mustprogress', + 'optnone', 'noinline', 'alwaysinline', 'readnone', 'readonly', + 'writeonly', 'inaccessiblememonly', 'inaccessiblemem_or_argmemonly', + }) + + def _ExtractLLVMAttrs(self, node: ast.AST) -> list[str]: + attrs: list[str] = [] + if isinstance(node, ast.BinOp): + attrs.extend(self._ExtractLLVMAttrs(node.left)) + attrs.extend(self._ExtractLLVMAttrs(node.right)) + elif isinstance(node, ast.Attribute): + attr_name: str | None = self._GetAttrLLVMName(node) + if attr_name: + attrs.append(attr_name) + return attrs + + def _GetAttrLLVMName(self, node: ast.AST) -> str | None: + if not isinstance(node, ast.Attribute): + return None + if node.attr not in self._LLVM_ATTR_NAMES: + return None + value: ast.AST = node.value + if not isinstance(value, ast.Attribute) or value.attr != 'llvm': + return None + inner: ast.AST = value.value + if not isinstance(inner, ast.Attribute) or inner.attr != 'attr': + return None + if not isinstance(inner.value, ast.Name) or inner.value.id != 't': + return None + return node.attr + + def _infer_return_type_from_body(self, Node: ast.AST, Gen: LlvmCodeGenerator | None = None, ClassName: str | None = None) -> tuple[str, bool] | None: + local_var_types: dict[str, tuple[str, bool]] = {} + for child in ast.walk(Node): + if isinstance(child, ast.Assign) and len(child.targets) == 1: + target: ast.AST = child.targets[0] + if isinstance(target, ast.Name) and isinstance(child.value, ast.Subscript): + sub: ast.Subscript = child.value + if (isinstance(sub.value, ast.Attribute) + and isinstance(sub.value.value, ast.Name) + and sub.value.value.id == 'self' + and ClassName and ClassName in Gen.class_members): + member_name: str = sub.value.attr + for m_name, m_type in Gen.class_members[ClassName]: + if m_name == member_name: + if isinstance(m_type, ir.ArrayType): + elem_type: ir.Type = m_type.element + if isinstance(elem_type, ir.FloatType): + local_var_types[target.id] = ('float', False) + elif isinstance(elem_type, ir.DoubleType): + local_var_types[target.id] = ('double', False) + elif isinstance(elem_type, ir.IntType): + if elem_type.width == 8: + local_var_types[target.id] = ('char', False) + else: + local_var_types[target.id] = ('int', False) + elif isinstance(elem_type, ir.PointerType): + if isinstance(elem_type.pointee, ir.IntType) and elem_type.pointee.width == 8: + local_var_types[target.id] = ('char', True) + else: + local_var_types[target.id] = ('void', True) + elif isinstance(elem_type, ir.IdentifiedStructType): + struct_name: str = elem_type.name + if struct_name: + for sn, st in Gen.structs.items(): + if st.name == struct_name or sn == struct_name: + local_var_types[target.id] = (sn, False) + break + else: + local_var_types[target.id] = (elem_type.name, False) + elif isinstance(m_type, ir.PointerType): + pointee: ir.Type = m_type.pointee + if isinstance(pointee, ir.FloatType): + local_var_types[target.id] = ('float', False) + elif isinstance(pointee, ir.DoubleType): + local_var_types[target.id] = ('double', False) + elif isinstance(pointee, ir.IntType): + if pointee.width == 8: + local_var_types[target.id] = ('char', False) + else: + local_var_types[target.id] = ('int', False) + elif isinstance(pointee, ir.PointerType): + local_var_types[target.id] = ('void', True) + elif isinstance(pointee, ir.IdentifiedStructType): + for sn, st in Gen.structs.items(): + if st == pointee or st.name == pointee.name: + local_var_types[target.id] = (sn, True) + break + break + for child in ast.walk(Node): + if isinstance(child, ast.Return) and child.value: + val: ast.AST = child.value + if isinstance(val, ast.Name) and val.id == 'self' and ClassName: + return (ClassName, True) + if isinstance(val, ast.Call): + if isinstance(val.func, ast.Name): + callee: str = val.func.id + if callee == 'malloc': + return ('char', True) + if callee == 'chr': + return ('char', False) + if Gen and callee in Gen.functions: + func: Any = Gen.functions[callee] + if hasattr(func, 'ftype'): + ret_type: ir.Type = func.ftype.return_type + if isinstance(ret_type, ir.PointerType): + if isinstance(ret_type.pointee, ir.IntType) and ret_type.pointee.width == 8: + return ('char', True) + return ('void', True) + elif isinstance(ret_type, ir.IntType): + if ret_type.width == 8: + return ('char', False) + return ('int', False) + elif isinstance(ret_type, ir.VoidType): + return ('void', False) + elif isinstance(val.func, ast.Attribute): + pass + callee_name: str | None = None + if isinstance(val.func, ast.Name): + callee_name = val.func.id + elif isinstance(val.func, ast.Attribute): + callee_name = getattr(val.func, 'attr', None) + if callee_name: + sym: Any = self.Trans.SymbolTable.lookup(callee_name) + if sym and isinstance(sym, dict): + ret_type_str: str = sym.get('return_type', '') + if ret_type_str and ret_type_str != 'int': + IsPtr: bool = sym.get('IsPtr', False) + return (ret_type_str, IsPtr) + elif isinstance(val, ast.Constant): + if val.value is None: + return ('char', True) + elif isinstance(val.value, str) and len(val.value) <= 1: + return ('char', True) + elif isinstance(val, ast.Name): + var_name: str = val.id + if var_name in local_var_types: + return local_var_types[var_name] + var_info: Any = self.Trans.SymbolTable.lookup(var_name) + if var_info and isinstance(var_info, dict): + var_type: str = var_info.get('type', '') + if var_type and var_type != 'int': + IsPtr: bool = var_info.get('IsPtr', False) + return (var_type, IsPtr) + elif isinstance(val, ast.Subscript): + if (isinstance(val.value, ast.Attribute) + and isinstance(val.value.value, ast.Name) + and val.value.value.id == 'self' + and ClassName): + member_name: str = val.value.attr + if ClassName in Gen.class_members: + for m_name, m_type in Gen.class_members[ClassName]: + if m_name == member_name: + if isinstance(m_type, ir.ArrayType): + elem_type: ir.Type = m_type.element + if isinstance(elem_type, ir.FloatType): + return ('float', False) + elif isinstance(elem_type, ir.DoubleType): + return ('double', False) + elif isinstance(elem_type, ir.IntType): + if elem_type.width == 8: + return ('char', False) + return ('int', False) + elif isinstance(elem_type, ir.PointerType): + if isinstance(elem_type.pointee, ir.IntType) and elem_type.pointee.width == 8: + return ('char', True) + return ('void', True) + elif isinstance(elem_type, ir.IdentifiedStructType): + struct_name: str = elem_type.name + if struct_name: + for sn, st in Gen.structs.items(): + if st.name == struct_name or sn == struct_name: + return (sn, False) + return (elem_type.name, False) + elif isinstance(m_type, ir.PointerType): + pointee: ir.Type = m_type.pointee + if isinstance(pointee, ir.FloatType): + return ('float', False) + elif isinstance(pointee, ir.DoubleType): + return ('double', False) + elif isinstance(pointee, ir.IntType): + if pointee.width == 8: + return ('char', False) + return ('int', False) + elif isinstance(pointee, ir.PointerType): + return ('void', True) + elif isinstance(pointee, ir.IdentifiedStructType): + for sn, st in Gen.structs.items(): + if st == pointee or st.name == pointee.name: + return (sn, True) + return (pointee.name, True) + break + if isinstance(val.value, ast.Name): + var_name: str = val.value.id + var_info: Any = self.Trans.SymbolTable.lookup(var_name) + if var_info and isinstance(var_info, dict): + var_type: str = var_info.get('type', '') + if var_type.startswith('list[') or var_type.startswith('List[') or var_type.startswith('t.CArray['): + inner: str = var_type[var_type.index('[') + 1:var_type.rindex(']')] + parts: list[str] = inner.split(',') + elem_type_str: str = parts[0].strip() + if elem_type_str and elem_type_str != 'int': + IsPtr: bool = var_info.get('IsPtr', False) + return (elem_type_str, IsPtr) + return None + + def _GetFunctionSignatureLlvm(self, Node: ast.FunctionDef, Gen: LlvmCodeGenerator, ClassName: str | None = None, extra_params: list[tuple[str, Any]] | None = None) -> tuple[str, ir.FunctionType, Any, list[Any], list[ast.AST], bool, str | None, bool]: + FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName) + if Node.decorator_list: + _RejectCReturnDecorator(Node, Node.decorator_list) + _RejectRefReturnAnnotation(Node) + MultiRetTypes: list[ast.AST] = [] + if Node.returns: + llvm_attrs: list[str] = self._ExtractLLVMAttrs(Node.returns) + if llvm_attrs: + if not hasattr(self, '_pending_llvm_attrs'): + self._pending_llvm_attrs = {} + self._pending_llvm_attrs[FuncName] = llvm_attrs + if isinstance(Node.returns, ast.Subscript) and isinstance(Node.returns.value, ast.Name) and Node.returns.value.id == 'tuple': + slice_node: ast.AST = Node.returns.slice + if isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + MultiRetTypes.append(elt) + else: + MultiRetTypes.append(slice_node) + IsPtr: bool = False + ReturnTypeInfo: Any = None + if Node.returns: + if MultiRetTypes: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() + else: + try: + if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): + ReturnTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.returns) + if not isinstance(ReturnTypeInfo, CTypeInfo): + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() if ((ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState) - or (ReturnTypeInfo.PtrCount >= 2 and ReturnTypeInfo.BaseType is None)): - def _FindNonVoidCTypeInfo(node: ast.AST) -> Any: - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - results: list[Any] = [] - for child in [node.left, node.right]: - r: Any = _FindNonVoidCTypeInfo(child) - if r and not r.IsVoid: - results.append(r) - for r in results: - if not r.IsPtr and not r.IsStruct: - return r - return results[0] if results else None - try: - SideInfo: Any = CTypeInfo.FromNode(node, self.Trans.SymbolTable) - if SideInfo: - return SideInfo - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - return None - found: Any = _FindNonVoidCTypeInfo(Node.returns) - if found and not found.IsVoid: - ReturnTypeInfo = found - if ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - # define 宏函数(返回注解含 t.CDefine,如 `int | t.CDefine`): - # 标记为编译期内联宏(alwaysinline),对齐裸 `-> t.CDefine` 路径 - # (合并路径丢失 IsDefine 时仍能命中) - if AnnotationContainsName(Node.returns, 'CDefine'): - if not hasattr(self, '_cdefine_funcs'): - self._cdefine_funcs = set() - self._cdefine_funcs.add(FuncName) - else: - is_str_type: bool = False - if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): - is_str_type = True - # 走 ResolveAnnotationType 而非 CTypeInfo.FromNode:指针下标 - # 返回注解(rawptr[T] / t.CPtr[T])需走 TypeMerge 通用解析, - # 否则内层跨模块类解析不出 → CVoid/1(i8*)。 - ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) - if ReturnTypeInfo and ReturnTypeInfo.IsDefine: - inferred: Any = self._infer_return_type_from_body(Node, Gen, ClassName) - if inferred: - ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) - ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 - else: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - if not hasattr(self, '_cdefine_funcs'): - self._cdefine_funcs = set() - self._cdefine_funcs.add(FuncName) - elif ReturnTypeInfo and ReturnTypeInfo.IsState: - if ReturnTypeInfo.PtrCount > 0 or (ReturnTypeInfo.BaseType and not isinstance(ReturnTypeInfo.BaseType, t.CVoid)): - pass - else: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() - if ReturnTypeInfo is None: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() - # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 - if is_str_type or (ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1): - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CChar() - ReturnTypeInfo.PtrCount = 1 - # 检查泛型类返回类型 (如 list[str]) - # 注意:_from_node_subscript 对未识别的泛型下标(如 list[str])返回空 CTypeInfo(BaseType=None), - # 此时 IsVoid 为 False(因为 IsVoid 仅检查 t.CVoid 实例),需额外检测 BaseType is None - if (isinstance(Node.returns, ast.Subscript) - and isinstance(Node.returns.value, ast.Name) - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and Node.returns.value.id in self.Trans.ClassHandler._generic_class_templates - and (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0): - ret_class: str = Node.returns.value.id - ret_slice: ast.AST = Node.returns.slice - ret_type_args: list[str] = [] - ret_type_names: list[str] = [] - if isinstance(ret_slice, ast.Tuple): - for elt in ret_slice.elts: - if isinstance(elt, ast.Name): - tn2: str = elt.id - ts2: str = tn2 - if tn2 == 'int': - ts2, tn2 = 'int', 'CInt' - ret_type_args.append(ts2) - ret_type_names.append(tn2) - elif isinstance(ret_slice, ast.Name): - tn2: str = ret_slice.id - ts2: str = tn2 - if tn2 == 'int': - ts2, tn2 = 'int', 'CInt' - ret_type_args.append(ts2) - ret_type_names.append(tn2) - # 返回类型解析只需 struct 定义,不需方法体;使用 declare_only=True - # 避免过早触发完整特化(此时 MemManager.alloc 可能尚未注册, - # 导致 __new__ 的 pool.alloc(48) 解析失败,IR 块未终止)。 - # 完整特化由后续类实例化(L3688/L3753)触发 declare_only=False。 - spec_name2: str | None = self.Trans.ClassHandler._specialize_generic_class( - ret_class, ret_type_args, Gen, type_names=ret_type_names, declare_only=True) - if spec_name2 and spec_name2 in Gen.structs: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CStruct(name=spec_name2) - ReturnTypeInfo.PtrCount = 1 - if (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - except Exception: # 回退:设置默认返回类型为 CInt - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - else: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - inferred: Any = self._infer_return_type_from_body(Node, Gen, ClassName) - if inferred: - ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) - ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 - IsPtr = ReturnTypeInfo.IsPtr if ReturnTypeInfo else False - ReturnType: ir.Type = Gen._ctype_to_llvm(ReturnTypeInfo) - # 此处原有 "Ptr(Ptr(Struct)) → Ptr(Struct)" 的压层(历史遗留):会把已解析的 - # 多层指针返回类型(`-> rawptr[rawptr[X]]` / `-> X | t.CPtr | t.CPtr`) - # 与泛型特化方法返回(`list[X**]` 的 get/pop/__getitem__/__next__)少算一层, - # 与同函数的形参(`append(item: T)` 保留 `X**`)、声明端 - # DeclarationGenerator._get_type_str 的 BinOp 分支(同样不压层,见 - # '_is_bare_class_ref_annotation' 为假时逐层外包)以及 _EmitFunctionLlvm - # 的定义体路径全部不一致。违反"多层指针已解析时照原样保留层数"。 - # 实测(36 用例 A/B)删除后仅 `list[X**]` 的 4 个方法 declare 由 `X*` 变 - # `X**`,与 append/set 的形参一致;stub 逐字节不变。 - if isinstance(ReturnType, ir.IntType) and ReturnType.width == 8: - if RawFuncName == '__str__': - ReturnType = ir.PointerType(ir.IntType(8)) - if isinstance(ReturnType, ir.VoidType) and FuncName == 'main': - ReturnType = ir.IntType(32) - IsMethod, IsClassMethod, ResolvedClassName = self._resolve_method_class(Node, ClassName, func_meta, FuncName, Gen) - # __new__ methods should return a pointer to the struct (for heap allocation replacement) - # 修复:前向声明也必须使用正确的返回类型,否则 _replace_function_decl 可能失败, - # 导致 vtable 引用错误的函数类型(i32 而非 ClassName*) - if RawFuncName == '__new__' and ResolvedClassName: - StructType = Gen.structs.get(ResolvedClassName) - if StructType: - if isinstance(StructType, ir.PointerType): - ReturnType = StructType - else: - ReturnType = ir.PointerType(StructType) - ParamTypes: list[ir.Type] = [] - ParamNames: list[str] = [] - ParamTypeStrs: list[Any] = [] - auto_addr_modes: list[str | None] = [] - MultiRetLlvmTypes: list[ir.Type] = [] - if MultiRetTypes: - for i, ReturnTypeNode in enumerate(MultiRetTypes): - ReturnTypeInfo = CTypeInfo.FromNode(ReturnTypeNode, self.Trans.SymbolTable) - # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 - if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CChar() - ReturnTypeInfo.PtrCount = 1 - if ReturnTypeInfo is None: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - RetType: ir.Type = Gen._ctype_to_llvm(ReturnTypeInfo) - MultiRetLlvmTypes.append(RetType) - ReturnType = ir.LiteralStructType(MultiRetLlvmTypes) - for Arg in Node.args.args: - ParamIsUnsigned: bool = False - if Arg.annotation: - try: - ParamTypeInfo: Any - # 走 ResolveParamAnnotation(= ResolveAnnotationType + 泛型模板 - # 识别):参数注解中的泛型容器(list[X] / rawptr[list[X]])与 - # 指针下标(rawptr[X])需走 TypeMerge 通用解析,否则 - # TypeAnnotationResolver 解析不出 → VoidType → 报 - # "无法识别的参数类型注解"。 - ParamTypeInfo = self.ResolveParamAnnotation(Arg.annotation) - if ParamTypeInfo is None: - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CInt() - IsPtr = ParamTypeInfo.IsPtr - if ParamTypeInfo and getattr(ParamTypeInfo, 'IsCpythonObject', False) and not IsPtr: - IsPtr = True - ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) - # 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), - # 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。 - if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')): - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CChar() - ParamTypeInfo.PtrCount = 1 - IsPtr = True - # C 语言中数组参数退化为指针:t.CArray[type, N] → type* - if ParamTypeInfo.ArrayDims: - ParamTypeInfo.ArrayDims = [] - ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) - ParamType: ir.Type = Gen._ctype_to_llvm(ParamTypeInfo) - # 参数类型不能是 VoidType:C 语言不允许 void 参数, - # VoidType 说明类型注解无法识别(如跨模块 ast.expr),直接报错终止 - if isinstance(ParamType, ir.VoidType): - AnnStr: str = ast.unparse(Arg.annotation) if hasattr(ast, 'unparse') else str(Arg.annotation) - raise SyntaxError( - f"无法识别的参数类型注解: '{AnnStr}'(在函数 '{Node.name}' 的参数 '{Arg.arg}' 上)。" - f"请使用有效的 TPV 类型(如 t.CPtr、ast.AST 或已注册的结构体)。" - ) - ParamTypeStr: Any = ParamTypeInfo - except SyntaxError: - raise - except Exception as e: - ParamType = ir.IntType(32) - ParamTypeStr = CTypeInfo() - ParamTypeStr.BaseType = t.CInt() - else: - if ClassName and Arg.arg != 'self': - op_method_names: list[str] = ['__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__', '__mod__', '__pow__', - '__radd__', '__rsub__', '__rmul__', '__rtruediv__', '__rfloordiv__', '__rmod__', '__rpow__', - '__iadd__', '__isub__', '__imul__', '__itruediv__', '__ifloordiv__', '__imod__', '__ipow__', - '__eq__', '__ne__', '__lt__', '__le__', '__gt__', '__ge__', - '__neg__', '__pos__', '__abs__', '__call__'] - if RawFuncName in op_method_names and ClassName in Gen.structs: - ParamType = ir.PointerType(Gen.structs[ClassName]) - else: - ParamType = ir.IntType(32) - else: - ParamType = ir.IntType(32) - ParamTypeStr = CTypeInfo() - ParamTypeStr.BaseType = t.CInt() - ParamTypes.append(ParamType) - ParamNames.append(Arg.arg) - ParamTypeStrs.append(ParamTypeStr) - # 自动取址模式(由注解读出,不依赖 BaseType 类型): - # 'ref' = &T、'temp' = &temp T(合成联合 T | t.CRef / t.CTempRef), - # 'always' = t.CAutoPtr。经 Gen._auto_addr_params 供调用点分流。 - auto_mode: str | None = FindAutoAddrMode(Arg.annotation) - auto_addr_modes.append(auto_mode) - if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen): - ParamTypeStrs.insert(0, f"struct {ResolvedClassName}") - auto_addr_modes.insert(0, None) - IsVariadic: bool = Node.args.vararg is not None - if extra_params: - for var_name, ptr_type in extra_params: - ParamTypes.append(ptr_type) - ParamNames.append(f'__nonlocal_{var_name}__') - if IsMethod and RawFuncName == '__next__': - ParamTypes.append(ir.PointerType(ir.IntType(1))) - ParamNames.append('__stop_iter_flag__') - HasRaise: bool = self._body_contains_raise(Node.body) - if HasRaise and RawFuncName != 'main': - ParamTypes.append(ir.PointerType(ir.PointerType(ir.IntType(8)))) - ParamNames.append('__eh_msg_out__') - ParamTypes.append(ir.PointerType(ir.IntType(32))) - ParamNames.append('__eh_code_out__') - FuncType: ir.FunctionType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) - # 存储 auto_addr_modes(为额外参数补 None) - total_params: int = len(ParamTypes) - while len(auto_addr_modes) < total_params: - auto_addr_modes.append(None) - Gen._auto_addr_params[FuncName] = auto_addr_modes - return FuncName, FuncType, ReturnTypeInfo, ParamTypeStrs, MultiRetTypes, IsMethod, ResolvedClassName, IsVariadic - - def _is_generic_function(self, Node: ast.FunctionDef) -> bool: - if hasattr(Node, 'type_params') and Node.type_params: - return True - return False - - def _mangle_generic_name(self, func_name: str, type_args: list[str], has_cexport: bool = False) -> str: - mangled_args: list[str] = [] - for ta in type_args: - llvm_str: str | None = CTypeRegistry.NameToLLVM(ta) - if llvm_str: - if llvm_str in ('float', 'double', 'half', 'fp128'): - mangled: str = 'f' + str(CTypeRegistry.GetClassByName(ta)().Size) - else: - mangled = llvm_str - else: - mangled = ta - mangled_args.append(mangled) - if has_cexport: - return func_name + '_T_' + '_'.join(mangled_args) - return func_name + '[' + ']['.join(mangled_args) + ']' - - def _apply_type_map_to_node(self, Node: ast.FunctionDef, type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: - for arg in Node.args.args: - if arg.annotation: - arg.annotation = self._replace_type_in_annotation(arg.annotation, type_map, cptr_params) - if Node.returns: - Node.returns = self._replace_type_in_annotation(Node.returns, type_map, cptr_params) - self._apply_type_map_to_body(Node.body, type_map, cptr_params) - - def _replace_type_in_annotation(self, node: ast.AST, type_map: dict[str, str], cptr_params: set[str] | None = None) -> ast.AST: - if isinstance(node, ast.Name): - if node.id in type_map: - replacement: str = type_map[node.id] - return self._make_replacement_node(replacement) - elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - if isinstance(node.left, ast.Name) and node.left.id in type_map: - replacement: str = type_map[node.left.id] - node.left = self._make_replacement_node(replacement) - return node - node.left = self._replace_type_in_annotation(node.left, type_map, cptr_params) - node.right = self._replace_type_in_annotation(node.right, type_map, cptr_params) - elif isinstance(node, ast.Attribute): - if hasattr(node, 'attr') and node.attr in type_map: - replacement: str = type_map[node.attr] - return self._make_replacement_node(replacement) - elif isinstance(node, ast.Subscript): - # 嵌套泛型(如 list[T]):递归替换 value 和 slice 中的类型参数 - node.value = self._replace_type_in_annotation(node.value, type_map, cptr_params) - node.slice = self._replace_type_in_annotation(node.slice, type_map, cptr_params) - return node - - def _make_replacement_node(self, replacement: str) -> ast.AST: - """将 type_map 中的替换字符串转换为 AST 节点。 - mangled 名称如 '.AST*' 需要还原为 AST | t.CPtr 结构, - 否则类型解析会丢失 PtrCount 信息。 - 含点号的名称(如 '.AST')需解析为 ast.Attribute 链。 - 下标形态('rawptr[ast.AST]' / 'list[AST]' 等)必须整体解析为 Subscript: - _make_name_node 只按 '.' 切分会把 'rawptr[ast.AST]' 拆成 - Name('rawptr[ast') + Attribute('AST]'),两段都不是有效类型名 → - 参数/返回注解解析为空 → VoidType → "无法识别的参数类型注解"。 - 指针下标(rawptr[T] / t.CPtr[T])再折叠为等价的 `T | t.CPtr`,与源码 - 联合写法同解析路径(对齐 TPV TypeGenResolve._parse_type_string 的 '[' - 分支 + ClassGenGeneric._clone_annotation 的 T 替换)。 - 内层为已混淆名(数字开头,如 'rawptr[7565e081d88517ac.AST]')时 - ast.parse 无法解析(数字开头的令牌),改用手工拆 base/slice(同 TPV - TypeGenResolve._parse_type_string 的数字开头手工拆分分支)。 - 尾部 '*'(泛型实参 '.Node*' / '.Node**',来自 rawptr[T] / - t.CPtr 的逐层 mangling)逐层还原为 `Node | t.CPtr [| t.CPtr]` → 对应 - 层数指针;只剥一层会把 'Node*' 当结构体名建出 phantom opaque - (item 类型与特化名 '.Node**' 的语义不一致)。 - 尾部 'ptr'(旧规则 mangling,如 'Nodeptr' / 'ast.ASTptr')保留兼容: - .vpi/stub 缓存里仍是旧名时按同样方式还原。 - """ - StarCount: int = len(replacement) - len(replacement.rstrip('*')) - if StarCount > 0: - BaseName: str = replacement[:len(replacement) - StarCount] - base_node: ast.AST - if BaseName in ('i8', 'void', 'void*'): - # i8*(void*)在源码侧写作 t.CPtr;再叠加 StarCount-1 层 - base_node = ast.Attribute( - value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - StarCount -= 1 - else: - base_node = self._make_name_node(BaseName) - node: ast.AST = base_node - for _ in range(StarCount): - node = ast.BinOp( - left=node, - op=ast.BitOr(), - right=ast.Attribute( - value=ast.Name(id='t', ctx=ast.Load()), - attr='CPtr', ctx=ast.Load()), - ) - return node - if replacement.endswith('ptr'): - # 提取基础名称:'sha1.ASTptr' → 'sha1.AST';'Nodeptrptr' → 'Node'(2 层) - base: str = replacement - levels: int = 0 - while base.endswith('ptr') and len(base) > 3: - base = base[:-3] - levels += 1 - node: ast.AST = self._make_name_node(base) - for _ in range(levels): - node = ast.BinOp( - left=node, - op=ast.BitOr(), - right=ast.Attribute( - value=ast.Name(id='t', ctx=ast.Load()), - attr='CPtr', ctx=ast.Load()), - ) - return node - if '[' in replacement: - SubNode: ast.AST | None = None - try: - SubNode = ast.parse(replacement, '', mode='eval').body - except Exception: - SubNode = None - if not isinstance(SubNode, ast.Subscript): - SubNode = self._parse_subscript_manually(replacement) - if isinstance(SubNode, ast.Subscript): - if IsPtrSubscriptNode(SubNode, self.Trans.SymbolTable): - return DesugarPtrSubscriptToUnion(SubNode, self.Trans.SymbolTable) - return SubNode - return self._make_name_node(replacement) - - def _parse_subscript_manually(self, replacement: str) -> ast.AST | None: - """手工拆分单实参下标字符串(ast.parse 因数字开头标识符失败时使用)。 - - 处理 `[]`(整体以 ']' 结尾、单实参、无逗号;内层可为嵌套 - 单实参下标):'rawptr[7565e081d88517ac.AST]' → Subscript(Name('rawptr'), - Attribute(Name('7565e081d88517ac'), 'AST')); - 'rawptr[rawptr[7565e081d88517ac.AST]]' → 外层 Subscript 的 slice 递归 - 拆出内层 Subscript(嵌套 rawptr 需两层指针)。带逗号等复杂形态返回 - None(交调用方按普通名称处理),不做字符串猜测。 - """ - open_idx: int = replacement.find('[') - if open_idx <= 0 or not replacement.endswith(']'): - return None - inner: str = replacement[open_idx + 1:-1] - if not inner or ',' in inner: - return None - base_name: str = replacement[:open_idx].strip() - inner_name: str = inner.strip() - if not base_name or not inner_name: - return None - slice_node: ast.AST | None - if '[' in inner_name or inner_name.endswith('*'): - # 嵌套单实参下标 / 指针层数内层(如 rawptr[rawptr[.CLS]]、 - # .list[.X*]):递归按同一还原规则拆解 - slice_node = self._make_replacement_node(inner_name) - else: - try: - slice_node = ast.parse(inner_name, '', mode='eval').body - except Exception: - slice_node = self._make_name_node(inner_name) - if slice_node is None: - return None - return ast.Subscript( - value=self._make_name_node(base_name), slice=slice_node, ctx=ast.Load()) - - @staticmethod - def _make_name_node(name: str) -> ast.AST: - """将可能含点号的名称转换为 ast.Name 或 ast.Attribute 链。 - 'AST' -> ast.Name(id='AST') - 'sha1.AST' -> ast.Attribute(value=ast.Name(id='sha1'), attr='AST') - """ - if '.' not in name: - return ast.Name(id=name, ctx=ast.Load()) - parts: list[str] = name.split('.') - node: ast.AST = ast.Name(id=parts[0], ctx=ast.Load()) - for part in parts[1:]: - node = ast.Attribute(value=node, attr=part, ctx=ast.Load()) - return node - - def _apply_type_map_to_body(self, body: list[ast.AST], type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: - for stmt in body: - self._apply_type_map_to_stmt(stmt, type_map, cptr_params) - - def _apply_type_map_to_stmt(self, node: ast.AST, type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: - if isinstance(node, ast.Return) and node.value: - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - elif isinstance(node, ast.Assign): - for t in node.targets: - self._apply_type_map_to_expr(t, type_map, cptr_params) - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - elif isinstance(node, ast.AnnAssign): - if node.annotation: - node.annotation = self._replace_type_in_annotation(node.annotation, type_map, cptr_params) - if node.value: - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - elif isinstance(node, ast.AugAssign): - self._apply_type_map_to_expr(node.target, type_map, cptr_params) - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - elif isinstance(node, ast.Expr): - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - elif isinstance(node, ast.If): - self._apply_type_map_to_expr(node.test, type_map, cptr_params) - for s in node.body: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - for s in node.orelse: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - elif isinstance(node, ast.For): - self._apply_type_map_to_expr(node.iter, type_map, cptr_params) - for s in node.body: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - for s in node.orelse: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - elif isinstance(node, ast.While): - self._apply_type_map_to_expr(node.test, type_map, cptr_params) - for s in node.body: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - for s in node.orelse: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - elif isinstance(node, ast.Try): - for s in node.body: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - for handler in node.handlers: - for s in handler.body: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - for s in node.orelse: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - for s in node.finalbody: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - elif isinstance(node, ast.With): - for item in node.items: - self._apply_type_map_to_expr(item.context_expr, type_map, cptr_params) - if item.optional_vars: - self._apply_type_map_to_expr(item.optional_vars, type_map, cptr_params) - for s in node.body: - self._apply_type_map_to_stmt(s, type_map, cptr_params) - elif isinstance(node, ast.Raise): - if node.exc: - self._apply_type_map_to_expr(node.exc, type_map, cptr_params) - if node.cause: - self._apply_type_map_to_expr(node.cause, type_map, cptr_params) - elif isinstance(node, ast.Assert): - self._apply_type_map_to_expr(node.test, type_map, cptr_params) - if node.msg: - self._apply_type_map_to_expr(node.msg, type_map, cptr_params) - - def _apply_type_map_to_expr(self, node: ast.AST | None, type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: - if node is None: - return - if isinstance(node, ast.Call): - if isinstance(node.func, ast.Name) and node.func.id in type_map: - replacement: str = type_map[node.func.id] - # 使用 _make_replacement_node 处理含点号的类型名(如 't.CUnsignedChar'), - # 避免 T(0) 替换为 CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域 - # 而报 "Undefined function: 'CUnsignedChar'" - node.func = self._make_replacement_node(replacement) - elif isinstance(node.func, ast.Attribute): - if isinstance(node.func.value, ast.Name) and node.func.value.id in type_map: - replacement: str = type_map[node.func.value.id] - # 指针类型参数(如 list[AST | t.CPtr] 中的 T)在表达式上下文中 - # 需替换为 BinOp(AST | t.CPtr),使 __sizeof__ 等处理器能正确识别 - # 指针语义并直接返回 8,避免触发 struct 加载和重入编译 - if cptr_params and node.func.value.id in cptr_params: - node.func.value = ast.BinOp( - left=self._make_name_node(replacement), - op=ast.BitOr(), - right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - ) - else: - node.func.value = self._make_name_node(replacement) - else: - self._apply_type_map_to_expr(node.func.value, type_map, cptr_params) - for arg in node.args: - self._apply_type_map_to_expr(arg, type_map, cptr_params) - elif isinstance(node, ast.Attribute): - if isinstance(node.value, ast.Name) and node.value.id in type_map: - replacement: str = type_map[node.value.id] - if cptr_params and node.value.id in cptr_params: - node.value = ast.BinOp( - left=self._make_name_node(replacement), - op=ast.BitOr(), - right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) - ) - else: - node.value = ast.Name(id=replacement, ctx=ast.Load()) - else: - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - elif isinstance(node, ast.BinOp): - self._apply_type_map_to_expr(node.left, type_map, cptr_params) - self._apply_type_map_to_expr(node.right, type_map, cptr_params) - elif isinstance(node, ast.UnaryOp): - self._apply_type_map_to_expr(node.operand, type_map, cptr_params) - elif isinstance(node, ast.Compare): - self._apply_type_map_to_expr(node.left, type_map, cptr_params) - for comp in node.comparators: - self._apply_type_map_to_expr(comp, type_map, cptr_params) - elif isinstance(node, ast.BoolOp): - for val in node.values: - self._apply_type_map_to_expr(val, type_map, cptr_params) - elif isinstance(node, ast.IfExp): - self._apply_type_map_to_expr(node.test, type_map, cptr_params) - self._apply_type_map_to_expr(node.body, type_map, cptr_params) - self._apply_type_map_to_expr(node.orelse, type_map, cptr_params) - elif isinstance(node, ast.Subscript): - self._apply_type_map_to_expr(node.value, type_map, cptr_params) - self._apply_type_map_to_expr(node.slice, type_map, cptr_params) - elif isinstance(node, ast.Name): - if node.id in type_map: - node.id = type_map[node.id] - - def _infer_type_arg_from_value(self, val: ir.Value, Gen: LlvmCodeGenerator) -> str: - if val is None: - return 'int' - if isinstance(val.type, ir.IntType): - if val.type.width == 64: - return 'i64' - elif val.type.width == 16: - return 'i16' - elif val.type.width == 8: - return 'i8' - return 'int' - elif isinstance(val.type, ir.DoubleType): - return 'double' - elif isinstance(val.type, ir.FloatType): - return 'float' - elif isinstance(val.type, ir.PointerType): - return 'void*' - return 'int' - - def _specialize_generic_function(self, FuncName: str, type_args: list[str], Gen: LlvmCodeGenerator) -> str | None: - if not hasattr(self, '_generic_templates') or FuncName not in self._generic_templates: - return None - template: dict[str, Any] = self._generic_templates[FuncName] - Node: ast.FunctionDef = template['node'] - type_param_names: list[str] = template['type_params'] - ClassName: str | None = template['class_name'] - if len(type_args) != len(type_param_names): - return None - spec_key: str = FuncName + '<' + ','.join(type_args) + '>' - if not hasattr(self, '_generic_specializations'): - self._generic_specializations = {} - if spec_key in self._generic_specializations: - return self._generic_specializations[spec_key] - has_cexport: bool = False - if Node.returns: - try: - RetTypeInfo: Any = None - if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): - RetTypeInfo = self.Trans.TypeMergeHandler.MergeTypes(Node.returns) - else: - RetTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExport): - has_cexport = True - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - spec_name: str = self._mangle_generic_name(FuncName, type_args, has_cexport) - type_map: dict[str, str] = {} - for i, tp_name in enumerate(type_param_names): - type_map[tp_name] = type_args[i] - SpecNode: ast.FunctionDef = copy.deepcopy(Node) - SpecNode.type_params = [] - SpecNode.name = spec_name - self._apply_type_map_to_node(SpecNode, type_map) - if has_cexport: - if not hasattr(Gen, '_export_funcs'): - Gen._export_funcs = set() - Gen._export_funcs.add(spec_name) - saved_builder: Any = Gen.builder - saved_variables: dict[str, Any] = dict(Gen.variables) if Gen.variables else {} - saved_direct_values: dict[str, Any] = dict(Gen._direct_values) if Gen._direct_values else {} - saved_var_type_info: dict[str, Any] = dict(Gen.var_type_info) if Gen.var_type_info else {} - saved_var_signedness: dict[str, Any] = dict(Gen.var_signedness) if Gen.var_signedness else {} - saved_global_vars: set[str] = set(Gen.global_vars) if Gen.global_vars else set() - saved_var_scopes: list[dict[str, Any]] = [dict(s) for s in self.Trans.VarScopes] if self.Trans.VarScopes else [] - saved_func: Any = Gen.func - saved_current_func_name: str | None = getattr(Gen, '_current_func_name', None) - saved_block: Any = None - if Gen.builder and Gen.builder.block and not Gen.builder.block.is_terminated: - saved_block = Gen.builder.block - self._EmitFunctionForwardDeclLlvm(SpecNode, Gen, ClassName=ClassName) - self._EmitFunctionLlvm(SpecNode, Gen, ClassName=ClassName) - Gen.builder = saved_builder - if saved_block is not None and Gen.builder is not None: - Gen.builder.position_at_end(saved_block) - Gen.variables = saved_variables - Gen._direct_values = saved_direct_values - Gen.var_type_info = saved_var_type_info - Gen.var_signedness = saved_var_signedness - Gen.global_vars = saved_global_vars - Gen.func = saved_func - if saved_current_func_name is not None: - Gen._current_func_name = saved_current_func_name - self.Trans.VarScopes = saved_var_scopes - self._generic_specializations[spec_key] = spec_name - if not hasattr(self.Trans, '_generic_specializations'): - self.Trans._generic_specializations = {} - self.Trans._generic_specializations[spec_key] = spec_name - return spec_name - - def _EmitFunctionForwardDeclLlvm(self, Node: ast.FunctionDef, Gen: LlvmCodeGenerator, ClassName: str | None = None) -> None: - if self._is_generic_function(Node): - if not hasattr(self, '_generic_templates'): - self._generic_templates = {} - RawFuncName: str = Node.name - if ClassName: - FuncName: str = f"{ClassName}.{RawFuncName}" - else: - FuncName = RawFuncName - type_params: list[str] = [tp.name for tp in Node.type_params] - self._generic_templates[FuncName] = { - 'node': Node, - 'type_params': type_params, - 'class_name': ClassName, - } - return - result: Any = self._GetFunctionSignatureLlvm(Node, Gen, ClassName) - if result is None: - return - FuncName: str - FuncType: ir.FunctionType - ReturnTypeInfo: Any - ParamTypeStrs: list[Any] - MultiRetTypes: list[ast.AST] - IsMethod: bool - ResolvedClassName: str | None - IsVariadic: bool - FuncName, FuncType, ReturnTypeInfo, ParamTypeStrs, MultiRetTypes, IsMethod, ResolvedClassName, IsVariadic = result - IsUserMarkedExport: bool = False - if Node.returns: - try: - RetTypeInfo: Any = None - RetTypeInfo = self.ResolveAnnotationType(Node.returns) - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExport) and not RetTypeInfo.IsState: - Gen._export_funcs.add(FuncName) - IsUserMarkedExport = True - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExtern): - Gen._export_funcs.add(FuncName) - IsUserMarkedExport = True - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.IsState: - Gen._export_funcs.add(FuncName) # t.State 标记的函数必须保持原始名称,以便链接器解析 - IsUserMarkedExport = True - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - # 装饰器形式的存储类标记(@t.CExport / @t.CExtern / @t.State 等) - deco_flags: dict[str, bool] = self._extract_decorator_storage_flags(Node) - if deco_flags['is_export'] or deco_flags['is_extern'] or deco_flags['is_state']: - Gen._export_funcs.add(FuncName) - IsUserMarkedExport = True - # 注册当前模块定义的函数名,使 _mangle_func_name 跳过 _export_extern_funcs 检查 - # (_export_extern_funcs 是跨模块共享集合,包含所有 CExtern 函数名;当前模块定义的 - # 同名用户函数应使用 SHA1 前缀,而非被误判为外部 C 函数返回裸名) - Gen._current_module_func_names.add(FuncName) - # 移除导入污染:用户函数未显式标记导出,但名称在 _export_funcs 中(来自导入的同名 C 库函数) - if not IsUserMarkedExport and FuncName in Gen._export_funcs: - Gen._export_funcs.discard(FuncName) - MangledName: str = Gen._mangle_func_name(FuncName) - # 自动取址模式:查表侧统一按**真实发射名**(func.name,即 MangledName)查 - # (FuncGen._adjust_args)。签名阶段(_GetFunctionSignatureLlvm)只能按逻辑名 - # (FuncName)登记,故在此把该逻辑名下的模式复制到真实名下——否则同模块 - # "先调用、后定义"的 &T / &temp T 会在前向声明路径上静默丢失取址。 - _aam_fwd: list[str | None] | None = Gen._auto_addr_params.get(FuncName) - if _aam_fwd: - Gen._auto_addr_params[MangledName] = _aam_fwd - need_new: bool = MangledName not in Gen.functions - if not need_new: - existing: Any = Gen.functions[MangledName] - if getattr(existing, 'name', MangledName) != MangledName: - need_new = True - if need_new: - try: - # 统一走幂等入口:module.globals 已有同名函数时复用(并补登记 - # Gen.functions),签名冲突则 fail-fast —— 绝不直接新建同名 - # ir.Function(llvmlite NameScope 按名去重,必抛 DuplicatedNameError; - # 现场:5d84ea579d83ef73.CallGenFd._translate_fakeduck_call 被此路径新建, - # 而 BaseGen._get_or_declare_function 已建过同名声明却未登记 - # Gen.functions ⇒ need_new 误判为真)。 - func: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) - func.attributes.add('noredzone') - if hasattr(self, '_cdefine_funcs') and FuncName in self._cdefine_funcs: - func.linkage = 'linkonce_odr' - func.attributes.add('alwaysinline') - if hasattr(self, '_pending_llvm_attrs') and FuncName in self._pending_llvm_attrs: - for attr_name in self._pending_llvm_attrs[FuncName]: - try: - func.attributes.add(attr_name) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"添加函数属性失败: {_e}", "Exception") - Gen.functions[MangledName] = func - if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: - Gen.functions[FuncName] = func - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError - if isinstance(_e, FailFastError): - # 签名冲突 = declare/define 分裂,必须终止(不吞) - raise - # 其余异常同样必须终止:本 try 块内只有「创建/复用声明 + 挂属性」, - # 失败即该声明不可用(调用点会按错签名传参)。原实现非严格模式静默 - # `pass`、严格模式仅 LogWarning —— 正是它把 llvmlite 的 AssertionError - # (形参类型不是 types.Type 时 `Argument(...)` 断言失败,见 - # BaseGen._CreateOrReuseFuncDecl 的创建前自检注释)吞成"名字半注册": - # 名字已进 module.scope、未进 module.globals,直到定义阶段才以 - # DuplicatedNameError(报文只有名字)暴露,真因与现场彻底脱节。 - raise FailFastError( - f"函数前置声明创建失败:'{MangledName}'" - f"\n · 本次签名: {FuncType}" - f"\n · 原始异常: {type(_e).__name__}: {_e}" - + Gen._get_node_info()) - else: - existing = Gen.functions[MangledName] - existing_type: ir.FunctionType | None = getattr(existing, 'ftype', None) - if existing_type and existing_type != FuncType: - Gen.functions[MangledName] = self._replace_function_decl(Gen, FuncName, FuncType) - if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: - Gen.functions[FuncName] = Gen.functions[MangledName] - elif not existing_type: - try: - Gen.functions[MangledName] = self._replace_function_decl(Gen, FuncName, FuncType) - if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: - Gen.functions[FuncName] = Gen.functions[MangledName] - except Exception as _e: - # fail-fast 一律上抛(本仓既定口径):吞掉它 = 声明静默缺失 + - # 半注册名字留给下一轮,真因与现场脱节。 - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - raise - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - self.Trans.FunctionDefCache[FuncName] = Node - - def _replace_function_decl(self, Gen: LlvmCodeGenerator, FuncName: str, NewFuncType: ir.FunctionType) -> ir.Function: - MangledName: str = Gen._mangle_func_name(FuncName) - old_func: Any = Gen.functions.get(MangledName) - if old_func is None: - try: - # 走唯一幂等入口:名字可能已存在于 module.globals 但未登记进 Gen.functions - # (别的路径先建过声明)——直接 ir.Function 必抛裸 DuplicatedNameError, - # 入口则同名同类型复用、异类型带两侧签名 fail-fast。 - return Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType) - except Exception as _e: # 回退:函数已存在时返回旧函数 - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - # 创建中途失败(如形参/返回类型不是 llvmlite Type)由 BaseGen 的 - # Function 守卫转成 fail-fast:必须上抛,原实现会 `return old_func` - # (此处 old_func 必为 None)⇒ 调用点把 None 当声明用。 - raise - return old_func - try: - old_name: str = old_func.name - # 必须同时清 old_name(对象真名)与 MangledName(字典键 / 目标名): - # 别名场景下二者不同,只清真名会让 MangledName 仍占住 module.scope - # (llvmlite 按名字去重)→ 重建抛 DuplicatedNameError。 - module_scope: Any = getattr(Gen.module, 'scope', None) - for _stale_name in {old_name, MangledName}: - _stale: Any = Gen.module.globals.get(_stale_name) - if isinstance(_stale, ir.Function): - del Gen.module.globals[_stale_name] - if getattr(module_scope, '_useset', None): - module_scope._useset.discard(_stale_name) - if getattr(Gen.module, '_guessed_names', None): - Gen.module._guessed_names.discard(FuncName) - new_func: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType) - Gen.functions[MangledName] = new_func - if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: - Gen.functions[FuncName] = new_func - return new_func - except Exception as ex: - from lib.core.LLVMCG.FuncGen import FailFastError - if isinstance(ex, FailFastError): - # 同名符号冲突(函数签名分裂 / 名字被非函数 global 占住):必须终止, - # 不能回退成"复用旧声明"——那正是 declare/define 分裂的现场。 - raise - traceback.print_exc() - Gen.functions[MangledName] = old_func - if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: - Gen.functions[FuncName] = old_func - return old_func - - def _EmitFunctionLlvm(self, Node: ast.FunctionDef, Gen: LlvmCodeGenerator, ClassName: str | None = None, extra_params: list[tuple[str, ir.Type]] | None = None) -> ir.Function | None: - if self._is_generic_function(Node): - return - FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName) - _RejectRefReturnAnnotation(Node) - # 保存父级 _CurrentCpythonObjectClass,防止嵌套函数发射污染父方法类上下文 - # (例如 HandleImport body 内触发 list[ast.ASTptr] 方法发射,结束后会清空 _CurrentCpythonObjectClass) - saved_cpython_class: str | None = self.Trans._CurrentCpythonObjectClass - MultiRetTypes = [] - IsPtr = False - fn_attrs = {} - # 自定义行为装饰器列表,用于生成 wrapper 函数 - # 任何 @name 或 @name(args) 只要不是内置装饰器,都视为自定义行为装饰器 - behavior_decorators = [] - # 内置装饰器名称,这些由编译器特殊处理,不作为行为装饰器 - # 另含**编译期生命期标注**(P2):@life(willFree(...)) 与 @willFree(...) 是纯标注, - # 没有运行时装饰器函数;不排除会生成 wrapper 去调用不存在的 @life/@willFree, - # 并把被装饰的 extern def(如 stdlib.free)变成本模块 internal 声明(llc 报 linkage 错)。 - _BUILTIN_DECORATORS = {'staticmethod', 'property', 'classmethod', 'life', 'willFree'} - _symtab: object = self.Trans.SymbolTable - if Node.decorator_list: - for decorator in Node.decorator_list: - # 跳过 t 模块存储类标记装饰器(@t.CExport / @CExport 等) - if self._is_t_storage_decorator(decorator, _symtab): - continue - # 识别自定义行为装饰器:@name(ast.Name 形式) - if isinstance(decorator, ast.Name): - if decorator.id not in _BUILTIN_DECORATORS: - behavior_decorators.append({'name': decorator.id, 'args': [], 'kwargs': {}}) - continue - # 识别带参数的自定义行为装饰器:@name(arg1, key=val)(ast.Call + ast.Name 形式) - if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Name): - deco_name = decorator.func.id - if deco_name not in _BUILTIN_DECORATORS: - deco_info = {'name': deco_name, 'args': [], 'kwargs': {}} - for arg in decorator.args: - if isinstance(arg, ast.Constant): - deco_info['args'].append(arg.value) - for kw in decorator.keywords: - if isinstance(kw.value, ast.Constant): - deco_info['kwargs'][kw.arg] = kw.value.value - behavior_decorators.append(deco_info) - continue - if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): - if decorator.func.attr == 'CReturn': - _RejectCReturnDecorator(Node, [decorator]) - elif decorator.func.attr == 'Attribute' and isinstance(decorator.func.value, ast.Name) and decorator.func.value.id == 'c': - for arg in decorator.args: - if isinstance(arg, ast.Call) and isinstance(arg.func, ast.Attribute): - if isinstance(arg.func.value, ast.Attribute) and isinstance(arg.func.value.value, ast.Name) and IsTModule(arg.func.value.value.id, self.Trans.SymbolTable) and arg.func.value.attr == 'attr': - attr_name = arg.func.attr - str_args = [a.value for a in arg.args if isinstance(a, ast.Constant) and isinstance(a.value, str)] - int_args = [a.value for a in arg.args if isinstance(a, ast.Constant) and isinstance(a.value, int)] - if attr_name == 'section' and str_args: - fn_attrs['section'] = str_args[0] - elif attr_name == 'aligned' and int_args: - fn_attrs['aligned'] = int_args[0] - elif attr_name == 'visibility' and str_args: - fn_attrs['visibility'] = str_args[0] - elif attr_name in ('noreturn', 'always_inline', 'noinline', 'cold', 'hot', 'constructor', 'destructor', 'pure', 'const', 'malloc', 'returns_nonnull', 'used', 'naked', 'no_instrument_function', 'warn_unused_result', 'weak', 'fallthrough'): - if not arg.args: - fn_attrs[attr_name] = True - elif isinstance(arg.func.value, ast.Attribute) and arg.func.value.attr == 'llvm' and isinstance(arg.func.value.value, ast.Attribute) and arg.func.value.value.attr == 'attr' and isinstance(arg.func.value.value.value, ast.Name) and IsTModule(arg.func.value.value.value.id, self.Trans.SymbolTable): - llvm_attr_name = arg.func.attr - if llvm_attr_name in self._LLVM_ATTR_NAMES: - fn_attrs[f'llvm.{llvm_attr_name}'] = True - elif isinstance(arg, ast.Attribute): - if isinstance(arg.value, ast.Attribute) and isinstance(arg.value.value, ast.Name) and IsTModule(arg.value.value.id, self.Trans.SymbolTable) and arg.value.attr == 'attr': - attr_name = arg.attr - if attr_name in ('noreturn', 'always_inline', 'noinline', 'cold', 'hot', 'constructor', 'destructor', 'pure', 'const', 'malloc', 'returns_nonnull', 'used', 'naked', 'no_instrument_function', 'warn_unused_result', 'weak', 'fallthrough', 'packed'): - fn_attrs[attr_name] = True - elif isinstance(arg.value, ast.Attribute) and arg.value.attr == 'llvm' and isinstance(arg.value.value, ast.Attribute) and arg.value.value.attr == 'attr' and isinstance(arg.value.value.value, ast.Name) and IsTModule(arg.value.value.value.id, self.Trans.SymbolTable): - llvm_attr_name = arg.attr - if llvm_attr_name in self._LLVM_ATTR_NAMES: - fn_attrs[f'llvm.{llvm_attr_name}'] = True - # 检查 @t.TLS 装饰器(一次性初始化标记) - is_tls_func: bool = False - if Node.decorator_list: - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): - if IsTModule(decorator.value.id, _symtab) and decorator.attr == 'TLS': - is_tls_func = True - break - elif isinstance(decorator, ast.Name) and decorator.id == 'TLS' and hasattr(t, 'TLS'): - is_tls_func = True - break - if Node.returns: - if isinstance(Node.returns, ast.Subscript) and isinstance(Node.returns.value, ast.Name) and Node.returns.value.id == 'tuple': - slice_node = Node.returns.slice - if isinstance(slice_node, ast.Tuple): - for elt in slice_node.elts: - MultiRetTypes.append(elt) - else: - MultiRetTypes.append(slice_node) - - # 先强制检查 str 类型注解 - is_str_return = False - ReturnTypeInfo = None - if Node.returns: - if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): - is_str_return = True - else: - try: - rt_info = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) - # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), - # 不能压平,否则返回类型错误。 - if rt_info and rt_info.IsStr and rt_info.PtrCount <= 1: - is_str_return = True - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - - if is_str_return: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CChar() - ReturnTypeInfo.PtrCount = 1 - elif Node.returns: - if MultiRetTypes: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() - else: - try: - if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): - ReturnTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.returns) - if not isinstance(ReturnTypeInfo, CTypeInfo): - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() + or (ReturnTypeInfo.PtrCount >= 2 and ReturnTypeInfo.BaseType is None)): + def _FindNonVoidCTypeInfo(node: ast.AST) -> Any: + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + results: list[Any] = [] + for child in [node.left, node.right]: + r: Any = _FindNonVoidCTypeInfo(child) + if r and not r.IsVoid: + results.append(r) + for r in results: + if not r.IsPtr and not r.IsStruct: + return r + return results[0] if results else None + try: + SideInfo: Any = CTypeInfo.FromNode(node, self.Trans.SymbolTable) + if SideInfo: + return SideInfo + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + return None + found: Any = _FindNonVoidCTypeInfo(Node.returns) + if found and not found.IsVoid: + ReturnTypeInfo = found + if ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + # define 宏函数(返回注解含 t.CDefine,如 `int | t.CDefine`): + # 标记为编译期内联宏(alwaysinline),对齐裸 `-> t.CDefine` 路径 + # (合并路径丢失 IsDefine 时仍能命中) + if AnnotationContainsName(Node.returns, 'CDefine'): + if not hasattr(self, '_cdefine_funcs'): + self._cdefine_funcs = set() + self._cdefine_funcs.add(FuncName) + else: + is_str_type: bool = False + if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): + is_str_type = True + # 走 ResolveAnnotationType 而非 CTypeInfo.FromNode:指针下标 + # 返回注解(rawptr[T] / t.CPtr[T])需走 TypeMerge 通用解析, + # 否则内层跨模块类解析不出 → CVoid/1(i8*)。 + ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) + if ReturnTypeInfo and ReturnTypeInfo.IsDefine: + inferred: Any = self._infer_return_type_from_body(Node, Gen, ClassName) + if inferred: + ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) + ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 + else: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + if not hasattr(self, '_cdefine_funcs'): + self._cdefine_funcs = set() + self._cdefine_funcs.add(FuncName) + elif ReturnTypeInfo and ReturnTypeInfo.IsState: + if ReturnTypeInfo.PtrCount > 0 or (ReturnTypeInfo.BaseType and not isinstance(ReturnTypeInfo.BaseType, t.CVoid)): + pass + else: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() + if ReturnTypeInfo is None: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() + # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 + if is_str_type or (ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1): + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CChar() + ReturnTypeInfo.PtrCount = 1 + # 检查泛型类返回类型 (如 list[str]) + # 注意:_from_node_subscript 对未识别的泛型下标(如 list[str])返回空 CTypeInfo(BaseType=None), + # 此时 IsVoid 为 False(因为 IsVoid 仅检查 t.CVoid 实例),需额外检测 BaseType is None + if (isinstance(Node.returns, ast.Subscript) + and isinstance(Node.returns.value, ast.Name) + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and Node.returns.value.id in self.Trans.ClassHandler._generic_class_templates + and (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0): + ret_class: str = Node.returns.value.id + ret_slice: ast.AST = Node.returns.slice + ret_type_args: list[str] = [] + ret_type_names: list[str] = [] + if isinstance(ret_slice, ast.Tuple): + for elt in ret_slice.elts: + if isinstance(elt, ast.Name): + tn2: str = elt.id + ts2: str = tn2 + if tn2 == 'int': + ts2, tn2 = 'int', 'CInt' + ret_type_args.append(ts2) + ret_type_names.append(tn2) + elif isinstance(ret_slice, ast.Name): + tn2: str = ret_slice.id + ts2: str = tn2 + if tn2 == 'int': + ts2, tn2 = 'int', 'CInt' + ret_type_args.append(ts2) + ret_type_names.append(tn2) + # 返回类型解析只需 struct 定义,不需方法体;使用 declare_only=True + # 避免过早触发完整特化(此时 MemManager.alloc 可能尚未注册, + # 导致 __new__ 的 pool.alloc(48) 解析失败,IR 块未终止)。 + # 完整特化由后续类实例化(L3688/L3753)触发 declare_only=False。 + spec_name2: str | None = self.Trans.ClassHandler._specialize_generic_class( + ret_class, ret_type_args, Gen, type_names=ret_type_names, declare_only=True) + if spec_name2 and spec_name2 in Gen.structs: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CStruct(name=spec_name2) + ReturnTypeInfo.PtrCount = 1 + if (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + except Exception: # 回退:设置默认返回类型为 CInt + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + else: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + inferred: Any = self._infer_return_type_from_body(Node, Gen, ClassName) + if inferred: + ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) + ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 + IsPtr = ReturnTypeInfo.IsPtr if ReturnTypeInfo else False + ReturnType: ir.Type = Gen._ctype_to_llvm(ReturnTypeInfo) + # 此处原有 "Ptr(Ptr(Struct)) → Ptr(Struct)" 的压层(历史遗留):会把已解析的 + # 多层指针返回类型(`-> rawptr[rawptr[X]]` / `-> X | t.CPtr | t.CPtr`) + # 与泛型特化方法返回(`list[X**]` 的 get/pop/__getitem__/__next__)少算一层, + # 与同函数的形参(`append(item: T)` 保留 `X**`)、声明端 + # DeclarationGenerator._get_type_str 的 BinOp 分支(同样不压层,见 + # '_is_bare_class_ref_annotation' 为假时逐层外包)以及 _EmitFunctionLlvm + # 的定义体路径全部不一致。违反"多层指针已解析时照原样保留层数"。 + # 实测(36 用例 A/B)删除后仅 `list[X**]` 的 4 个方法 declare 由 `X*` 变 + # `X**`,与 append/set 的形参一致;stub 逐字节不变。 + if isinstance(ReturnType, ir.IntType) and ReturnType.width == 8: + if RawFuncName == '__str__': + ReturnType = ir.PointerType(ir.IntType(8)) + if isinstance(ReturnType, ir.VoidType) and FuncName == 'main': + ReturnType = ir.IntType(32) + IsMethod, IsClassMethod, ResolvedClassName = self._resolve_method_class(Node, ClassName, func_meta, FuncName, Gen) + # __new__ methods should return a pointer to the struct (for heap allocation replacement) + # 修复:前向声明也必须使用正确的返回类型,否则 _replace_function_decl 可能失败, + # 导致 vtable 引用错误的函数类型(i32 而非 ClassName*) + if RawFuncName == '__new__' and ResolvedClassName: + StructType = Gen.structs.get(ResolvedClassName) + if StructType: + if isinstance(StructType, ir.PointerType): + ReturnType = StructType + else: + ReturnType = ir.PointerType(StructType) + ParamTypes: list[ir.Type] = [] + ParamNames: list[str] = [] + ParamTypeStrs: list[Any] = [] + auto_addr_modes: list[str | None] = [] + MultiRetLlvmTypes: list[ir.Type] = [] + if MultiRetTypes: + for i, ReturnTypeNode in enumerate(MultiRetTypes): + ReturnTypeInfo = CTypeInfo.FromNode(ReturnTypeNode, self.Trans.SymbolTable) + # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 + if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CChar() + ReturnTypeInfo.PtrCount = 1 + if ReturnTypeInfo is None: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + RetType: ir.Type = Gen._ctype_to_llvm(ReturnTypeInfo) + MultiRetLlvmTypes.append(RetType) + ReturnType = ir.LiteralStructType(MultiRetLlvmTypes) + for Arg in Node.args.args: + ParamIsUnsigned: bool = False + if Arg.annotation: + try: + ParamTypeInfo: Any + # 走 ResolveParamAnnotation(= ResolveAnnotationType + 泛型模板 + # 识别):参数注解中的泛型容器(list[X] / rawptr[list[X]])与 + # 指针下标(rawptr[X])需走 TypeMerge 通用解析,否则 + # TypeAnnotationResolver 解析不出 → VoidType → 报 + # "无法识别的参数类型注解"。 + ParamTypeInfo = self.ResolveParamAnnotation(Arg.annotation) + if ParamTypeInfo is None: + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CInt() + IsPtr = ParamTypeInfo.IsPtr + if ParamTypeInfo and getattr(ParamTypeInfo, 'IsCpythonObject', False) and not IsPtr: + IsPtr = True + ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) + # 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), + # 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。 + if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')): + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CChar() + ParamTypeInfo.PtrCount = 1 + IsPtr = True + # C 语言中数组参数退化为指针:t.CArray[type, N] → type* + if ParamTypeInfo.ArrayDims: + ParamTypeInfo.ArrayDims = [] + ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) + ParamType: ir.Type = Gen._ctype_to_llvm(ParamTypeInfo) + # 参数类型不能是 VoidType:C 语言不允许 void 参数, + # VoidType 说明类型注解无法识别(如跨模块 ast.expr),直接报错终止 + if isinstance(ParamType, ir.VoidType): + AnnStr: str = ast.unparse(Arg.annotation) if hasattr(ast, 'unparse') else str(Arg.annotation) + raise SyntaxError( + f"无法识别的参数类型注解: '{AnnStr}'(在函数 '{Node.name}' 的参数 '{Arg.arg}' 上)。" + f"请使用有效的 VPC 类型(如 t.CPtr、ast.AST 或已注册的结构体)。" + ) + ParamTypeStr: Any = ParamTypeInfo + except SyntaxError: + raise + except Exception as e: + ParamType = ir.IntType(32) + ParamTypeStr = CTypeInfo() + ParamTypeStr.BaseType = t.CInt() + else: + if ClassName and Arg.arg != 'self': + op_method_names: list[str] = ['__add__', '__sub__', '__mul__', '__truediv__', '__floordiv__', '__mod__', '__pow__', + '__radd__', '__rsub__', '__rmul__', '__rtruediv__', '__rfloordiv__', '__rmod__', '__rpow__', + '__iadd__', '__isub__', '__imul__', '__itruediv__', '__ifloordiv__', '__imod__', '__ipow__', + '__eq__', '__ne__', '__lt__', '__le__', '__gt__', '__ge__', + '__neg__', '__pos__', '__abs__', '__call__'] + if RawFuncName in op_method_names and ClassName in Gen.structs: + ParamType = ir.PointerType(Gen.structs[ClassName]) + else: + ParamType = ir.IntType(32) + else: + ParamType = ir.IntType(32) + ParamTypeStr = CTypeInfo() + ParamTypeStr.BaseType = t.CInt() + ParamTypes.append(ParamType) + ParamNames.append(Arg.arg) + ParamTypeStrs.append(ParamTypeStr) + # 自动取址模式(由注解读出,不依赖 BaseType 类型): + # 'ref' = &T、'temp' = &temp T(合成联合 T | t.CRef / t.CTempRef), + # 'always' = t.CAutoPtr。经 Gen._auto_addr_params 供调用点分流。 + auto_mode: str | None = FindAutoAddrMode(Arg.annotation) + auto_addr_modes.append(auto_mode) + if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen): + ParamTypeStrs.insert(0, f"struct {ResolvedClassName}") + auto_addr_modes.insert(0, None) + IsVariadic: bool = Node.args.vararg is not None + if extra_params: + for var_name, ptr_type in extra_params: + ParamTypes.append(ptr_type) + ParamNames.append(f'__nonlocal_{var_name}__') + if IsMethod and RawFuncName == '__next__': + ParamTypes.append(ir.PointerType(ir.IntType(1))) + ParamNames.append('__stop_iter_flag__') + HasRaise: bool = self._body_contains_raise(Node.body) + if HasRaise and RawFuncName != 'main': + ParamTypes.append(ir.PointerType(ir.PointerType(ir.IntType(8)))) + ParamNames.append('__eh_msg_out__') + ParamTypes.append(ir.PointerType(ir.IntType(32))) + ParamNames.append('__eh_code_out__') + FuncType: ir.FunctionType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) + # 存储 auto_addr_modes(为额外参数补 None) + total_params: int = len(ParamTypes) + while len(auto_addr_modes) < total_params: + auto_addr_modes.append(None) + Gen._auto_addr_params[FuncName] = auto_addr_modes + return FuncName, FuncType, ReturnTypeInfo, ParamTypeStrs, MultiRetTypes, IsMethod, ResolvedClassName, IsVariadic + + def _is_generic_function(self, Node: ast.FunctionDef) -> bool: + if hasattr(Node, 'type_params') and Node.type_params: + return True + return False + + def _mangle_generic_name(self, func_name: str, type_args: list[str], has_cexport: bool = False) -> str: + mangled_args: list[str] = [] + for ta in type_args: + llvm_str: str | None = CTypeRegistry.NameToLLVM(ta) + if llvm_str: + if llvm_str in ('float', 'double', 'half', 'fp128'): + mangled: str = 'f' + str(CTypeRegistry.GetClassByName(ta)().Size) + else: + mangled = llvm_str + else: + mangled = ta + mangled_args.append(mangled) + if has_cexport: + return func_name + '_T_' + '_'.join(mangled_args) + return func_name + '[' + ']['.join(mangled_args) + ']' + + def _apply_type_map_to_node(self, Node: ast.FunctionDef, type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: + for arg in Node.args.args: + if arg.annotation: + arg.annotation = self._replace_type_in_annotation(arg.annotation, type_map, cptr_params) + if Node.returns: + Node.returns = self._replace_type_in_annotation(Node.returns, type_map, cptr_params) + self._apply_type_map_to_body(Node.body, type_map, cptr_params) + + def _replace_type_in_annotation(self, node: ast.AST, type_map: dict[str, str], cptr_params: set[str] | None = None) -> ast.AST: + if isinstance(node, ast.Name): + if node.id in type_map: + replacement: str = type_map[node.id] + return self._make_replacement_node(replacement) + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + if isinstance(node.left, ast.Name) and node.left.id in type_map: + replacement: str = type_map[node.left.id] + node.left = self._make_replacement_node(replacement) + return node + node.left = self._replace_type_in_annotation(node.left, type_map, cptr_params) + node.right = self._replace_type_in_annotation(node.right, type_map, cptr_params) + elif isinstance(node, ast.Attribute): + if hasattr(node, 'attr') and node.attr in type_map: + replacement: str = type_map[node.attr] + return self._make_replacement_node(replacement) + elif isinstance(node, ast.Subscript): + # 嵌套泛型(如 list[T]):递归替换 value 和 slice 中的类型参数 + node.value = self._replace_type_in_annotation(node.value, type_map, cptr_params) + node.slice = self._replace_type_in_annotation(node.slice, type_map, cptr_params) + return node + + def _make_replacement_node(self, replacement: str) -> ast.AST: + """将 type_map 中的替换字符串转换为 AST 节点。 + mangled 名称如 '.AST*' 需要还原为 AST | t.CPtr 结构, + 否则类型解析会丢失 PtrCount 信息。 + 含点号的名称(如 '.AST')需解析为 ast.Attribute 链。 + 下标形态('rawptr[ast.AST]' / 'list[AST]' 等)必须整体解析为 Subscript: + _make_name_node 只按 '.' 切分会把 'rawptr[ast.AST]' 拆成 + Name('rawptr[ast') + Attribute('AST]'),两段都不是有效类型名 → + 参数/返回注解解析为空 → VoidType → "无法识别的参数类型注解"。 + 指针下标(rawptr[T] / t.CPtr[T])再折叠为等价的 `T | t.CPtr`,与源码 + 联合写法同解析路径(对齐 VPC TypeGenResolve._parse_type_string 的 '[' + 分支 + ClassGenGeneric._clone_annotation 的 T 替换)。 + 内层为已混淆名(数字开头,如 'rawptr[7565e081d88517ac.AST]')时 + ast.parse 无法解析(数字开头的令牌),改用手工拆 base/slice(同 VPC + TypeGenResolve._parse_type_string 的数字开头手工拆分分支)。 + 尾部 '*'(泛型实参 '.Node*' / '.Node**',来自 rawptr[T] / + t.CPtr 的逐层 mangling)逐层还原为 `Node | t.CPtr [| t.CPtr]` → 对应 + 层数指针;只剥一层会把 'Node*' 当结构体名建出 phantom opaque + (item 类型与特化名 '.Node**' 的语义不一致)。 + 尾部 'ptr'(旧规则 mangling,如 'Nodeptr' / 'ast.ASTptr')保留兼容: + .vpi/stub 缓存里仍是旧名时按同样方式还原。 + """ + StarCount: int = len(replacement) - len(replacement.rstrip('*')) + if StarCount > 0: + BaseName: str = replacement[:len(replacement) - StarCount] + base_node: ast.AST + if BaseName in ('i8', 'void', 'void*'): + # i8*(void*)在源码侧写作 t.CPtr;再叠加 StarCount-1 层 + base_node = ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + StarCount -= 1 + else: + base_node = self._make_name_node(BaseName) + node: ast.AST = base_node + for _ in range(StarCount): + node = ast.BinOp( + left=node, + op=ast.BitOr(), + right=ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), + attr='CPtr', ctx=ast.Load()), + ) + return node + if replacement.endswith('ptr'): + # 提取基础名称:'sha1.ASTptr' → 'sha1.AST';'Nodeptrptr' → 'Node'(2 层) + base: str = replacement + levels: int = 0 + while base.endswith('ptr') and len(base) > 3: + base = base[:-3] + levels += 1 + node: ast.AST = self._make_name_node(base) + for _ in range(levels): + node = ast.BinOp( + left=node, + op=ast.BitOr(), + right=ast.Attribute( + value=ast.Name(id='t', ctx=ast.Load()), + attr='CPtr', ctx=ast.Load()), + ) + return node + if '[' in replacement: + SubNode: ast.AST | None = None + try: + SubNode = ast.parse(replacement, '', mode='eval').body + except Exception: + SubNode = None + if not isinstance(SubNode, ast.Subscript): + SubNode = self._parse_subscript_manually(replacement) + if isinstance(SubNode, ast.Subscript): + if IsPtrSubscriptNode(SubNode, self.Trans.SymbolTable): + return DesugarPtrSubscriptToUnion(SubNode, self.Trans.SymbolTable) + return SubNode + return self._make_name_node(replacement) + + def _parse_subscript_manually(self, replacement: str) -> ast.AST | None: + """手工拆分单实参下标字符串(ast.parse 因数字开头标识符失败时使用)。 + + 处理 `[]`(整体以 ']' 结尾、单实参、无逗号;内层可为嵌套 + 单实参下标):'rawptr[7565e081d88517ac.AST]' → Subscript(Name('rawptr'), + Attribute(Name('7565e081d88517ac'), 'AST')); + 'rawptr[rawptr[7565e081d88517ac.AST]]' → 外层 Subscript 的 slice 递归 + 拆出内层 Subscript(嵌套 rawptr 需两层指针)。带逗号等复杂形态返回 + None(交调用方按普通名称处理),不做字符串猜测。 + """ + open_idx: int = replacement.find('[') + if open_idx <= 0 or not replacement.endswith(']'): + return None + inner: str = replacement[open_idx + 1:-1] + if not inner or ',' in inner: + return None + base_name: str = replacement[:open_idx].strip() + inner_name: str = inner.strip() + if not base_name or not inner_name: + return None + slice_node: ast.AST | None + if '[' in inner_name or inner_name.endswith('*'): + # 嵌套单实参下标 / 指针层数内层(如 rawptr[rawptr[.CLS]]、 + # .list[.X*]):递归按同一还原规则拆解 + slice_node = self._make_replacement_node(inner_name) + else: + try: + slice_node = ast.parse(inner_name, '', mode='eval').body + except Exception: + slice_node = self._make_name_node(inner_name) + if slice_node is None: + return None + return ast.Subscript( + value=self._make_name_node(base_name), slice=slice_node, ctx=ast.Load()) + + @staticmethod + def _make_name_node(name: str) -> ast.AST: + """将可能含点号的名称转换为 ast.Name 或 ast.Attribute 链。 + 'AST' -> ast.Name(id='AST') + 'sha1.AST' -> ast.Attribute(value=ast.Name(id='sha1'), attr='AST') + """ + if '.' not in name: + return ast.Name(id=name, ctx=ast.Load()) + parts: list[str] = name.split('.') + node: ast.AST = ast.Name(id=parts[0], ctx=ast.Load()) + for part in parts[1:]: + node = ast.Attribute(value=node, attr=part, ctx=ast.Load()) + return node + + def _apply_type_map_to_body(self, body: list[ast.AST], type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: + for stmt in body: + self._apply_type_map_to_stmt(stmt, type_map, cptr_params) + + def _apply_type_map_to_stmt(self, node: ast.AST, type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: + if isinstance(node, ast.Return) and node.value: + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + elif isinstance(node, ast.Assign): + for t in node.targets: + self._apply_type_map_to_expr(t, type_map, cptr_params) + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + elif isinstance(node, ast.AnnAssign): + if node.annotation: + node.annotation = self._replace_type_in_annotation(node.annotation, type_map, cptr_params) + if node.value: + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + elif isinstance(node, ast.AugAssign): + self._apply_type_map_to_expr(node.target, type_map, cptr_params) + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + elif isinstance(node, ast.Expr): + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + elif isinstance(node, ast.If): + self._apply_type_map_to_expr(node.test, type_map, cptr_params) + for s in node.body: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + for s in node.orelse: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + elif isinstance(node, ast.For): + self._apply_type_map_to_expr(node.iter, type_map, cptr_params) + for s in node.body: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + for s in node.orelse: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + elif isinstance(node, ast.While): + self._apply_type_map_to_expr(node.test, type_map, cptr_params) + for s in node.body: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + for s in node.orelse: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + elif isinstance(node, ast.Try): + for s in node.body: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + for handler in node.handlers: + for s in handler.body: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + for s in node.orelse: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + for s in node.finalbody: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + elif isinstance(node, ast.With): + for item in node.items: + self._apply_type_map_to_expr(item.context_expr, type_map, cptr_params) + if item.optional_vars: + self._apply_type_map_to_expr(item.optional_vars, type_map, cptr_params) + for s in node.body: + self._apply_type_map_to_stmt(s, type_map, cptr_params) + elif isinstance(node, ast.Raise): + if node.exc: + self._apply_type_map_to_expr(node.exc, type_map, cptr_params) + if node.cause: + self._apply_type_map_to_expr(node.cause, type_map, cptr_params) + elif isinstance(node, ast.Assert): + self._apply_type_map_to_expr(node.test, type_map, cptr_params) + if node.msg: + self._apply_type_map_to_expr(node.msg, type_map, cptr_params) + + def _apply_type_map_to_expr(self, node: ast.AST | None, type_map: dict[str, str], cptr_params: set[str] | None = None) -> None: + if node is None: + return + if isinstance(node, ast.Call): + if isinstance(node.func, ast.Name) and node.func.id in type_map: + replacement: str = type_map[node.func.id] + # 使用 _make_replacement_node 处理含点号的类型名(如 't.CUnsignedChar'), + # 避免 T(0) 替换为 CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域 + # 而报 "Undefined function: 'CUnsignedChar'" + node.func = self._make_replacement_node(replacement) + elif isinstance(node.func, ast.Attribute): + if isinstance(node.func.value, ast.Name) and node.func.value.id in type_map: + replacement: str = type_map[node.func.value.id] + # 指针类型参数(如 list[AST | t.CPtr] 中的 T)在表达式上下文中 + # 需替换为 BinOp(AST | t.CPtr),使 __sizeof__ 等处理器能正确识别 + # 指针语义并直接返回 8,避免触发 struct 加载和重入编译 + if cptr_params and node.func.value.id in cptr_params: + node.func.value = ast.BinOp( + left=self._make_name_node(replacement), + op=ast.BitOr(), + right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + ) + else: + node.func.value = self._make_name_node(replacement) + else: + self._apply_type_map_to_expr(node.func.value, type_map, cptr_params) + for arg in node.args: + self._apply_type_map_to_expr(arg, type_map, cptr_params) + elif isinstance(node, ast.Attribute): + if isinstance(node.value, ast.Name) and node.value.id in type_map: + replacement: str = type_map[node.value.id] + if cptr_params and node.value.id in cptr_params: + node.value = ast.BinOp( + left=self._make_name_node(replacement), + op=ast.BitOr(), + right=ast.Attribute(value=ast.Name(id='t', ctx=ast.Load()), attr='CPtr', ctx=ast.Load()) + ) + else: + node.value = ast.Name(id=replacement, ctx=ast.Load()) + else: + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + elif isinstance(node, ast.BinOp): + self._apply_type_map_to_expr(node.left, type_map, cptr_params) + self._apply_type_map_to_expr(node.right, type_map, cptr_params) + elif isinstance(node, ast.UnaryOp): + self._apply_type_map_to_expr(node.operand, type_map, cptr_params) + elif isinstance(node, ast.Compare): + self._apply_type_map_to_expr(node.left, type_map, cptr_params) + for comp in node.comparators: + self._apply_type_map_to_expr(comp, type_map, cptr_params) + elif isinstance(node, ast.BoolOp): + for val in node.values: + self._apply_type_map_to_expr(val, type_map, cptr_params) + elif isinstance(node, ast.IfExp): + self._apply_type_map_to_expr(node.test, type_map, cptr_params) + self._apply_type_map_to_expr(node.body, type_map, cptr_params) + self._apply_type_map_to_expr(node.orelse, type_map, cptr_params) + elif isinstance(node, ast.Subscript): + self._apply_type_map_to_expr(node.value, type_map, cptr_params) + self._apply_type_map_to_expr(node.slice, type_map, cptr_params) + elif isinstance(node, ast.Name): + if node.id in type_map: + node.id = type_map[node.id] + + def _infer_type_arg_from_value(self, val: ir.Value, Gen: LlvmCodeGenerator) -> str: + if val is None: + return 'int' + if isinstance(val.type, ir.IntType): + if val.type.width == 64: + return 'i64' + elif val.type.width == 16: + return 'i16' + elif val.type.width == 8: + return 'i8' + return 'int' + elif isinstance(val.type, ir.DoubleType): + return 'double' + elif isinstance(val.type, ir.FloatType): + return 'float' + elif isinstance(val.type, ir.PointerType): + return 'void*' + return 'int' + + def _specialize_generic_function(self, FuncName: str, type_args: list[str], Gen: LlvmCodeGenerator) -> str | None: + if not hasattr(self, '_generic_templates') or FuncName not in self._generic_templates: + return None + template: dict[str, Any] = self._generic_templates[FuncName] + Node: ast.FunctionDef = template['node'] + type_param_names: list[str] = template['type_params'] + ClassName: str | None = template['class_name'] + if len(type_args) != len(type_param_names): + return None + spec_key: str = FuncName + '<' + ','.join(type_args) + '>' + if not hasattr(self, '_generic_specializations'): + self._generic_specializations = {} + if spec_key in self._generic_specializations: + return self._generic_specializations[spec_key] + has_cexport: bool = False + if Node.returns: + try: + RetTypeInfo: Any = None + if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): + RetTypeInfo = self.Trans.TypeMergeHandler.MergeTypes(Node.returns) + else: + RetTypeInfo = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExport): + has_cexport = True + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + spec_name: str = self._mangle_generic_name(FuncName, type_args, has_cexport) + type_map: dict[str, str] = {} + for i, tp_name in enumerate(type_param_names): + type_map[tp_name] = type_args[i] + SpecNode: ast.FunctionDef = copy.deepcopy(Node) + SpecNode.type_params = [] + SpecNode.name = spec_name + self._apply_type_map_to_node(SpecNode, type_map) + if has_cexport: + if not hasattr(Gen, '_export_funcs'): + Gen._export_funcs = set() + Gen._export_funcs.add(spec_name) + saved_builder: Any = Gen.builder + saved_variables: dict[str, Any] = dict(Gen.variables) if Gen.variables else {} + saved_direct_values: dict[str, Any] = dict(Gen._direct_values) if Gen._direct_values else {} + saved_var_type_info: dict[str, Any] = dict(Gen.var_type_info) if Gen.var_type_info else {} + saved_var_signedness: dict[str, Any] = dict(Gen.var_signedness) if Gen.var_signedness else {} + saved_global_vars: set[str] = set(Gen.global_vars) if Gen.global_vars else set() + saved_var_scopes: list[dict[str, Any]] = [dict(s) for s in self.Trans.VarScopes] if self.Trans.VarScopes else [] + saved_func: Any = Gen.func + saved_current_func_name: str | None = getattr(Gen, '_current_func_name', None) + saved_block: Any = None + if Gen.builder and Gen.builder.block and not Gen.builder.block.is_terminated: + saved_block = Gen.builder.block + self._EmitFunctionForwardDeclLlvm(SpecNode, Gen, ClassName=ClassName) + self._EmitFunctionLlvm(SpecNode, Gen, ClassName=ClassName) + Gen.builder = saved_builder + if saved_block is not None and Gen.builder is not None: + Gen.builder.position_at_end(saved_block) + Gen.variables = saved_variables + Gen._direct_values = saved_direct_values + Gen.var_type_info = saved_var_type_info + Gen.var_signedness = saved_var_signedness + Gen.global_vars = saved_global_vars + Gen.func = saved_func + if saved_current_func_name is not None: + Gen._current_func_name = saved_current_func_name + self.Trans.VarScopes = saved_var_scopes + self._generic_specializations[spec_key] = spec_name + if not hasattr(self.Trans, '_generic_specializations'): + self.Trans._generic_specializations = {} + self.Trans._generic_specializations[spec_key] = spec_name + return spec_name + + def _EmitFunctionForwardDeclLlvm(self, Node: ast.FunctionDef, Gen: LlvmCodeGenerator, ClassName: str | None = None) -> None: + if self._is_generic_function(Node): + if not hasattr(self, '_generic_templates'): + self._generic_templates = {} + RawFuncName: str = Node.name + if ClassName: + FuncName: str = f"{ClassName}.{RawFuncName}" + else: + FuncName = RawFuncName + type_params: list[str] = [tp.name for tp in Node.type_params] + self._generic_templates[FuncName] = { + 'node': Node, + 'type_params': type_params, + 'class_name': ClassName, + } + return + result: Any = self._GetFunctionSignatureLlvm(Node, Gen, ClassName) + if result is None: + return + FuncName: str + FuncType: ir.FunctionType + ReturnTypeInfo: Any + ParamTypeStrs: list[Any] + MultiRetTypes: list[ast.AST] + IsMethod: bool + ResolvedClassName: str | None + IsVariadic: bool + FuncName, FuncType, ReturnTypeInfo, ParamTypeStrs, MultiRetTypes, IsMethod, ResolvedClassName, IsVariadic = result + IsUserMarkedExport: bool = False + if Node.returns: + try: + RetTypeInfo: Any = None + RetTypeInfo = self.ResolveAnnotationType(Node.returns) + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExport) and not RetTypeInfo.IsState: + Gen._export_funcs.add(FuncName) + IsUserMarkedExport = True + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExtern): + Gen._export_funcs.add(FuncName) + IsUserMarkedExport = True + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.IsState: + Gen._export_funcs.add(FuncName) # t.State 标记的函数必须保持原始名称,以便链接器解析 + IsUserMarkedExport = True + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + # 装饰器形式的存储类标记(@t.CExport / @t.CExtern / @t.State 等) + deco_flags: dict[str, bool] = self._extract_decorator_storage_flags(Node) + if deco_flags['is_export'] or deco_flags['is_extern'] or deco_flags['is_state']: + Gen._export_funcs.add(FuncName) + IsUserMarkedExport = True + # 注册当前模块定义的函数名,使 _mangle_func_name 跳过 _export_extern_funcs 检查 + # (_export_extern_funcs 是跨模块共享集合,包含所有 CExtern 函数名;当前模块定义的 + # 同名用户函数应使用 SHA1 前缀,而非被误判为外部 C 函数返回裸名) + Gen._current_module_func_names.add(FuncName) + # 移除导入污染:用户函数未显式标记导出,但名称在 _export_funcs 中(来自导入的同名 C 库函数) + if not IsUserMarkedExport and FuncName in Gen._export_funcs: + Gen._export_funcs.discard(FuncName) + MangledName: str = Gen._mangle_func_name(FuncName) + # 自动取址模式:查表侧统一按**真实发射名**(func.name,即 MangledName)查 + # (FuncGen._adjust_args)。签名阶段(_GetFunctionSignatureLlvm)只能按逻辑名 + # (FuncName)登记,故在此把该逻辑名下的模式复制到真实名下——否则同模块 + # "先调用、后定义"的 &T / &temp T 会在前向声明路径上静默丢失取址。 + _aam_fwd: list[str | None] | None = Gen._auto_addr_params.get(FuncName) + if _aam_fwd: + Gen._auto_addr_params[MangledName] = _aam_fwd + need_new: bool = MangledName not in Gen.functions + if not need_new: + existing: Any = Gen.functions[MangledName] + if getattr(existing, 'name', MangledName) != MangledName: + need_new = True + if need_new: + try: + # 统一走幂等入口:module.globals 已有同名函数时复用(并补登记 + # Gen.functions),签名冲突则 fail-fast —— 绝不直接新建同名 + # ir.Function(llvmlite NameScope 按名去重,必抛 DuplicatedNameError; + # 现场:5d84ea579d83ef73.CallGenFd._translate_fakeduck_call 被此路径新建, + # 而 BaseGen._get_or_declare_function 已建过同名声明却未登记 + # Gen.functions ⇒ need_new 误判为真)。 + func: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) + func.attributes.add('noredzone') + if hasattr(self, '_cdefine_funcs') and FuncName in self._cdefine_funcs: + func.linkage = 'linkonce_odr' + func.attributes.add('alwaysinline') + if hasattr(self, '_pending_llvm_attrs') and FuncName in self._pending_llvm_attrs: + for attr_name in self._pending_llvm_attrs[FuncName]: + try: + func.attributes.add(attr_name) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"添加函数属性失败: {_e}", "Exception") + Gen.functions[MangledName] = func + if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: + Gen.functions[FuncName] = func + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError + if isinstance(_e, FailFastError): + # 签名冲突 = declare/define 分裂,必须终止(不吞) + raise + # 其余异常同样必须终止:本 try 块内只有「创建/复用声明 + 挂属性」, + # 失败即该声明不可用(调用点会按错签名传参)。原实现非严格模式静默 + # `pass`、严格模式仅 LogWarning —— 正是它把 llvmlite 的 AssertionError + # (形参类型不是 types.Type 时 `Argument(...)` 断言失败,见 + # BaseGen._CreateOrReuseFuncDecl 的创建前自检注释)吞成"名字半注册": + # 名字已进 module.scope、未进 module.globals,直到定义阶段才以 + # DuplicatedNameError(报文只有名字)暴露,真因与现场彻底脱节。 + raise FailFastError( + f"函数前置声明创建失败:'{MangledName}'" + f"\n · 本次签名: {FuncType}" + f"\n · 原始异常: {type(_e).__name__}: {_e}" + + Gen._get_node_info()) + else: + existing = Gen.functions[MangledName] + existing_type: ir.FunctionType | None = getattr(existing, 'ftype', None) + if existing_type and existing_type != FuncType: + Gen.functions[MangledName] = self._replace_function_decl(Gen, FuncName, FuncType) + if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: + Gen.functions[FuncName] = Gen.functions[MangledName] + elif not existing_type: + try: + Gen.functions[MangledName] = self._replace_function_decl(Gen, FuncName, FuncType) + if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: + Gen.functions[FuncName] = Gen.functions[MangledName] + except Exception as _e: + # fail-fast 一律上抛(本仓既定口径):吞掉它 = 声明静默缺失 + + # 半注册名字留给下一轮,真因与现场脱节。 + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + self.Trans.FunctionDefCache[FuncName] = Node + + def _replace_function_decl(self, Gen: LlvmCodeGenerator, FuncName: str, NewFuncType: ir.FunctionType) -> ir.Function: + MangledName: str = Gen._mangle_func_name(FuncName) + old_func: Any = Gen.functions.get(MangledName) + if old_func is None: + try: + # 走唯一幂等入口:名字可能已存在于 module.globals 但未登记进 Gen.functions + # (别的路径先建过声明)——直接 ir.Function 必抛裸 DuplicatedNameError, + # 入口则同名同类型复用、异类型带两侧签名 fail-fast。 + return Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType) + except Exception as _e: # 回退:函数已存在时返回旧函数 + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # 创建中途失败(如形参/返回类型不是 llvmlite Type)由 BaseGen 的 + # Function 守卫转成 fail-fast:必须上抛,原实现会 `return old_func` + # (此处 old_func 必为 None)⇒ 调用点把 None 当声明用。 + raise + return old_func + try: + old_name: str = old_func.name + # 必须同时清 old_name(对象真名)与 MangledName(字典键 / 目标名): + # 别名场景下二者不同,只清真名会让 MangledName 仍占住 module.scope + # (llvmlite 按名字去重)→ 重建抛 DuplicatedNameError。 + module_scope: Any = getattr(Gen.module, 'scope', None) + for _stale_name in {old_name, MangledName}: + _stale: Any = Gen.module.globals.get(_stale_name) + if isinstance(_stale, ir.Function): + del Gen.module.globals[_stale_name] + if getattr(module_scope, '_useset', None): + module_scope._useset.discard(_stale_name) + if getattr(Gen.module, '_guessed_names', None): + Gen.module._guessed_names.discard(FuncName) + new_func: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType) + Gen.functions[MangledName] = new_func + if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: + Gen.functions[FuncName] = new_func + return new_func + except Exception as ex: + from lib.core.LLVMCG.FuncGen import FailFastError + if isinstance(ex, FailFastError): + # 同名符号冲突(函数签名分裂 / 名字被非函数 global 占住):必须终止, + # 不能回退成"复用旧声明"——那正是 declare/define 分裂的现场。 + raise + traceback.print_exc() + Gen.functions[MangledName] = old_func + if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: + Gen.functions[FuncName] = old_func + return old_func + + def _EmitFunctionLlvm(self, Node: ast.FunctionDef, Gen: LlvmCodeGenerator, ClassName: str | None = None, extra_params: list[tuple[str, ir.Type]] | None = None) -> ir.Function | None: + if self._is_generic_function(Node): + return + FuncName, RawFuncName, func_meta = self._compute_func_name_meta(Node, ClassName) + _RejectRefReturnAnnotation(Node) + # 保存父级 _CurrentCpythonObjectClass,防止嵌套函数发射污染父方法类上下文 + # (例如 HandleImport body 内触发 list[ast.ASTptr] 方法发射,结束后会清空 _CurrentCpythonObjectClass) + saved_cpython_class: str | None = self.Trans._CurrentCpythonObjectClass + MultiRetTypes = [] + IsPtr = False + fn_attrs = {} + # 自定义行为装饰器列表,用于生成 wrapper 函数 + # 任何 @name 或 @name(args) 只要不是内置装饰器,都视为自定义行为装饰器 + behavior_decorators = [] + # 内置装饰器名称,这些由编译器特殊处理,不作为行为装饰器 + # 另含**编译期生命期标注**(P2):@life(willFree(...)) 与 @willFree(...) 是纯标注, + # 没有运行时装饰器函数;不排除会生成 wrapper 去调用不存在的 @life/@willFree, + # 并把被装饰的 extern def(如 stdlib.free)变成本模块 internal 声明(llc 报 linkage 错)。 + _BUILTIN_DECORATORS = {'staticmethod', 'property', 'classmethod', 'life', 'willFree'} + _symtab: object = self.Trans.SymbolTable + if Node.decorator_list: + for decorator in Node.decorator_list: + # 跳过 t 模块存储类标记装饰器(@t.CExport / @CExport 等) + if self._is_t_storage_decorator(decorator, _symtab): + continue + # 识别自定义行为装饰器:@name(ast.Name 形式) + if isinstance(decorator, ast.Name): + if decorator.id not in _BUILTIN_DECORATORS: + behavior_decorators.append({'name': decorator.id, 'args': [], 'kwargs': {}}) + continue + # 识别带参数的自定义行为装饰器:@name(arg1, key=val)(ast.Call + ast.Name 形式) + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Name): + deco_name = decorator.func.id + if deco_name not in _BUILTIN_DECORATORS: + deco_info = {'name': deco_name, 'args': [], 'kwargs': {}} + for arg in decorator.args: + if isinstance(arg, ast.Constant): + deco_info['args'].append(arg.value) + for kw in decorator.keywords: + if isinstance(kw.value, ast.Constant): + deco_info['kwargs'][kw.arg] = kw.value.value + behavior_decorators.append(deco_info) + continue + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): + if decorator.func.attr == 'CReturn': + _RejectCReturnDecorator(Node, [decorator]) + elif decorator.func.attr == 'Attribute' and isinstance(decorator.func.value, ast.Name) and decorator.func.value.id == 'c': + for arg in decorator.args: + if isinstance(arg, ast.Call) and isinstance(arg.func, ast.Attribute): + if isinstance(arg.func.value, ast.Attribute) and isinstance(arg.func.value.value, ast.Name) and IsTModule(arg.func.value.value.id, self.Trans.SymbolTable) and arg.func.value.attr == 'attr': + attr_name = arg.func.attr + str_args = [a.value for a in arg.args if isinstance(a, ast.Constant) and isinstance(a.value, str)] + int_args = [a.value for a in arg.args if isinstance(a, ast.Constant) and isinstance(a.value, int)] + if attr_name == 'section' and str_args: + fn_attrs['section'] = str_args[0] + elif attr_name == 'aligned' and int_args: + fn_attrs['aligned'] = int_args[0] + elif attr_name == 'visibility' and str_args: + fn_attrs['visibility'] = str_args[0] + elif attr_name in ('noreturn', 'always_inline', 'noinline', 'cold', 'hot', 'constructor', 'destructor', 'pure', 'const', 'malloc', 'returns_nonnull', 'used', 'naked', 'no_instrument_function', 'warn_unused_result', 'weak', 'fallthrough'): + if not arg.args: + fn_attrs[attr_name] = True + elif isinstance(arg.func.value, ast.Attribute) and arg.func.value.attr == 'llvm' and isinstance(arg.func.value.value, ast.Attribute) and arg.func.value.value.attr == 'attr' and isinstance(arg.func.value.value.value, ast.Name) and IsTModule(arg.func.value.value.value.id, self.Trans.SymbolTable): + llvm_attr_name = arg.func.attr + if llvm_attr_name in self._LLVM_ATTR_NAMES: + fn_attrs[f'llvm.{llvm_attr_name}'] = True + elif isinstance(arg, ast.Attribute): + if isinstance(arg.value, ast.Attribute) and isinstance(arg.value.value, ast.Name) and IsTModule(arg.value.value.id, self.Trans.SymbolTable) and arg.value.attr == 'attr': + attr_name = arg.attr + if attr_name in ('noreturn', 'always_inline', 'noinline', 'cold', 'hot', 'constructor', 'destructor', 'pure', 'const', 'malloc', 'returns_nonnull', 'used', 'naked', 'no_instrument_function', 'warn_unused_result', 'weak', 'fallthrough', 'packed'): + fn_attrs[attr_name] = True + elif isinstance(arg.value, ast.Attribute) and arg.value.attr == 'llvm' and isinstance(arg.value.value, ast.Attribute) and arg.value.value.attr == 'attr' and isinstance(arg.value.value.value, ast.Name) and IsTModule(arg.value.value.value.id, self.Trans.SymbolTable): + llvm_attr_name = arg.attr + if llvm_attr_name in self._LLVM_ATTR_NAMES: + fn_attrs[f'llvm.{llvm_attr_name}'] = True + # 检查 @t.TLS 装饰器(一次性初始化标记) + is_tls_func: bool = False + if Node.decorator_list: + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Attribute) and isinstance(decorator.value, ast.Name): + if IsTModule(decorator.value.id, _symtab) and decorator.attr == 'TLS': + is_tls_func = True + break + elif isinstance(decorator, ast.Name) and decorator.id == 'TLS' and hasattr(t, 'TLS'): + is_tls_func = True + break + if Node.returns: + if isinstance(Node.returns, ast.Subscript) and isinstance(Node.returns.value, ast.Name) and Node.returns.value.id == 'tuple': + slice_node = Node.returns.slice + if isinstance(slice_node, ast.Tuple): + for elt in slice_node.elts: + MultiRetTypes.append(elt) + else: + MultiRetTypes.append(slice_node) + + # 先强制检查 str 类型注解 + is_str_return = False + ReturnTypeInfo = None + if Node.returns: + if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): + is_str_return = True + else: + try: + rt_info = CTypeInfo.FromNode(Node.returns, self.Trans.SymbolTable) + # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), + # 不能压平,否则返回类型错误。 + if rt_info and rt_info.IsStr and rt_info.PtrCount <= 1: + is_str_return = True + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + + if is_str_return: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CChar() + ReturnTypeInfo.PtrCount = 1 + elif Node.returns: + if MultiRetTypes: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() + else: + try: + if isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): + ReturnTypeInfo = getattr(self.Trans, 'TypeMergeHandler', None) and self.Trans.TypeMergeHandler.MergeTypes(Node.returns) + if not isinstance(ReturnTypeInfo, CTypeInfo): + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() if ((ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState) - or (ReturnTypeInfo.PtrCount >= 2 and ReturnTypeInfo.BaseType is None)): - def _FindNonVoidCTypeInfo(node): - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - results = [] - for child in [node.left, node.right]: - r = _FindNonVoidCTypeInfo(child) - if r and not r.IsVoid: - results.append(r) - for r in results: - if not r.IsPtr and not r.IsStruct: - return r - return results[0] if results else None - try: - SideInfo = CTypeInfo.FromNode(node, self.Trans.SymbolTable) - if SideInfo: - return SideInfo - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - return None - found = _FindNonVoidCTypeInfo(Node.returns) - if found and not found.IsVoid: - ReturnTypeInfo = found - if ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - # define 宏函数(返回注解含 t.CDefine,如 `int | t.CDefine`): - # 标记为编译期内联宏(alwaysinline),对齐裸 `-> t.CDefine` 路径 - if AnnotationContainsName(Node.returns, 'CDefine'): - if not hasattr(self, '_cdefine_funcs'): - self._cdefine_funcs = set() - self._cdefine_funcs.add(FuncName) - else: - is_str_type = False - if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): - is_str_type = True - # 与 _GetFunctionSignatureLlvm 同口径:指针下标返回注解 - # (rawptr[T] / t.CPtr[T])走 ResolveAnnotationType 的 - # TypeMerge 通用解析,避免内层跨模块类退化为 i8*。 - ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) - if ReturnTypeInfo and ReturnTypeInfo.IsDefine: - inferred = self._infer_return_type_from_body(Node, Gen, ClassName) - if inferred: - ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) - ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 - else: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - if not hasattr(self, '_cdefine_funcs'): - self._cdefine_funcs = set() - self._cdefine_funcs.add(FuncName) - elif ReturnTypeInfo and ReturnTypeInfo.IsState: - pass # t.State is a modifier, preserve the actual return type - if ReturnTypeInfo is None: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CVoid() - # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 - if is_str_type or (ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1): - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CChar() - ReturnTypeInfo.PtrCount = 1 - # 检查泛型类返回类型 (如 list[str]) - # 注意:_from_node_subscript 对未识别的泛型下标(如 list[str])返回空 CTypeInfo(BaseType=None), - # 此时 IsVoid 为 False(因为 IsVoid 仅检查 t.CVoid 实例),需额外检测 BaseType is None - if (isinstance(Node.returns, ast.Subscript) - and isinstance(Node.returns.value, ast.Name) - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and Node.returns.value.id in self.Trans.ClassHandler._generic_class_templates - and (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0): - ret_class: str = Node.returns.value.id - ret_slice: ast.AST = Node.returns.slice - ret_type_args: list[str] = [] - ret_type_names: list[str] = [] - if isinstance(ret_slice, ast.Tuple): - for elt in ret_slice.elts: - if isinstance(elt, ast.Name): - tn2: str = elt.id - ts2: str = tn2 - if tn2 == 'int': - ts2, tn2 = 'int', 'CInt' - ret_type_args.append(ts2) - ret_type_names.append(tn2) - elif isinstance(ret_slice, ast.Name): - tn2: str = ret_slice.id - ts2: str = tn2 - if tn2 == 'int': - ts2, tn2 = 'int', 'CInt' - ret_type_args.append(ts2) - ret_type_names.append(tn2) - # 返回类型解析只需 struct 定义,不需方法体;使用 declare_only=True - # 避免过早触发完整特化(此时 MemManager.alloc 可能尚未注册, - # 导致 __new__ 的 pool.alloc(48) 解析失败,IR 块未终止)。 - spec_name2: str | None = self.Trans.ClassHandler._specialize_generic_class( - ret_class, ret_type_args, Gen, type_names=ret_type_names, declare_only=True) - if spec_name2 and spec_name2 in Gen.structs: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CStruct(name=spec_name2) - ReturnTypeInfo.PtrCount = 1 - if (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - except Exception: # 回退:设置默认返回类型为 CInt - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - else: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - inferred = self._infer_return_type_from_body(Node, Gen, ClassName) - if inferred: - ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) - ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 - - ReturnType = Gen._ctype_to_llvm(ReturnTypeInfo) - - if isinstance(ReturnType, ir.VoidType) and FuncName == 'main': - ReturnType = ir.IntType(32) - IsMethod, IsClassMethod, ResolvedClassName = self._resolve_method_class(Node, ClassName, func_meta, FuncName, Gen) - # __new__ methods should return a pointer to the struct (for heap allocation replacement) - if RawFuncName == '__new__' and ResolvedClassName: - StructType = Gen.structs.get(ResolvedClassName) - if StructType: - if isinstance(StructType, ir.PointerType): - ReturnType = StructType - else: - ReturnType = ir.PointerType(StructType) - ParamTypes = [] - ParamNames = [] - ParamTypeStrs = [] - auto_addr_modes_emit: list[str | None] = [] - self.Trans.VarScopes.append({}) - for Arg in Node.args.args: - ParamIsUnsigned = False - if Arg.annotation: - try: - ParamTypeInfo = self.ResolveParamAnnotation(Arg.annotation) - if ParamTypeInfo is None: - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CInt() - IsPtr = ParamTypeInfo.IsPtr - if ParamTypeInfo and getattr(ParamTypeInfo, 'IsCpythonObject', False) and not IsPtr: - IsPtr = True - ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) - # 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), - # 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。 - if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')): - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CChar() - ParamTypeInfo.PtrCount = 1 - IsPtr = True - # C 语言中数组参数退化为指针:t.CArray[type, N] → type* - if ParamTypeInfo.ArrayDims: - ParamTypeInfo.ArrayDims = [] - ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) - ParamType = Gen._ctype_to_llvm(ParamTypeInfo) - # 参数类型不能是 VoidType:C 语言不允许 void 参数, - # VoidType 说明类型注解无法识别(如跨模块 ast.expr),直接报错终止 - if isinstance(ParamType, ir.VoidType): - AnnStr: str = ast.unparse(Arg.annotation) if hasattr(ast, 'unparse') else str(Arg.annotation) - raise SyntaxError( - f"无法识别的参数类型注解: '{AnnStr}'(在函数 '{Node.name}' 的参数 '{Arg.arg}' 上)。" - f"请使用有效的 TPV 类型(如 t.CPtr、ast.AST 或已注册的结构体)。" - ) - if ParamTypeInfo and ParamTypeInfo.IsFuncPtr: - ParamType = ir.IntType(8).as_pointer() - ParamIsUnsigned = ParamTypeInfo.IsUInt - except SyntaxError: - raise - except Exception: # 回退:参数类型解析失败时使用默认 i32 - ParamType = ir.IntType(32) - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CInt() - else: - ParamType = ir.IntType(32) - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CInt() - ParamTypes.append(ParamType) - ParamNames.append(Arg.arg) - ParamTypeStrs.append(ParamTypeInfo) - # 自动取址模式(由注解读出):'ref' = &T、'temp' = &temp T、'always' = t.CAutoPtr - auto_mode_emit: str | None = FindAutoAddrMode(Arg.annotation) - auto_addr_modes_emit.append(auto_mode_emit) - Gen._record_var_signedness(Arg.arg, ParamIsUnsigned) - self.Trans.VarScopes[-1][Arg.arg] = ParamTypeInfo - if Arg.annotation: - try: - PInfo = self.ResolveAnnotationType(Arg.annotation) - # const/volatile 形参登记(统一登记入口)。const 判据沿用形参历史 - # 条件(IsParam=True,见 BaseGen.RegisterVarQualifiers 说明); - # volatile / pointee-volatile 与 TransPyV 侧同口径。 - Gen.RegisterVarQualifiers(Arg.arg, PInfo, Arg.annotation, IsParam=True) - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - if MultiRetTypes: - MultiRetLlvmTypes = [] - for i, ReturnTypeNode in enumerate(MultiRetTypes): - ReturnTypeInfo = CTypeInfo.FromNode(ReturnTypeNode, self.Trans.SymbolTable) - # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 - if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CChar() - ReturnTypeInfo.PtrCount = 1 - if ReturnTypeInfo is None: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - RetType = Gen._ctype_to_llvm(ReturnTypeInfo) - MultiRetLlvmTypes.append(RetType) - ReturnType = ir.LiteralStructType(MultiRetLlvmTypes) - self.Trans.CurrentMultiRetTypes = MultiRetTypes - else: - self.Trans.CurrentMultiRetTypes = None - if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen): - auto_addr_modes_emit.insert(0, None) - IsVariadic = Node.args.vararg is not None - if extra_params: - for var_name, ptr_type in extra_params: - ParamTypes.append(ptr_type) - ParamNames.append(f'__nonlocal_{var_name}__') - HasStopIterFlag = False - if IsMethod and RawFuncName == '__next__': - ParamTypes.append(ir.PointerType(ir.IntType(1))) - ParamNames.append('__stop_iter_flag__') - HasStopIterFlag = True - HasRaise = self._body_contains_raise(Node.body) - if HasRaise and RawFuncName != 'main': - ParamTypes.append(ir.PointerType(ir.PointerType(ir.IntType(8)))) - ParamNames.append('__eh_msg_out__') - ParamTypes.append(ir.PointerType(ir.IntType(32))) - ParamNames.append('__eh_code_out__') - FuncType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) - # 存储 auto_addr_modes(为额外参数补 None) - total_params_emit: int = len(ParamTypes) - while len(auto_addr_modes_emit) < total_params_emit: - auto_addr_modes_emit.append(None) - # 逻辑名(FuncName = 裸名 / "类名.方法")登记:真身定义路径下它是真实发射名的 - # 别名,保留以便按逻辑名查表;真实发射名(func.name)的登记在下方 func 就绪后 - # 进行(查表侧 FuncGen._adjust_args 统一按 func.name 查)。 - Gen._auto_addr_params[FuncName] = auto_addr_modes_emit - self.Trans.FunctionDefCache[FuncName] = Node - IsInlineFunc = False - IsExternFunc = False - IsStateFunc = False - IsUserMarkedExport: bool = False - if Node.returns: - try: - RetTypeInfo = None - RetTypeInfo = self.ResolveAnnotationType(Node.returns) - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CInline): - IsInlineFunc = True - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExport) and not RetTypeInfo.IsState: - Gen._export_funcs.add(FuncName) - IsUserMarkedExport = True - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExtern): - IsExternFunc = True - Gen._export_funcs.add(FuncName) - IsUserMarkedExport = True - if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.IsState: - IsStateFunc = True - Gen._export_funcs.add(FuncName) # 外部 C 函数声明必须保持原始名称 - IsUserMarkedExport = True - except Exception as _e: - if _config_mode == "strict": - self.Trans.LogWarning(f"异常被忽略: {_e}") - # 装饰器形式的存储类标记(@t.CExport / @t.CExtern / @t.CStatic / @t.State / @t.CInline) - deco_flags: dict[str, bool] = self._extract_decorator_storage_flags(Node) - if deco_flags['is_inline']: - IsInlineFunc = True - if deco_flags['is_extern']: - IsExternFunc = True - if deco_flags['is_state']: - IsStateFunc = True - if deco_flags['is_export'] or deco_flags['is_extern'] or deco_flags['is_state']: - Gen._export_funcs.add(FuncName) - IsUserMarkedExport = True - # 注册当前模块定义的函数名,使 _mangle_func_name 跳过 _export_extern_funcs 检查 - Gen._current_module_func_names.add(FuncName) - # 移除导入污染:用户函数未显式标记导出,但名称在 _export_funcs 中(来自导入的同名 C 库函数) - if not IsUserMarkedExport and FuncName in Gen._export_funcs: - Gen._export_funcs.discard(FuncName) - MangledName = Gen._mangle_func_name(FuncName) - # 补登记:module.globals 已有同名函数(stub 声明 / 调用点先建)但未登记到 - # Gen.functions 时,先登记再走下方判定 —— 否则 1750 分支裸建 ir.Function - # 会抛 llvmlite DuplicatedNameError(报文只有名字,不含冲突双方)。 - _g_existing_fn: Any = Gen.module.globals.get(MangledName) - if MangledName not in Gen.functions and isinstance(_g_existing_fn, ir.Function): - Gen.functions[MangledName] = _g_existing_fn - if MangledName in Gen.functions: - func = Gen.functions[MangledName] - if getattr(func, 'name', MangledName) != MangledName: - func = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) - else: - existing_type = getattr(func, 'ftype', None) - if existing_type and existing_type != FuncType: - func = self._replace_function_decl(Gen, FuncName, FuncType) - else: - func = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) - if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: - Gen.functions[FuncName] = func - # 自动取址模式(ref/temp/always)的**主键**必须是真实发射出的函数名 func.name, - # 与查表侧 FuncGen._adjust_args 的 func.name 逐字一致;FuncName(上方已登记)作别名。 - _auto_addr_real_name: str | None = getattr(func, 'name', None) - if _auto_addr_real_name: - Gen._auto_addr_params[_auto_addr_real_name] = auto_addr_modes_emit - func.attributes.add('noredzone') - # 泛型特化类方法(如 GSList[NameEntry].__before_init__):多个模块各自 - # 特化会生成同名定义(external linkage)→ ld.lld duplicate symbol warning。 - # 特化方法代码由相同模板+类型参数生成,定义相同,设 linkonce_odr 让 - # 链接器合并重复定义(linkonce_odr 的 alwaysinline 不适用,仅改 linkage)。 - is_spec_generic_method: bool = '[' in FuncName - if IsInlineFunc: - func.linkage = 'linkonce_odr' - func.attributes.add('alwaysinline') - if is_spec_generic_method: - func.linkage = 'linkonce_odr' - if hasattr(self, '_cdefine_funcs') and FuncName in self._cdefine_funcs: - func.linkage = 'linkonce_odr' - func.attributes.add('alwaysinline') - if fn_attrs: - _LLVM_FN_ATTR_MAP = { - 'noreturn': 'noreturn', - 'always_inline': 'alwaysinline', - 'noinline': 'noinline', - 'cold': 'cold', - 'hot': 'hot', - 'constructor': 'constructor', - 'destructor': 'destructor', - 'malloc': 'malloc', - 'returns_nonnull': 'returns_nonnull', - 'used': 'used', - 'naked': 'naked', - 'no_instrument_function': 'no_instrument_function', - 'warn_unused_result': 'warn_unused_result', - 'fallthrough': 'fallthrough', - 'pure': 'readonly', - 'const': 'readnone', - } - if 'section' in fn_attrs: - func.section = fn_attrs['section'] - if 'visibility' in fn_attrs: - func.linkage = 'internal' - func.attributes.add('visibility') - if fn_attrs.get('weak'): - func.linkage = 'weak' - for attr_name, llvm_name in _LLVM_FN_ATTR_MAP.items(): - if fn_attrs.get(attr_name): - try: - func.attributes.add(llvm_name) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"添加函数属性失败: {_e}", "Exception") - for key in fn_attrs: - if key.startswith('llvm.'): - llvm_attr_name = key[5:] - try: - func.attributes.add(llvm_attr_name) - except Exception as _e: - # llvmlite 不支持某些较新的 LLVM 属性(如 willreturn, mustprogress) - # 记录下来,后续通过 IR 后处理添加 - if not hasattr(Gen, '_pending_str_attrs'): - Gen._pending_str_attrs = {} - Gen._pending_str_attrs.setdefault(MangledName, []).append(llvm_attr_name) - if hasattr(self, '_pending_llvm_attrs') and FuncName in self._pending_llvm_attrs: - for attr_name in self._pending_llvm_attrs.pop(FuncName): - try: - func.attributes.add(attr_name) - except Exception as _e: - # llvmlite 不支持的属性,记录下来后续通过 IR 后处理添加 - if not hasattr(Gen, '_pending_str_attrs'): - Gen._pending_str_attrs = {} - Gen._pending_str_attrs.setdefault(MangledName, []).append(attr_name) - # 记录自定义行为装饰器信息,供 DecoratorPass 使用 - if behavior_decorators: - Gen._decorated_funcs[MangledName] = { - 'decorators': behavior_decorators, - 'func_name': FuncName, - 'func_type': FuncType, - 'return_type': FuncType.return_type, - 'param_types': [p.type for p in func.args], - 'param_names': ParamNames, - 'is_export': FuncName in Gen._export_funcs, - } - if IsExternFunc or IsStateFunc: - return - # 治本修复:如果函数已经有 body(blocks 不为空),跳过重复编译。 - # 否则会导致同一函数被追加第二个 entry block(llvmlite 自动重命名为 entry.1), - # 产生非法 IR:两个 entry block + 第二个 block 无前驱成为死代码。 - # 触发场景:泛型特化路径 _EmitGenericSpecialization 先调用 _EmitFunctionForwardDeclLlvm - # 再调用 _EmitFunctionLlvm,若特化在多个调用点被重复触发(如 list[str] 在 split/upper 等多处使用), - # 同一 MangledName 会进入此函数两次,第二次会重复追加 entry block。 - existing_blocks: list = getattr(func, 'blocks', []) - if existing_blocks: - return func - EntryBlock = func.append_basic_block(name="entry") - Gen.builder = Gen._NewIrBuilder(EntryBlock) - Gen.func = func - # 函数级 + 语句级 DWARF 调试元数据(--dwarf / --asan 自动开启):建 - # DISubprogram 挂 func.metadata["dbg"],并把本 builder 的默认调试位置设为 - # 入口 DILocation(line/column = 该 def 语句的位置)—— 之后本函数体内每条 - # 新建指令自动带 !dbg,逐语句位置由 Gen._set_node_info 覆盖。这样函数体内 - # 所有 call(对同模块有定义的被调函数)都有合法 !dbg,llc 不再报 - # "inlinable function call in a function with debug info must have a !dbg - # location" 而丢弃整份 DI。declare/extern 在此之前已 return,不会被挂 - # !dbg;未启用时 _GetDebugInfo() 返回 None,不产生任何 IR 差异(零影响)。 - _DbgInfo = Gen._GetDebugInfo() - if _DbgInfo is not None: - try: - _DbgInfo.PushFunction( - Gen.builder, func, getattr(Node, 'lineno', 0) or 0, - getattr(Node, 'col_offset', 0) + 1) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"发射调试元数据失败: {_e}", "Exception") - Gen.variables = {} - Gen._reg_values = {} - Gen.global_vars = set() - if 'aligned' in fn_attrs: - Gen._emit_stack_align(fn_attrs['aligned']) - # 保存当前的 var_type_info,以便函数结束时恢复 - saved_var_type_info = Gen.var_type_info.copy() - saved_var_type_assignments = Gen.var_type_assignments.copy() - # 治本修复:保存并清空 _local_heap_ptrs / _var_to_heap_ptr。 - # 当 with 上下文中的代码(如 s1.split(','))触发内联编译 list[str].__new__ 时, - # 若不清空,__new__ 方法会继承外层 with 的 _local_heap_ptrs(含 %call_enter 条目), - # 方法结束时 _emit_local_heap_frees 会尝试 bitcast/free %call_enter, - # 但该值在 __new__ 作用域中不存在 → llc 报错 'use of undefined value'。 - saved_local_heap_ptrs = list(Gen._local_heap_ptrs) - saved_var_to_heap_ptr = dict(Gen._var_to_heap_ptr) - Gen._local_heap_ptrs = [] - Gen._var_to_heap_ptr = {} - # 治本修复:保存 var_struct_class / global_struct_class。 - # 当方法调用(如 a.delete())触发 _try_complete_generic_specialization 时, - # 该路径走 _EmitFunctionLlvm 编译方法体,函数结尾会清空 var_struct_class。 - # 若不保存/恢复,外层函数中已设置的 var_struct_class(如 b='ndarray[double]') - # 会被清空,导致后续方法调用 fallback 匹配 opaque 基础类型(如 ndarray), - # 生成对 stub declare(无方法体)的调用 → 链接报 undefined reference。 - saved_var_struct_class = dict(Gen.var_struct_class) if Gen.var_struct_class else {} - saved_global_struct_class = dict(Gen.global_struct_class) if getattr(Gen, 'global_struct_class', None) else {} - # 函数内部定义的变量会在赋值时添加到 var_type_info 中 - # 这样函数内部定义的元类型变量(如 a = t.CInt32T)就能被正确处理 - for stmt in Node.body: - if isinstance(stmt, ast.Global): - self.Trans.ForHandler._RegisterGlobalNames(stmt.names, Gen) - for param, param_name in zip(func.args, ParamNames): - param.name = param_name - if param_name == '__stop_iter_flag__': - Gen._stop_iter_flag_param = param - Gen.builder.store(ir.Constant(ir.IntType(1), 0), param) - elif param_name.startswith('__nonlocal_') and param_name.endswith('__'): - var_name = param_name[len('__nonlocal_'):-2] - Gen.variables[var_name] = param - else: - if isinstance(param.type, ir.PointerType) and isinstance(param.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): - var = Gen.builder.alloca(param.type, name=param_name) - Gen._store(param, var) - Gen.variables[param_name] = var - for CN, ST in Gen.structs.items(): - if param.type.pointee == ST or (isinstance(param.type.pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and param.type.pointee.name == ST.name): - Gen.var_struct_class[param_name] = CN - break - elif isinstance(param.type, (ir.LiteralStructType, ir.IdentifiedStructType)): - var = Gen.builder.alloca(param.type, name=param_name) - Gen._store(param, var) - Gen.variables[param_name] = var - for CN, ST in Gen.structs.items(): - if param.type == ST or (isinstance(param.type, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and param.type.name == ST.name): - Gen.var_struct_class[param_name] = CN - break - else: - # 所有参数都存入 alloca,确保循环体中对参数的修改能正确反映 - var = Gen._allocaEntry(param.type, name=param_name) - Gen._store(param, var) - Gen.variables[param_name] = var - # 为联合类型注解(如 Function | t.CPtr)的参数设置 var_struct_class, - # 确保编译器能正确生成 GEP 访问结构体成员(如 func.Blocks、func.Params) - for i, arg in enumerate(Node.args.args): - if arg.annotation and isinstance(arg.annotation, ast.BinOp) and isinstance(arg.annotation.op, ast.BitOr): - param_name: str = ParamNames[i] if i < len(ParamNames) else arg.arg - if param_name in Gen.variables: - # 若 L1650-1657 已根据 param.type.pointee 匹配到正确的特化名 - # (如 ndarray[double]),不覆盖:FindStructNameInAnnotation 对 - # 泛型注解 ndarray[t.CDouble] 会构造 ndarray[CDouble](与实际 - # ndarray[double] 不匹配),回退到裸模板类名 ndarray(opaque), - # 导致方法调用查找 ndarray.sum 而非 ndarray[double].sum。 - if param_name in Gen.var_struct_class: - _existing_ps: str = Gen.var_struct_class[param_name] - # 指针占位名(rawptr / {sha1}.rawptr / rawptr[...])是模块限定 - # struct 联合(llvmlite.Function | t.CPtr)被 resolver 退化 - # 的产物,必须允许 FindStructNameInAnnotation 短名修正 - # (否则 func.IsVarArg → "属性在类 'rawptr' 中找不到")。 - if not (_existing_ps == 'rawptr' or '.rawptr' in _existing_ps - or _existing_ps.startswith('rawptr[')): - continue + or (ReturnTypeInfo.PtrCount >= 2 and ReturnTypeInfo.BaseType is None)): + def _FindNonVoidCTypeInfo(node): + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + results = [] + for child in [node.left, node.right]: + r = _FindNonVoidCTypeInfo(child) + if r and not r.IsVoid: + results.append(r) + for r in results: + if not r.IsPtr and not r.IsStruct: + return r + return results[0] if results else None + try: + SideInfo = CTypeInfo.FromNode(node, self.Trans.SymbolTable) + if SideInfo: + return SideInfo + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + return None + found = _FindNonVoidCTypeInfo(Node.returns) + if found and not found.IsVoid: + ReturnTypeInfo = found + if ReturnTypeInfo.IsVoid and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + # define 宏函数(返回注解含 t.CDefine,如 `int | t.CDefine`): + # 标记为编译期内联宏(alwaysinline),对齐裸 `-> t.CDefine` 路径 + if AnnotationContainsName(Node.returns, 'CDefine'): + if not hasattr(self, '_cdefine_funcs'): + self._cdefine_funcs = set() + self._cdefine_funcs.add(FuncName) + else: + is_str_type = False + if isinstance(Node.returns, ast.Name) and Node.returns.id in ('str', 'bytes'): + is_str_type = True + # 与 _GetFunctionSignatureLlvm 同口径:指针下标返回注解 + # (rawptr[T] / t.CPtr[T])走 ResolveAnnotationType 的 + # TypeMerge 通用解析,避免内层跨模块类退化为 i8*。 + ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) + if ReturnTypeInfo and ReturnTypeInfo.IsDefine: + inferred = self._infer_return_type_from_body(Node, Gen, ClassName) + if inferred: + ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) + ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 + else: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + if not hasattr(self, '_cdefine_funcs'): + self._cdefine_funcs = set() + self._cdefine_funcs.add(FuncName) + elif ReturnTypeInfo and ReturnTypeInfo.IsState: + pass # t.State is a modifier, preserve the actual return type + if ReturnTypeInfo is None: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CVoid() + # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 + if is_str_type or (ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1): + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CChar() + ReturnTypeInfo.PtrCount = 1 + # 检查泛型类返回类型 (如 list[str]) + # 注意:_from_node_subscript 对未识别的泛型下标(如 list[str])返回空 CTypeInfo(BaseType=None), + # 此时 IsVoid 为 False(因为 IsVoid 仅检查 t.CVoid 实例),需额外检测 BaseType is None + if (isinstance(Node.returns, ast.Subscript) + and isinstance(Node.returns.value, ast.Name) + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and Node.returns.value.id in self.Trans.ClassHandler._generic_class_templates + and (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0): + ret_class: str = Node.returns.value.id + ret_slice: ast.AST = Node.returns.slice + ret_type_args: list[str] = [] + ret_type_names: list[str] = [] + if isinstance(ret_slice, ast.Tuple): + for elt in ret_slice.elts: + if isinstance(elt, ast.Name): + tn2: str = elt.id + ts2: str = tn2 + if tn2 == 'int': + ts2, tn2 = 'int', 'CInt' + ret_type_args.append(ts2) + ret_type_names.append(tn2) + elif isinstance(ret_slice, ast.Name): + tn2: str = ret_slice.id + ts2: str = tn2 + if tn2 == 'int': + ts2, tn2 = 'int', 'CInt' + ret_type_args.append(ts2) + ret_type_names.append(tn2) + # 返回类型解析只需 struct 定义,不需方法体;使用 declare_only=True + # 避免过早触发完整特化(此时 MemManager.alloc 可能尚未注册, + # 导致 __new__ 的 pool.alloc(48) 解析失败,IR 块未终止)。 + spec_name2: str | None = self.Trans.ClassHandler._specialize_generic_class( + ret_class, ret_type_args, Gen, type_names=ret_type_names, declare_only=True) + if spec_name2 and spec_name2 in Gen.structs: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CStruct(name=spec_name2) + ReturnTypeInfo.PtrCount = 1 + if (ReturnTypeInfo.IsVoid or ReturnTypeInfo.BaseType is None) and ReturnTypeInfo.PtrCount == 0 and not ReturnTypeInfo.IsState: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + except Exception: # 回退:设置默认返回类型为 CInt + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + else: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + inferred = self._infer_return_type_from_body(Node, Gen, ClassName) + if inferred: + ReturnTypeInfo = CTypeInfo.FromTypeName(inferred[0]) + ReturnTypeInfo.PtrCount = 1 if inferred[1] else 0 + + ReturnType = Gen._ctype_to_llvm(ReturnTypeInfo) + + if isinstance(ReturnType, ir.VoidType) and FuncName == 'main': + ReturnType = ir.IntType(32) + IsMethod, IsClassMethod, ResolvedClassName = self._resolve_method_class(Node, ClassName, func_meta, FuncName, Gen) + # __new__ methods should return a pointer to the struct (for heap allocation replacement) + if RawFuncName == '__new__' and ResolvedClassName: + StructType = Gen.structs.get(ResolvedClassName) + if StructType: + if isinstance(StructType, ir.PointerType): + ReturnType = StructType + else: + ReturnType = ir.PointerType(StructType) + ParamTypes = [] + ParamNames = [] + ParamTypeStrs = [] + auto_addr_modes_emit: list[str | None] = [] + self.Trans.VarScopes.append({}) + for Arg in Node.args.args: + ParamIsUnsigned = False + if Arg.annotation: + try: + ParamTypeInfo = self.ResolveParamAnnotation(Arg.annotation) + if ParamTypeInfo is None: + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CInt() + IsPtr = ParamTypeInfo.IsPtr + if ParamTypeInfo and getattr(ParamTypeInfo, 'IsCpythonObject', False) and not IsPtr: + IsPtr = True + ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) + # 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), + # 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。 + if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')): + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CChar() + ParamTypeInfo.PtrCount = 1 + IsPtr = True + # C 语言中数组参数退化为指针:t.CArray[type, N] → type* + if ParamTypeInfo.ArrayDims: + ParamTypeInfo.ArrayDims = [] + ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) + ParamType = Gen._ctype_to_llvm(ParamTypeInfo) + # 参数类型不能是 VoidType:C 语言不允许 void 参数, + # VoidType 说明类型注解无法识别(如跨模块 ast.expr),直接报错终止 + if isinstance(ParamType, ir.VoidType): + AnnStr: str = ast.unparse(Arg.annotation) if hasattr(ast, 'unparse') else str(Arg.annotation) + raise SyntaxError( + f"无法识别的参数类型注解: '{AnnStr}'(在函数 '{Node.name}' 的参数 '{Arg.arg}' 上)。" + f"请使用有效的 VPC 类型(如 t.CPtr、ast.AST 或已注册的结构体)。" + ) + if ParamTypeInfo and ParamTypeInfo.IsFuncPtr: + ParamType = ir.IntType(8).as_pointer() + ParamIsUnsigned = ParamTypeInfo.IsUInt + except SyntaxError: + raise + except Exception: # 回退:参数类型解析失败时使用默认 i32 + ParamType = ir.IntType(32) + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CInt() + else: + ParamType = ir.IntType(32) + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CInt() + ParamTypes.append(ParamType) + ParamNames.append(Arg.arg) + ParamTypeStrs.append(ParamTypeInfo) + # 自动取址模式(由注解读出):'ref' = &T、'temp' = &temp T、'always' = t.CAutoPtr + auto_mode_emit: str | None = FindAutoAddrMode(Arg.annotation) + auto_addr_modes_emit.append(auto_mode_emit) + Gen._record_var_signedness(Arg.arg, ParamIsUnsigned) + self.Trans.VarScopes[-1][Arg.arg] = ParamTypeInfo + if Arg.annotation: + try: + PInfo = self.ResolveAnnotationType(Arg.annotation) + # const/volatile 形参登记(统一登记入口)。const 判据沿用形参历史 + # 条件(IsParam=True,见 BaseGen.RegisterVarQualifiers 说明); + # volatile / pointee-volatile 与 ViperC 侧同口径。 + Gen.RegisterVarQualifiers(Arg.arg, PInfo, Arg.annotation, IsParam=True) + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + if MultiRetTypes: + MultiRetLlvmTypes = [] + for i, ReturnTypeNode in enumerate(MultiRetTypes): + ReturnTypeInfo = CTypeInfo.FromNode(ReturnTypeNode, self.Trans.SymbolTable) + # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 + if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CChar() + ReturnTypeInfo.PtrCount = 1 + if ReturnTypeInfo is None: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + RetType = Gen._ctype_to_llvm(ReturnTypeInfo) + MultiRetLlvmTypes.append(RetType) + ReturnType = ir.LiteralStructType(MultiRetLlvmTypes) + self.Trans.CurrentMultiRetTypes = MultiRetTypes + else: + self.Trans.CurrentMultiRetTypes = None + if self._apply_struct_self_cls_types(ParamTypes, ParamNames, IsMethod, IsClassMethod, ResolvedClassName, Gen): + auto_addr_modes_emit.insert(0, None) + IsVariadic = Node.args.vararg is not None + if extra_params: + for var_name, ptr_type in extra_params: + ParamTypes.append(ptr_type) + ParamNames.append(f'__nonlocal_{var_name}__') + HasStopIterFlag = False + if IsMethod and RawFuncName == '__next__': + ParamTypes.append(ir.PointerType(ir.IntType(1))) + ParamNames.append('__stop_iter_flag__') + HasStopIterFlag = True + HasRaise = self._body_contains_raise(Node.body) + if HasRaise and RawFuncName != 'main': + ParamTypes.append(ir.PointerType(ir.PointerType(ir.IntType(8)))) + ParamNames.append('__eh_msg_out__') + ParamTypes.append(ir.PointerType(ir.IntType(32))) + ParamNames.append('__eh_code_out__') + FuncType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) + # 存储 auto_addr_modes(为额外参数补 None) + total_params_emit: int = len(ParamTypes) + while len(auto_addr_modes_emit) < total_params_emit: + auto_addr_modes_emit.append(None) + # 逻辑名(FuncName = 裸名 / "类名.方法")登记:真身定义路径下它是真实发射名的 + # 别名,保留以便按逻辑名查表;真实发射名(func.name)的登记在下方 func 就绪后 + # 进行(查表侧 FuncGen._adjust_args 统一按 func.name 查)。 + Gen._auto_addr_params[FuncName] = auto_addr_modes_emit + self.Trans.FunctionDefCache[FuncName] = Node + IsInlineFunc = False + IsExternFunc = False + IsStateFunc = False + IsUserMarkedExport: bool = False + if Node.returns: + try: + RetTypeInfo = None + RetTypeInfo = self.ResolveAnnotationType(Node.returns) + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CInline): + IsInlineFunc = True + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExport) and not RetTypeInfo.IsState: + Gen._export_funcs.add(FuncName) + IsUserMarkedExport = True + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.Storage and isinstance(RetTypeInfo.Storage, t.CExtern): + IsExternFunc = True + Gen._export_funcs.add(FuncName) + IsUserMarkedExport = True + if isinstance(RetTypeInfo, CTypeInfo) and RetTypeInfo.IsState: + IsStateFunc = True + Gen._export_funcs.add(FuncName) # 外部 C 函数声明必须保持原始名称 + IsUserMarkedExport = True + except Exception as _e: + if _config_mode == "strict": + self.Trans.LogWarning(f"异常被忽略: {_e}") + # 装饰器形式的存储类标记(@t.CExport / @t.CExtern / @t.CStatic / @t.State / @t.CInline) + deco_flags: dict[str, bool] = self._extract_decorator_storage_flags(Node) + if deco_flags['is_inline']: + IsInlineFunc = True + if deco_flags['is_extern']: + IsExternFunc = True + if deco_flags['is_state']: + IsStateFunc = True + if deco_flags['is_export'] or deco_flags['is_extern'] or deco_flags['is_state']: + Gen._export_funcs.add(FuncName) + IsUserMarkedExport = True + # 注册当前模块定义的函数名,使 _mangle_func_name 跳过 _export_extern_funcs 检查 + Gen._current_module_func_names.add(FuncName) + # 移除导入污染:用户函数未显式标记导出,但名称在 _export_funcs 中(来自导入的同名 C 库函数) + if not IsUserMarkedExport and FuncName in Gen._export_funcs: + Gen._export_funcs.discard(FuncName) + MangledName = Gen._mangle_func_name(FuncName) + # 补登记:module.globals 已有同名函数(stub 声明 / 调用点先建)但未登记到 + # Gen.functions 时,先登记再走下方判定 —— 否则 1750 分支裸建 ir.Function + # 会抛 llvmlite DuplicatedNameError(报文只有名字,不含冲突双方)。 + _g_existing_fn: Any = Gen.module.globals.get(MangledName) + if MangledName not in Gen.functions and isinstance(_g_existing_fn, ir.Function): + Gen.functions[MangledName] = _g_existing_fn + if MangledName in Gen.functions: + func = Gen.functions[MangledName] + if getattr(func, 'name', MangledName) != MangledName: + func = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) + else: + existing_type = getattr(func, 'ftype', None) + if existing_type and existing_type != FuncType: + func = self._replace_function_decl(Gen, FuncName, FuncType) + else: + func = Gen._CreateOrReuseFuncDecl(MangledName, FuncType) + if MangledName == FuncName or FuncName not in Gen._export_extern_funcs: + Gen.functions[FuncName] = func + # 自动取址模式(ref/temp/always)的**主键**必须是真实发射出的函数名 func.name, + # 与查表侧 FuncGen._adjust_args 的 func.name 逐字一致;FuncName(上方已登记)作别名。 + _auto_addr_real_name: str | None = getattr(func, 'name', None) + if _auto_addr_real_name: + Gen._auto_addr_params[_auto_addr_real_name] = auto_addr_modes_emit + func.attributes.add('noredzone') + # 泛型特化类方法(如 GSList[NameEntry].__before_init__):多个模块各自 + # 特化会生成同名定义(external linkage)→ ld.lld duplicate symbol warning。 + # 特化方法代码由相同模板+类型参数生成,定义相同,设 linkonce_odr 让 + # 链接器合并重复定义(linkonce_odr 的 alwaysinline 不适用,仅改 linkage)。 + is_spec_generic_method: bool = '[' in FuncName + if IsInlineFunc: + func.linkage = 'linkonce_odr' + func.attributes.add('alwaysinline') + if is_spec_generic_method: + func.linkage = 'linkonce_odr' + if hasattr(self, '_cdefine_funcs') and FuncName in self._cdefine_funcs: + func.linkage = 'linkonce_odr' + func.attributes.add('alwaysinline') + if fn_attrs: + _LLVM_FN_ATTR_MAP = { + 'noreturn': 'noreturn', + 'always_inline': 'alwaysinline', + 'noinline': 'noinline', + 'cold': 'cold', + 'hot': 'hot', + 'constructor': 'constructor', + 'destructor': 'destructor', + 'malloc': 'malloc', + 'returns_nonnull': 'returns_nonnull', + 'used': 'used', + 'naked': 'naked', + 'no_instrument_function': 'no_instrument_function', + 'warn_unused_result': 'warn_unused_result', + 'fallthrough': 'fallthrough', + 'pure': 'readonly', + 'const': 'readnone', + } + if 'section' in fn_attrs: + func.section = fn_attrs['section'] + if 'visibility' in fn_attrs: + func.linkage = 'internal' + func.attributes.add('visibility') + if fn_attrs.get('weak'): + func.linkage = 'weak' + for attr_name, llvm_name in _LLVM_FN_ATTR_MAP.items(): + if fn_attrs.get(attr_name): + try: + func.attributes.add(llvm_name) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"添加函数属性失败: {_e}", "Exception") + for key in fn_attrs: + if key.startswith('llvm.'): + llvm_attr_name = key[5:] + try: + func.attributes.add(llvm_attr_name) + except Exception as _e: + # llvmlite 不支持某些较新的 LLVM 属性(如 willreturn, mustprogress) + # 记录下来,后续通过 IR 后处理添加 + if not hasattr(Gen, '_pending_str_attrs'): + Gen._pending_str_attrs = {} + Gen._pending_str_attrs.setdefault(MangledName, []).append(llvm_attr_name) + if hasattr(self, '_pending_llvm_attrs') and FuncName in self._pending_llvm_attrs: + for attr_name in self._pending_llvm_attrs.pop(FuncName): + try: + func.attributes.add(attr_name) + except Exception as _e: + # llvmlite 不支持的属性,记录下来后续通过 IR 后处理添加 + if not hasattr(Gen, '_pending_str_attrs'): + Gen._pending_str_attrs = {} + Gen._pending_str_attrs.setdefault(MangledName, []).append(attr_name) + # 记录自定义行为装饰器信息,供 DecoratorPass 使用 + if behavior_decorators: + Gen._decorated_funcs[MangledName] = { + 'decorators': behavior_decorators, + 'func_name': FuncName, + 'func_type': FuncType, + 'return_type': FuncType.return_type, + 'param_types': [p.type for p in func.args], + 'param_names': ParamNames, + 'is_export': FuncName in Gen._export_funcs, + } + if IsExternFunc or IsStateFunc: + return + # 治本修复:如果函数已经有 body(blocks 不为空),跳过重复编译。 + # 否则会导致同一函数被追加第二个 entry block(llvmlite 自动重命名为 entry.1), + # 产生非法 IR:两个 entry block + 第二个 block 无前驱成为死代码。 + # 触发场景:泛型特化路径 _EmitGenericSpecialization 先调用 _EmitFunctionForwardDeclLlvm + # 再调用 _EmitFunctionLlvm,若特化在多个调用点被重复触发(如 list[str] 在 split/upper 等多处使用), + # 同一 MangledName 会进入此函数两次,第二次会重复追加 entry block。 + existing_blocks: list = getattr(func, 'blocks', []) + if existing_blocks: + return func + EntryBlock = func.append_basic_block(name="entry") + Gen.builder = Gen._NewIrBuilder(EntryBlock) + Gen.func = func + # 函数级 + 语句级 DWARF 调试元数据(--dwarf / --asan 自动开启):建 + # DISubprogram 挂 func.metadata["dbg"],并把本 builder 的默认调试位置设为 + # 入口 DILocation(line/column = 该 def 语句的位置)—— 之后本函数体内每条 + # 新建指令自动带 !dbg,逐语句位置由 Gen._set_node_info 覆盖。这样函数体内 + # 所有 call(对同模块有定义的被调函数)都有合法 !dbg,llc 不再报 + # "inlinable function call in a function with debug info must have a !dbg + # location" 而丢弃整份 DI。declare/extern 在此之前已 return,不会被挂 + # !dbg;未启用时 _GetDebugInfo() 返回 None,不产生任何 IR 差异(零影响)。 + _DbgInfo = Gen._GetDebugInfo() + if _DbgInfo is not None: + try: + _DbgInfo.PushFunction( + Gen.builder, func, getattr(Node, 'lineno', 0) or 0, + getattr(Node, 'col_offset', 0) + 1) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"发射调试元数据失败: {_e}", "Exception") + Gen.variables = {} + Gen._reg_values = {} + Gen.global_vars = set() + if 'aligned' in fn_attrs: + Gen._emit_stack_align(fn_attrs['aligned']) + # 保存当前的 var_type_info,以便函数结束时恢复 + saved_var_type_info = Gen.var_type_info.copy() + saved_var_type_assignments = Gen.var_type_assignments.copy() + # 治本修复:保存并清空 _local_heap_ptrs / _var_to_heap_ptr。 + # 当 with 上下文中的代码(如 s1.split(','))触发内联编译 list[str].__new__ 时, + # 若不清空,__new__ 方法会继承外层 with 的 _local_heap_ptrs(含 %call_enter 条目), + # 方法结束时 _emit_local_heap_frees 会尝试 bitcast/free %call_enter, + # 但该值在 __new__ 作用域中不存在 → llc 报错 'use of undefined value'。 + saved_local_heap_ptrs = list(Gen._local_heap_ptrs) + saved_var_to_heap_ptr = dict(Gen._var_to_heap_ptr) + Gen._local_heap_ptrs = [] + Gen._var_to_heap_ptr = {} + # 治本修复:保存 var_struct_class / global_struct_class。 + # 当方法调用(如 a.delete())触发 _try_complete_generic_specialization 时, + # 该路径走 _EmitFunctionLlvm 编译方法体,函数结尾会清空 var_struct_class。 + # 若不保存/恢复,外层函数中已设置的 var_struct_class(如 b='ndarray[double]') + # 会被清空,导致后续方法调用 fallback 匹配 opaque 基础类型(如 ndarray), + # 生成对 stub declare(无方法体)的调用 → 链接报 undefined reference。 + saved_var_struct_class = dict(Gen.var_struct_class) if Gen.var_struct_class else {} + saved_global_struct_class = dict(Gen.global_struct_class) if getattr(Gen, 'global_struct_class', None) else {} + # 函数内部定义的变量会在赋值时添加到 var_type_info 中 + # 这样函数内部定义的元类型变量(如 a = t.CInt32T)就能被正确处理 + for stmt in Node.body: + if isinstance(stmt, ast.Global): + self.Trans.ForHandler._RegisterGlobalNames(stmt.names, Gen) + for param, param_name in zip(func.args, ParamNames): + param.name = param_name + if param_name == '__stop_iter_flag__': + Gen._stop_iter_flag_param = param + Gen.builder.store(ir.Constant(ir.IntType(1), 0), param) + elif param_name.startswith('__nonlocal_') and param_name.endswith('__'): + var_name = param_name[len('__nonlocal_'):-2] + Gen.variables[var_name] = param + else: + if isinstance(param.type, ir.PointerType) and isinstance(param.type.pointee, (ir.LiteralStructType, ir.IdentifiedStructType)): + var = Gen.builder.alloca(param.type, name=param_name) + Gen._store(param, var) + Gen.variables[param_name] = var + for CN, ST in Gen.structs.items(): + if param.type.pointee == ST or (isinstance(param.type.pointee, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and param.type.pointee.name == ST.name): + Gen.var_struct_class[param_name] = CN + break + elif isinstance(param.type, (ir.LiteralStructType, ir.IdentifiedStructType)): + var = Gen.builder.alloca(param.type, name=param_name) + Gen._store(param, var) + Gen.variables[param_name] = var + for CN, ST in Gen.structs.items(): + if param.type == ST or (isinstance(param.type, ir.IdentifiedStructType) and isinstance(ST, ir.IdentifiedStructType) and param.type.name == ST.name): + Gen.var_struct_class[param_name] = CN + break + else: + # 所有参数都存入 alloca,确保循环体中对参数的修改能正确反映 + var = Gen._allocaEntry(param.type, name=param_name) + Gen._store(param, var) + Gen.variables[param_name] = var + # 为联合类型注解(如 Function | t.CPtr)的参数设置 var_struct_class, + # 确保编译器能正确生成 GEP 访问结构体成员(如 func.Blocks、func.Params) + for i, arg in enumerate(Node.args.args): + if arg.annotation and isinstance(arg.annotation, ast.BinOp) and isinstance(arg.annotation.op, ast.BitOr): + param_name: str = ParamNames[i] if i < len(ParamNames) else arg.arg + if param_name in Gen.variables: + # 若 L1650-1657 已根据 param.type.pointee 匹配到正确的特化名 + # (如 ndarray[double]),不覆盖:FindStructNameInAnnotation 对 + # 泛型注解 ndarray[t.CDouble] 会构造 ndarray[CDouble](与实际 + # ndarray[double] 不匹配),回退到裸模板类名 ndarray(opaque), + # 导致方法调用查找 ndarray.sum 而非 ndarray[double].sum。 + if param_name in Gen.var_struct_class: + _existing_ps: str = Gen.var_struct_class[param_name] + # 指针占位名(rawptr / {sha1}.rawptr / rawptr[...])是模块限定 + # struct 联合(llvmlite.Function | t.CPtr)被 resolver 退化 + # 的产物,必须允许 FindStructNameInAnnotation 短名修正 + # (否则 func.IsVarArg → "属性在类 'rawptr' 中找不到")。 + if not (_existing_ps == 'rawptr' or '.rawptr' in _existing_ps + or _existing_ps.startswith('rawptr[')): + continue found_struct: str | None = FindStructNameInAnnotationWithContext( - arg.annotation, self.Trans, Gen) - if found_struct: - Gen.var_struct_class[param_name] = found_struct - # 普通类注解形参(非 BinOp 联合):rawptr[X] / t.CPtr[X] / X / t.X - # 一律按"声明类"登记 var_struct_class。避免按 param.type.pointee 匹配 - # 到泛型父特化(如 class Function(GSListNode[Function]) 的形参 func 被 - # 登记成 GSListNode[Function],其 struct 仅含 Next → func.Blocks 访问 - # fast-fail "属性 'Blocks' 在类 'GSListNode[Function]' 中找不到")。 - # 泛型占位(rawptr[T])的 T 不在 Gen.structs,自动跳过、保留 pointee 匹配。 - for i, arg in enumerate(Node.args.args): - if arg.annotation is None: - continue - if isinstance(arg.annotation, ast.BinOp) and isinstance(arg.annotation.op, ast.BitOr): - continue # 联合注解由上方循环处理 - param_name = ParamNames[i] if i < len(ParamNames) else arg.arg - if param_name not in Gen.variables: - continue - ann = arg.annotation - target: str | None = None - if isinstance(ann, ast.Name): - target = ann.id - elif isinstance(ann, ast.Attribute): - target = ann.attr - elif isinstance(ann, ast.Subscript): - subv = ann.value - subname = getattr(subv, 'id', None) or getattr(subv, 'attr', '') - if subname in ('rawptr', 'CPtr', 'Ptr'): - slc = ann.slice - if isinstance(slc, ast.Name): - target = slc.id - elif isinstance(slc, ast.Attribute): - target = slc.attr - if not target: - continue - key: str | None = target if target in Gen.structs else None - if key is None: - _sha_t: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(target) - if _sha_t and f'{_sha_t}.{target}' in Gen.structs: - key = f'{_sha_t}.{target}' - if key: - Gen.var_struct_class[param_name] = key - if ResolvedClassName and (IsMethod or RawFuncName == '__init__'): - self.Trans._CurrentCpythonObjectClass = ResolvedClassName - Gen._variadic_info = None - if IsVariadic and Node.args.vararg: - vararg_name = getattr(Node.args.vararg, 'arg', 'args') - last_param_name = ParamNames[-1] if ParamNames else None - last_param_ptr = None - for param, pn in zip(func.args, ParamNames): - if pn == last_param_name: - last_param_ptr = param - break - triple = getattr(Gen, 'module_triple', '') or getattr(Gen.module, 'triple', '') - is_windows = 'windows' in triple if triple else False - ptr_size = getattr(Gen, 'ptr_size', 8) - # va_list 大小取决于 ABI:Windows x64 = __va_list_tag(24字节),Linux x64 = __va_list_tag[1](24字节) - # 注意:x86_64-pc-windows-gnu 的 llvm.va_start 写的是 24 字节的 struct __va_list_tag - # ({i32 gp_offset, i32 fp_offset, i8* overflow_arg_area, i8* reg_save_area}), - # 若只分配 8 字节会向调用者栈帧越界写 16 字节,导致随机栈破坏(历史崩溃根因)。 - if ptr_size == 4: - va_list_size = 4 if is_windows else 12 - va_list_align = 4 - else: - va_list_size = 24 - va_list_align = 16 - va_list_type = ir.ArrayType(ir.IntType(8), va_list_size) - va_list_alloc = Gen._allocaEntry(va_list_type, name=f"{vararg_name}_va_list", align=va_list_align) - va_list_ptr = Gen.builder.bitcast(va_list_alloc, ir.IntType(8).as_pointer(), name=f"{vararg_name}_va_list_i8ptr") - Gen._variadic_info = { - 'vararg_name': vararg_name, - 'va_list_ptr': va_list_ptr, - 'last_param_ptr': last_param_ptr, - 'va_start_called': False, - } - if last_param_ptr: - last_param_alloc = Gen._allocaEntry(last_param_ptr.type, name="last_param_copy") - Gen.builder.store(last_param_ptr, last_param_alloc) - Gen.emit_va_start(va_list_ptr) - Gen._variadic_info['va_start_called'] = True - # 用 _direct_values 注册,Name 解析时直接返回 va_list 指针(i8*), - # 不走 variables 的 load 路径(否则会把 i8* 当 char* 加载一个字节) - Gen._direct_values[vararg_name] = va_list_ptr - # @t.TLS 一次性初始化检查:首次调用执行函数体,后续调用直接返回 - if is_tls_func: - flag_name: str = f"__tls_init_done_{FuncName}" - flag_global = None - for gv in Gen.module.global_values: - if gv.name == flag_name: - flag_global = gv - break - if flag_global is None: - flag_global = ir.GlobalVariable(Gen.module, ir.IntType(1), name=flag_name) - flag_global.initializer = ir.Constant(ir.IntType(1), 0) - flag_global.linkage = 'internal' - flag_val = Gen.builder.load(flag_global, name="tls_flag_check") - skip_bb = func.append_basic_block("tls_skip") - cont_bb = func.append_basic_block("tls_continue") - Gen.builder.cbranch(flag_val, skip_bb, cont_bb) - Gen.builder.position_at_end(skip_bb) - ActualReturnType = func.function_type.return_type - if isinstance(ActualReturnType, ir.VoidType): - Gen.builder.ret_void() - elif isinstance(ActualReturnType, ir.IntType): - Gen.builder.ret(ir.Constant(ActualReturnType, 0)) - elif isinstance(ActualReturnType, (ir.FloatType, ir.DoubleType)): - Gen.builder.ret(ir.Constant(ActualReturnType, 0.0)) - elif isinstance(ActualReturnType, ir.PointerType): - Gen.builder.ret(ir.Constant(ActualReturnType, None)) - else: - Gen.builder.ret(ir.Constant(ActualReturnType, None)) - Gen.builder.position_at_end(cont_bb) - Gen.builder.store(ir.Constant(ir.IntType(1), 1), flag_global) - _saved_class_name = getattr(Gen, '_current_class_name', None) - Gen._current_class_name = ClassName - # 提取首个裸字符串字面量作为 __doc__ docstring,并跳过该节点避免生成死代码 - func_body: list[ast.stmt] = Node.body - doc_symbol: str = f"{ClassName}.{Node.name}" if ClassName else Node.name - if (func_body and isinstance(func_body[0], ast.Expr) - and isinstance(func_body[0].value, ast.Constant) - and isinstance(func_body[0].value.value, str)): - Gen._create_doc_global(doc_symbol, func_body[0].value.value) - func_body = func_body[1:] - else: - Gen._create_doc_global(doc_symbol, None) - try: - self.Trans.BodyHandler.HandleBodyLlvm(func_body) - except Exception as _body_exc: - # 方法体编译失败:确保 block 终止(防止 expected instruction opcode 错误), - # 然后重新抛出异常让编译立即终止(不吞掉错误) - _fn_name: str = Node.name - _cls_name: str = ClassName if ClassName else '' - import sys as _sys - import traceback as _tb - print(f"[FATAL] 方法体编译失败 ({_cls_name}.{_fn_name}): {type(_body_exc).__name__}: {_body_exc}", file=_sys.stderr) - _tb.print_exc(file=_sys.stderr) - if Gen.builder is not None: - try: - if not Gen.builder.block.is_terminated: - _ret_ty = func.function_type.return_type - if isinstance(_ret_ty, ir.VoidType): - Gen.builder.ret_void() - elif isinstance(_ret_ty, ir.PointerType): - Gen.builder.ret(ir.Constant(_ret_ty, None)) - elif isinstance(_ret_ty, ir.IntType): - Gen.builder.ret(ir.Constant(_ret_ty, 0)) - else: - Gen.builder.ret(ir.Constant(_ret_ty, None)) - except Exception: - try: - if Gen.builder and not Gen.builder.block.is_terminated: - Gen.builder.ret_void() - except Exception: - pass - # 失败路径也要复位「当前方法」上下文(成功路径的复位在函数末尾,异常 - # 直接 re-raise 会跳过它)。上层有吞掉异常的调用点(如泛型特化按需补全 - # _try_complete_generic_specialization、类按需实例化),它们让外层方法 - # 继续编译;若此时 _CurrentCpythonObjectClass / var_struct_class 仍指向 - # 内层特化类,外层方法的 `self.X` 会被按内层类解析(典型现象: - # ExprGenFstr.translate_joined_str 的 `self.Gen` 被按 - # `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到")。 - Gen._current_class_name = _saved_class_name - self.Trans._CurrentCpythonObjectClass = saved_cpython_class - Gen.var_struct_class = saved_var_struct_class - if hasattr(Gen, 'global_struct_class'): - Gen.global_struct_class = saved_global_struct_class - raise - if not Gen.builder.block.is_terminated: - if 'naked' in fn_attrs: - Gen.builder.unreachable() - self.Trans._CurrentCpythonObjectClass = saved_cpython_class - return - # fail-fast:有非 void 返回类型注解但函数体掉出末尾(无显式 return 覆盖) - # → 立即报错,不静默 ret 0(用户隐式返回规则)。 - # 判据按**源码层面**函数体末句判断(不依赖 builder 状态——翻译后未 - # terminated 可能由其它翻译问题导致,如 HandleExprLlvm 返回 None 静默 - # 跳过 ret,不该归类为"掉出末尾"缺陷): - # - 末句是显式 return → 有覆盖,正常 - # - 末句是控制流(If/Match/While/For/Try/With)→ 掉出合法(隐式返回 - # 规则:末句控制流 → unreachable;match/if 合成 after 块机械掉出, - # 所有 arm 已 terminated 属正常) - # - 末句是普通语句 → 真掉出末尾;-> None / -> t.CVoid 视为 Void 合法, - # 无注解 → C 风格过程(ConstZero,保持现状);其余注解报错 - _last_stmt = Node.body[-1] if Node.body else None - _is_ret_or_ctrl_tail: bool = isinstance(_last_stmt, ( - ast.Return, ast.If, ast.Match, ast.While, ast.For, ast.Try, ast.With)) - if Node.returns is not None and not _is_ret_or_ctrl_tail: - # void(None / t.CVoid,含 | 联合)/ extern|export 声明型辅助符号 - # (State / CExport / CExtern)→ 掉出合法,不 fail-fast - if not _IsVoidOrDeclAnnotation(Node.returns): - raise Exception( - f"函数 {Node.name} 声明了返回类型但函数体可能掉出末尾(缺少 return 语句);" - f"普通语句掉出末尾 + 有返回注解属于定义缺陷,请补显式 return") - Gen._emit_local_heap_frees() - if Gen._variadic_info and Gen._variadic_info.get('va_start_called'): - va_list_ptr = Gen._variadic_info.get('va_list_ptr') - if va_list_ptr: - Gen.emit_va_end(va_list_ptr) - ActualReturnType = func.function_type.return_type - if isinstance(ActualReturnType, ir.VoidType): - Gen.builder.ret_void() - elif isinstance(ActualReturnType, ir.IntType): - Gen.builder.ret(ir.Constant(ActualReturnType, 0)) - elif isinstance(ActualReturnType, (ir.FloatType, ir.DoubleType)): - Gen.builder.ret(ir.Constant(ActualReturnType, 0.0)) - elif isinstance(ActualReturnType, ir.PointerType): - Gen.builder.ret(ir.Constant(ActualReturnType, None)) - elif isinstance(ActualReturnType, ir.IdentifiedStructType): - if ActualReturnType.elements: - zero_val = ir.Constant(ActualReturnType, [ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) else ir.Constant(et, ir.Undefined) for et in ActualReturnType.elements]) - else: - zero_val = ir.Constant(ActualReturnType, None) - Gen.builder.ret(zero_val) - elif isinstance(ActualReturnType, ir.ArrayType): - Gen.builder.ret(ir.Constant(ActualReturnType, None)) - else: - Gen.builder.ret_void() - # 离开函数体:停用本 builder 的默认调试位置(含 SP 绑定),防止遗留状态 - # 被后续模块级指令误用(未启用 --dwarf 时 _DbgInfo 为 None,不做任何事)。 - if _DbgInfo is not None: - _DbgInfo.PopFunction(Gen.builder) - Gen.builder = None - Gen.func = None - Gen._current_class_name = _saved_class_name - Gen.variables = {} - Gen.var_signedness = {} - Gen._reg_values = {} - Gen._direct_values = {} + arg.annotation, self.Trans, Gen) + if found_struct: + Gen.var_struct_class[param_name] = found_struct + # 普通类注解形参(非 BinOp 联合):rawptr[X] / t.CPtr[X] / X / t.X + # 一律按"声明类"登记 var_struct_class。避免按 param.type.pointee 匹配 + # 到泛型父特化(如 class Function(GSListNode[Function]) 的形参 func 被 + # 登记成 GSListNode[Function],其 struct 仅含 Next → func.Blocks 访问 + # fast-fail "属性 'Blocks' 在类 'GSListNode[Function]' 中找不到")。 + # 泛型占位(rawptr[T])的 T 不在 Gen.structs,自动跳过、保留 pointee 匹配。 + for i, arg in enumerate(Node.args.args): + if arg.annotation is None: + continue + if isinstance(arg.annotation, ast.BinOp) and isinstance(arg.annotation.op, ast.BitOr): + continue # 联合注解由上方循环处理 + param_name = ParamNames[i] if i < len(ParamNames) else arg.arg + if param_name not in Gen.variables: + continue + ann = arg.annotation + target: str | None = None + if isinstance(ann, ast.Name): + target = ann.id + elif isinstance(ann, ast.Attribute): + target = ann.attr + elif isinstance(ann, ast.Subscript): + subv = ann.value + subname = getattr(subv, 'id', None) or getattr(subv, 'attr', '') + if subname in ('rawptr', 'CPtr', 'Ptr'): + slc = ann.slice + if isinstance(slc, ast.Name): + target = slc.id + elif isinstance(slc, ast.Attribute): + target = slc.attr + if not target: + continue + key: str | None = target if target in Gen.structs else None + if key is None: + _sha_t: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(target) + if _sha_t and f'{_sha_t}.{target}' in Gen.structs: + key = f'{_sha_t}.{target}' + if key: + Gen.var_struct_class[param_name] = key + if ResolvedClassName and (IsMethod or RawFuncName == '__init__'): + self.Trans._CurrentCpythonObjectClass = ResolvedClassName + Gen._variadic_info = None + if IsVariadic and Node.args.vararg: + vararg_name = getattr(Node.args.vararg, 'arg', 'args') + last_param_name = ParamNames[-1] if ParamNames else None + last_param_ptr = None + for param, pn in zip(func.args, ParamNames): + if pn == last_param_name: + last_param_ptr = param + break + triple = getattr(Gen, 'module_triple', '') or getattr(Gen.module, 'triple', '') + is_windows = 'windows' in triple if triple else False + ptr_size = getattr(Gen, 'ptr_size', 8) + # va_list 大小取决于 ABI:Windows x64 = __va_list_tag(24字节),Linux x64 = __va_list_tag[1](24字节) + # 注意:x86_64-pc-windows-gnu 的 llvm.va_start 写的是 24 字节的 struct __va_list_tag + # ({i32 gp_offset, i32 fp_offset, i8* overflow_arg_area, i8* reg_save_area}), + # 若只分配 8 字节会向调用者栈帧越界写 16 字节,导致随机栈破坏(历史崩溃根因)。 + if ptr_size == 4: + va_list_size = 4 if is_windows else 12 + va_list_align = 4 + else: + va_list_size = 24 + va_list_align = 16 + va_list_type = ir.ArrayType(ir.IntType(8), va_list_size) + va_list_alloc = Gen._allocaEntry(va_list_type, name=f"{vararg_name}_va_list", align=va_list_align) + va_list_ptr = Gen.builder.bitcast(va_list_alloc, ir.IntType(8).as_pointer(), name=f"{vararg_name}_va_list_i8ptr") + Gen._variadic_info = { + 'vararg_name': vararg_name, + 'va_list_ptr': va_list_ptr, + 'last_param_ptr': last_param_ptr, + 'va_start_called': False, + } + if last_param_ptr: + last_param_alloc = Gen._allocaEntry(last_param_ptr.type, name="last_param_copy") + Gen.builder.store(last_param_ptr, last_param_alloc) + Gen.emit_va_start(va_list_ptr) + Gen._variadic_info['va_start_called'] = True + # 用 _direct_values 注册,Name 解析时直接返回 va_list 指针(i8*), + # 不走 variables 的 load 路径(否则会把 i8* 当 char* 加载一个字节) + Gen._direct_values[vararg_name] = va_list_ptr + # @t.TLS 一次性初始化检查:首次调用执行函数体,后续调用直接返回 + if is_tls_func: + flag_name: str = f"__tls_init_done_{FuncName}" + flag_global = None + for gv in Gen.module.global_values: + if gv.name == flag_name: + flag_global = gv + break + if flag_global is None: + flag_global = ir.GlobalVariable(Gen.module, ir.IntType(1), name=flag_name) + flag_global.initializer = ir.Constant(ir.IntType(1), 0) + flag_global.linkage = 'internal' + flag_val = Gen.builder.load(flag_global, name="tls_flag_check") + skip_bb = func.append_basic_block("tls_skip") + cont_bb = func.append_basic_block("tls_continue") + Gen.builder.cbranch(flag_val, skip_bb, cont_bb) + Gen.builder.position_at_end(skip_bb) + ActualReturnType = func.function_type.return_type + if isinstance(ActualReturnType, ir.VoidType): + Gen.builder.ret_void() + elif isinstance(ActualReturnType, ir.IntType): + Gen.builder.ret(ir.Constant(ActualReturnType, 0)) + elif isinstance(ActualReturnType, (ir.FloatType, ir.DoubleType)): + Gen.builder.ret(ir.Constant(ActualReturnType, 0.0)) + elif isinstance(ActualReturnType, ir.PointerType): + Gen.builder.ret(ir.Constant(ActualReturnType, None)) + else: + Gen.builder.ret(ir.Constant(ActualReturnType, None)) + Gen.builder.position_at_end(cont_bb) + Gen.builder.store(ir.Constant(ir.IntType(1), 1), flag_global) + _saved_class_name = getattr(Gen, '_current_class_name', None) + Gen._current_class_name = ClassName + # 提取首个裸字符串字面量作为 __doc__ docstring,并跳过该节点避免生成死代码 + func_body: list[ast.stmt] = Node.body + doc_symbol: str = f"{ClassName}.{Node.name}" if ClassName else Node.name + if (func_body and isinstance(func_body[0], ast.Expr) + and isinstance(func_body[0].value, ast.Constant) + and isinstance(func_body[0].value.value, str)): + Gen._create_doc_global(doc_symbol, func_body[0].value.value) + func_body = func_body[1:] + else: + Gen._create_doc_global(doc_symbol, None) + try: + self.Trans.BodyHandler.HandleBodyLlvm(func_body) + except Exception as _body_exc: + # 方法体编译失败:确保 block 终止(防止 expected instruction opcode 错误), + # 然后重新抛出异常让编译立即终止(不吞掉错误) + _fn_name: str = Node.name + _cls_name: str = ClassName if ClassName else '' + import sys as _sys + import traceback as _tb + print(f"[FATAL] 方法体编译失败 ({_cls_name}.{_fn_name}): {type(_body_exc).__name__}: {_body_exc}", file=_sys.stderr) + _tb.print_exc(file=_sys.stderr) + if Gen.builder is not None: + try: + if not Gen.builder.block.is_terminated: + _ret_ty = func.function_type.return_type + if isinstance(_ret_ty, ir.VoidType): + Gen.builder.ret_void() + elif isinstance(_ret_ty, ir.PointerType): + Gen.builder.ret(ir.Constant(_ret_ty, None)) + elif isinstance(_ret_ty, ir.IntType): + Gen.builder.ret(ir.Constant(_ret_ty, 0)) + else: + Gen.builder.ret(ir.Constant(_ret_ty, None)) + except Exception: + try: + if Gen.builder and not Gen.builder.block.is_terminated: + Gen.builder.ret_void() + except Exception: + pass + # 失败路径也要复位「当前方法」上下文(成功路径的复位在函数末尾,异常 + # 直接 re-raise 会跳过它)。上层有吞掉异常的调用点(如泛型特化按需补全 + # _try_complete_generic_specialization、类按需实例化),它们让外层方法 + # 继续编译;若此时 _CurrentCpythonObjectClass / var_struct_class 仍指向 + # 内层特化类,外层方法的 `self.X` 会被按内层类解析(典型现象: + # ExprGenFstr.translate_joined_str 的 `self.Gen` 被按 + # `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到")。 + Gen._current_class_name = _saved_class_name + self.Trans._CurrentCpythonObjectClass = saved_cpython_class + Gen.var_struct_class = saved_var_struct_class + if hasattr(Gen, 'global_struct_class'): + Gen.global_struct_class = saved_global_struct_class + raise + if not Gen.builder.block.is_terminated: + if 'naked' in fn_attrs: + Gen.builder.unreachable() + self.Trans._CurrentCpythonObjectClass = saved_cpython_class + return + # fail-fast:有非 void 返回类型注解但函数体掉出末尾(无显式 return 覆盖) + # → 立即报错,不静默 ret 0(用户隐式返回规则)。 + # 判据按**源码层面**函数体末句判断(不依赖 builder 状态——翻译后未 + # terminated 可能由其它翻译问题导致,如 HandleExprLlvm 返回 None 静默 + # 跳过 ret,不该归类为"掉出末尾"缺陷): + # - 末句是显式 return → 有覆盖,正常 + # - 末句是控制流(If/Match/While/For/Try/With)→ 掉出合法(隐式返回 + # 规则:末句控制流 → unreachable;match/if 合成 after 块机械掉出, + # 所有 arm 已 terminated 属正常) + # - 末句是普通语句 → 真掉出末尾;-> None / -> t.CVoid 视为 Void 合法, + # 无注解 → C 风格过程(ConstZero,保持现状);其余注解报错 + _last_stmt = Node.body[-1] if Node.body else None + _is_ret_or_ctrl_tail: bool = isinstance(_last_stmt, ( + ast.Return, ast.If, ast.Match, ast.While, ast.For, ast.Try, ast.With)) + if Node.returns is not None and not _is_ret_or_ctrl_tail: + # void(None / t.CVoid,含 | 联合)/ extern|export 声明型辅助符号 + # (State / CExport / CExtern)→ 掉出合法,不 fail-fast + if not _IsVoidOrDeclAnnotation(Node.returns): + raise Exception( + f"函数 {Node.name} 声明了返回类型但函数体可能掉出末尾(缺少 return 语句);" + f"普通语句掉出末尾 + 有返回注解属于定义缺陷,请补显式 return") + Gen._emit_local_heap_frees() + if Gen._variadic_info and Gen._variadic_info.get('va_start_called'): + va_list_ptr = Gen._variadic_info.get('va_list_ptr') + if va_list_ptr: + Gen.emit_va_end(va_list_ptr) + ActualReturnType = func.function_type.return_type + if isinstance(ActualReturnType, ir.VoidType): + Gen.builder.ret_void() + elif isinstance(ActualReturnType, ir.IntType): + Gen.builder.ret(ir.Constant(ActualReturnType, 0)) + elif isinstance(ActualReturnType, (ir.FloatType, ir.DoubleType)): + Gen.builder.ret(ir.Constant(ActualReturnType, 0.0)) + elif isinstance(ActualReturnType, ir.PointerType): + Gen.builder.ret(ir.Constant(ActualReturnType, None)) + elif isinstance(ActualReturnType, ir.IdentifiedStructType): + if ActualReturnType.elements: + zero_val = ir.Constant(ActualReturnType, [ir.Constant(et, None) if isinstance(et, (ir.PointerType, ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)) else ir.Constant(et, 0) if isinstance(et, (ir.IntType, ir.FloatType, ir.DoubleType)) else ir.Constant(et, ir.Undefined) for et in ActualReturnType.elements]) + else: + zero_val = ir.Constant(ActualReturnType, None) + Gen.builder.ret(zero_val) + elif isinstance(ActualReturnType, ir.ArrayType): + Gen.builder.ret(ir.Constant(ActualReturnType, None)) + else: + Gen.builder.ret_void() + # 离开函数体:停用本 builder 的默认调试位置(含 SP 绑定),防止遗留状态 + # 被后续模块级指令误用(未启用 --dwarf 时 _DbgInfo 为 None,不做任何事)。 + if _DbgInfo is not None: + _DbgInfo.PopFunction(Gen.builder) + Gen.builder = None + Gen.func = None + Gen._current_class_name = _saved_class_name + Gen.variables = {} + Gen.var_signedness = {} + Gen._reg_values = {} + Gen._direct_values = {} Gen.var_struct_class = {} # 注解声明类台账是**函数作用域**的(局部变量的声明随函数出栈失效), # 与 var_struct_class 同处清空 —— 否则同名变量会在别的函数里被误判为 # "已由注解声明",从而挡住右值推出类的正常登记。 - Gen.var_annotated_class = {} - Gen.var_ptr_element = {} - Gen.var_const_flags = {} - # const/volatile 的**函数作用域层**随函数出栈清空(模块级层 global_* 与 - # 类字段层 field_* 是持久的,不在此清;见 BaseGen 的登记说明)。 - Gen.var_volatile_flags = {} - Gen.var_pointee_volatile_flags = {} - # 恢复保存的 var_type_info - Gen.var_type_info = saved_var_type_info - Gen.var_type_assignments = saved_var_type_assignments - Gen._stop_iter_flag_param = None - # 恢复外层作用域的 _local_heap_ptrs / _var_to_heap_ptr - Gen._local_heap_ptrs = saved_local_heap_ptrs - Gen._var_to_heap_ptr = saved_var_to_heap_ptr - # 恢复外层作用域的 var_struct_class / global_struct_class - # (内层方法体编译不应污染外层函数的变量-类型映射) - Gen.var_struct_class = saved_var_struct_class - if hasattr(Gen, 'global_struct_class'): - Gen.global_struct_class = saved_global_struct_class - Gen._variadic_info = None - self.Trans._CurrentCpythonObjectClass = saved_cpython_class - self.Trans.CurrentMultiRetTypes = None - if self.Trans.VarScopes: - self.Trans.VarScopes.pop() - self.Trans.FunctionReturnTypes[FuncName] = ReturnTypeInfo - if not self.Trans.SymbolTable.has(FuncName): - FuncInfo = CTypeInfo() - FuncInfo.Name = FuncName - FuncInfo.IsFunction = True - FuncInfo.MetaList = func_meta - if IsInlineFunc: - FuncInfo.IsInline = True - FuncInfo.InlineBody = Node.body - FuncInfo.InlineParams = [arg.arg for arg in Node.args.args] - self.Trans.SymbolTable.insert(FuncName, FuncInfo) - else: - existing = self.Trans.SymbolTable[FuncName] - if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE: - existing.MetaList = func_meta - if IsInlineFunc: - existing.IsInline = True - existing.InlineBody = Node.body - existing.InlineParams = [arg.arg for arg in Node.args.args] - # property setter/deleter: 在原始 PropKey(不带后缀)下注册 MetaList - if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta: - BasePropKey = f"{ClassName}.{RawFuncName}" if ClassName else RawFuncName - base_existing = self.Trans.SymbolTable.lookup(BasePropKey) - if base_existing: - if func_meta != FuncMeta.NONE: - base_existing.MetaList = base_existing.MetaList | func_meta - else: - PropInfo = CTypeInfo() - PropInfo.Name = BasePropKey - PropInfo.IsFunction = True - PropInfo.MetaList = func_meta - self.Trans.SymbolTable.insert(BasePropKey, PropInfo) - return func + Gen.var_annotated_class = {} + Gen.var_ptr_element = {} + Gen.var_const_flags = {} + # const/volatile 的**函数作用域层**随函数出栈清空(模块级层 global_* 与 + # 类字段层 field_* 是持久的,不在此清;见 BaseGen 的登记说明)。 + Gen.var_volatile_flags = {} + Gen.var_pointee_volatile_flags = {} + # 恢复保存的 var_type_info + Gen.var_type_info = saved_var_type_info + Gen.var_type_assignments = saved_var_type_assignments + Gen._stop_iter_flag_param = None + # 恢复外层作用域的 _local_heap_ptrs / _var_to_heap_ptr + Gen._local_heap_ptrs = saved_local_heap_ptrs + Gen._var_to_heap_ptr = saved_var_to_heap_ptr + # 恢复外层作用域的 var_struct_class / global_struct_class + # (内层方法体编译不应污染外层函数的变量-类型映射) + Gen.var_struct_class = saved_var_struct_class + if hasattr(Gen, 'global_struct_class'): + Gen.global_struct_class = saved_global_struct_class + Gen._variadic_info = None + self.Trans._CurrentCpythonObjectClass = saved_cpython_class + self.Trans.CurrentMultiRetTypes = None + if self.Trans.VarScopes: + self.Trans.VarScopes.pop() + self.Trans.FunctionReturnTypes[FuncName] = ReturnTypeInfo + if not self.Trans.SymbolTable.has(FuncName): + FuncInfo = CTypeInfo() + FuncInfo.Name = FuncName + FuncInfo.IsFunction = True + FuncInfo.MetaList = func_meta + if IsInlineFunc: + FuncInfo.IsInline = True + FuncInfo.InlineBody = Node.body + FuncInfo.InlineParams = [arg.arg for arg in Node.args.args] + self.Trans.SymbolTable.insert(FuncName, FuncInfo) + else: + existing = self.Trans.SymbolTable[FuncName] + if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE: + existing.MetaList = func_meta + if IsInlineFunc: + existing.IsInline = True + existing.InlineBody = Node.body + existing.InlineParams = [arg.arg for arg in Node.args.args] + # property setter/deleter: 在原始 PropKey(不带后缀)下注册 MetaList + if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta: + BasePropKey = f"{ClassName}.{RawFuncName}" if ClassName else RawFuncName + base_existing = self.Trans.SymbolTable.lookup(BasePropKey) + if base_existing: + if func_meta != FuncMeta.NONE: + base_existing.MetaList = base_existing.MetaList | func_meta + else: + PropInfo = CTypeInfo() + PropInfo.Name = BasePropKey + PropInfo.IsFunction = True + PropInfo.MetaList = func_meta + self.Trans.SymbolTable.insert(BasePropKey, PropInfo) + return func diff --git a/lib/core/Handles/HandlesImports.py b/lib/core/Handles/HandlesImports.py index 5b3f099..64d0176 100644 --- a/lib/core/Handles/HandlesImports.py +++ b/lib/core/Handles/HandlesImports.py @@ -1,3030 +1,3030 @@ -from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.translator import Translator - from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin -import vast as ast -import inspect -import os -import re -import hashlib -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode -from lib.includes import t -from lib.includes.t import CTypeRegistry +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator + from lib.core.Translator.LlvmGenerator import LlvmGeneratorMixin +import vast as ast +import inspect +import os +import re +import hashlib +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode +from lib.includes import t +from lib.includes.t import CTypeRegistry from lib.core.SymbolUtils import ParseListAnnotation, ExtractTypeNameFromBinOp, AnnotationContainsName, FindStructNameInAnnotationWithContext, FindAutoAddrMode from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords -from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod - - -# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写, -# 如 `t.CExtern | rawptr[CompilerFactory]`),提取"指针指向的类"时必须滤掉。 -_MARKER_ANNOT_NAMES: frozenset[str] = frozenset({ - 'CExtern', 'CExport', 'CStatic', 'CInline', 'CConst', 'CVolatile', 'CRegister', - 'CAuto', 'CDefine', 'CTypedef', 'CPostdefinition', 'CPtr', 'CArrayPtr', 'CVoid', - 'State', 'TLS', -}) - -# 标记基类名(不是真实父类):与 HandlesClassDef._EmitClassLlvm 的 base 跳过表同口径。 -_MARKER_BASE_NAMES: frozenset[str] = frozenset({ - 'Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', - 'Exception', 'Enum', -}) - -# LLVM 基础类型表(模块级常量):`_parse_llvm_type_impl` 原先把这份字典**每次调用现建** -# (profile 2026-09-29:本函数 1,689,809 次调用 ⇒ 字典字面量 1.69M 次、 -# `IntType.__new__` 9.09M 次)。这些基础类型在 llvmlite 里无状态且 IntType 按宽度 -# 缓存同一实例,跨调用/跨 Gen 共享安全 ⇒ 提升为模块常量,表意逐字不变。 -_LLVM_PRIMITIVE_TYPE_MAP: dict[str, ir.Type] = { - 'void': ir.VoidType(), - 'i1': ir.IntType(1), - 'i8': ir.IntType(8), - 'i16': ir.IntType(16), - 'i32': ir.IntType(32), - 'i64': ir.IntType(64), - 'float': ir.FloatType(), - 'double': ir.DoubleType(), -} - - -# 字面量正则的模块级预编译(性能:这些调用点做逐行扫描/逐条解析,命中次数极高; -# 每次 `re.match(字面量, s)` 都要做"模式串哈希 → re 缓存查找"。预编译成模块级常量 -# 后省掉该查找,且匹配路径更短。模式串一字未改。 -# 解析异常报文里兜底取 `at line N` -_RE_AT_LINE: re.Pattern[str] = re.compile(r'at line (\d+)') -# stub 里 `%.Name = type <...>` 结构体定义行 -_RE_STRUCT_TYPE_DEF: re.Pattern[str] = re.compile(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)') -# `[N x ]` 数组类型 -_RE_ARRAY_TYPE: re.Pattern[str] = re.compile(r'\[(\d+)\s*x\s+(.+)\]') -# declare 行(函数名限定 `[\w.]+` 字符集) -_RE_DECLARE_SIMPLE: re.Pattern[str] = re.compile(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$') -# declare 行(函数名允许任意非括号字符) -_RE_DECLARE_GENERIC: re.Pattern[str] = re.compile(r'declare\s+(.+?)\s+@("?[^()]+"?)\s*\((.*)\)$') -# 剥离 LLVM IR 参数名:`\s+%name` 或 `\s+%"quoted.name"` 结尾 -_RE_PARAM_NAME_TRAILING: re.Pattern[str] = re.compile(r'\s+%("[^"]*"|[\w.]+)\s*$') -# 剥离 LLVM IR 参数名:`\s+%name`(无引号形式) -_RE_PARAM_NAME_PLAIN: re.Pattern[str] = re.compile(r'\s+%[\w.]+\s*$') -# stub 里引用到的 sha1 前缀 `%<16hex>.` -_RE_SHA1_PREFIX_REF: re.Pattern[str] = re.compile(r'%\"?([a-f0-9]{16})\.') -# `@name = external global <...>` 全局变量定义行 -_RE_EXTERNAL_GLOBAL_DEF: re.Pattern[str] = re.compile(r'@("?)(?:([a-f0-9]{16})\.)?([\w.\[\]]+)\1\s*=\s*external\s+global\s+(\S.*)') -# 去掉全局类型尾部 zeroinitializer -_RE_STRIP_ZEROINIT: re.Pattern[str] = re.compile(r'\s+zeroinitializer.*$') -# 去掉全局类型尾部 `, align N` -_RE_STRIP_ALIGN: re.Pattern[str] = re.compile(r'\s*,\s*align\s+\d+.*$') -# struct 定义名(不强制 `= type ...` 后缀) -_RE_STRUCT_NAME: re.Pattern[str] = re.compile(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]*]+)"?') - - -def _HasRealParentClass(Node: ast.ClassDef) -> bool: - """类是否声明了**真实父类**(排除 Object/CVTable/NoVTable/CEnum/... 标记基类)。""" - for base in getattr(Node, 'bases', None) or []: - base_name: str | None = None - if hasattr(base, 'id'): - base_name = base.id - elif hasattr(base, 'attr'): - base_name = base.attr - elif isinstance(base, ast.Subscript): - if hasattr(base.value, 'id'): - base_name = base.value.id - elif hasattr(base.value, 'attr'): - base_name = base.value.attr - if base_name and base_name not in _MARKER_BASE_NAMES: - return True - return False - - -def _VtableByClassLevelForImport(Node: ast.ClassDef, Gen: object) -> bool: - """跨模块导入类的 VtableByClassLevel(忽略方法级 CVTable 标记的 vtable 状态)。 - - 与 HandlesClassDef._resolve_class_flags 的口径一致:类级 @t.CVTable 或 - "有真实父类且父类不是 NoVTable" ⇒ True。供 SymbolUtils.IsVtableSlotMethod - 判定方法是否占虚表槽(唯一的判据源),使导入侧的虚表槽序与定义模块逐槽相同。 - """ - if getattr(Node, 'decorator_list', None): - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Attribute) and decorator.attr == 'CVTable': - return True - if isinstance(decorator, ast.Name) and decorator.id == 'CVTable': - return True - if not _HasRealParentClass(Node): - return False - _novtable_set: set = getattr(Gen, '_cross_module_novtable', None) or set() - for base in getattr(Node, 'bases', None) or []: - base_name: str | None = None - if hasattr(base, 'id'): - base_name = base.id - elif hasattr(base, 'attr'): - base_name = base.attr - if base_name and base_name not in _MARKER_BASE_NAMES: - if base_name in _novtable_set: - return False - return True - - -def _ParseFailureSite(Exc: BaseException) -> str: - """解析异常 → `行[:列]` 文本;定位信息完全取不到时返回 `'?'`。 - - CPython/vast 的 SyntaxError 若按 (msg, (file, lineno, offset, text)) 构造则带 - `.lineno`/`.offset`;而 TPC 自研 vast 的 Parser/ExprParser 只把行号写进报文 - (`f"... at line {tok.Line}"`,见 lib/vast/Parser.py:826、ExprParser.py:640), - 字段为空 ⇒ 必须正则兜底,否则诊断退化成"无行号",无法直接跳到失败行。 - """ - Line: int = int(getattr(Exc, 'lineno', 0) or 0) - Col: int = int(getattr(Exc, 'offset', 0) or 0) - if not Line: - Matched: re.Match | None = _RE_AT_LINE.search(str(Exc)) - if Matched: - Line = int(Matched.group(1)) - if not Line: - return '?' - return f"{Line}:{Col}" if Col else str(Line) - - -def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequence: str) -> None: - """声明文件"读 → parse"失败的统一诊断口径(strict fail-fast / 非严格告警)。 - - 诊断必带四要素:**文件路径**、**失败行[:列]**、**异常类型与文本**、**后果说明** - (后果由调用方按点位给出,如"该模块声明将全部缺失,后续可能表现为 - `Undefined function`")。原实现(`_LoadModuleDeclarationsFromFile:364-367`)只在 - strict 模式调 `Trans.LogWarning`——而 `Trans.Warnings` 全仓只被 append、无人消费 - (BaseTranslator.py:55/137),**连 strict 模式都是无声的**;非严格模式更是直接 - `return`。于是 `includes/ast/base.vp:168` 的 `enum ASTFlag:` 让整个 base.vp 的 - 声明(含顶层 `_init_ast`)一个都不注册,下游只报 `Undefined function: '_init_ast'`, - 浪费了一轮排查。 - - fail-fast 依据:仓库有严格模式开关 `lib/constants/config.py:59 mode = "strict"`, - 且既有多处同口径 `if _config_mode == "strict": raise`(本文件 - `_RegisterSubModuleSha1:547`、`_build_stub_index:2443`、`_TryLoadStructFromStub:2539` - 等)——本函数与它们一致,未放宽任何既有 fail-fast。 - - `FileNotFoundError` 例外:属"isfile 与 open 之间文件被删/被 `--clean` 清掉"的 - 竞态(先例见本文件 `_build_stub_index:2439-2441`),不是"源码解析失败", - 故只告警、不触发 fail-fast。 - """ - Site: str = _ParseFailureSite(Exc) - Message: str = ( - f"声明解析失败 {FilePath}:{Site} — {type(Exc).__name__}: {Exc};{Consequence}" - ) - if _config_mode == "strict" and not isinstance(Exc, FileNotFoundError): - raise ImportError(Message) from Exc - _vlog().warning(Message, "Exception") - - -def PrependInheritedBlock(Gen: object, ClassName: str, Block: list) -> bool: - """把祖先字段块 `Block` 前置到 `Gen.class_members[ClassName]`(幂等,且**不删块内条目**)。 - - 为什么单独成函数:这段判据是"继承段与 body 继承前缀一一对应"的唯一保障,必须可 - 独立验证(见本仓"按'表头是否已等于块'判幂等"的三条性质)。 - - 判据与性质: - ① **不删块内条目**。块里每一项都对应 body 的一个继承槽;只要块内某项被删, - 被删项之后的继承字段的表内下标就会整体前移 k,而查询侧回退路径用 - `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型不同的槽。 - 旧实现按"名字已在整表内"过滤整块,正是这样删掉了**与子类自有字段重名**的 - 继承项(现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 - LastBuiltinsTypedefResult 下标 32→27,写进 i32 槽 → "指针隐式转整数已禁止")。 - ② **幂等**。本函数会被多个加载路径重复调用(_LoadModuleDeclarationsFromFile / - _LoadDeclarationsFromStubLlvm / _TryLoadClassMembersFromPyi 懒加载);判据是 - "表头前 len(Block) 项的名字序列是否等于 Block 的名字序列"——相等即整块已前置, - 直接返回 False(不再前置),避免 2026-08-10 那类重复字段 - (BasicBlock: [Next, Next, Name, ...] → 与 struct elements 数量不匹配 → 偏移错乱)。 - ③ **同名并存**。子类自有字段与继承项同名时,两者都保留(定义端 - HandlesClassDef 的 body 同样是"继承前缀 + 自有段",两侧槽数一致); - 源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, - 此处只是不让它把布局判据带偏。 - - 已知残留(**已在下方修复,2026-09-30**):若此前只前置过**部分**祖先块(父类表当时 - 较短),本次会整块再前置一次,该前缀出现重复条目。现于前置前调用 - `DropStaleAncestralCopies` 剥掉两端上的陈旧半截副本(判据:插入边上与 `Block` 前缀 - 逐项同名同型,且这些名字在表里**另有**一份正本)⇒ 不再残留。目标串接点与 - HandlesClassDef 的追加侧共用同一函数(两侧判据对称)。 - - 返回 True 表示本次真的前置了。 - """ - _Members: list = getattr(Gen, 'class_members', {}).get(ClassName) or [] - _BlockNames: list = [nm for nm, _ in Block] - if not _BlockNames: - return False - # 【规范化:剥掉"祖先块前缀的陈旧副本"(2026-09-30)】判据与成因见 - # NormalizeAncestralCopies —— 本函数文档"已知残留"一段的实测残留 - # (ClassGenDef 表 50 项 / 去重 42 项 / body 42 槽)在此消除。 - # 返回的 `_HasFull` 与原有的两条幂等判据("表头等于块" / InheritedBlockAlreadyPresent - # 的头尾整段判据)等价,故原两条提前 return 合并为这一条。 - _Trimmed: list - _HasFull: bool - _Trimmed, _HasFull = NormalizeAncestralCopies(list(_Members), Block, InsertAtHead=True) - if len(_Trimmed) != len(_Members): - _vlog().warning( - f"[成员展平] 类 '{ClassName}' 的祖先块({len(Block)} 项)插入前剥掉 " - f"{len(_Members) - len(_Trimmed)} 条**陈旧半截副本**(重名条目不占布局槽位," - f"却会让 base/下标回退失准):表 {len(_Members)} → {len(_Trimmed)} 项", - "成员展平") - getattr(Gen, 'class_members')[ClassName] = _Trimmed - _Members = _Trimmed - if _HasFull: - return False - getattr(Gen, 'class_members')[ClassName] = list(Block) + list(_Members) - return True - - -def InheritedBlockAlreadyPresent(Table: list, Block: list) -> bool: - """祖先字段块 `Block` 是否已**整段**位于 `Table` 的**头部或尾部**(名字 + IR 类型逐项相同)。 - - 为什么查头/尾两处而不是"表中任意位置":这两处恰好就是块被插入的两个方向 —— - 导入侧 `PrependInheritedBlock` 前置(⇒ 头),定义侧 HandlesClassDef 展平追加 - (⇒ 尾)。表中段出现的同序列与**合法的同名并存**(子类在自有段重复声明父类字段, - body 本来就有两个槽)不可区分,判成"已存在"会误删本该有的槽,故刻意不判—— - 真出现中段重复时仍由 StructGen 的重名条目告警兜住。 - - 为什么需要独立判据:两侧判据此前不对称 —— 导入侧只比"表头是否等于块"(前置幂等), - 定义侧**没有任何判据**(直接 `class_members + _flat`)⇒ "块已在尾部"时整块再追加 - 一次,表里多出 `len(Block)` 条重复条目,body 随之多出同名槽: - - 现场(2026-09-30,on-disk 实证):`output/.stub.ll` 的 - `750b8c10137d273e.TypeGenCType` body **73** 槽 = temp(Phase 1 声明)**41** 槽 - + 其 `[9:41]`(32 项,恰为追加前表的**尾部**)的原样副本;TypeGenCDef / - TypeGenErr 同为 +32,ClassGenMixin / ClassGenVTable / ClassGenEnum / - ClassGenDef 为 +8(两阶段布局不一致的类共 7 个,全在 Generator/ClassGen/ - TypeGen 继承链上)。下游表现:`Repl.vp:802` - `GenApi._g_gen.TypeGenCType.CTypedefNames` 因两份 body(41/73)歧义而 - "基座 pointee 未匹配 Gen.structs 中任何 struct"。 - """ - _n: int = len(Block) - if _n == 0 or _n > len(Table): - return False - - def _SameAt(_Start: int) -> bool: - for _j in range(_n): - _t: Any = Table[_Start + _j] - _b: Any = Block[_j] - if (not isinstance(_t, tuple) or not isinstance(_b, tuple) - or len(_t) < 2 or len(_b) < 2): - return False - if _t[0] != _b[0] or _t[1] != _b[1]: - return False - return True - - return _SameAt(0) or _SameAt(len(Table) - _n) - - -def _EqRun(Table: list, TableStart: int, Block: list, BlockStart: int, Count: int) -> bool: - """`Table[TableStart : TableStart+Count]` 与 `Block[BlockStart : BlockStart+Count]` - 是否**逐项同 name 同 IR 类型**。""" - _i: int - for _i in range(Count): - _t: Any = Table[TableStart + _i] - _b: Any = Block[BlockStart + _i] - if (not isinstance(_t, tuple) or not isinstance(_b, tuple) - or len(_t) < 2 or len(_b) < 2): - return False - if _t[0] != _b[0] or _t[1] != _b[1]: - return False - return True - - -def TrimStalePrefixCopy(Table: list, Block: list, AtHead: bool) -> list: - """剥掉 `Table` **指定端**上那段"祖先块 `Block` 前缀的陈旧副本"(返回新表)。 - - 只在 `AtHead` 指定的那一端找,且**不会**碰另一端 —— 因为另一端往往是**正本** - (完整块本身)。实测反例(本函数第一版的两端都剥):`[A,B,C,D]+[A,B]` 这种 - "正本在头、副本在尾"的表,两端都剥会把**头的正本**剥掉,得到 `[C,D,A,B]`, - 再把完整块前置就变成 `[A,B,C,D,C,D,A,B]` —— 副本反而更多。故判据必须**先定位 - 正本**(见 NormalizeAncestralCopies),再剥另一端。 - - 判据(全部为正向事实): - · 该端连续游程 `Table[:j]`(AtHead)或 `Table[len-j:]` 与 `Block[:j]` 逐项同名同型; - · `1 ≤ j < len(Block)`:整块本身不算"陈旧副本"; - · 这 j 个名字在 `Table` 里各自**另有**一份(计数 ≥ 2)—— 陈旧副本的特征就是 - "名字已有一份正本";子类合法的自有同名段只会出现一次 ⇒ 该条件把"自家字段 - 恰好与祖先块前缀同名同型"的误判排除。 - """ - if not Table or len(Block) < 2: - return Table - _j_max: int = min(len(Table), len(Block) - 1) - _counts: dict = {} - _it: Any - for _it in Table: - if isinstance(_it, tuple) and len(_it) >= 2: - _counts[_it[0]] = _counts.get(_it[0], 0) + 1 - _j: int - for _j in range(_j_max, 0, -1): - _start: int = 0 if AtHead else len(Table) - _j - if not _EqRun(Table, _start, Block, 0, _j): - continue - _all_dup: bool = True - _i: int - for _i in range(_j): - if _counts.get(Block[_i][0], 0) < 2: - _all_dup = False - break - if _all_dup: - return Table[_j:] if AtHead else Table[:len(Table) - _j] - return Table - - -def NormalizeAncestralCopies(Table: list, Block: list, InsertAtHead: bool) -> tuple[list, bool]: - """插入祖先块 `Block` 前把 `Table` 规范化成"完整块最多出现一次",返回 `(新表, 是否已含完整块)`。 - - 三种现场(都由"**半截祖先块**被写入 + 事后整块插入"造成;`PrependInheritedBlock` - 文档里的"已知残留"即此),一并处理: - - ① 完整块在**头**、尾部挂着前缀副本 ⇒ 剥**尾**(副本是被后来的整块前置推过去的); - ② 完整块在**尾**、头部挂着前缀副本 ⇒ 剥**头**(副本先写入,整块随后追加); - ③ 完整块尚未出现、且**插入边**上挂着前缀副本 ⇒ 剥插入边。③ 只在"这些名字在表里 - 已另有正本"时成立(判据见 TrimStalePrefixCopy):**首次**写入半截块时表里还没有 - 正本,此时不剥(无可判据),代价是该半截块会先留在表里,等下一次整块插入时由 - ①/② 剥掉 —— 即本函数对残留是**自愈**的(`PrependInheritedBlock` 会被多个加载 - 路径重复调用,定义侧也有重复收尾)。 - - 实测现场(用户构建日志 2026-09-30):`ClassGenDef` 表 **50** 项 / 去重 **42** 项, - 而 body 恰为 42 槽(= 父类 ClassGenBase 的 42 字段,ClassGenDef 自身 0 字段) - ⇒ 多出的 8 条正是祖先块前缀的陈旧副本。重名条目**不是布局槽位**(见 - `FuncGen._get_member_offset` 的重名段),却让 `FuncGen._member_base` 的 - `_Delta = 槽数 - 表长 = 42 - 50 = -8` 走进类型校准、并让回退路径的 - `base + 表内下标` 整体偏斜(同族现场:MatchCase / TypeGenResolve 的偏移算错)。 - - 剥掉的每一项在(已在表里的、或随后插入的)完整块里同名同型 ⇒ **不丢任何布局信息**, - 与 `PrependInheritedBlock` 性质①"不删块内条目"不冲突(被剥的是副本,正本仍在)。 - """ - _n: int = len(Block) - if _n == 0 or not Table or len(Table) < _n: - # 表尚不足以容纳完整块:只可能是现场③(或表为空/正常),按插入边剥 - return TrimStalePrefixCopy(Table, Block, AtHead=InsertAtHead), False - _HasHead: bool = _EqRun(Table, 0, Block, 0, _n) - _HasTail: bool = _EqRun(Table, len(Table) - _n, Block, 0, _n) - if _HasHead: - return TrimStalePrefixCopy(Table, Block, AtHead=False), True - if _HasTail: - return TrimStalePrefixCopy(Table, Block, AtHead=True), True - return TrimStalePrefixCopy(Table, Block, AtHead=InsertAtHead), False - - -class ImportHandle(BaseHandle): - _struct_Load_cache = {} - _pyi_cache = {} - _pyi_cache_dir = None - _project_root_cache = None - # 目录 os.walk 结果缓存(import 失败兜底查找用):目录 -> [(root, files)]。 - # 编译进程内 SearchDirsForSubmodule(includes/LibraryPaths/工程目录)内容不变, - # 避免每次 import 解析失败都全量递归扫描目录树(PROF:import 热路径高频调用)。 - _walk_cache: dict[str, list[tuple[str, list[str]]]] = {} - # 类级 stub 索引:class_name -> list of (full_name, struct_body, file_sha1, from_output) - # 一次扫描所有 .stub.ll 文件构建索引,避免 _TryLoadStructFromStub 每次调用都全量扫描 - # (原 609 次调用 × ~100 stub 文件 × ~50 行 = 5.5M 正则匹配 → 一次扫描 ~5000 匹配) - _stub_index: dict[str, list[tuple[str, str, str, bool]]] = {} - # 已解析进索引的 stub 文件绝对路径集合(增量重建用)。 - # 单次编译进程内 stub 只增不减(output 逐步填充),记录已扫描文件后, - # 签名变化只需增量解析新增文件,避免每编译完一个文件就全量重扫(原 847 次全量重建)。 - _stub_index_scanned: set[str] = set() - # 类级缓存:跨 worker 任务共享(同一进程内,worker 单线程无竞争)。 - # 键均为 (source_sha1, class_name),不同文件同名类(不同 sha1)不冲突。 - # classify:_EmitExternalClassDeclLlvm 开头的 bases/decorator_list 分类遍历结果 - # (与 Gen 无关的纯函数计算,同一 (sha1, class) 跨任务重复调用时跳过遍历)。 - # decl:pyi 树中的 ClassDef 节点引用(pyi 树索引,pyi 树由 _pyi_cache 类级缓存 - # 保证进程内唯一),避免 _TryLoadClassMembersFromPyi 跨任务反复线性扫描 body。 - _extern_class_classify_cache: dict[tuple[str, str], tuple] = {} - _extern_class_decl_cache: dict[tuple[str, str], ast.ClassDef] = {} - # 类级 stub 函数签名缓存(_LookupStubFuncType):跨 worker 任务共享。 - # 与 _stub_index/_stub_index_scanned 同模式:签名变化只增量解析新增 stub - # 文件(编译进程内 stub 只增不改),避免每次任务全量重扫 + 每文件 - # os.path.isfile(Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。 - _stub_func_cache: dict[str, tuple[str, str, bool, str]] = {} - _stub_func_cache_signature: tuple[int, int] | None = None - _stub_func_scanned: set[str] = set() - # stub 文件集合自上次建索引以来是否**可能**变化(写 *.stub.ll 的一方显式置脏)。 - # 见 MarkStubSetDirty / _build_stub_index 的零 syscall 快路径:原实现每次 - # _TryLoadStructFromStub 索引 miss 都要 2× os.path.isdir + 2× os.listdir 重新 - # 拼文件集合(profile:_build_stub_index 被调 238k 次;nt._path_isdir 491s + - # nt.listdir 183s ≈ 总时长 30%),而"集合是否变化"完全可以由写入方免费告知。 - _stub_set_dirty: bool = True - # 目录 → (listdir 时的目录 mtime_ns, 该次统计出的 *.stub.ll 数量)。 - # 键为目录绝对路径:temp/ 与 output/ 分别计缓存。见 _CountStubFilesInDir。 - _stub_dir_count_cache: dict[str, tuple[int, int]] = {} - - @classmethod - def MarkStubSetDirty(cls) -> None: - """标记「stub 文件集合可能已变化」——**所有写 *.stub.ll 的一方都必须调用**。 - - 调用点多在 Phase2Translator:每个文件翻译入口(跨文件新增一律可见)、 - 写 temp/.stub.ll 之后、注册 include 写 output/.stub.ll 之后。 - """ - ImportHandle._stub_set_dirty = True - - def __init__(self, translator: "Translator") -> None: - super().__init__(translator) - # 实例级去重集合:每个 translator 独立跟踪已加载的 .pyi 和已发射的模块声明。 - # 若作为类变量共享,main.py 编译时加载的 __nodes.py 会阻止 __parser.py 编译时 - # 重新加载,导致函数未注册到 __parser.py 的 Gen.functions(NamedExpr bug 根因)。 - self._loaded_pyi_paths: set[str] = set() - self._emitted_module_decls: set[tuple] = set() - # `_parse_llvm_type` 的结果 memo(键 = (类型串, source_sha1, create_structs))。 - # 只缓存**与 Gen 状态无关**的结果,见 `_parse_llvm_type` 的判据;因此跨文件 - # 复用是安全的(i8* 之类纯字面类型与哪个 Gen 无关),不随 `_reset_file_cache` 清空。 - self._llvm_type_memo: dict[tuple, ir.Type] = {} - - def _reset_file_cache(self) -> None: - self._struct_Load_cache.clear() - self._loaded_pyi_paths.clear() - self._emitted_module_decls.clear() - # stub 索引在文件间保留(同一 worker 进程内),但标记需检查是否过期 - # 不清除 _stub_index / _stub_index_scanned,通过增量扫描校验新鲜度 - - def _find_project_root(self) -> str: - if self._project_root_cache is not None: - return self._project_root_cache - # 优先使用 translator 上显式设置的 _temp_dir(由 Phase2Translator._translate_file 传入), - # 这样可以正确处理 include 文件——其源文件目录不在项目根目录下, - # 从源文件目录向上搜索 temp/output 会失败并回退到错误的路径。 - temp_dir_attr = getattr(self.Trans, '_temp_dir', None) - if temp_dir_attr and os.path.isdir(temp_dir_attr): - project_root = os.path.dirname(os.path.abspath(temp_dir_attr)) - if os.path.isdir(os.path.join(project_root, 'temp')) and os.path.isdir(os.path.join(project_root, 'output')): - self._project_root_cache = project_root - return project_root - current_file = getattr(self.Trans, 'CurrentFile', '') or '' - if current_file: - current_dir = os.path.dirname(os.path.abspath(current_file)) - search_dir = current_dir - for _ in range(10): - if os.path.isdir(os.path.join(search_dir, 'temp')) and os.path.isdir(os.path.join(search_dir, 'output')): - self._project_root_cache = search_dir - return search_dir - parent = os.path.dirname(search_dir) - if parent == search_dir: - break - search_dir = parent - # 回退到当前工作目录(编译运行时的项目根目录) - cwd = os.getcwd() - if os.path.isdir(os.path.join(cwd, 'temp')) and os.path.isdir(os.path.join(cwd, 'output')): - self._project_root_cache = cwd - return cwd - if current_file: - self._project_root_cache = current_dir - return current_dir - self._project_root_cache = cwd - return cwd - - def _EmitImportDeclarationsLlvm(self, Node: ast.Import, Gen: LlvmGeneratorMixin) -> None: - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - for alias in Node.names: - name = alias.name - if name in ('c', 't'): - if alias.asname: - self.Trans.SymbolTable.import_aliases[alias.asname] = name - AliasInfo = CTypeInfo() - AliasInfo.IsModuleAlias = True - AliasInfo.ResolvedModule = name - self.Trans.SymbolTable.insert(alias.asname, AliasInfo) - continue - self.Trans._ImportedModules.add(name) - if alias.asname: - self.Trans.SymbolTable.import_aliases[alias.asname] = name - # 同时在符号表中注册模块别名,以便 CTypeInfo.FromNode 能解析 - AliasInfo = CTypeInfo() - AliasInfo.IsModuleAlias = True - AliasInfo.ResolvedModule = name - self.Trans.SymbolTable.insert(alias.asname, AliasInfo) - current_module = self._get_current_module_name() - self._EmitModuleDeclarationsLlvm(name, Gen, register_module_name=current_module) - - def _RegisterFromImportAliases(self, Node: ast.ImportFrom, Gen: LlvmGeneratorMixin, module: str) -> None: - """Register 'from module import y as z' aliases after module declarations are Loaded""" - if not Node.names: - return - for alias in Node.names: - name = alias.name - asname = alias.asname - # For 'from X import Y' (no asname), register CDefine constants - # into _define_constants so they can be found by _HandleNameLlvm - if not asname or asname == name: - define_constants = vars(Gen).setdefault('_define_constants', {}) - # Check if this name is a CDefine constant in SymbolTable - sym_info = self.Trans.SymbolTable.lookup(name) - if sym_info and sym_info.IsDefine and sym_info.DefineValue is not None: - if name not in define_constants: - define_constants[name] = sym_info.DefineValue - # Also check with module prefix - for prefix_key in [f"{module}.{name}", name]: - sym_info2 = self.Trans.SymbolTable.lookup(prefix_key) - if sym_info2 and sym_info2.IsDefine and sym_info2.DefineValue is not None: - if name not in define_constants: - define_constants[name] = sym_info2.DefineValue - break - # 治本修复:如果符号表中没有,回退查询 _all_define_constants - # _all_define_constants 由 Phase2Translator 从所有 .pyi 文件预提取, - # 包含跨模块导入的 CDefine 常量(如 FOREGROUND_GREEN、STD_OUTPUT_HANDLE) - if name not in define_constants: - all_dc: dict = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} - if name in all_dc: - define_constants[name] = all_dc[name] - # 带类型注册:把跨模块 define 的**声明整数类型**一并带入本模块, - # 供使用点按声明类型(如 DWORD=u32)物化,而不是按值大小猜 i32/i64。 - all_dt: dict = getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None) or {} - if name in all_dt: - _dt_map: dict = vars(Gen).setdefault('_define_decl_types', {}) - if name not in _dt_map: - _dt_map[name] = all_dt[name] - continue - if not hasattr(self.Trans, '_t_c_imported_names'): - self.Trans._t_c_imported_names = {} - self.Trans._t_c_imported_names[asname] = (module, name) - if name in Gen.functions: - Gen.functions[asname] = Gen.functions[name] - if name in Gen.structs: - Gen.structs[asname] = Gen.structs[name] - info = self.Trans.SymbolTable.lookup(name) - if info: - self.Trans.SymbolTable.insert(asname, info) - if name in Gen.variables: - Gen.variables[asname] = Gen.variables[name] - if name in Gen.global_vars: - Gen.global_vars[asname] = Gen.global_vars[name] - for func_name in list(Gen.functions.keys()): - if func_name.startswith(f'{name}.'): - method_suffix = func_name[len(name):] - Gen.functions[f'{asname}{method_suffix}'] = Gen.functions[func_name] - for key in list(self.Trans.SymbolTable.keys()): - if key.startswith(f'{name}.'): - suffix = key[len(name):] - self.Trans.SymbolTable.insert(f'{asname}{suffix}', self.Trans.SymbolTable[key]) - for meta_dict in (Gen.class_members, Gen.class_member_defaults, - Gen.class_member_signeds, Gen.class_member_bitfields, - Gen.class_member_byteorders, Gen.class_member_bitoffsets, - Gen.class_methods): - if name in meta_dict: - meta_dict[asname] = meta_dict[name] - if name in Gen.class_vtable: - Gen.class_vtable.add(asname) - if name in Gen.class_packed: - Gen.class_packed.add(asname) - struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) - if name in struct_sha1_map: - struct_sha1_map[asname] = struct_sha1_map[name] - - def _EmitImportFromDeclarationsLlvm(self, Node: ast.ImportFrom, Gen: LlvmGeneratorMixin) -> None: - module = Node.module if Node.module else '' - if module in ('c', 't'): - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(module) - if not hasattr(self.Trans, '_t_c_imported_names'): - self.Trans._t_c_imported_names = {} - # 注册 t/c 类型别名到 _t_type_symbols(CType 子类) - t_type_syms = self.Trans.SymbolTable._t_type_symbols - for alias in Node.names: - name = alias.name - asname = alias.asname or name - self.Trans._t_c_imported_names[asname] = (module, name) - # 仅注册 CType 子类(CEnum/CUnion/CStruct/REnum 等) - if module == 't': - cls = getattr(t, name, None) - if isinstance(cls, type) and issubclass(cls, t.CType): - t_type_syms[asname] = cls - return - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - current_module = self._get_current_module_name() - if Node.level and Node.level > 0: - current_file = getattr(self.Trans, 'CurrentFile', '') or '' - current_dir = os.path.dirname(os.path.abspath(current_file)) - parent_dir = current_dir - for _ in range(Node.level - 1): - parent_dir = os.path.dirname(parent_dir) - if module: - SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] - sub_path_base = os.path.join(parent_dir, module) - for ext in SearchExtensions: - candidate = sub_path_base + ext - if os.path.isfile(candidate): - self.Trans._ImportedModules.add(module) - self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module, register_module_name=current_module) - self._RegisterFromImportAliases(Node, Gen, module) - return - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - # 尝试完整模块名和短模块名 - full_mod_name = f"{current_module}.{module}" if current_module else module - target_sha1 = ModuleSha1Map.get(full_mod_name) or ModuleSha1Map.get(module) - if target_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") - if os.path.isfile(sha1_pyi): - self.Trans._ImportedModules.add(module) - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module, register_module_name=current_module) - self._RegisterFromImportAliases(Node, Gen, module) - return - else: - mod_dir = parent_dir - - SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] - pkg_name = '' - if not module: - # When 'from . import name' is used, Load the package's __init__ - # to make package-level names (functions, classes, constants) available - pkg_name = self._LoadPackageInitForRelativeImport(mod_dir, Gen, current_module) - - # Also try to Load each alias as a submodule - for alias in Node.names: - found_sub = False - for ext in SearchExtensions: - sub_path = os.path.join(mod_dir, alias.name + ext) - if os.path.isfile(sub_path): - self.Trans._ImportedModules.add(alias.name) - self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=current_module) - if alias.asname: - self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name - found_sub = True - break - if not found_sub: - # SHA1 map fallback for submodule - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - sub_ModulePath = f"{pkg_name}.{alias.name}" if pkg_name else alias.name - target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name) - if target_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") - if os.path.isfile(sha1_pyi): - self.Trans._ImportedModules.add(sub_ModulePath) - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=current_module) - if alias.asname: - self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name - self._RegisterFromImportAliases(Node, Gen, module or pkg_name) - return - self.Trans._ImportedModules.add(module) - self._EmitModuleDeclarationsLlvm(module, Gen, register_module_name=current_module) - self._RegisterFromImportAliases(Node, Gen, module) - - def _RegisterCrossModuleDocstrings(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_prefix: str | None) -> None: - """从 {sha1}.doc.json 加载跨模块 docstring 注册到 Gen._doc_strings。 - - - 长名 {module_prefix}.{symbol} 总是注册 - - 短名 {symbol} 只在不存在时注册(避免覆盖本模块的) - - slice_level != 0(优化开启)时注册为 None - - 跨模块 docstring 在 _get_doc_ptr 访问时创建本地常量副本 - """ - if not module_prefix: - return - basename: str = os.path.basename(pyi_path) - # 若传入 .vp/.py 文件,则从 _source_module_sig_files 查找对应的 {sha1}.vpi - if basename.endswith('.vp') or basename.endswith('.py'): - source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) - if source_sig_files: - sig_path = source_sig_files.get(module_prefix) - if sig_path and os.path.isfile(sig_path): - pyi_path = sig_path - basename = os.path.basename(pyi_path) - else: - return - else: - return - if not (basename.endswith('.vpi') or basename.endswith('.pyi')): - return - sha1: str = basename[:-4] # 去掉 .vpi 或 .pyi 后缀(均为 4 字符) - shared_doc_meta = getattr(self.Trans, '_shared_doc_meta', None) - if not shared_doc_meta: - return - docs: dict[str, str] | None = shared_doc_meta.get(sha1) - if not docs: - return - trans = getattr(Gen, '_Trans', None) - slice_level: int = getattr(trans, 'SliceLevel', 3) - optimized: bool = slice_level != 0 - for symbol, doc in docs.items(): - long_key: str = f"{module_prefix}.{symbol}" - if optimized: - Gen._doc_strings[long_key] = None - if symbol not in Gen._doc_strings: - Gen._doc_strings[symbol] = None - else: - Gen._doc_strings[long_key] = doc - if symbol not in Gen._doc_strings: - Gen._doc_strings[symbol] = doc - - def _LoadModuleDeclarationsFromFile(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_name: str | None = None, register_module_name: str | None = None, actual_module_name: str | None = None, reexport_package: str | None = None) -> None: - if not pyi_path or not os.path.isfile(pyi_path): - return - # 去重:同一 .pyi 文件只加载一次(菱形依赖下避免重复 read+parse) - abs_pyi = os.path.abspath(pyi_path) - if abs_pyi in self._loaded_pyi_paths: - return - self._loaded_pyi_paths.add(abs_pyi) - # 注册跨模块 __doc__ docstring 到 Gen._doc_strings - self._RegisterCrossModuleDocstrings(pyi_path, Gen, register_module_name or module_name) - try: - with open(pyi_path, 'r', encoding='utf-8') as f: - ModuleCode = f.read() - # Viper 软关键字层(与 TPC 其余前端入口同一层):`.vp` 源码可含 - # `enum X:` / `fn` 等语法糖,直接 vast.parse 会在糖行 SyntaxError → - # 被下面的 except 吞掉并 return → **该模块的全部声明(函数/类/全局) - # 一个都不注册**,调用方随后报 "Undefined function" - # (includes/ast/base.vp 的 `enum ASTFlag:` → 六处 `from .base import …` - # 全部拿不到 `_init_ast`,即本次现场)。`.vpi`/`.pyi` 无糖,展开是空操作。 - ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), pyi_path) - except Exception as _e: - # 统一诊断口径(strict fail-fast / 非严格告警),必带文件+行号+异常+后果。 - # 原实现"吞异常 + return"(strict 也只写进无人消费的 Trans.Warnings), - # 是本次 `Undefined function: '_init_ast'` 被伪装成下游症状的直接原因。 - _ReportDeclarationParseFailure( - pyi_path, _e, - "该模块的全部声明(函数/类/全局/枚举)不会注册," - "后续引用方可能表现为 Undefined function / Undefined struct") - return - - # 计算源模块名(从 .pyi 文件路径推导)。 - # 治本修复:module_name 参数可能来自 alias.name(如 `from stmts import Module` - # 中的 'Module' 是类名而非模块名),直接传给 _EmitExternalClassDeclLlvm 会导致 - # ModuleSha1Map 查找失败 → _self_struct_sha1=None → 使用错误的同名 struct。 - # 使用文件名推导的模块名(如 'stmts')作为 source_module_name 才是正确的。 - # 对于 sha1 命名的 .pyi 文件(如 e64295c03b564a53.pyi),文件名是 sha1 而非 - # 模块名,需回退到 actual_module_name 或 module_name。 - file_module_name = os.path.splitext(os.path.basename(pyi_path))[0] - if len(file_module_name) == 16 and all(_c in '0123456789abcdef' for _c in file_module_name): - file_module_name = actual_module_name or module_name or file_module_name - - for node in ModuleTree.body: - try: - if isinstance(node, ast.FunctionDef): - if node.name in ('va_start', 'va_arg', 'va_end'): - continue - effective_register = register_module_name or module_name - reexport_names = [] - if module_name and module_name != file_module_name and module_name != effective_register: - reexport_names.append(module_name) - # When Loading a submodule for 'from .xxx import yyy' inside a package's __init__.py, - # re-export functions under the package name so 'pkg.func()' resolves correctly - if reexport_package and reexport_package != file_module_name and reexport_package not in reexport_names: - reexport_names.append(reexport_package) - self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=file_module_name, register_module_name=effective_register, reexport_module_names=reexport_names if reexport_names else None) - elif isinstance(node, ast.AnnAssign): - self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path) - elif isinstance(node, ast.Assign): - self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path) - elif isinstance(node, ast.ClassDef): - if hasattr(node, 'type_params') and node.type_params: - if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'): - self.Trans.ClassHandler._generic_class_templates = {} - type_params = [tp.name for tp in node.type_params] - # 保留 prescan 阶段已注册的 sha1;若无则从 _ModuleSha1Map 查找, - # 避免覆盖带 sha1 的注册导致特化时 SHA1 前缀丢失 - existing = self.Trans.ClassHandler._generic_class_templates.get(node.name) - template_sha1 = existing.get('sha1') if existing else None - if template_sha1 is None: - msm = getattr(self.Trans, '_ModuleSha1Map', None) - if msm: - for lookup_name in (file_module_name, module_name, actual_module_name): - if lookup_name and lookup_name in msm: - template_sha1 = msm[lookup_name] - break - self.Trans.ClassHandler._generic_class_templates[node.name] = { - 'node': node, - 'type_params': type_params, - 'sha1': template_sha1, - } - # 治本修复:使用 file_module_name(源模块名)而非 module_name(可能是别名/类名)。 - # 这样 _EmitExternalClassDeclLlvm 内的 ModuleSha1Map 查找才能命中, - # _EmitExternalFuncDeclLlvm 才能拿到正确的 source_module_name → 正确的 struct sha1。 - # 注意:actual_module_name 保持原值(可能为 None),不回退到 module_name(可能是别名) - self._EmitExternalClassDeclLlvm(node, Gen, module_name=file_module_name, actual_module_name=actual_module_name) - elif isinstance(node, ast.ImportFrom): - if node.level > 0: - current_dir = os.path.dirname(os.path.abspath(pyi_path)) - parent_dir = current_dir - for _ in range(node.level - 1): - parent_dir = os.path.dirname(parent_dir) - if node.module: - SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] - sub_path_base = os.path.join(parent_dir, node.module) - found_sub = False - # 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。 - # 当 _dict.py 导入 json 包,递归处理 json/__init__.py 中的 `from .__parser import parse` 时, - # register_module_name='_dict' 会导致 actual_module='_dict.__parser', - # 使 _mangle_func_name 查找失败回退到 self.module_sha1,生成错误的 mangled name。 - # 包名推导优先级: - # 1. module_name(如果它在 ModuleSha1Map 中,说明是已注册的包名,如 'json') - # — 处理 sha1 命名的 .pyi 文件(parent_dir 是 temp 目录时) - # 2. os.path.basename(parent_dir)(原始路径推导,如 'json') - if module_name and module_name in Gen.ModuleSha1Map: - pkg_basename = module_name - else: - pkg_basename = os.path.basename(parent_dir) - for ext in SearchExtensions: - candidate = sub_path_base + ext - if os.path.isfile(candidate): - actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module - self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen) - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(actual_module) - for alias in node.names: - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) - else: - self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name) - found_sub = True - break - # SHA1 map 回退 - if not found_sub: - actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module) - if target_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") - if os.path.isfile(sha1_pyi): - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(actual_module) - for alias in node.names: - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) - else: - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name) - else: - mod_dir = parent_dir - SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] - # Load __init__.py to make package-level names available - self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name) - # 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。 - # 同 if node.module 分支的修复逻辑。 - if module_name and module_name in Gen.ModuleSha1Map: - pkg_basename = module_name - else: - pkg_basename = os.path.basename(mod_dir) - for alias in node.names: - found_alias = False - for ext in SearchExtensions: - sub_path = os.path.join(mod_dir, alias.name + ext) - if os.path.isfile(sub_path): - sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(sub_ModulePath) - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) - else: - self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) - found_alias = True - break - # SHA1 map 回退 - if not found_alias: - sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name) - if target_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") - if os.path.isfile(sha1_pyi): - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(sub_ModulePath) - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) - else: - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - # fail-fast 一律上抛(本仓既定口径:FailFastError 不被吞,见 - # HandlesBase.py 的说明)。原实现把它与其它异常一起吞成一行 - # "忽略节点 … 异常" WARN 并 continue ⇒ 该节点(如某个 ClassDef 的 - # 全部方法声明)被整条静默丢弃,创建中途失败的**半注册名字**留到 - # 下一轮才以"函数声明名字悬空占用"暴露,真因与现场彻底脱节 - # (现场:50741ad4b4b0934d.LLVMTypeCore.__init__)。 - raise - _vlog().warning( - f"HandlesImports[_LoadModuleDeclarationsFromFile]: 忽略节点 " - f"{type(node).__name__} {getattr(node, 'name', '?')} 异常: {_e}", - exc_info=_e - ) - continue - - def _get_current_module_name(self) -> str | None: - current_file = getattr(self.Trans, 'CurrentFile', '') or '' - if current_file: - return os.path.splitext(os.path.basename(current_file))[0] - return None - - def _RegisterSubModuleSha1(self, candidate_path: str, actual_module: str, short_module: str, Gen: LlvmGeneratorMixin) -> None: - """Register a submodule's SHA1 in ModuleSha1Map so cross-module class references work""" - try: - with open(candidate_path, 'r', encoding='utf-8') as f: - sub_content = f.read() - sub_sha1 = hashlib.sha1(sub_content.encode('utf-8')).hexdigest()[:16] - if hasattr(Gen, 'ModuleSha1Map'): - Gen.ModuleSha1Map[actual_module] = sub_sha1 - if short_module and short_module not in Gen.ModuleSha1Map: - Gen.ModuleSha1Map[short_module] = sub_sha1 - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - - def _LoadPackageInitForRelativeImport(self, mod_dir: str, Gen: LlvmGeneratorMixin, register_module_name: str | None) -> str: - """Load __init__.py from the package directory for 'from . import name' resolution. - This makes package-level names (functions, classes, constants) available. - Returns the package name.""" - pkg_name = os.path.basename(mod_dir) - init_Loaded = False - - # 跳过当前正在编译的文件,避免重复加载导致 class_members 重复 - current_sha1 = getattr(Gen, 'module_sha1', None) - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - pkg_sha1 = ModuleSha1Map.get(pkg_name) - if pkg_sha1 and pkg_sha1 == current_sha1: - return pkg_name - - # Try SHA1 map first (correct mangled names from stub) - if pkg_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{pkg_sha1}.vpi") - if os.path.isfile(sha1_pyi): - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=pkg_name, register_module_name=register_module_name) - init_Loaded = True - - # Fallback: try Loading __init__.py directly from the package directory - if not init_Loaded: - for ext in ['.vpi', '.vp', '.pyi', '.py']: - init_path = os.path.join(mod_dir, '__init__' + ext) - if os.path.isfile(init_path): - self._LoadModuleDeclarationsFromFile(init_path, Gen, module_name=pkg_name, register_module_name=register_module_name) - init_Loaded = True - break - - return pkg_name - - def _EmitModuleDeclarationsLlvm(self, module_name: str, Gen: LlvmGeneratorMixin, register_module_name: str | None = None) -> None: - # 去重:同一模块声明只发射一次(避免多个 import 同一模块时重复加载) - decl_key = (module_name, register_module_name) - if decl_key in self._emitted_module_decls: - return - self._emitted_module_decls.add(decl_key) - if module_name in ('pyzlib',): - source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) - ModulePathBase = module_name.replace('.', os.sep) - PackageInitPathBase = module_name.replace('.', os.sep) + os.sep + '__init__' - ProjectRoot = os.path.dirname(os.path.abspath(getattr(self.Trans, 'CurrentFile', '') or '')) or os.getcwd() - - HandlesFile = os.path.abspath(inspect.getfile(self.__class__)) - TransPyCRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(HandlesFile)))) - - FullModulePath = None - # includes 只认项目(project.vpj includes → LibraryPaths)目录,禁止回退仓库硬编码 - # TransPyCRoot/includes:仓库副本缺失时回退会造成声明解析时序错乱(memhub 未注册→i32)。 - SearchDirs = list(self.Trans.LibraryPaths) - - for LibPath in SearchDirs: - SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath - # 优先尝试 .vpi,回退 .pyi - for stub_ext in ('.vpi', '.pyi'): - CandidatePath = os.path.join(SearchPath, ModulePathBase + stub_ext) - if os.path.isfile(CandidatePath): - FullModulePath = CandidatePath - break - CandidatePath = os.path.join(SearchPath, PackageInitPathBase + stub_ext) - if os.path.isfile(CandidatePath): - FullModulePath = CandidatePath - break - if FullModulePath: - break - ModuleParts = ModulePathBase.split(os.sep) - if len(ModuleParts) > 1: - LibPathBasename = os.path.basename(LibPath.rstrip('/\\')) - if LibPathBasename == ModuleParts[0]: - RemainingPath = os.sep.join(ModuleParts[1:]) - for stub_ext in ('.vpi', '.pyi'): - CandidatePath = os.path.join(SearchPath, RemainingPath + stub_ext) - if os.path.isfile(CandidatePath): - FullModulePath = CandidatePath - break - if FullModulePath: - break - if not FullModulePath: - ModulePathPyBase = module_name.replace('.', os.sep) - PackageInitPathPyBase = module_name.replace('.', os.sep) + os.sep + '__init__' - SearchDirsForPy = list(self.Trans.LibraryPaths) - for LibPath in SearchDirsForPy: - SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath - # 优先尝试 .vp,回退 .py - for src_ext in ('.vp', '.py'): - CandidatePath = os.path.join(SearchPath, ModulePathPyBase + src_ext) - if os.path.isfile(CandidatePath): - FullModulePath = CandidatePath - break - CandidatePath = os.path.join(SearchPath, PackageInitPathPyBase + src_ext) - if os.path.isfile(CandidatePath): - FullModulePath = CandidatePath - break - if FullModulePath: - break - if not FullModulePath or not os.path.isfile(FullModulePath): - source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) - if source_sig_files: - sig_path = source_sig_files.get(module_name) - if sig_path and os.path.isfile(sig_path): - FullModulePath = sig_path - else: - for key, val in source_sig_files.items(): - if key.endswith('.' + module_name) or key == module_name: - FullModulePath = val - break - if not FullModulePath or not os.path.isfile(FullModulePath): - if module_name in ('pyzlib', 'zdeflate', 'zinflate', 'zchecksum', 'zdef', 'zhuff'): - pass - if not FullModulePath: - CurrentFile = getattr(self.Trans, 'CurrentFile', None) or '' - if CurrentFile: - CurrentFileDir = os.path.dirname(os.path.abspath(CurrentFile)) - SearchFromDirs = [CurrentFileDir, os.getcwd()] - else: - SearchFromDirs = [os.getcwd()] - SearchDirsForSubmodule = SearchFromDirs + list(self.Trans.LibraryPaths) - for SearchPath in SearchDirsForSubmodule: - if not os.path.isdir(SearchPath): - continue - # 多级模块名(含 '.')与 basename 文件列表(files 无路径) - # 永不匹配 `module_name + ext in files`,walk 纯属浪费,跳过。 - if '.' in module_name: - continue - # 目录 walk 结果缓存(编译进程内目录内容不变):import 热路径 - # 高频解析失败时避免重复递归扫描 includes/工程目录(PROF 热点) - walk_entries: list[tuple[str, list[str]]] = ImportHandle._walk_cache.get(SearchPath) - if walk_entries is None: - walk_entries = [(r, fs) for r, dirs, fs in os.walk(SearchPath) if '__pycache__' not in r] - ImportHandle._walk_cache[SearchPath] = walk_entries - for root, files in walk_entries: - for src_ext in ('.vp', '.py', '.vpi', '.pyi'): - if module_name + src_ext in files: - FullModulePath = os.path.join(root, module_name + src_ext) - break - if FullModulePath: - break - if FullModulePath: - break - # fail-fast:模块在所有来源都"完全未知"时立即报错,暴露 import 路径错误 - # (如目录重组后未更新的旧路径)。之前静默容忍导致引用方跨模块 mangled - # 前缀 fallback 当前模块 sha1,链接期才暴露 undefined symbol。 - # 注意:文件系统找不到 ≠ 模块不存在——TPV 自举工程 includes 为合并形态, - # 源码模块(如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由 - # ModuleSha1Map/_source_module_sig_files 定位 sha1 → temp/*.stub.ll 加载, - # 属正常路径;只有模块在编译上下文完全未注册(无 sha1)才算真正不存在。 - if not FullModulePath or not os.path.isfile(FullModulePath): - _known_sha1: str | None = None - _sig_files = getattr(self.Trans, '_source_module_sig_files', None) - if _sig_files: - for _key, _val in _sig_files.items(): - if _key == module_name or _key.endswith('.' + module_name): - _known_sha1 = os.path.basename(_val).replace('.vpi', '').replace('.pyi', '') - break - if not _known_sha1: - _sha_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', None) or {} - _known_sha1 = _sha_map.get(module_name) - if not _known_sha1: - raise ImportError( - f"无法解析 import 模块 '{module_name}': " - f"在 includes/LibraryPaths/工程目录均未找到对应 .vp/.py/.vpi/.pyi 文件," - f"且编译上下文无该模块的 sha1 注册(模块完全未知)") - # 注册跨模块 __doc__ docstring(module_name 是被导入模块名) - if FullModulePath: - self._RegisterCrossModuleDocstrings(FullModulePath, Gen, module_name) - self._LoadDeclarationsFromStubLlvm(module_name, Gen) - if module_name == 'pyzlib': - pass - try: - with open(FullModulePath, 'r', encoding='utf-8') as f: - ModuleCode = f.read() - # 同上:`.vp` 源码须先过 Viper 软关键字层再 vast.parse(否则含 - # `enum X:` / `fn` 的模块声明整体丢失,落到 except 的 stub 兜底)。 - ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), FullModulePath) - if module_name == 'pyzlib': - func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)] - has_functions_or_classes = False - for node in ModuleTree.body: - if module_name == 'pyzlib': - node_type = type(node).__name__ - name = getattr(node, 'name', getattr(node, 'attr', '?')) - try: - if isinstance(node, ast.FunctionDef): - if node.name in ('va_start', 'va_arg', 'va_end'): - continue - effective_register = register_module_name or module_name - self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=module_name, register_module_name=effective_register) - has_functions_or_classes = True - elif isinstance(node, ast.AnnAssign): - self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath) - has_functions_or_classes = True - elif isinstance(node, ast.Assign): - self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath) - has_functions_or_classes = True - elif isinstance(node, ast.ClassDef): - if hasattr(node, 'type_params') and node.type_params: - if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'): - self.Trans.ClassHandler._generic_class_templates = {} - type_params = [tp.name for tp in node.type_params] - # 保留 prescan 阶段已注册的 sha1;若无则从 _ModuleSha1Map 查找, - # 避免覆盖带 sha1 的注册导致特化时 SHA1 前缀丢失 - existing = self.Trans.ClassHandler._generic_class_templates.get(node.name) - template_sha1 = existing.get('sha1') if existing else None - if template_sha1 is None: - msm = getattr(self.Trans, '_ModuleSha1Map', None) - if msm and module_name in msm: - template_sha1 = msm[module_name] - self.Trans.ClassHandler._generic_class_templates[node.name] = { - 'node': node, - 'type_params': type_params, - 'sha1': template_sha1, - } - self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name) - has_functions_or_classes = True - elif isinstance(node, ast.ImportFrom): - if node.level > 0: - current_dir = os.path.dirname(os.path.abspath(FullModulePath)) - parent_dir = current_dir - for _ in range(node.level - 1): - parent_dir = os.path.dirname(parent_dir) - if node.module: - SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] - sub_path_base = os.path.join(parent_dir, node.module) - found_sub = False - for ext in SearchExtensions: - candidate = sub_path_base + ext - if os.path.isfile(candidate): - actual_module = f"{module_name}.{node.module}" if module_name else node.module - self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen) - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(actual_module) - for alias in node.names: - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) - else: - self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name) - has_functions_or_classes = True - found_sub = True - break - # SHA1 map 回退:当文件查找失败时,从 SHA1 映射中查找子模块 stub - if not found_sub: - actual_module = f"{module_name}.{node.module}" if module_name else node.module - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - # 尝试完整模块名和短模块名 - target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module) - if target_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") - if os.path.isfile(sha1_pyi): - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(actual_module) - for alias in node.names: - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) - else: - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name) - has_functions_or_classes = True - else: - mod_dir = parent_dir - SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] - # Load __init__.py to make package-level names available - self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name or module_name) - for alias in node.names: - found_alias = False - for ext in SearchExtensions: - sub_path = os.path.join(mod_dir, alias.name + ext) - if os.path.isfile(sub_path): - sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(sub_ModulePath) - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) - else: - self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name) - has_functions_or_classes = True - found_alias = True - break - # SHA1 map 回退 - if not found_alias: - sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name) - if target_sha1: - temp_dir = getattr(Gen, '_temp_dir', None) - if temp_dir: - sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") - if os.path.isfile(sha1_pyi): - if not getattr(self.Trans, '_ImportedModules', None): - self.Trans._ImportedModules = set() - self.Trans._ImportedModules.add(sub_ModulePath) - if alias.name == '*': - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) - else: - self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name) - has_functions_or_classes = True - except RecursionError: - # 家族口径(HandlesBase.py:1060-1068):RecursionError 不是"可忽略异常", - # 而是类型解析/声明发射链**成环**的真缺陷信号。此前它被本处 - # `except Exception` 吞成一行 `WARN: 忽略异常 maximum recursion depth exceeded` - # + 声明静默退化(现场与 :2985 深探针记录一致:`match.MatchOr.patterns`、 - # `stmts.Interactive.body` 等"首字段即指针/容器"的类会触发 - # `_TryLoadClassMembersFromPyi ↔ _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub` - # 环)。此处 fail-fast 上抛(保留原始栈),暴露上游根因而非掩盖。 - raise - except Exception as e: - # NoneType 异常是数据缺失的正常兜底,静默不 WARN(干扰太多) - if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)): - if module_name == 'pyzlib': - node_type = type(node).__name__ - name = getattr(node, 'name', getattr(node, 'attr', '?')) - _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) - continue - if not has_functions_or_classes and module_name: - self._LoadDeclarationsFromStubLlvm(module_name, Gen) - elif 'includes' in FullModulePath and module_name: - self._LoadDeclarationsFromStubLlvm(module_name, Gen) - except Exception as e: - # 同族口径:读/parse 失败必须显式(文件+行号+异常+后果),不允许静默降级。 - # 非严格模式仍保留既有 stub 兜底(StubGen 产物声明仍可用);strict 模式 - # 由 _ReportDeclarationParseFailure 直接 fail-fast,与 :412 主点一致。 - # - # **限定条件**:只有"确实有源码文件却读/解析失败"才算声明解析失败。本函数 - # 的 `FullModulePath` 可为 None——TPV 自举工程 includes 为合并形态,源码模块 - # (如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由上方 - # `_LoadDeclarationsFromStubLlvm` 从 temp/*.stub.ll 加载,属**正常路径** - # (见本函数 :750 附近的长注释);此时 `open(None)` 的 TypeError 与"文件恰好 - # 不在"都不应触发 fail-fast。 - if FullModulePath and os.path.isfile(FullModulePath): - _ReportDeclarationParseFailure( - FullModulePath, e, - "该模块的源码声明全部未注册," - "仅能依赖 stub 兜底(非严格模式);声明可能与源码不一致") - if module_name: - self._LoadDeclarationsFromStubLlvm(module_name, Gen) - - def _AnnotationStructName(self, annotation: ast.AST) -> str | None: - """从注解里提取「指针指向的类」短名(递归,0 判定返回 None) - - 覆盖形态:`X` / `mod.X` / `rawptr[X]` / `t.CPtr[X]` / 联合 `A | B`(两翼都试)。 - 存储类与限定符标记(`t.CExtern` / `t.CExport` / `t.CPtr` 等)不算类名, - 由 `_MARKER_ANNOT_NAMES` 过滤;返回的是**短名**,调用方再解析成 - `Gen.structs` 真实键。 - """ - if annotation is None: - return None - if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): - _left: str | None = self._AnnotationStructName(annotation.left) - if _left: - return _left - return self._AnnotationStructName(annotation.right) - if isinstance(annotation, ast.Subscript): - _base: ast.AST = annotation.value - _bn: str = '' - if isinstance(_base, ast.Name): - _bn = _base.id - elif isinstance(_base, ast.Attribute): - _bn = _base.attr - if _bn in ('rawptr', 'CPtr', 'CArrayPtr'): - _sl: ast.AST = annotation.slice - if hasattr(ast, 'Index') and isinstance(_sl, ast.Index): - _sl = _sl.value - return self._AnnotationStructName(_sl) - return None - if isinstance(annotation, ast.Name): - if annotation.id in _MARKER_ANNOT_NAMES: - return None - return annotation.id - if isinstance(annotation, ast.Attribute): - # `mod.Class`:mod 为 t/c 时是内建标记/内建类型名,不作为类的键 - if isinstance(annotation.value, ast.Name) and annotation.value.id in ('t', 'c'): - return None - if annotation.attr in _MARKER_ANNOT_NAMES: - return None - return annotation.attr - return None - - def _ResolveAnnotationStructKey(self, Gen: LlvmGeneratorMixin, name: str) -> str | None: - """把注解里的类短名解析为 `Gen.structs` 的真实键(0 命中返回 None) - - 三条路径与类字段侧的 `rawptr[裸结构]` 登记保持一致:裸名直查 / - `class_sha1_map` 加前缀 / 全量短名模糊扫描(跨模块类在 - `class_sha1_map` 尚未登记该短名时靠这条兜住)。 - """ - if not name: - return None - if name in Gen.structs: - return name - _sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(name) - if _sha and f'{_sha}.{name}' in Gen.structs: - return f'{_sha}.{name}' - for _k in Gen.structs: - _short: str = _k.split('.')[-1] if '.' in _k else _k - if _short == name: - return _k - return None - - def _EmitExternalGlobalDeclLlvm(self, Node: ast.AnnAssign | ast.Assign, Gen: LlvmGeneratorMixin, module_name: str | None = None, ModulePath: str | None = None) -> None: - """Emit external global variable declaration from imported module""" - if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name): - VarName = Node.target.id - # 不再跳过 _ 开头的全局变量,跨模块引用需要声明它们 - var_type = ir.IntType(32) - IsPtr = False - is_string_val = False - TypeInfo = None - # 【跨模块模块级变量的**类登记**】`CTypeInfo.FromNode` 对 - # `X | t.CPtr` / `rawptr[X]` 注解只给出指针占位(rawptr[X] 折叠成 i8*), - # 且本函数**只算类型、不登记变量类** → 消费侧 `Mod.Var.Field` 链在 - # `_get_var_class` 里解析为空:StructGenMixin.vp:139 "属性访问未能解析出 - # 目标类型/成员"、CallGenLeafKit.vp:589 / ClassGenVTable.vp:99 误报 - # Unknown module(factory.PermPool.alloc)。这里对齐 HandlesAnnAssign - # 变量侧补偿:从注解找回真实结构体键并登记;仅当缺失或指针占位时覆盖。 - # 注意:必须放在下面的「已存在则 return」**之前**,否则全局量先由别的 - # 路径声明(如 stub 文本 i8*)时这里永远不执行。 - _eg_tn: str | None = self._AnnotationStructName(Node.annotation) - if _eg_tn: - # 只在能落到 Gen.structs 真实键时登记(避免把 sizet/CPtr 等标量名当类) - _eg_tk: str | None = self._ResolveAnnotationStructKey(Gen, _eg_tn) - if _eg_tk: - _eg_cur: str | None = Gen.var_struct_class.get(VarName) - if (not _eg_cur or _eg_cur == 'rawptr' or _eg_cur.endswith('.rawptr') - or _eg_cur.startswith('rawptr[')): - Gen.var_struct_class[VarName] = _eg_tk - if getattr(Gen, 'global_struct_class', None) is not None: - Gen.global_struct_class[VarName] = _eg_tk - if VarName in Gen.module.globals: - existing = Gen.module.globals[VarName] - if isinstance(existing, ir.GlobalVariable): - return - if Node.annotation: - parse_result = ParseListAnnotation(Node.annotation) - if parse_result and not parse_result.is_pointer: - # t.CArray[type, count] 数组声明 - elem_type_node = parse_result.elem_type_node - count_node = parse_result.count_node - ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable) - if ElemTypeInfo: - elem_type = Gen._ctype_to_llvm(ElemTypeInfo) - if isinstance(elem_type, ir.VoidType): - elem_type = ir.IntType(8) - if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int): - var_type = ir.ArrayType(elem_type, count_node.value) - elif isinstance(count_node, ast.Constant) and count_node.value is None: - var_type = ir.ArrayType(elem_type, 0) - else: - var_type = Gen._ctype_to_llvm(ElemTypeInfo) - IsPtr = False - else: - TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) - if TypeInfo: - var_type = Gen._ctype_to_llvm(TypeInfo) - IsPtr = TypeInfo.IsPtr - if isinstance(var_type, ir.IdentifiedStructType) and (var_type.elements is None or len(var_type.elements) == 0): - var_type = ir.IntType(32) - if Node.value and ModulePath and IsPtr and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str): - str_val = Node.value.value + '\x00' - str_bytes = str_val.encode('utf-8') - arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes)) - str_gv_name = VarName + '_str' - if str_gv_name not in Gen.module.globals: - str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name) - str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) - str_gv.linkage = 'internal' - ptr_type = ir.PointerType(ir.IntType(8)) - if VarName not in Gen.module.globals: - ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName) - str_gv = Gen.module.globals[str_gv_name] - ptr_gv.initializer = ir.Constant.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)]) - ptr_gv.linkage = 'internal' - Gen.variables[VarName] = None - return - - # 检查是否是 CDefine 常量或 CTypedef 类型别名 - IsCDefine = False - IsCTypedef = False - if Node.annotation: - if isinstance(Node.annotation, ast.Attribute): - if getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CDefine': - IsCDefine = True - elif getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CTypedef': - IsCTypedef = True - elif isinstance(Node.annotation, ast.Name): - if Node.annotation.id == 'CDefine': - IsCDefine = True - elif Node.annotation.id == 'CTypedef': - IsCTypedef = True - - if IsCTypedef: - # 检查符号是否已存在且已正确解析(OriginalType 已设置),避免覆盖 - existing_sym = self.Trans.SymbolTable.lookup(VarName) if hasattr(self.Trans.SymbolTable, 'lookup') else None - if existing_sym and isinstance(existing_sym, CTypeInfo) and existing_sym.IsTypedef and existing_sym.OriginalType: - return - if Node.value: - ValueTypeInfo = None - if isinstance(Node.value, ast.BinOp) and isinstance(Node.value.op, ast.BitOr): - ValueTypeInfo = self.Trans.TypeMergeHandler.MergeTypes(Node.value) - else: - ValueTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.value) - if isinstance(ValueTypeInfo, CTypeInfo) and (ValueTypeInfo.BaseType or ValueTypeInfo.PtrCount > 0): - # GetCTypeInfo 对 t.CInt 等 Attribute 返回的 BaseType 可能是 CType 基类实例 - # (而非具体子类如 CInt),导致 _resolve_typedef_int_width 无法解析位宽。 - # 此时从 Node.value 提取类型名,设置 OriginalType 为字符串, - # 让 _resolve_typedef_int_width 通过 CTypeRegistry.GetClassByName 解析。 - if ValueTypeInfo.BaseType and type(ValueTypeInfo.BaseType) is t.CType: - if isinstance(Node.value, ast.Attribute) and getattr(Node.value.value, 'id', None) == 't': - type_name: str = Node.value.attr - resolved_cls = CTypeRegistry.GetClassByName(type_name) - if resolved_cls is not None: - ValueTypeInfo.OriginalType = type_name - ValueTypeInfo.IsTypedef = True - ValueTypeInfo.Name = VarName - # 设置 OriginalType,确保 _resolve_name 能解析(MergeTypes 不为 BinOp 结果设置 OriginalType) - if not ValueTypeInfo.OriginalType: - OriginalCopy: CTypeInfo = ValueTypeInfo.Copy() - OriginalCopy.IsTypedef = False - OriginalCopy.Name = '' - ValueTypeInfo.OriginalType = OriginalCopy - self.Trans.SymbolTable.insert(VarName, ValueTypeInfo) - if module_name: - FullName = f"{module_name}.{VarName}" - self.Trans.SymbolTable.insert(FullName, ValueTypeInfo) - return - TTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) - if TTypeInfo: - TTypeInfo.IsTypedef = True - TTypeInfo.Name = VarName - self.Trans.SymbolTable.insert(VarName, TTypeInfo) - if module_name: - FullName = f"{module_name}.{VarName}" - self.Trans.SymbolTable.insert(FullName, TTypeInfo) - return - - if IsCDefine: - # CDefine 宏不生成 LLVM 全局变量 - # 治本修复(方案C):裸名只注册到本模块 _define_constants, - # 不再注册到跨模块共享的 SymbolTable,避免同名 CDefine 污染其他模块的全局变量 - # (如 Phase1.SHA1_LEN=17 覆盖 VpiReader.SHA1_LEN=16 导致双点链接错误)。 - # 跨模块导入通过 _RegisterFromImportAliases 查带前缀全名后注册到本模块 _define_constants。 - DefineValue = None - if Node.value: - DefineValue = self._ExtractConstValue(Node.value) - # 仅本模块 CDefine 注册到 _define_constants(供 _HandleNameLlvm 步骤1 查找) - if module_name is None and DefineValue is not None: - define_constants = vars(Gen).setdefault('_define_constants', {}) - if VarName not in define_constants: - define_constants[VarName] = DefineValue - # 如果提供了模块名,注册带模块前缀的全名到 SymbolTable - # (供 _RegisterFromImportAliases 的 from X import Y 显式导入查找) - if module_name: - FullName = f"{module_name}.{VarName}" - existing_full = self.Trans.SymbolTable.lookup(FullName) - if not (existing_full and existing_full.IsTypedef and existing_full.OriginalType): - self._RegisterCDefineSymbol(FullName, DefineValue, Node.lineno, ModulePath or 'unknown') - return # 不再继续生成 LLVM 全局变量 - - # t.State 标记的全局变量:只声明不定义(external linkage),避免 DSO/non-DSO 重定义 - if TypeInfo and TypeInfo.IsState: - if VarName not in Gen.module.globals: - gv = ir.GlobalVariable(Gen.module, var_type, name=VarName) - gv.linkage = 'external' - Gen._export_funcs.add(VarName) - return - - gv = ir.GlobalVariable(Gen.module, var_type, name=VarName) - # 外部全局变量声明:只声明不定义(external linkage),避免多重定义 - gv.linkage = 'external' - elif isinstance(Node, ast.Assign) and Node.targets and isinstance(Node.targets[0], ast.Name): - VarName = Node.targets[0].id - if VarName.startswith('_'): - return - if VarName in Gen.module.globals: - return - gv = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName) - gv.linkage = 'external' - - def _ExtractValue(self, value_node: ast.AST, target_type: ir.Type, var_name: str | None = None) -> ir.Constant | None: - """Extract constant value from AST node for global variable initializer""" - # 处理负号 - if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub): - if isinstance(value_node.operand, ast.Constant) and isinstance(value_node.operand.value, int): - neg_val = -value_node.operand.value - if isinstance(target_type, ir.IntType): - return ir.Constant(target_type, neg_val) - return ir.Constant(ir.IntType(32), neg_val) - # 处理正号 - elif isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.UAdd): - return self._ExtractValue(value_node.operand, target_type) - - if isinstance(value_node, ast.Constant): - if isinstance(value_node.value, bool): - if isinstance(target_type, ir.IntType): - return ir.Constant(target_type, 1 if value_node.value else 0) - return ir.Constant(ir.IntType(32), 1 if value_node.value else 0) - elif isinstance(value_node.value, int): - if isinstance(target_type, ir.IntType): - return ir.Constant(target_type, value_node.value) - return ir.Constant(ir.IntType(32), value_node.value) - elif isinstance(value_node.value, str): - str_val = value_node.value + '\x00' - str_bytes = str_val.encode('utf-8') - arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes)) - Gen = self.Trans.LlvmGen - gv_name = var_name + '_str' if var_name else f"str_global_{id(value_node)}" - if gv_name in Gen.module.globals: - gv = Gen.module.globals[gv_name] - else: - gv = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) - gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) - gv.linkage = 'internal' - ptr_type = ir.PointerType(ir.IntType(8)) - return ir.Constant(ptr_type, gv.reference) - elif isinstance(value_node.value, bool): - return ir.Constant(ir.IntType(32), 1 if value_node.value else 0) - elif value_node.value is None: - if isinstance(target_type, ir.BaseStructType): - return ir.Constant(target_type, None) - if isinstance(target_type, ir.PointerType): - return ir.Constant(target_type, None) - return ir.Constant(target_type, 0) - elif isinstance(value_node, ast.Name): - if value_node.id == 'None': - if isinstance(target_type, ir.BaseStructType): - return ir.Constant(target_type, None) - if isinstance(target_type, ir.PointerType): - return ir.Constant(target_type, None) - return ir.Constant(target_type, 0) - return None - - def _ExtractConstValue(self, value_node: ast.AST) -> object: - """Extract constant value from AST node for SymbolTable registration - 支持常量、一元运算符、二元运算符和常量引用 - """ - if isinstance(value_node, ast.Constant): - return value_node.value - elif isinstance(value_node, ast.BinOp): - left = self._ExtractConstValue(value_node.left) - right = self._ExtractConstValue(value_node.right) - if left is None or right is None: - return None - if isinstance(value_node.op, ast.Add): - return left + right - elif isinstance(value_node.op, ast.Sub): - return left - right - elif isinstance(value_node.op, ast.Mult): - return left * right - elif isinstance(value_node.op, ast.Div): - return left // right if isinstance(left, int) and isinstance(right, int) else left / right - elif isinstance(value_node.op, ast.FloorDiv): - return left // right - elif isinstance(value_node.op, ast.Mod): - return left % right - elif isinstance(value_node.op, ast.Pow): - return left ** right - elif isinstance(value_node.op, ast.LShift): - return left << right - elif isinstance(value_node.op, ast.RShift): - return left >> right - elif isinstance(value_node.op, ast.BitOr): - return left | right - elif isinstance(value_node.op, ast.BitXor): - return left ^ right - elif isinstance(value_node.op, ast.BitAnd): - return left & right - elif isinstance(value_node, ast.UnaryOp): - operand = self._ExtractConstValue(value_node.operand) - if operand is None: - return None - if isinstance(value_node.op, ast.USub): - return -operand - elif isinstance(value_node.op, ast.UAdd): - return +operand - elif isinstance(value_node.op, ast.Invert): - return ~operand - elif isinstance(value_node, ast.Name): - if value_node.id in getattr(self.Trans, 'SymbolTable', {}): - info = self.Trans.SymbolTable[value_node.id] - if info.IsDefine and info.DefineValue is not None: - return info.DefineValue - elif isinstance(value_node, ast.Call): - if isinstance(value_node.func, ast.Attribute): - if getattr(value_node.func.value, 'id', None) == 't': - if value_node.args: - return self._ExtractConstValue(value_node.args[0]) - elif isinstance(value_node.func, ast.Name): - if value_node.args: - return self._ExtractConstValue(value_node.args[0]) - return None - - def _ExtractTypedefOriginalType(self, value_node: ast.AST) -> str | None: - if isinstance(value_node, ast.Attribute): - if getattr(value_node.value, 'id', None) == 't': - llvm_str = CTypeRegistry.NameToLLVM(value_node.attr) - if llvm_str: - return llvm_str - elif isinstance(value_node, ast.Name): - llvm_str = CTypeRegistry.NameToLLVM(value_node.id) - if llvm_str: - return llvm_str - elif isinstance(value_node, ast.BinOp) and isinstance(value_node.op, ast.BitOr): - left = self._ExtractTypedefOriginalType(value_node.left) - right = self._ExtractTypedefOriginalType(value_node.right) - parts = [] - if left: - parts.append(left) - if right: - parts.append(right) - if parts: - return '|'.join(parts) - elif isinstance(value_node, ast.Call): - if isinstance(value_node.func, ast.Attribute): - if getattr(value_node.func.value, 'id', None) == 't': - llvm_str = CTypeRegistry.NameToLLVM(value_node.func.attr) - if llvm_str: - return llvm_str - elif isinstance(value_node.func, ast.Name): - llvm_str = CTypeRegistry.NameToLLVM(value_node.func.id) - if llvm_str: - return llvm_str - return None - - def _RegisterCDefineSymbol(self, FullName: str, DefineValue: object, lineno: int, FilePath: str) -> None: - """Register CDefine constant to SymbolTable""" - # 防御:新值为 None 但已存在非 None 值时保留旧值。 - # 翻译期间声明发射调用 _ExtractConstValue(不支持 ast.Attribute 形式如 t.CSizeT().Size), - # 会返回 None 并覆盖共享符号表中已由 ConstEvaluator 正确求值的值(如 8)。 - # 共享符号表在 _build_shared_symbol_data 阶段已正确求值,此处不应覆盖。 - if DefineValue is None: - existing: Any = self.Trans.SymbolTable.lookup(FullName) - if existing and existing.IsDefine and existing.DefineValue is not None: - return - info = CTypeInfo() - info.IsDefine = True - info.DefineValue = DefineValue - info.Lineno = lineno - info.file = FilePath - # 直接添加到 SymbolTable 字典 - self.Trans.SymbolTable.insert(FullName, info) - - def _check_annotation_for_state(self, annotation: ast.AST) -> bool: - return (AnnotationContainsName(annotation, 'CExport') or - AnnotationContainsName(annotation, 'CExtern') or - AnnotationContainsName(annotation, 'State')) - - def _check_decorators_for_state(self, Node: ast.FunctionDef) -> bool: - """检查装饰器列表中是否包含 @t.CExport / @t.CExtern / @t.State(t.CExport 既能作返回值注解也能作装饰器)""" - if not Node.decorator_list: - return False - for decorator in Node.decorator_list: - if (AnnotationContainsName(decorator, 'CExport') or - AnnotationContainsName(decorator, 'CExtern') or - AnnotationContainsName(decorator, 'State')): - return True - return False - - def _EmitExternalFuncDeclLlvm(self, Node: ast.FunctionDef, Gen: LlvmGeneratorMixin, is_class_method: bool = False, source_module_name: str | None = None, register_module_name: str | None = None, reexport_module_names: list[str] | None = None) -> None: - FuncName = Node.name - if (Node.returns and self._check_annotation_for_state(Node.returns)) or self._check_decorators_for_state(Node): - Gen._export_funcs.add(FuncName) # t.State 标记的外部 C 函数必须保持原始名称,以便链接器解析 - if source_module_name and source_module_name not in ('c', 't'): - _src_sha1 = Gen.ModuleSha1Map.get(source_module_name) - if not _src_sha1: - _init_name = f"{source_module_name}.__init__" - if _init_name in Gen.ModuleSha1Map: - _src_sha1 = Gen.ModuleSha1Map[_init_name] - if _src_sha1: - Gen._export_extern_funcs[FuncName] = _src_sha1 - # 自动取址模式(ref/temp/always)的登记**必须先于下面的 `FuncName in Gen.functions` - # 早退**:该函数已存在(stub / 调用点先建)时本函数会在此直接返回,若把登记放在 - # 后面 func 创建处(约 :1885)就永远不会执行 ⇒ 跨模块函数的取址模式静默失效。 - # 查表键与查表侧 FuncGen._adjust_args 的 func.name 逐字一致,故这里按"真实名候选" - # 双写:新算出的 mangled 名,以及已存在函数对象的实际名字(若已存在)。 - _auto_modes: list[str | None] = [ - FindAutoAddrMode(a.annotation) for a in Node.args.args] - if any(m is not None for m in _auto_modes): - _early_mangled: str = Gen._mangle_func_name(FuncName, module_name=source_module_name) - Gen._auto_addr_params[_early_mangled] = _auto_modes - Gen._auto_addr_params[FuncName] = _auto_modes - _early_existing = Gen.functions.get(FuncName) - _early_real: str | None = getattr(_early_existing, 'name', None) - if _early_real: - Gen._auto_addr_params[_early_real] = _auto_modes - if FuncName in Gen.functions: - return - if Node.decorator_list: - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): - # fail-fast:`@c.CReturn` 已移除,扫到即报错。 - if decorator.func.attr == 'CReturn': - raise SyntaxError( - f"函数 '{FuncName}':`@c.CReturn` 已移除:请改用返回注解 " - f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" - ) - IsPtr = False - ReturnTypeInfo = None - if Node.returns: - try: - ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) - # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 - if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CChar() - ReturnTypeInfo.PtrCount = 1 - if ReturnTypeInfo is None: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - IsPtr = ReturnTypeInfo.IsPtr +from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod + + +# 注解里的存储类 / 限定符标记:这些名字**不是类名**(联合注解里与指针类混写, +# 如 `t.CExtern | rawptr[CompilerFactory]`),提取"指针指向的类"时必须滤掉。 +_MARKER_ANNOT_NAMES: frozenset[str] = frozenset({ + 'CExtern', 'CExport', 'CStatic', 'CInline', 'CConst', 'CVolatile', 'CRegister', + 'CAuto', 'CDefine', 'CTypedef', 'CPostdefinition', 'CPtr', 'CArrayPtr', 'CVoid', + 'State', 'TLS', +}) + +# 标记基类名(不是真实父类):与 HandlesClassDef._EmitClassLlvm 的 base 跳过表同口径。 +_MARKER_BASE_NAMES: frozenset[str] = frozenset({ + 'Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', + 'Exception', 'Enum', +}) + +# LLVM 基础类型表(模块级常量):`_parse_llvm_type_impl` 原先把这份字典**每次调用现建** +# (profile 2026-09-29:本函数 1,689,809 次调用 ⇒ 字典字面量 1.69M 次、 +# `IntType.__new__` 9.09M 次)。这些基础类型在 llvmlite 里无状态且 IntType 按宽度 +# 缓存同一实例,跨调用/跨 Gen 共享安全 ⇒ 提升为模块常量,表意逐字不变。 +_LLVM_PRIMITIVE_TYPE_MAP: dict[str, ir.Type] = { + 'void': ir.VoidType(), + 'i1': ir.IntType(1), + 'i8': ir.IntType(8), + 'i16': ir.IntType(16), + 'i32': ir.IntType(32), + 'i64': ir.IntType(64), + 'float': ir.FloatType(), + 'double': ir.DoubleType(), +} + + +# 字面量正则的模块级预编译(性能:这些调用点做逐行扫描/逐条解析,命中次数极高; +# 每次 `re.match(字面量, s)` 都要做"模式串哈希 → re 缓存查找"。预编译成模块级常量 +# 后省掉该查找,且匹配路径更短。模式串一字未改。 +# 解析异常报文里兜底取 `at line N` +_RE_AT_LINE: re.Pattern[str] = re.compile(r'at line (\d+)') +# stub 里 `%.Name = type <...>` 结构体定义行 +_RE_STRUCT_TYPE_DEF: re.Pattern[str] = re.compile(r'%"?((?:[a-f0-9]+\.)?([\w\[\]]+))"?\s*=\s*type\s*(.*)') +# `[N x ]` 数组类型 +_RE_ARRAY_TYPE: re.Pattern[str] = re.compile(r'\[(\d+)\s*x\s+(.+)\]') +# declare 行(函数名限定 `[\w.]+` 字符集) +_RE_DECLARE_SIMPLE: re.Pattern[str] = re.compile(r'declare\s+(.+?)\s+@("?[\w.]+"?)\s*\((.*)\)$') +# declare 行(函数名允许任意非括号字符) +_RE_DECLARE_GENERIC: re.Pattern[str] = re.compile(r'declare\s+(.+?)\s+@("?[^()]+"?)\s*\((.*)\)$') +# 剥离 LLVM IR 参数名:`\s+%name` 或 `\s+%"quoted.name"` 结尾 +_RE_PARAM_NAME_TRAILING: re.Pattern[str] = re.compile(r'\s+%("[^"]*"|[\w.]+)\s*$') +# 剥离 LLVM IR 参数名:`\s+%name`(无引号形式) +_RE_PARAM_NAME_PLAIN: re.Pattern[str] = re.compile(r'\s+%[\w.]+\s*$') +# stub 里引用到的 sha1 前缀 `%<16hex>.` +_RE_SHA1_PREFIX_REF: re.Pattern[str] = re.compile(r'%\"?([a-f0-9]{16})\.') +# `@name = external global <...>` 全局变量定义行 +_RE_EXTERNAL_GLOBAL_DEF: re.Pattern[str] = re.compile(r'@("?)(?:([a-f0-9]{16})\.)?([\w.\[\]]+)\1\s*=\s*external\s+global\s+(\S.*)') +# 去掉全局类型尾部 zeroinitializer +_RE_STRIP_ZEROINIT: re.Pattern[str] = re.compile(r'\s+zeroinitializer.*$') +# 去掉全局类型尾部 `, align N` +_RE_STRIP_ALIGN: re.Pattern[str] = re.compile(r'\s*,\s*align\s+\d+.*$') +# struct 定义名(不强制 `= type ...` 后缀) +_RE_STRUCT_NAME: re.Pattern[str] = re.compile(r'%"?((?:[a-f0-9]+\.)?[\w.\[\]*]+)"?') + + +def _HasRealParentClass(Node: ast.ClassDef) -> bool: + """类是否声明了**真实父类**(排除 Object/CVTable/NoVTable/CEnum/... 标记基类)。""" + for base in getattr(Node, 'bases', None) or []: + base_name: str | None = None + if hasattr(base, 'id'): + base_name = base.id + elif hasattr(base, 'attr'): + base_name = base.attr + elif isinstance(base, ast.Subscript): + if hasattr(base.value, 'id'): + base_name = base.value.id + elif hasattr(base.value, 'attr'): + base_name = base.value.attr + if base_name and base_name not in _MARKER_BASE_NAMES: + return True + return False + + +def _VtableByClassLevelForImport(Node: ast.ClassDef, Gen: object) -> bool: + """跨模块导入类的 VtableByClassLevel(忽略方法级 CVTable 标记的 vtable 状态)。 + + 与 HandlesClassDef._resolve_class_flags 的口径一致:类级 @t.CVTable 或 + "有真实父类且父类不是 NoVTable" ⇒ True。供 SymbolUtils.IsVtableSlotMethod + 判定方法是否占虚表槽(唯一的判据源),使导入侧的虚表槽序与定义模块逐槽相同。 + """ + if getattr(Node, 'decorator_list', None): + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Attribute) and decorator.attr == 'CVTable': + return True + if isinstance(decorator, ast.Name) and decorator.id == 'CVTable': + return True + if not _HasRealParentClass(Node): + return False + _novtable_set: set = getattr(Gen, '_cross_module_novtable', None) or set() + for base in getattr(Node, 'bases', None) or []: + base_name: str | None = None + if hasattr(base, 'id'): + base_name = base.id + elif hasattr(base, 'attr'): + base_name = base.attr + if base_name and base_name not in _MARKER_BASE_NAMES: + if base_name in _novtable_set: + return False + return True + + +def _ParseFailureSite(Exc: BaseException) -> str: + """解析异常 → `行[:列]` 文本;定位信息完全取不到时返回 `'?'`。 + + CPython/vast 的 SyntaxError 若按 (msg, (file, lineno, offset, text)) 构造则带 + `.lineno`/`.offset`;而 TPC 自研 vast 的 Parser/ExprParser 只把行号写进报文 + (`f"... at line {tok.Line}"`,见 lib/vast/Parser.py:826、ExprParser.py:640), + 字段为空 ⇒ 必须正则兜底,否则诊断退化成"无行号",无法直接跳到失败行。 + """ + Line: int = int(getattr(Exc, 'lineno', 0) or 0) + Col: int = int(getattr(Exc, 'offset', 0) or 0) + if not Line: + Matched: re.Match | None = _RE_AT_LINE.search(str(Exc)) + if Matched: + Line = int(Matched.group(1)) + if not Line: + return '?' + return f"{Line}:{Col}" if Col else str(Line) + + +def _ReportDeclarationParseFailure(FilePath: str, Exc: BaseException, Consequence: str) -> None: + """声明文件"读 → parse"失败的统一诊断口径(strict fail-fast / 非严格告警)。 + + 诊断必带四要素:**文件路径**、**失败行[:列]**、**异常类型与文本**、**后果说明** + (后果由调用方按点位给出,如"该模块声明将全部缺失,后续可能表现为 + `Undefined function`")。原实现(`_LoadModuleDeclarationsFromFile:364-367`)只在 + strict 模式调 `Trans.LogWarning`——而 `Trans.Warnings` 全仓只被 append、无人消费 + (BaseTranslator.py:55/137),**连 strict 模式都是无声的**;非严格模式更是直接 + `return`。于是 `includes/ast/base.vp:168` 的 `enum ASTFlag:` 让整个 base.vp 的 + 声明(含顶层 `_init_ast`)一个都不注册,下游只报 `Undefined function: '_init_ast'`, + 浪费了一轮排查。 + + fail-fast 依据:仓库有严格模式开关 `lib/constants/config.py:59 mode = "strict"`, + 且既有多处同口径 `if _config_mode == "strict": raise`(本文件 + `_RegisterSubModuleSha1:547`、`_build_stub_index:2443`、`_TryLoadStructFromStub:2539` + 等)——本函数与它们一致,未放宽任何既有 fail-fast。 + + `FileNotFoundError` 例外:属"isfile 与 open 之间文件被删/被 `--clean` 清掉"的 + 竞态(先例见本文件 `_build_stub_index:2439-2441`),不是"源码解析失败", + 故只告警、不触发 fail-fast。 + """ + Site: str = _ParseFailureSite(Exc) + Message: str = ( + f"声明解析失败 {FilePath}:{Site} — {type(Exc).__name__}: {Exc};{Consequence}" + ) + if _config_mode == "strict" and not isinstance(Exc, FileNotFoundError): + raise ImportError(Message) from Exc + _vlog().warning(Message, "Exception") + + +def PrependInheritedBlock(Gen: object, ClassName: str, Block: list) -> bool: + """把祖先字段块 `Block` 前置到 `Gen.class_members[ClassName]`(幂等,且**不删块内条目**)。 + + 为什么单独成函数:这段判据是"继承段与 body 继承前缀一一对应"的唯一保障,必须可 + 独立验证(见本仓"按'表头是否已等于块'判幂等"的三条性质)。 + + 判据与性质: + ① **不删块内条目**。块里每一项都对应 body 的一个继承槽;只要块内某项被删, + 被删项之后的继承字段的表内下标就会整体前移 k,而查询侧回退路径用 + `base + 表内下标`(FuncGen._get_member_offset)⇒ 落到类型不同的槽。 + 旧实现按"名字已在整表内"过滤整块,正是这样删掉了**与子类自有字段重名**的 + 继承项(现场:TypeGenCType 重复声明 5 个父类已有字段后,继承字段 + LastBuiltinsTypedefResult 下标 32→27,写进 i32 槽 → "指针隐式转整数已禁止")。 + ② **幂等**。本函数会被多个加载路径重复调用(_LoadModuleDeclarationsFromFile / + _LoadDeclarationsFromStubLlvm / _TryLoadClassMembersFromPyi 懒加载);判据是 + "表头前 len(Block) 项的名字序列是否等于 Block 的名字序列"——相等即整块已前置, + 直接返回 False(不再前置),避免 2026-08-10 那类重复字段 + (BasicBlock: [Next, Next, Name, ...] → 与 struct elements 数量不匹配 → 偏移错乱)。 + ③ **同名并存**。子类自有字段与继承项同名时,两者都保留(定义端 + HandlesClassDef 的 body 同样是"继承前缀 + 自有段",两侧槽数一致); + 源码层面的这种重名已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, + 此处只是不让它把布局判据带偏。 + + 已知残留(**已在下方修复,2026-09-30**):若此前只前置过**部分**祖先块(父类表当时 + 较短),本次会整块再前置一次,该前缀出现重复条目。现于前置前调用 + `DropStaleAncestralCopies` 剥掉两端上的陈旧半截副本(判据:插入边上与 `Block` 前缀 + 逐项同名同型,且这些名字在表里**另有**一份正本)⇒ 不再残留。目标串接点与 + HandlesClassDef 的追加侧共用同一函数(两侧判据对称)。 + + 返回 True 表示本次真的前置了。 + """ + _Members: list = getattr(Gen, 'class_members', {}).get(ClassName) or [] + _BlockNames: list = [nm for nm, _ in Block] + if not _BlockNames: + return False + # 【规范化:剥掉"祖先块前缀的陈旧副本"(2026-09-30)】判据与成因见 + # NormalizeAncestralCopies —— 本函数文档"已知残留"一段的实测残留 + # (ClassGenDef 表 50 项 / 去重 42 项 / body 42 槽)在此消除。 + # 返回的 `_HasFull` 与原有的两条幂等判据("表头等于块" / InheritedBlockAlreadyPresent + # 的头尾整段判据)等价,故原两条提前 return 合并为这一条。 + _Trimmed: list + _HasFull: bool + _Trimmed, _HasFull = NormalizeAncestralCopies(list(_Members), Block, InsertAtHead=True) + if len(_Trimmed) != len(_Members): + _vlog().warning( + f"[成员展平] 类 '{ClassName}' 的祖先块({len(Block)} 项)插入前剥掉 " + f"{len(_Members) - len(_Trimmed)} 条**陈旧半截副本**(重名条目不占布局槽位," + f"却会让 base/下标回退失准):表 {len(_Members)} → {len(_Trimmed)} 项", + "成员展平") + getattr(Gen, 'class_members')[ClassName] = _Trimmed + _Members = _Trimmed + if _HasFull: + return False + getattr(Gen, 'class_members')[ClassName] = list(Block) + list(_Members) + return True + + +def InheritedBlockAlreadyPresent(Table: list, Block: list) -> bool: + """祖先字段块 `Block` 是否已**整段**位于 `Table` 的**头部或尾部**(名字 + IR 类型逐项相同)。 + + 为什么查头/尾两处而不是"表中任意位置":这两处恰好就是块被插入的两个方向 —— + 导入侧 `PrependInheritedBlock` 前置(⇒ 头),定义侧 HandlesClassDef 展平追加 + (⇒ 尾)。表中段出现的同序列与**合法的同名并存**(子类在自有段重复声明父类字段, + body 本来就有两个槽)不可区分,判成"已存在"会误删本该有的槽,故刻意不判—— + 真出现中段重复时仍由 StructGen 的重名条目告警兜住。 + + 为什么需要独立判据:两侧判据此前不对称 —— 导入侧只比"表头是否等于块"(前置幂等), + 定义侧**没有任何判据**(直接 `class_members + _flat`)⇒ "块已在尾部"时整块再追加 + 一次,表里多出 `len(Block)` 条重复条目,body 随之多出同名槽: + + 现场(2026-09-30,on-disk 实证):`output/.stub.ll` 的 + `750b8c10137d273e.TypeGenCType` body **73** 槽 = temp(Phase 1 声明)**41** 槽 + + 其 `[9:41]`(32 项,恰为追加前表的**尾部**)的原样副本;TypeGenCDef / + TypeGenErr 同为 +32,ClassGenMixin / ClassGenVTable / ClassGenEnum / + ClassGenDef 为 +8(两阶段布局不一致的类共 7 个,全在 Generator/ClassGen/ + TypeGen 继承链上)。下游表现:`Repl.vp:802` + `GenApi._g_gen.TypeGenCType.CTypedefNames` 因两份 body(41/73)歧义而 + "基座 pointee 未匹配 Gen.structs 中任何 struct"。 + """ + _n: int = len(Block) + if _n == 0 or _n > len(Table): + return False + + def _SameAt(_Start: int) -> bool: + for _j in range(_n): + _t: Any = Table[_Start + _j] + _b: Any = Block[_j] + if (not isinstance(_t, tuple) or not isinstance(_b, tuple) + or len(_t) < 2 or len(_b) < 2): + return False + if _t[0] != _b[0] or _t[1] != _b[1]: + return False + return True + + return _SameAt(0) or _SameAt(len(Table) - _n) + + +def _EqRun(Table: list, TableStart: int, Block: list, BlockStart: int, Count: int) -> bool: + """`Table[TableStart : TableStart+Count]` 与 `Block[BlockStart : BlockStart+Count]` + 是否**逐项同 name 同 IR 类型**。""" + _i: int + for _i in range(Count): + _t: Any = Table[TableStart + _i] + _b: Any = Block[BlockStart + _i] + if (not isinstance(_t, tuple) or not isinstance(_b, tuple) + or len(_t) < 2 or len(_b) < 2): + return False + if _t[0] != _b[0] or _t[1] != _b[1]: + return False + return True + + +def TrimStalePrefixCopy(Table: list, Block: list, AtHead: bool) -> list: + """剥掉 `Table` **指定端**上那段"祖先块 `Block` 前缀的陈旧副本"(返回新表)。 + + 只在 `AtHead` 指定的那一端找,且**不会**碰另一端 —— 因为另一端往往是**正本** + (完整块本身)。实测反例(本函数第一版的两端都剥):`[A,B,C,D]+[A,B]` 这种 + "正本在头、副本在尾"的表,两端都剥会把**头的正本**剥掉,得到 `[C,D,A,B]`, + 再把完整块前置就变成 `[A,B,C,D,C,D,A,B]` —— 副本反而更多。故判据必须**先定位 + 正本**(见 NormalizeAncestralCopies),再剥另一端。 + + 判据(全部为正向事实): + · 该端连续游程 `Table[:j]`(AtHead)或 `Table[len-j:]` 与 `Block[:j]` 逐项同名同型; + · `1 ≤ j < len(Block)`:整块本身不算"陈旧副本"; + · 这 j 个名字在 `Table` 里各自**另有**一份(计数 ≥ 2)—— 陈旧副本的特征就是 + "名字已有一份正本";子类合法的自有同名段只会出现一次 ⇒ 该条件把"自家字段 + 恰好与祖先块前缀同名同型"的误判排除。 + """ + if not Table or len(Block) < 2: + return Table + _j_max: int = min(len(Table), len(Block) - 1) + _counts: dict = {} + _it: Any + for _it in Table: + if isinstance(_it, tuple) and len(_it) >= 2: + _counts[_it[0]] = _counts.get(_it[0], 0) + 1 + _j: int + for _j in range(_j_max, 0, -1): + _start: int = 0 if AtHead else len(Table) - _j + if not _EqRun(Table, _start, Block, 0, _j): + continue + _all_dup: bool = True + _i: int + for _i in range(_j): + if _counts.get(Block[_i][0], 0) < 2: + _all_dup = False + break + if _all_dup: + return Table[_j:] if AtHead else Table[:len(Table) - _j] + return Table + + +def NormalizeAncestralCopies(Table: list, Block: list, InsertAtHead: bool) -> tuple[list, bool]: + """插入祖先块 `Block` 前把 `Table` 规范化成"完整块最多出现一次",返回 `(新表, 是否已含完整块)`。 + + 三种现场(都由"**半截祖先块**被写入 + 事后整块插入"造成;`PrependInheritedBlock` + 文档里的"已知残留"即此),一并处理: + + ① 完整块在**头**、尾部挂着前缀副本 ⇒ 剥**尾**(副本是被后来的整块前置推过去的); + ② 完整块在**尾**、头部挂着前缀副本 ⇒ 剥**头**(副本先写入,整块随后追加); + ③ 完整块尚未出现、且**插入边**上挂着前缀副本 ⇒ 剥插入边。③ 只在"这些名字在表里 + 已另有正本"时成立(判据见 TrimStalePrefixCopy):**首次**写入半截块时表里还没有 + 正本,此时不剥(无可判据),代价是该半截块会先留在表里,等下一次整块插入时由 + ①/② 剥掉 —— 即本函数对残留是**自愈**的(`PrependInheritedBlock` 会被多个加载 + 路径重复调用,定义侧也有重复收尾)。 + + 实测现场(用户构建日志 2026-09-30):`ClassGenDef` 表 **50** 项 / 去重 **42** 项, + 而 body 恰为 42 槽(= 父类 ClassGenBase 的 42 字段,ClassGenDef 自身 0 字段) + ⇒ 多出的 8 条正是祖先块前缀的陈旧副本。重名条目**不是布局槽位**(见 + `FuncGen._get_member_offset` 的重名段),却让 `FuncGen._member_base` 的 + `_Delta = 槽数 - 表长 = 42 - 50 = -8` 走进类型校准、并让回退路径的 + `base + 表内下标` 整体偏斜(同族现场:MatchCase / TypeGenResolve 的偏移算错)。 + + 剥掉的每一项在(已在表里的、或随后插入的)完整块里同名同型 ⇒ **不丢任何布局信息**, + 与 `PrependInheritedBlock` 性质①"不删块内条目"不冲突(被剥的是副本,正本仍在)。 + """ + _n: int = len(Block) + if _n == 0 or not Table or len(Table) < _n: + # 表尚不足以容纳完整块:只可能是现场③(或表为空/正常),按插入边剥 + return TrimStalePrefixCopy(Table, Block, AtHead=InsertAtHead), False + _HasHead: bool = _EqRun(Table, 0, Block, 0, _n) + _HasTail: bool = _EqRun(Table, len(Table) - _n, Block, 0, _n) + if _HasHead: + return TrimStalePrefixCopy(Table, Block, AtHead=False), True + if _HasTail: + return TrimStalePrefixCopy(Table, Block, AtHead=True), True + return TrimStalePrefixCopy(Table, Block, AtHead=InsertAtHead), False + + +class ImportHandle(BaseHandle): + _struct_Load_cache = {} + _pyi_cache = {} + _pyi_cache_dir = None + _project_root_cache = None + # 目录 os.walk 结果缓存(import 失败兜底查找用):目录 -> [(root, files)]。 + # 编译进程内 SearchDirsForSubmodule(includes/LibraryPaths/工程目录)内容不变, + # 避免每次 import 解析失败都全量递归扫描目录树(PROF:import 热路径高频调用)。 + _walk_cache: dict[str, list[tuple[str, list[str]]]] = {} + # 类级 stub 索引:class_name -> list of (full_name, struct_body, file_sha1, from_output) + # 一次扫描所有 .stub.ll 文件构建索引,避免 _TryLoadStructFromStub 每次调用都全量扫描 + # (原 609 次调用 × ~100 stub 文件 × ~50 行 = 5.5M 正则匹配 → 一次扫描 ~5000 匹配) + _stub_index: dict[str, list[tuple[str, str, str, bool]]] = {} + # 已解析进索引的 stub 文件绝对路径集合(增量重建用)。 + # 单次编译进程内 stub 只增不减(output 逐步填充),记录已扫描文件后, + # 签名变化只需增量解析新增文件,避免每编译完一个文件就全量重扫(原 847 次全量重建)。 + _stub_index_scanned: set[str] = set() + # 类级缓存:跨 worker 任务共享(同一进程内,worker 单线程无竞争)。 + # 键均为 (source_sha1, class_name),不同文件同名类(不同 sha1)不冲突。 + # classify:_EmitExternalClassDeclLlvm 开头的 bases/decorator_list 分类遍历结果 + # (与 Gen 无关的纯函数计算,同一 (sha1, class) 跨任务重复调用时跳过遍历)。 + # decl:pyi 树中的 ClassDef 节点引用(pyi 树索引,pyi 树由 _pyi_cache 类级缓存 + # 保证进程内唯一),避免 _TryLoadClassMembersFromPyi 跨任务反复线性扫描 body。 + _extern_class_classify_cache: dict[tuple[str, str], tuple] = {} + _extern_class_decl_cache: dict[tuple[str, str], ast.ClassDef] = {} + # 类级 stub 函数签名缓存(_LookupStubFuncType):跨 worker 任务共享。 + # 与 _stub_index/_stub_index_scanned 同模式:签名变化只增量解析新增 stub + # 文件(编译进程内 stub 只增不改),避免每次任务全量重扫 + 每文件 + # os.path.isfile(Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。 + _stub_func_cache: dict[str, tuple[str, str, bool, str]] = {} + _stub_func_cache_signature: tuple[int, int] | None = None + _stub_func_scanned: set[str] = set() + # stub 文件集合自上次建索引以来是否**可能**变化(写 *.stub.ll 的一方显式置脏)。 + # 见 MarkStubSetDirty / _build_stub_index 的零 syscall 快路径:原实现每次 + # _TryLoadStructFromStub 索引 miss 都要 2× os.path.isdir + 2× os.listdir 重新 + # 拼文件集合(profile:_build_stub_index 被调 238k 次;nt._path_isdir 491s + + # nt.listdir 183s ≈ 总时长 30%),而"集合是否变化"完全可以由写入方免费告知。 + _stub_set_dirty: bool = True + # 目录 → (listdir 时的目录 mtime_ns, 该次统计出的 *.stub.ll 数量)。 + # 键为目录绝对路径:temp/ 与 output/ 分别计缓存。见 _CountStubFilesInDir。 + _stub_dir_count_cache: dict[str, tuple[int, int]] = {} + + @classmethod + def MarkStubSetDirty(cls) -> None: + """标记「stub 文件集合可能已变化」——**所有写 *.stub.ll 的一方都必须调用**。 + + 调用点多在 Phase2Translator:每个文件翻译入口(跨文件新增一律可见)、 + 写 temp/.stub.ll 之后、注册 include 写 output/.stub.ll 之后。 + """ + ImportHandle._stub_set_dirty = True + + def __init__(self, translator: "Translator") -> None: + super().__init__(translator) + # 实例级去重集合:每个 translator 独立跟踪已加载的 .pyi 和已发射的模块声明。 + # 若作为类变量共享,main.py 编译时加载的 __nodes.py 会阻止 __parser.py 编译时 + # 重新加载,导致函数未注册到 __parser.py 的 Gen.functions(NamedExpr bug 根因)。 + self._loaded_pyi_paths: set[str] = set() + self._emitted_module_decls: set[tuple] = set() + # `_parse_llvm_type` 的结果 memo(键 = (类型串, source_sha1, create_structs))。 + # 只缓存**与 Gen 状态无关**的结果,见 `_parse_llvm_type` 的判据;因此跨文件 + # 复用是安全的(i8* 之类纯字面类型与哪个 Gen 无关),不随 `_reset_file_cache` 清空。 + self._llvm_type_memo: dict[tuple, ir.Type] = {} + + def _reset_file_cache(self) -> None: + self._struct_Load_cache.clear() + self._loaded_pyi_paths.clear() + self._emitted_module_decls.clear() + # stub 索引在文件间保留(同一 worker 进程内),但标记需检查是否过期 + # 不清除 _stub_index / _stub_index_scanned,通过增量扫描校验新鲜度 + + def _find_project_root(self) -> str: + if self._project_root_cache is not None: + return self._project_root_cache + # 优先使用 translator 上显式设置的 _temp_dir(由 Phase2Translator._translate_file 传入), + # 这样可以正确处理 include 文件——其源文件目录不在项目根目录下, + # 从源文件目录向上搜索 temp/output 会失败并回退到错误的路径。 + temp_dir_attr = getattr(self.Trans, '_temp_dir', None) + if temp_dir_attr and os.path.isdir(temp_dir_attr): + project_root = os.path.dirname(os.path.abspath(temp_dir_attr)) + if os.path.isdir(os.path.join(project_root, 'temp')) and os.path.isdir(os.path.join(project_root, 'output')): + self._project_root_cache = project_root + return project_root + current_file = getattr(self.Trans, 'CurrentFile', '') or '' + if current_file: + current_dir = os.path.dirname(os.path.abspath(current_file)) + search_dir = current_dir + for _ in range(10): + if os.path.isdir(os.path.join(search_dir, 'temp')) and os.path.isdir(os.path.join(search_dir, 'output')): + self._project_root_cache = search_dir + return search_dir + parent = os.path.dirname(search_dir) + if parent == search_dir: + break + search_dir = parent + # 回退到当前工作目录(编译运行时的项目根目录) + cwd = os.getcwd() + if os.path.isdir(os.path.join(cwd, 'temp')) and os.path.isdir(os.path.join(cwd, 'output')): + self._project_root_cache = cwd + return cwd + if current_file: + self._project_root_cache = current_dir + return current_dir + self._project_root_cache = cwd + return cwd + + def _EmitImportDeclarationsLlvm(self, Node: ast.Import, Gen: LlvmGeneratorMixin) -> None: + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + for alias in Node.names: + name = alias.name + if name in ('c', 't'): + if alias.asname: + self.Trans.SymbolTable.import_aliases[alias.asname] = name + AliasInfo = CTypeInfo() + AliasInfo.IsModuleAlias = True + AliasInfo.ResolvedModule = name + self.Trans.SymbolTable.insert(alias.asname, AliasInfo) + continue + self.Trans._ImportedModules.add(name) + if alias.asname: + self.Trans.SymbolTable.import_aliases[alias.asname] = name + # 同时在符号表中注册模块别名,以便 CTypeInfo.FromNode 能解析 + AliasInfo = CTypeInfo() + AliasInfo.IsModuleAlias = True + AliasInfo.ResolvedModule = name + self.Trans.SymbolTable.insert(alias.asname, AliasInfo) + current_module = self._get_current_module_name() + self._EmitModuleDeclarationsLlvm(name, Gen, register_module_name=current_module) + + def _RegisterFromImportAliases(self, Node: ast.ImportFrom, Gen: LlvmGeneratorMixin, module: str) -> None: + """Register 'from module import y as z' aliases after module declarations are Loaded""" + if not Node.names: + return + for alias in Node.names: + name = alias.name + asname = alias.asname + # For 'from X import Y' (no asname), register CDefine constants + # into _define_constants so they can be found by _HandleNameLlvm + if not asname or asname == name: + define_constants = vars(Gen).setdefault('_define_constants', {}) + # Check if this name is a CDefine constant in SymbolTable + sym_info = self.Trans.SymbolTable.lookup(name) + if sym_info and sym_info.IsDefine and sym_info.DefineValue is not None: + if name not in define_constants: + define_constants[name] = sym_info.DefineValue + # Also check with module prefix + for prefix_key in [f"{module}.{name}", name]: + sym_info2 = self.Trans.SymbolTable.lookup(prefix_key) + if sym_info2 and sym_info2.IsDefine and sym_info2.DefineValue is not None: + if name not in define_constants: + define_constants[name] = sym_info2.DefineValue + break + # 治本修复:如果符号表中没有,回退查询 _all_define_constants + # _all_define_constants 由 Phase2Translator 从所有 .pyi 文件预提取, + # 包含跨模块导入的 CDefine 常量(如 FOREGROUND_GREEN、STD_OUTPUT_HANDLE) + if name not in define_constants: + all_dc: dict = getattr(Gen, '_all_define_constants', None) or getattr(self.Trans, '_all_define_constants', None) or {} + if name in all_dc: + define_constants[name] = all_dc[name] + # 带类型注册:把跨模块 define 的**声明整数类型**一并带入本模块, + # 供使用点按声明类型(如 DWORD=u32)物化,而不是按值大小猜 i32/i64。 + all_dt: dict = getattr(Gen, '_define_decl_types', None) or getattr(self.Trans, '_define_decl_types', None) or {} + if name in all_dt: + _dt_map: dict = vars(Gen).setdefault('_define_decl_types', {}) + if name not in _dt_map: + _dt_map[name] = all_dt[name] + continue + if not hasattr(self.Trans, '_t_c_imported_names'): + self.Trans._t_c_imported_names = {} + self.Trans._t_c_imported_names[asname] = (module, name) + if name in Gen.functions: + Gen.functions[asname] = Gen.functions[name] + if name in Gen.structs: + Gen.structs[asname] = Gen.structs[name] + info = self.Trans.SymbolTable.lookup(name) + if info: + self.Trans.SymbolTable.insert(asname, info) + if name in Gen.variables: + Gen.variables[asname] = Gen.variables[name] + if name in Gen.global_vars: + Gen.global_vars[asname] = Gen.global_vars[name] + for func_name in list(Gen.functions.keys()): + if func_name.startswith(f'{name}.'): + method_suffix = func_name[len(name):] + Gen.functions[f'{asname}{method_suffix}'] = Gen.functions[func_name] + for key in list(self.Trans.SymbolTable.keys()): + if key.startswith(f'{name}.'): + suffix = key[len(name):] + self.Trans.SymbolTable.insert(f'{asname}{suffix}', self.Trans.SymbolTable[key]) + for meta_dict in (Gen.class_members, Gen.class_member_defaults, + Gen.class_member_signeds, Gen.class_member_bitfields, + Gen.class_member_byteorders, Gen.class_member_bitoffsets, + Gen.class_methods): + if name in meta_dict: + meta_dict[asname] = meta_dict[name] + if name in Gen.class_vtable: + Gen.class_vtable.add(asname) + if name in Gen.class_packed: + Gen.class_packed.add(asname) + struct_sha1_map = getattr(Gen, '_struct_sha1_map', {}) + if name in struct_sha1_map: + struct_sha1_map[asname] = struct_sha1_map[name] + + def _EmitImportFromDeclarationsLlvm(self, Node: ast.ImportFrom, Gen: LlvmGeneratorMixin) -> None: + module = Node.module if Node.module else '' + if module in ('c', 't'): + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(module) + if not hasattr(self.Trans, '_t_c_imported_names'): + self.Trans._t_c_imported_names = {} + # 注册 t/c 类型别名到 _t_type_symbols(CType 子类) + t_type_syms = self.Trans.SymbolTable._t_type_symbols + for alias in Node.names: + name = alias.name + asname = alias.asname or name + self.Trans._t_c_imported_names[asname] = (module, name) + # 仅注册 CType 子类(CEnum/CUnion/CStruct/REnum 等) + if module == 't': + cls = getattr(t, name, None) + if isinstance(cls, type) and issubclass(cls, t.CType): + t_type_syms[asname] = cls + return + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + current_module = self._get_current_module_name() + if Node.level and Node.level > 0: + current_file = getattr(self.Trans, 'CurrentFile', '') or '' + current_dir = os.path.dirname(os.path.abspath(current_file)) + parent_dir = current_dir + for _ in range(Node.level - 1): + parent_dir = os.path.dirname(parent_dir) + if module: + SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] + sub_path_base = os.path.join(parent_dir, module) + for ext in SearchExtensions: + candidate = sub_path_base + ext + if os.path.isfile(candidate): + self.Trans._ImportedModules.add(module) + self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module, register_module_name=current_module) + self._RegisterFromImportAliases(Node, Gen, module) + return + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + # 尝试完整模块名和短模块名 + full_mod_name = f"{current_module}.{module}" if current_module else module + target_sha1 = ModuleSha1Map.get(full_mod_name) or ModuleSha1Map.get(module) + if target_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") + if os.path.isfile(sha1_pyi): + self.Trans._ImportedModules.add(module) + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module, register_module_name=current_module) + self._RegisterFromImportAliases(Node, Gen, module) + return + else: + mod_dir = parent_dir + + SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] + pkg_name = '' + if not module: + # When 'from . import name' is used, Load the package's __init__ + # to make package-level names (functions, classes, constants) available + pkg_name = self._LoadPackageInitForRelativeImport(mod_dir, Gen, current_module) + + # Also try to Load each alias as a submodule + for alias in Node.names: + found_sub = False + for ext in SearchExtensions: + sub_path = os.path.join(mod_dir, alias.name + ext) + if os.path.isfile(sub_path): + self.Trans._ImportedModules.add(alias.name) + self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=current_module) + if alias.asname: + self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name + found_sub = True + break + if not found_sub: + # SHA1 map fallback for submodule + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + sub_ModulePath = f"{pkg_name}.{alias.name}" if pkg_name else alias.name + target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name) + if target_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") + if os.path.isfile(sha1_pyi): + self.Trans._ImportedModules.add(sub_ModulePath) + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=current_module) + if alias.asname: + self.Trans.SymbolTable.import_aliases[alias.asname] = alias.name + self._RegisterFromImportAliases(Node, Gen, module or pkg_name) + return + self.Trans._ImportedModules.add(module) + self._EmitModuleDeclarationsLlvm(module, Gen, register_module_name=current_module) + self._RegisterFromImportAliases(Node, Gen, module) + + def _RegisterCrossModuleDocstrings(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_prefix: str | None) -> None: + """从 {sha1}.doc.json 加载跨模块 docstring 注册到 Gen._doc_strings。 + + - 长名 {module_prefix}.{symbol} 总是注册 + - 短名 {symbol} 只在不存在时注册(避免覆盖本模块的) + - slice_level != 0(优化开启)时注册为 None + - 跨模块 docstring 在 _get_doc_ptr 访问时创建本地常量副本 + """ + if not module_prefix: + return + basename: str = os.path.basename(pyi_path) + # 若传入 .vp/.py 文件,则从 _source_module_sig_files 查找对应的 {sha1}.vpi + if basename.endswith('.vp') or basename.endswith('.py'): + source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) + if source_sig_files: + sig_path = source_sig_files.get(module_prefix) + if sig_path and os.path.isfile(sig_path): + pyi_path = sig_path + basename = os.path.basename(pyi_path) + else: + return + else: + return + if not (basename.endswith('.vpi') or basename.endswith('.pyi')): + return + sha1: str = basename[:-4] # 去掉 .vpi 或 .pyi 后缀(均为 4 字符) + shared_doc_meta = getattr(self.Trans, '_shared_doc_meta', None) + if not shared_doc_meta: + return + docs: dict[str, str] | None = shared_doc_meta.get(sha1) + if not docs: + return + trans = getattr(Gen, '_Trans', None) + slice_level: int = getattr(trans, 'SliceLevel', 3) + optimized: bool = slice_level != 0 + for symbol, doc in docs.items(): + long_key: str = f"{module_prefix}.{symbol}" + if optimized: + Gen._doc_strings[long_key] = None + if symbol not in Gen._doc_strings: + Gen._doc_strings[symbol] = None + else: + Gen._doc_strings[long_key] = doc + if symbol not in Gen._doc_strings: + Gen._doc_strings[symbol] = doc + + def _LoadModuleDeclarationsFromFile(self, pyi_path: str, Gen: LlvmGeneratorMixin, module_name: str | None = None, register_module_name: str | None = None, actual_module_name: str | None = None, reexport_package: str | None = None) -> None: + if not pyi_path or not os.path.isfile(pyi_path): + return + # 去重:同一 .pyi 文件只加载一次(菱形依赖下避免重复 read+parse) + abs_pyi = os.path.abspath(pyi_path) + if abs_pyi in self._loaded_pyi_paths: + return + self._loaded_pyi_paths.add(abs_pyi) + # 注册跨模块 __doc__ docstring 到 Gen._doc_strings + self._RegisterCrossModuleDocstrings(pyi_path, Gen, register_module_name or module_name) + try: + with open(pyi_path, 'r', encoding='utf-8') as f: + ModuleCode = f.read() + # Viper 软关键字层(与 TPC 其余前端入口同一层):`.vp` 源码可含 + # `enum X:` / `fn` 等语法糖,直接 vast.parse 会在糖行 SyntaxError → + # 被下面的 except 吞掉并 return → **该模块的全部声明(函数/类/全局) + # 一个都不注册**,调用方随后报 "Undefined function" + # (includes/ast/base.vp 的 `enum ASTFlag:` → 六处 `from .base import …` + # 全部拿不到 `_init_ast`,即本次现场)。`.vpi`/`.pyi` 无糖,展开是空操作。 + ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), pyi_path) + except Exception as _e: + # 统一诊断口径(strict fail-fast / 非严格告警),必带文件+行号+异常+后果。 + # 原实现"吞异常 + return"(strict 也只写进无人消费的 Trans.Warnings), + # 是本次 `Undefined function: '_init_ast'` 被伪装成下游症状的直接原因。 + _ReportDeclarationParseFailure( + pyi_path, _e, + "该模块的全部声明(函数/类/全局/枚举)不会注册," + "后续引用方可能表现为 Undefined function / Undefined struct") + return + + # 计算源模块名(从 .pyi 文件路径推导)。 + # 治本修复:module_name 参数可能来自 alias.name(如 `from stmts import Module` + # 中的 'Module' 是类名而非模块名),直接传给 _EmitExternalClassDeclLlvm 会导致 + # ModuleSha1Map 查找失败 → _self_struct_sha1=None → 使用错误的同名 struct。 + # 使用文件名推导的模块名(如 'stmts')作为 source_module_name 才是正确的。 + # 对于 sha1 命名的 .pyi 文件(如 e64295c03b564a53.pyi),文件名是 sha1 而非 + # 模块名,需回退到 actual_module_name 或 module_name。 + file_module_name = os.path.splitext(os.path.basename(pyi_path))[0] + if len(file_module_name) == 16 and all(_c in '0123456789abcdef' for _c in file_module_name): + file_module_name = actual_module_name or module_name or file_module_name + + for node in ModuleTree.body: + try: + if isinstance(node, ast.FunctionDef): + if node.name in ('va_start', 'va_arg', 'va_end'): + continue + effective_register = register_module_name or module_name + reexport_names = [] + if module_name and module_name != file_module_name and module_name != effective_register: + reexport_names.append(module_name) + # When Loading a submodule for 'from .xxx import yyy' inside a package's __init__.py, + # re-export functions under the package name so 'pkg.func()' resolves correctly + if reexport_package and reexport_package != file_module_name and reexport_package not in reexport_names: + reexport_names.append(reexport_package) + self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=file_module_name, register_module_name=effective_register, reexport_module_names=reexport_names if reexport_names else None) + elif isinstance(node, ast.AnnAssign): + self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path) + elif isinstance(node, ast.Assign): + self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=pyi_path) + elif isinstance(node, ast.ClassDef): + if hasattr(node, 'type_params') and node.type_params: + if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'): + self.Trans.ClassHandler._generic_class_templates = {} + type_params = [tp.name for tp in node.type_params] + # 保留 prescan 阶段已注册的 sha1;若无则从 _ModuleSha1Map 查找, + # 避免覆盖带 sha1 的注册导致特化时 SHA1 前缀丢失 + existing = self.Trans.ClassHandler._generic_class_templates.get(node.name) + template_sha1 = existing.get('sha1') if existing else None + if template_sha1 is None: + msm = getattr(self.Trans, '_ModuleSha1Map', None) + if msm: + for lookup_name in (file_module_name, module_name, actual_module_name): + if lookup_name and lookup_name in msm: + template_sha1 = msm[lookup_name] + break + self.Trans.ClassHandler._generic_class_templates[node.name] = { + 'node': node, + 'type_params': type_params, + 'sha1': template_sha1, + } + # 治本修复:使用 file_module_name(源模块名)而非 module_name(可能是别名/类名)。 + # 这样 _EmitExternalClassDeclLlvm 内的 ModuleSha1Map 查找才能命中, + # _EmitExternalFuncDeclLlvm 才能拿到正确的 source_module_name → 正确的 struct sha1。 + # 注意:actual_module_name 保持原值(可能为 None),不回退到 module_name(可能是别名) + self._EmitExternalClassDeclLlvm(node, Gen, module_name=file_module_name, actual_module_name=actual_module_name) + elif isinstance(node, ast.ImportFrom): + if node.level > 0: + current_dir = os.path.dirname(os.path.abspath(pyi_path)) + parent_dir = current_dir + for _ in range(node.level - 1): + parent_dir = os.path.dirname(parent_dir) + if node.module: + SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] + sub_path_base = os.path.join(parent_dir, node.module) + found_sub = False + # 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。 + # 当 _dict.py 导入 json 包,递归处理 json/__init__.py 中的 `from .__parser import parse` 时, + # register_module_name='_dict' 会导致 actual_module='_dict.__parser', + # 使 _mangle_func_name 查找失败回退到 self.module_sha1,生成错误的 mangled name。 + # 包名推导优先级: + # 1. module_name(如果它在 ModuleSha1Map 中,说明是已注册的包名,如 'json') + # — 处理 sha1 命名的 .pyi 文件(parent_dir 是 temp 目录时) + # 2. os.path.basename(parent_dir)(原始路径推导,如 'json') + if module_name and module_name in Gen.ModuleSha1Map: + pkg_basename = module_name + else: + pkg_basename = os.path.basename(parent_dir) + for ext in SearchExtensions: + candidate = sub_path_base + ext + if os.path.isfile(candidate): + actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module + self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen) + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(actual_module) + for alias in node.names: + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) + else: + self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name) + found_sub = True + break + # SHA1 map 回退 + if not found_sub: + actual_module = f"{pkg_basename}.{node.module}" if pkg_basename else node.module + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module) + if target_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") + if os.path.isfile(sha1_pyi): + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(actual_module) + for alias in node.names: + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) + else: + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=reexport_package or module_name) + else: + mod_dir = parent_dir + SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] + # Load __init__.py to make package-level names available + self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name) + # 治本修复:用包目录名(如 'json')而非 register_module_name(当前编译模块名,如 '_dict')。 + # 同 if node.module 分支的修复逻辑。 + if module_name and module_name in Gen.ModuleSha1Map: + pkg_basename = module_name + else: + pkg_basename = os.path.basename(mod_dir) + for alias in node.names: + found_alias = False + for ext in SearchExtensions: + sub_path = os.path.join(mod_dir, alias.name + ext) + if os.path.isfile(sub_path): + sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(sub_ModulePath) + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) + else: + self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) + found_alias = True + break + # SHA1 map 回退 + if not found_alias: + sub_ModulePath = f"{pkg_basename}.{alias.name}" if pkg_basename else alias.name + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name) + if target_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") + if os.path.isfile(sha1_pyi): + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(sub_ModulePath) + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) + else: + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=reexport_package or module_name) + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # fail-fast 一律上抛(本仓既定口径:FailFastError 不被吞,见 + # HandlesBase.py 的说明)。原实现把它与其它异常一起吞成一行 + # "忽略节点 … 异常" WARN 并 continue ⇒ 该节点(如某个 ClassDef 的 + # 全部方法声明)被整条静默丢弃,创建中途失败的**半注册名字**留到 + # 下一轮才以"函数声明名字悬空占用"暴露,真因与现场彻底脱节 + # (现场:50741ad4b4b0934d.LLVMTypeCore.__init__)。 + raise + _vlog().warning( + f"HandlesImports[_LoadModuleDeclarationsFromFile]: 忽略节点 " + f"{type(node).__name__} {getattr(node, 'name', '?')} 异常: {_e}", + exc_info=_e + ) + continue + + def _get_current_module_name(self) -> str | None: + current_file = getattr(self.Trans, 'CurrentFile', '') or '' + if current_file: + return os.path.splitext(os.path.basename(current_file))[0] + return None + + def _RegisterSubModuleSha1(self, candidate_path: str, actual_module: str, short_module: str, Gen: LlvmGeneratorMixin) -> None: + """Register a submodule's SHA1 in ModuleSha1Map so cross-module class references work""" + try: + with open(candidate_path, 'r', encoding='utf-8') as f: + sub_content = f.read() + sub_sha1 = hashlib.sha1(sub_content.encode('utf-8')).hexdigest()[:16] + if hasattr(Gen, 'ModuleSha1Map'): + Gen.ModuleSha1Map[actual_module] = sub_sha1 + if short_module and short_module not in Gen.ModuleSha1Map: + Gen.ModuleSha1Map[short_module] = sub_sha1 + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + + def _LoadPackageInitForRelativeImport(self, mod_dir: str, Gen: LlvmGeneratorMixin, register_module_name: str | None) -> str: + """Load __init__.py from the package directory for 'from . import name' resolution. + This makes package-level names (functions, classes, constants) available. + Returns the package name.""" + pkg_name = os.path.basename(mod_dir) + init_Loaded = False + + # 跳过当前正在编译的文件,避免重复加载导致 class_members 重复 + current_sha1 = getattr(Gen, 'module_sha1', None) + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + pkg_sha1 = ModuleSha1Map.get(pkg_name) + if pkg_sha1 and pkg_sha1 == current_sha1: + return pkg_name + + # Try SHA1 map first (correct mangled names from stub) + if pkg_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{pkg_sha1}.vpi") + if os.path.isfile(sha1_pyi): + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=pkg_name, register_module_name=register_module_name) + init_Loaded = True + + # Fallback: try Loading __init__.py directly from the package directory + if not init_Loaded: + for ext in ['.vpi', '.vp', '.pyi', '.py']: + init_path = os.path.join(mod_dir, '__init__' + ext) + if os.path.isfile(init_path): + self._LoadModuleDeclarationsFromFile(init_path, Gen, module_name=pkg_name, register_module_name=register_module_name) + init_Loaded = True + break + + return pkg_name + + def _EmitModuleDeclarationsLlvm(self, module_name: str, Gen: LlvmGeneratorMixin, register_module_name: str | None = None) -> None: + # 去重:同一模块声明只发射一次(避免多个 import 同一模块时重复加载) + decl_key = (module_name, register_module_name) + if decl_key in self._emitted_module_decls: + return + self._emitted_module_decls.add(decl_key) + if module_name in ('pyzlib',): + source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) + ModulePathBase = module_name.replace('.', os.sep) + PackageInitPathBase = module_name.replace('.', os.sep) + os.sep + '__init__' + ProjectRoot = os.path.dirname(os.path.abspath(getattr(self.Trans, 'CurrentFile', '') or '')) or os.getcwd() + + HandlesFile = os.path.abspath(inspect.getfile(self.__class__)) + TransPyCRoot = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(HandlesFile)))) + + FullModulePath = None + # includes 只认项目(project.vpj includes → LibraryPaths)目录,禁止回退仓库硬编码 + # TransPyCRoot/includes:仓库副本缺失时回退会造成声明解析时序错乱(memhub 未注册→i32)。 + SearchDirs = list(self.Trans.LibraryPaths) + + for LibPath in SearchDirs: + SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath + # 优先尝试 .vpi,回退 .pyi + for stub_ext in ('.vpi', '.pyi'): + CandidatePath = os.path.join(SearchPath, ModulePathBase + stub_ext) + if os.path.isfile(CandidatePath): + FullModulePath = CandidatePath + break + CandidatePath = os.path.join(SearchPath, PackageInitPathBase + stub_ext) + if os.path.isfile(CandidatePath): + FullModulePath = CandidatePath + break + if FullModulePath: + break + ModuleParts = ModulePathBase.split(os.sep) + if len(ModuleParts) > 1: + LibPathBasename = os.path.basename(LibPath.rstrip('/\\')) + if LibPathBasename == ModuleParts[0]: + RemainingPath = os.sep.join(ModuleParts[1:]) + for stub_ext in ('.vpi', '.pyi'): + CandidatePath = os.path.join(SearchPath, RemainingPath + stub_ext) + if os.path.isfile(CandidatePath): + FullModulePath = CandidatePath + break + if FullModulePath: + break + if not FullModulePath: + ModulePathPyBase = module_name.replace('.', os.sep) + PackageInitPathPyBase = module_name.replace('.', os.sep) + os.sep + '__init__' + SearchDirsForPy = list(self.Trans.LibraryPaths) + for LibPath in SearchDirsForPy: + SearchPath = os.path.join(ProjectRoot, LibPath) if not os.path.isabs(LibPath) else LibPath + # 优先尝试 .vp,回退 .py + for src_ext in ('.vp', '.py'): + CandidatePath = os.path.join(SearchPath, ModulePathPyBase + src_ext) + if os.path.isfile(CandidatePath): + FullModulePath = CandidatePath + break + CandidatePath = os.path.join(SearchPath, PackageInitPathPyBase + src_ext) + if os.path.isfile(CandidatePath): + FullModulePath = CandidatePath + break + if FullModulePath: + break + if not FullModulePath or not os.path.isfile(FullModulePath): + source_sig_files = getattr(self.Trans, '_source_module_sig_files', None) + if source_sig_files: + sig_path = source_sig_files.get(module_name) + if sig_path and os.path.isfile(sig_path): + FullModulePath = sig_path + else: + for key, val in source_sig_files.items(): + if key.endswith('.' + module_name) or key == module_name: + FullModulePath = val + break + if not FullModulePath or not os.path.isfile(FullModulePath): + if module_name in ('pyzlib', 'zdeflate', 'zinflate', 'zchecksum', 'zdef', 'zhuff'): + pass + if not FullModulePath: + CurrentFile = getattr(self.Trans, 'CurrentFile', None) or '' + if CurrentFile: + CurrentFileDir = os.path.dirname(os.path.abspath(CurrentFile)) + SearchFromDirs = [CurrentFileDir, os.getcwd()] + else: + SearchFromDirs = [os.getcwd()] + SearchDirsForSubmodule = SearchFromDirs + list(self.Trans.LibraryPaths) + for SearchPath in SearchDirsForSubmodule: + if not os.path.isdir(SearchPath): + continue + # 多级模块名(含 '.')与 basename 文件列表(files 无路径) + # 永不匹配 `module_name + ext in files`,walk 纯属浪费,跳过。 + if '.' in module_name: + continue + # 目录 walk 结果缓存(编译进程内目录内容不变):import 热路径 + # 高频解析失败时避免重复递归扫描 includes/工程目录(PROF 热点) + walk_entries: list[tuple[str, list[str]]] = ImportHandle._walk_cache.get(SearchPath) + if walk_entries is None: + walk_entries = [(r, fs) for r, dirs, fs in os.walk(SearchPath) if '__pycache__' not in r] + ImportHandle._walk_cache[SearchPath] = walk_entries + for root, files in walk_entries: + for src_ext in ('.vp', '.py', '.vpi', '.pyi'): + if module_name + src_ext in files: + FullModulePath = os.path.join(root, module_name + src_ext) + break + if FullModulePath: + break + if FullModulePath: + break + # fail-fast:模块在所有来源都"完全未知"时立即报错,暴露 import 路径错误 + # (如目录重组后未更新的旧路径)。之前静默容忍导致引用方跨模块 mangled + # 前缀 fallback 当前模块 sha1,链接期才暴露 undefined symbol。 + # 注意:文件系统找不到 ≠ 模块不存在——VPC 自举工程 includes 为合并形态, + # 源码模块(如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由 + # ModuleSha1Map/_source_module_sig_files 定位 sha1 → temp/*.stub.ll 加载, + # 属正常路径;只有模块在编译上下文完全未注册(无 sha1)才算真正不存在。 + if not FullModulePath or not os.path.isfile(FullModulePath): + _known_sha1: str | None = None + _sig_files = getattr(self.Trans, '_source_module_sig_files', None) + if _sig_files: + for _key, _val in _sig_files.items(): + if _key == module_name or _key.endswith('.' + module_name): + _known_sha1 = os.path.basename(_val).replace('.vpi', '').replace('.pyi', '') + break + if not _known_sha1: + _sha_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', None) or {} + _known_sha1 = _sha_map.get(module_name) + if not _known_sha1: + raise ImportError( + f"无法解析 import 模块 '{module_name}': " + f"在 includes/LibraryPaths/工程目录均未找到对应 .vp/.py/.vpi/.pyi 文件," + f"且编译上下文无该模块的 sha1 注册(模块完全未知)") + # 注册跨模块 __doc__ docstring(module_name 是被导入模块名) + if FullModulePath: + self._RegisterCrossModuleDocstrings(FullModulePath, Gen, module_name) + self._LoadDeclarationsFromStubLlvm(module_name, Gen) + if module_name == 'pyzlib': + pass + try: + with open(FullModulePath, 'r', encoding='utf-8') as f: + ModuleCode = f.read() + # 同上:`.vp` 源码须先过 Viper 软关键字层再 vast.parse(否则含 + # `enum X:` / `fn` 的模块声明整体丢失,落到 except 的 stub 兜底)。 + ModuleTree = ast.parse(ExpandSoftKeywords(ModuleCode), FullModulePath) + if module_name == 'pyzlib': + func_names = [n.name for n in ModuleTree.body if isinstance(n, ast.FunctionDef)] + has_functions_or_classes = False + for node in ModuleTree.body: + if module_name == 'pyzlib': + node_type = type(node).__name__ + name = getattr(node, 'name', getattr(node, 'attr', '?')) + try: + if isinstance(node, ast.FunctionDef): + if node.name in ('va_start', 'va_arg', 'va_end'): + continue + effective_register = register_module_name or module_name + self._EmitExternalFuncDeclLlvm(node, Gen, source_module_name=module_name, register_module_name=effective_register) + has_functions_or_classes = True + elif isinstance(node, ast.AnnAssign): + self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath) + has_functions_or_classes = True + elif isinstance(node, ast.Assign): + self._EmitExternalGlobalDeclLlvm(node, Gen, module_name=module_name, ModulePath=FullModulePath) + has_functions_or_classes = True + elif isinstance(node, ast.ClassDef): + if hasattr(node, 'type_params') and node.type_params: + if not hasattr(self.Trans.ClassHandler, '_generic_class_templates'): + self.Trans.ClassHandler._generic_class_templates = {} + type_params = [tp.name for tp in node.type_params] + # 保留 prescan 阶段已注册的 sha1;若无则从 _ModuleSha1Map 查找, + # 避免覆盖带 sha1 的注册导致特化时 SHA1 前缀丢失 + existing = self.Trans.ClassHandler._generic_class_templates.get(node.name) + template_sha1 = existing.get('sha1') if existing else None + if template_sha1 is None: + msm = getattr(self.Trans, '_ModuleSha1Map', None) + if msm and module_name in msm: + template_sha1 = msm[module_name] + self.Trans.ClassHandler._generic_class_templates[node.name] = { + 'node': node, + 'type_params': type_params, + 'sha1': template_sha1, + } + self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name) + has_functions_or_classes = True + elif isinstance(node, ast.ImportFrom): + if node.level > 0: + current_dir = os.path.dirname(os.path.abspath(FullModulePath)) + parent_dir = current_dir + for _ in range(node.level - 1): + parent_dir = os.path.dirname(parent_dir) + if node.module: + SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] + sub_path_base = os.path.join(parent_dir, node.module) + found_sub = False + for ext in SearchExtensions: + candidate = sub_path_base + ext + if os.path.isfile(candidate): + actual_module = f"{module_name}.{node.module}" if module_name else node.module + self._RegisterSubModuleSha1(candidate, actual_module, node.module, Gen) + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(actual_module) + for alias in node.names: + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) + else: + self._LoadModuleDeclarationsFromFile(candidate, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name) + has_functions_or_classes = True + found_sub = True + break + # SHA1 map 回退:当文件查找失败时,从 SHA1 映射中查找子模块 stub + if not found_sub: + actual_module = f"{module_name}.{node.module}" if module_name else node.module + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + # 尝试完整模块名和短模块名 + target_sha1 = ModuleSha1Map.get(actual_module) or ModuleSha1Map.get(node.module) + if target_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") + if os.path.isfile(sha1_pyi): + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(actual_module) + for alias in node.names: + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) + else: + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, actual_module_name=actual_module, reexport_package=module_name) + has_functions_or_classes = True + else: + mod_dir = parent_dir + SearchExtensions = ['.vpi', '.vp', '.pyi', '.py'] + # Load __init__.py to make package-level names available + self._LoadPackageInitForRelativeImport(mod_dir, Gen, register_module_name or module_name) + for alias in node.names: + found_alias = False + for ext in SearchExtensions: + sub_path = os.path.join(mod_dir, alias.name + ext) + if os.path.isfile(sub_path): + sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(sub_ModulePath) + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) + else: + self._LoadModuleDeclarationsFromFile(sub_path, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name) + has_functions_or_classes = True + found_alias = True + break + # SHA1 map 回退 + if not found_alias: + sub_ModulePath = f"{module_name}.{alias.name}" if module_name else alias.name + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + target_sha1 = ModuleSha1Map.get(sub_ModulePath) or ModuleSha1Map.get(alias.name) + if target_sha1: + temp_dir = getattr(Gen, '_temp_dir', None) + if temp_dir: + sha1_pyi = os.path.join(temp_dir, f"{target_sha1}.vpi") + if os.path.isfile(sha1_pyi): + if not getattr(self.Trans, '_ImportedModules', None): + self.Trans._ImportedModules = set() + self.Trans._ImportedModules.add(sub_ModulePath) + if alias.name == '*': + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=module_name, register_module_name=register_module_name, reexport_package=module_name) + else: + self._LoadModuleDeclarationsFromFile(sha1_pyi, Gen, module_name=alias.name, register_module_name=register_module_name, reexport_package=module_name) + has_functions_or_classes = True + except RecursionError: + # 家族口径(HandlesBase.py:1060-1068):RecursionError 不是"可忽略异常", + # 而是类型解析/声明发射链**成环**的真缺陷信号。此前它被本处 + # `except Exception` 吞成一行 `WARN: 忽略异常 maximum recursion depth exceeded` + # + 声明静默退化(现场与 :2985 深探针记录一致:`match.MatchOr.patterns`、 + # `stmts.Interactive.body` 等"首字段即指针/容器"的类会触发 + # `_TryLoadClassMembersFromPyi ↔ _EmitExternalClassDeclLlvm ↔ _TryLoadStructFromStub` + # 环)。此处 fail-fast 上抛(保留原始栈),暴露上游根因而非掩盖。 + raise + except Exception as e: + # NoneType 异常是数据缺失的正常兜底,静默不 WARN(干扰太多) + if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)): + if module_name == 'pyzlib': + node_type = type(node).__name__ + name = getattr(node, 'name', getattr(node, 'attr', '?')) + _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) + continue + if not has_functions_or_classes and module_name: + self._LoadDeclarationsFromStubLlvm(module_name, Gen) + elif 'includes' in FullModulePath and module_name: + self._LoadDeclarationsFromStubLlvm(module_name, Gen) + except Exception as e: + # 同族口径:读/parse 失败必须显式(文件+行号+异常+后果),不允许静默降级。 + # 非严格模式仍保留既有 stub 兜底(StubGen 产物声明仍可用);strict 模式 + # 由 _ReportDeclarationParseFailure 直接 fail-fast,与 :412 主点一致。 + # + # **限定条件**:只有"确实有源码文件却读/解析失败"才算声明解析失败。本函数 + # 的 `FullModulePath` 可为 None——VPC 自举工程 includes 为合并形态,源码模块 + # (如 lib.Stub.StubMerger 包)不提供裸 .vp 文件,声明由上方 + # `_LoadDeclarationsFromStubLlvm` 从 temp/*.stub.ll 加载,属**正常路径** + # (见本函数 :750 附近的长注释);此时 `open(None)` 的 TypeError 与"文件恰好 + # 不在"都不应触发 fail-fast。 + if FullModulePath and os.path.isfile(FullModulePath): + _ReportDeclarationParseFailure( + FullModulePath, e, + "该模块的源码声明全部未注册," + "仅能依赖 stub 兜底(非严格模式);声明可能与源码不一致") + if module_name: + self._LoadDeclarationsFromStubLlvm(module_name, Gen) + + def _AnnotationStructName(self, annotation: ast.AST) -> str | None: + """从注解里提取「指针指向的类」短名(递归,0 判定返回 None) + + 覆盖形态:`X` / `mod.X` / `rawptr[X]` / `t.CPtr[X]` / 联合 `A | B`(两翼都试)。 + 存储类与限定符标记(`t.CExtern` / `t.CExport` / `t.CPtr` 等)不算类名, + 由 `_MARKER_ANNOT_NAMES` 过滤;返回的是**短名**,调用方再解析成 + `Gen.structs` 真实键。 + """ + if annotation is None: + return None + if isinstance(annotation, ast.BinOp) and isinstance(annotation.op, ast.BitOr): + _left: str | None = self._AnnotationStructName(annotation.left) + if _left: + return _left + return self._AnnotationStructName(annotation.right) + if isinstance(annotation, ast.Subscript): + _base: ast.AST = annotation.value + _bn: str = '' + if isinstance(_base, ast.Name): + _bn = _base.id + elif isinstance(_base, ast.Attribute): + _bn = _base.attr + if _bn in ('rawptr', 'CPtr', 'CArrayPtr'): + _sl: ast.AST = annotation.slice + if hasattr(ast, 'Index') and isinstance(_sl, ast.Index): + _sl = _sl.value + return self._AnnotationStructName(_sl) + return None + if isinstance(annotation, ast.Name): + if annotation.id in _MARKER_ANNOT_NAMES: + return None + return annotation.id + if isinstance(annotation, ast.Attribute): + # `mod.Class`:mod 为 t/c 时是内建标记/内建类型名,不作为类的键 + if isinstance(annotation.value, ast.Name) and annotation.value.id in ('t', 'c'): + return None + if annotation.attr in _MARKER_ANNOT_NAMES: + return None + return annotation.attr + return None + + def _ResolveAnnotationStructKey(self, Gen: LlvmGeneratorMixin, name: str) -> str | None: + """把注解里的类短名解析为 `Gen.structs` 的真实键(0 命中返回 None) + + 三条路径与类字段侧的 `rawptr[裸结构]` 登记保持一致:裸名直查 / + `class_sha1_map` 加前缀 / 全量短名模糊扫描(跨模块类在 + `class_sha1_map` 尚未登记该短名时靠这条兜住)。 + """ + if not name: + return None + if name in Gen.structs: + return name + _sha: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(name) + if _sha and f'{_sha}.{name}' in Gen.structs: + return f'{_sha}.{name}' + for _k in Gen.structs: + _short: str = _k.split('.')[-1] if '.' in _k else _k + if _short == name: + return _k + return None + + def _EmitExternalGlobalDeclLlvm(self, Node: ast.AnnAssign | ast.Assign, Gen: LlvmGeneratorMixin, module_name: str | None = None, ModulePath: str | None = None) -> None: + """Emit external global variable declaration from imported module""" + if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name): + VarName = Node.target.id + # 不再跳过 _ 开头的全局变量,跨模块引用需要声明它们 + var_type = ir.IntType(32) + IsPtr = False + is_string_val = False + TypeInfo = None + # 【跨模块模块级变量的**类登记**】`CTypeInfo.FromNode` 对 + # `X | t.CPtr` / `rawptr[X]` 注解只给出指针占位(rawptr[X] 折叠成 i8*), + # 且本函数**只算类型、不登记变量类** → 消费侧 `Mod.Var.Field` 链在 + # `_get_var_class` 里解析为空:StructGenMixin.vp:139 "属性访问未能解析出 + # 目标类型/成员"、CallGenLeafKit.vp:589 / ClassGenVTable.vp:99 误报 + # Unknown module(factory.PermPool.alloc)。这里对齐 HandlesAnnAssign + # 变量侧补偿:从注解找回真实结构体键并登记;仅当缺失或指针占位时覆盖。 + # 注意:必须放在下面的「已存在则 return」**之前**,否则全局量先由别的 + # 路径声明(如 stub 文本 i8*)时这里永远不执行。 + _eg_tn: str | None = self._AnnotationStructName(Node.annotation) + if _eg_tn: + # 只在能落到 Gen.structs 真实键时登记(避免把 sizet/CPtr 等标量名当类) + _eg_tk: str | None = self._ResolveAnnotationStructKey(Gen, _eg_tn) + if _eg_tk: + _eg_cur: str | None = Gen.var_struct_class.get(VarName) + if (not _eg_cur or _eg_cur == 'rawptr' or _eg_cur.endswith('.rawptr') + or _eg_cur.startswith('rawptr[')): + Gen.var_struct_class[VarName] = _eg_tk + if getattr(Gen, 'global_struct_class', None) is not None: + Gen.global_struct_class[VarName] = _eg_tk + if VarName in Gen.module.globals: + existing = Gen.module.globals[VarName] + if isinstance(existing, ir.GlobalVariable): + return + if Node.annotation: + parse_result = ParseListAnnotation(Node.annotation) + if parse_result and not parse_result.is_pointer: + # t.CArray[type, count] 数组声明 + elem_type_node = parse_result.elem_type_node + count_node = parse_result.count_node + ElemTypeInfo = CTypeInfo.FromNode(elem_type_node, self.Trans.SymbolTable) + if ElemTypeInfo: + elem_type = Gen._ctype_to_llvm(ElemTypeInfo) + if isinstance(elem_type, ir.VoidType): + elem_type = ir.IntType(8) + if isinstance(count_node, ast.Constant) and isinstance(count_node.value, int): + var_type = ir.ArrayType(elem_type, count_node.value) + elif isinstance(count_node, ast.Constant) and count_node.value is None: + var_type = ir.ArrayType(elem_type, 0) + else: + var_type = Gen._ctype_to_llvm(ElemTypeInfo) + IsPtr = False + else: + TypeInfo = CTypeInfo.FromNode(Node.annotation, self.Trans.SymbolTable) + if TypeInfo: + var_type = Gen._ctype_to_llvm(TypeInfo) + IsPtr = TypeInfo.IsPtr + if isinstance(var_type, ir.IdentifiedStructType) and (var_type.elements is None or len(var_type.elements) == 0): + var_type = ir.IntType(32) + if Node.value and ModulePath and IsPtr and isinstance(Node.value, ast.Constant) and isinstance(Node.value.value, str): + str_val = Node.value.value + '\x00' + str_bytes = str_val.encode('utf-8') + arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes)) + str_gv_name = VarName + '_str' + if str_gv_name not in Gen.module.globals: + str_gv = ir.GlobalVariable(Gen.module, arr_type, name=str_gv_name) + str_gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + str_gv.linkage = 'internal' + ptr_type = ir.PointerType(ir.IntType(8)) + if VarName not in Gen.module.globals: + ptr_gv = ir.GlobalVariable(Gen.module, ptr_type, name=VarName) + str_gv = Gen.module.globals[str_gv_name] + ptr_gv.initializer = ir.Constant.gep(str_gv, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)]) + ptr_gv.linkage = 'internal' + Gen.variables[VarName] = None + return + + # 检查是否是 CDefine 常量或 CTypedef 类型别名 + IsCDefine = False + IsCTypedef = False + if Node.annotation: + if isinstance(Node.annotation, ast.Attribute): + if getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CDefine': + IsCDefine = True + elif getattr(Node.annotation.value, 'id', None) == 't' and Node.annotation.attr == 'CTypedef': + IsCTypedef = True + elif isinstance(Node.annotation, ast.Name): + if Node.annotation.id == 'CDefine': + IsCDefine = True + elif Node.annotation.id == 'CTypedef': + IsCTypedef = True + + if IsCTypedef: + # 检查符号是否已存在且已正确解析(OriginalType 已设置),避免覆盖 + existing_sym = self.Trans.SymbolTable.lookup(VarName) if hasattr(self.Trans.SymbolTable, 'lookup') else None + if existing_sym and isinstance(existing_sym, CTypeInfo) and existing_sym.IsTypedef and existing_sym.OriginalType: + return + if Node.value: + ValueTypeInfo = None + if isinstance(Node.value, ast.BinOp) and isinstance(Node.value.op, ast.BitOr): + ValueTypeInfo = self.Trans.TypeMergeHandler.MergeTypes(Node.value) + else: + ValueTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.value) + if isinstance(ValueTypeInfo, CTypeInfo) and (ValueTypeInfo.BaseType or ValueTypeInfo.PtrCount > 0): + # GetCTypeInfo 对 t.CInt 等 Attribute 返回的 BaseType 可能是 CType 基类实例 + # (而非具体子类如 CInt),导致 _resolve_typedef_int_width 无法解析位宽。 + # 此时从 Node.value 提取类型名,设置 OriginalType 为字符串, + # 让 _resolve_typedef_int_width 通过 CTypeRegistry.GetClassByName 解析。 + if ValueTypeInfo.BaseType and type(ValueTypeInfo.BaseType) is t.CType: + if isinstance(Node.value, ast.Attribute) and getattr(Node.value.value, 'id', None) == 't': + type_name: str = Node.value.attr + resolved_cls = CTypeRegistry.GetClassByName(type_name) + if resolved_cls is not None: + ValueTypeInfo.OriginalType = type_name + ValueTypeInfo.IsTypedef = True + ValueTypeInfo.Name = VarName + # 设置 OriginalType,确保 _resolve_name 能解析(MergeTypes 不为 BinOp 结果设置 OriginalType) + if not ValueTypeInfo.OriginalType: + OriginalCopy: CTypeInfo = ValueTypeInfo.Copy() + OriginalCopy.IsTypedef = False + OriginalCopy.Name = '' + ValueTypeInfo.OriginalType = OriginalCopy + self.Trans.SymbolTable.insert(VarName, ValueTypeInfo) + if module_name: + FullName = f"{module_name}.{VarName}" + self.Trans.SymbolTable.insert(FullName, ValueTypeInfo) + return + TTypeInfo = self.Trans.TypeMergeHandler.GetCTypeInfo(Node.annotation) + if TTypeInfo: + TTypeInfo.IsTypedef = True + TTypeInfo.Name = VarName + self.Trans.SymbolTable.insert(VarName, TTypeInfo) + if module_name: + FullName = f"{module_name}.{VarName}" + self.Trans.SymbolTable.insert(FullName, TTypeInfo) + return + + if IsCDefine: + # CDefine 宏不生成 LLVM 全局变量 + # 治本修复(方案C):裸名只注册到本模块 _define_constants, + # 不再注册到跨模块共享的 SymbolTable,避免同名 CDefine 污染其他模块的全局变量 + # (如 Phase1.SHA1_LEN=17 覆盖 VpiReader.SHA1_LEN=16 导致双点链接错误)。 + # 跨模块导入通过 _RegisterFromImportAliases 查带前缀全名后注册到本模块 _define_constants。 + DefineValue = None + if Node.value: + DefineValue = self._ExtractConstValue(Node.value) + # 仅本模块 CDefine 注册到 _define_constants(供 _HandleNameLlvm 步骤1 查找) + if module_name is None and DefineValue is not None: + define_constants = vars(Gen).setdefault('_define_constants', {}) + if VarName not in define_constants: + define_constants[VarName] = DefineValue + # 如果提供了模块名,注册带模块前缀的全名到 SymbolTable + # (供 _RegisterFromImportAliases 的 from X import Y 显式导入查找) + if module_name: + FullName = f"{module_name}.{VarName}" + existing_full = self.Trans.SymbolTable.lookup(FullName) + if not (existing_full and existing_full.IsTypedef and existing_full.OriginalType): + self._RegisterCDefineSymbol(FullName, DefineValue, Node.lineno, ModulePath or 'unknown') + return # 不再继续生成 LLVM 全局变量 + + # t.State 标记的全局变量:只声明不定义(external linkage),避免 DSO/non-DSO 重定义 + if TypeInfo and TypeInfo.IsState: + if VarName not in Gen.module.globals: + gv = ir.GlobalVariable(Gen.module, var_type, name=VarName) + gv.linkage = 'external' + Gen._export_funcs.add(VarName) + return + + gv = ir.GlobalVariable(Gen.module, var_type, name=VarName) + # 外部全局变量声明:只声明不定义(external linkage),避免多重定义 + gv.linkage = 'external' + elif isinstance(Node, ast.Assign) and Node.targets and isinstance(Node.targets[0], ast.Name): + VarName = Node.targets[0].id + if VarName.startswith('_'): + return + if VarName in Gen.module.globals: + return + gv = ir.GlobalVariable(Gen.module, ir.IntType(32), name=VarName) + gv.linkage = 'external' + + def _ExtractValue(self, value_node: ast.AST, target_type: ir.Type, var_name: str | None = None) -> ir.Constant | None: + """Extract constant value from AST node for global variable initializer""" + # 处理负号 + if isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.USub): + if isinstance(value_node.operand, ast.Constant) and isinstance(value_node.operand.value, int): + neg_val = -value_node.operand.value + if isinstance(target_type, ir.IntType): + return ir.Constant(target_type, neg_val) + return ir.Constant(ir.IntType(32), neg_val) + # 处理正号 + elif isinstance(value_node, ast.UnaryOp) and isinstance(value_node.op, ast.UAdd): + return self._ExtractValue(value_node.operand, target_type) + + if isinstance(value_node, ast.Constant): + if isinstance(value_node.value, bool): + if isinstance(target_type, ir.IntType): + return ir.Constant(target_type, 1 if value_node.value else 0) + return ir.Constant(ir.IntType(32), 1 if value_node.value else 0) + elif isinstance(value_node.value, int): + if isinstance(target_type, ir.IntType): + return ir.Constant(target_type, value_node.value) + return ir.Constant(ir.IntType(32), value_node.value) + elif isinstance(value_node.value, str): + str_val = value_node.value + '\x00' + str_bytes = str_val.encode('utf-8') + arr_type = ir.ArrayType(ir.IntType(8), len(str_bytes)) + Gen = self.Trans.LlvmGen + gv_name = var_name + '_str' if var_name else f"str_global_{id(value_node)}" + if gv_name in Gen.module.globals: + gv = Gen.module.globals[gv_name] + else: + gv = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) + gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + gv.linkage = 'internal' + ptr_type = ir.PointerType(ir.IntType(8)) + return ir.Constant(ptr_type, gv.reference) + elif isinstance(value_node.value, bool): + return ir.Constant(ir.IntType(32), 1 if value_node.value else 0) + elif value_node.value is None: + if isinstance(target_type, ir.BaseStructType): + return ir.Constant(target_type, None) + if isinstance(target_type, ir.PointerType): + return ir.Constant(target_type, None) + return ir.Constant(target_type, 0) + elif isinstance(value_node, ast.Name): + if value_node.id == 'None': + if isinstance(target_type, ir.BaseStructType): + return ir.Constant(target_type, None) + if isinstance(target_type, ir.PointerType): + return ir.Constant(target_type, None) + return ir.Constant(target_type, 0) + return None + + def _ExtractConstValue(self, value_node: ast.AST) -> object: + """Extract constant value from AST node for SymbolTable registration + 支持常量、一元运算符、二元运算符和常量引用 + """ + if isinstance(value_node, ast.Constant): + return value_node.value + elif isinstance(value_node, ast.BinOp): + left = self._ExtractConstValue(value_node.left) + right = self._ExtractConstValue(value_node.right) + if left is None or right is None: + return None + if isinstance(value_node.op, ast.Add): + return left + right + elif isinstance(value_node.op, ast.Sub): + return left - right + elif isinstance(value_node.op, ast.Mult): + return left * right + elif isinstance(value_node.op, ast.Div): + return left // right if isinstance(left, int) and isinstance(right, int) else left / right + elif isinstance(value_node.op, ast.FloorDiv): + return left // right + elif isinstance(value_node.op, ast.Mod): + return left % right + elif isinstance(value_node.op, ast.Pow): + return left ** right + elif isinstance(value_node.op, ast.LShift): + return left << right + elif isinstance(value_node.op, ast.RShift): + return left >> right + elif isinstance(value_node.op, ast.BitOr): + return left | right + elif isinstance(value_node.op, ast.BitXor): + return left ^ right + elif isinstance(value_node.op, ast.BitAnd): + return left & right + elif isinstance(value_node, ast.UnaryOp): + operand = self._ExtractConstValue(value_node.operand) + if operand is None: + return None + if isinstance(value_node.op, ast.USub): + return -operand + elif isinstance(value_node.op, ast.UAdd): + return +operand + elif isinstance(value_node.op, ast.Invert): + return ~operand + elif isinstance(value_node, ast.Name): + if value_node.id in getattr(self.Trans, 'SymbolTable', {}): + info = self.Trans.SymbolTable[value_node.id] + if info.IsDefine and info.DefineValue is not None: + return info.DefineValue + elif isinstance(value_node, ast.Call): + if isinstance(value_node.func, ast.Attribute): + if getattr(value_node.func.value, 'id', None) == 't': + if value_node.args: + return self._ExtractConstValue(value_node.args[0]) + elif isinstance(value_node.func, ast.Name): + if value_node.args: + return self._ExtractConstValue(value_node.args[0]) + return None + + def _ExtractTypedefOriginalType(self, value_node: ast.AST) -> str | None: + if isinstance(value_node, ast.Attribute): + if getattr(value_node.value, 'id', None) == 't': + llvm_str = CTypeRegistry.NameToLLVM(value_node.attr) + if llvm_str: + return llvm_str + elif isinstance(value_node, ast.Name): + llvm_str = CTypeRegistry.NameToLLVM(value_node.id) + if llvm_str: + return llvm_str + elif isinstance(value_node, ast.BinOp) and isinstance(value_node.op, ast.BitOr): + left = self._ExtractTypedefOriginalType(value_node.left) + right = self._ExtractTypedefOriginalType(value_node.right) + parts = [] + if left: + parts.append(left) + if right: + parts.append(right) + if parts: + return '|'.join(parts) + elif isinstance(value_node, ast.Call): + if isinstance(value_node.func, ast.Attribute): + if getattr(value_node.func.value, 'id', None) == 't': + llvm_str = CTypeRegistry.NameToLLVM(value_node.func.attr) + if llvm_str: + return llvm_str + elif isinstance(value_node.func, ast.Name): + llvm_str = CTypeRegistry.NameToLLVM(value_node.func.id) + if llvm_str: + return llvm_str + return None + + def _RegisterCDefineSymbol(self, FullName: str, DefineValue: object, lineno: int, FilePath: str) -> None: + """Register CDefine constant to SymbolTable""" + # 防御:新值为 None 但已存在非 None 值时保留旧值。 + # 翻译期间声明发射调用 _ExtractConstValue(不支持 ast.Attribute 形式如 t.CSizeT().Size), + # 会返回 None 并覆盖共享符号表中已由 ConstEvaluator 正确求值的值(如 8)。 + # 共享符号表在 _build_shared_symbol_data 阶段已正确求值,此处不应覆盖。 + if DefineValue is None: + existing: Any = self.Trans.SymbolTable.lookup(FullName) + if existing and existing.IsDefine and existing.DefineValue is not None: + return + info = CTypeInfo() + info.IsDefine = True + info.DefineValue = DefineValue + info.Lineno = lineno + info.file = FilePath + # 直接添加到 SymbolTable 字典 + self.Trans.SymbolTable.insert(FullName, info) + + def _check_annotation_for_state(self, annotation: ast.AST) -> bool: + return (AnnotationContainsName(annotation, 'CExport') or + AnnotationContainsName(annotation, 'CExtern') or + AnnotationContainsName(annotation, 'State')) + + def _check_decorators_for_state(self, Node: ast.FunctionDef) -> bool: + """检查装饰器列表中是否包含 @t.CExport / @t.CExtern / @t.State(t.CExport 既能作返回值注解也能作装饰器)""" + if not Node.decorator_list: + return False + for decorator in Node.decorator_list: + if (AnnotationContainsName(decorator, 'CExport') or + AnnotationContainsName(decorator, 'CExtern') or + AnnotationContainsName(decorator, 'State')): + return True + return False + + def _EmitExternalFuncDeclLlvm(self, Node: ast.FunctionDef, Gen: LlvmGeneratorMixin, is_class_method: bool = False, source_module_name: str | None = None, register_module_name: str | None = None, reexport_module_names: list[str] | None = None) -> None: + FuncName = Node.name + if (Node.returns and self._check_annotation_for_state(Node.returns)) or self._check_decorators_for_state(Node): + Gen._export_funcs.add(FuncName) # t.State 标记的外部 C 函数必须保持原始名称,以便链接器解析 + if source_module_name and source_module_name not in ('c', 't'): + _src_sha1 = Gen.ModuleSha1Map.get(source_module_name) + if not _src_sha1: + _init_name = f"{source_module_name}.__init__" + if _init_name in Gen.ModuleSha1Map: + _src_sha1 = Gen.ModuleSha1Map[_init_name] + if _src_sha1: + Gen._export_extern_funcs[FuncName] = _src_sha1 + # 自动取址模式(ref/temp/always)的登记**必须先于下面的 `FuncName in Gen.functions` + # 早退**:该函数已存在(stub / 调用点先建)时本函数会在此直接返回,若把登记放在 + # 后面 func 创建处(约 :1885)就永远不会执行 ⇒ 跨模块函数的取址模式静默失效。 + # 查表键与查表侧 FuncGen._adjust_args 的 func.name 逐字一致,故这里按"真实名候选" + # 双写:新算出的 mangled 名,以及已存在函数对象的实际名字(若已存在)。 + _auto_modes: list[str | None] = [ + FindAutoAddrMode(a.annotation) for a in Node.args.args] + if any(m is not None for m in _auto_modes): + _early_mangled: str = Gen._mangle_func_name(FuncName, module_name=source_module_name) + Gen._auto_addr_params[_early_mangled] = _auto_modes + Gen._auto_addr_params[FuncName] = _auto_modes + _early_existing = Gen.functions.get(FuncName) + _early_real: str | None = getattr(_early_existing, 'name', None) + if _early_real: + Gen._auto_addr_params[_early_real] = _auto_modes + if FuncName in Gen.functions: + return + if Node.decorator_list: + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Call) and isinstance(decorator.func, ast.Attribute): + # fail-fast:`@c.CReturn` 已移除,扫到即报错。 + if decorator.func.attr == 'CReturn': + raise SyntaxError( + f"函数 '{FuncName}':`@c.CReturn` 已移除:请改用返回注解 " + f"`-> tuple[ty1, ty2, ...]`(多返回值打包为 struct)" + ) + IsPtr = False + ReturnTypeInfo = None + if Node.returns: + try: + ReturnTypeInfo = self.ResolveAnnotationType(Node.returns) + # 仅指针层数 <=1 才算裸 str/bytes(i8*)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组),不能压平。 + if ReturnTypeInfo and ReturnTypeInfo.IsStr and ReturnTypeInfo.PtrCount <= 1: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CChar() + ReturnTypeInfo.PtrCount = 1 + if ReturnTypeInfo is None: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + IsPtr = ReturnTypeInfo.IsPtr except RecursionError: # 家族口径(HandlesBase.py:1060-1068):RecursionError 是类型解析链成环的 # 真缺陷信号,不允许退化成"默认 CInt"(那会让被指类丢失、下游全部错型)。 raise - except Exception as e: # 回退:返回类型解析失败时使用默认 CInt - # NoneType 异常(如对 None 调 endswith)是数据缺失的正常兜底场景, - # 静默不输出 WARN(干扰太多);其他异常保留提示。 - if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)): - _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - else: - ReturnTypeInfo = CTypeInfo() - ReturnTypeInfo.BaseType = t.CInt() - ReturnType = Gen._ctype_to_llvm(ReturnTypeInfo) - if (isinstance(ReturnType, ir.VoidType) or (isinstance(ReturnType, ir.PointerType) and isinstance(ReturnType.pointee, ir.VoidType))) and Node.returns: - RetAnnName = None - if isinstance(Node.returns, ast.Name): - RetAnnName = Node.returns.id - elif isinstance(Node.returns, ast.Attribute): - RetAnnName = Node.returns.attr - elif isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): - left = Node.returns.left - if isinstance(left, ast.Name): - RetAnnName = left.id - elif isinstance(left, ast.Attribute): - RetAnnName = left.attr - if RetAnnName and RetAnnName in Gen.structs: - ReturnType = ir.PointerType(Gen.structs[RetAnnName]) - elif IsPtr or RetAnnName: - ReturnType = ir.PointerType(ir.IntType(8)) - # __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致 - # .pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName* - if '.__new__' in FuncName: - _np = FuncName.split('.') - if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]): - _new_class_name = _np[1] - else: - _new_class_name = _np[0] - # 治本修复:优先用 source sha1 查找正确的 struct,避免同名类冲突时返回错误类型 - _new_struct = None - _new_src_sha1: str | None = None - if source_module_name and hasattr(Gen, 'ModuleSha1Map'): - _new_src_sha1 = Gen.ModuleSha1Map.get(source_module_name) - if not _new_src_sha1: - _init_name = f"{source_module_name}.__init__" - if _init_name in Gen.ModuleSha1Map: - _new_src_sha1 = Gen.ModuleSha1Map[_init_name] - if _new_src_sha1 and _new_class_name: - _new_struct = Gen.structs.get(f"{_new_src_sha1}.{_new_class_name}") - if _new_struct is None: - _new_struct = Gen.structs.get(_new_class_name) - if _new_struct is None: - _full_name = FuncName.split('.__new__')[0] - _new_struct = Gen.structs.get(_full_name) - if _new_struct: - if isinstance(_new_struct, ir.PointerType): - ReturnType = _new_struct - else: - ReturnType = ir.PointerType(_new_struct) - ParamTypes = [] - is_method = is_class_method or '.__' in FuncName - class_name_for_method = FuncName.split('.')[0] if '.' in FuncName else None - class_is_cpython = class_name_for_method and self.Trans.SymbolTable.is_struct(class_name_for_method) - # 治本修复:通过 source_module_name 获取源模块 sha1,查找正确的 struct。 - # 当存在同名类冲突(如 stmts.py:Module 和 __module.py:Module)时, - # Gen.structs[short_name] 可能指向错误模块的 struct(set_body 已锁定错误布局)。 - # 优先查找以 sha1 为前缀的完整 key,避免使用错误的 struct 作为 self 参数类型。 - _self_struct_sha1: str | None = None - if source_module_name and hasattr(Gen, 'ModuleSha1Map'): - _self_struct_sha1 = Gen.ModuleSha1Map.get(source_module_name) - if not _self_struct_sha1: - _init_name = f"{source_module_name}.__init__" - if _init_name in Gen.ModuleSha1Map: - _self_struct_sha1 = Gen.ModuleSha1Map[_init_name] - for i, Arg in enumerate(Node.args.args): - if i == 0 and is_method: - # self parameter of a method should always be a pointer to the struct - _self_struct = None - if _self_struct_sha1 and class_name_for_method: - _full_key = f"{_self_struct_sha1}.{class_name_for_method}" - _self_struct = Gen.structs.get(_full_key) - if _self_struct is None: - _self_struct = Gen.structs.get(class_name_for_method or FuncName.split('.')[0], ir.IntType(8)) - ParamType = ir.PointerType(_self_struct) - elif Arg.annotation: - try: - ParamTypeInfo = self.ResolveAnnotationType(Arg.annotation) - if ParamTypeInfo is None: - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CInt() - ParamIsPtr = ParamTypeInfo.IsPtr - if i == 0 and is_method and Arg.arg == 'self' and class_is_cpython: - ParamIsPtr = True - ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) - # 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。 - # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), - # 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。 - if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')): - ParamTypeInfo = CTypeInfo() - ParamTypeInfo.BaseType = t.CChar() - ParamTypeInfo.PtrCount = 1 - ParamIsPtr = True - # C 语言中数组参数退化为指针:t.CArray[type, N] → type* - if ParamTypeInfo.ArrayDims: - ParamTypeInfo.ArrayDims = [] - ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) - ParamType = Gen._ctype_to_llvm(ParamTypeInfo) + except Exception as e: # 回退:返回类型解析失败时使用默认 CInt + # NoneType 异常(如对 None 调 endswith)是数据缺失的正常兜底场景, + # 静默不输出 WARN(干扰太多);其他异常保留提示。 + if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)): + _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + else: + ReturnTypeInfo = CTypeInfo() + ReturnTypeInfo.BaseType = t.CInt() + ReturnType = Gen._ctype_to_llvm(ReturnTypeInfo) + if (isinstance(ReturnType, ir.VoidType) or (isinstance(ReturnType, ir.PointerType) and isinstance(ReturnType.pointee, ir.VoidType))) and Node.returns: + RetAnnName = None + if isinstance(Node.returns, ast.Name): + RetAnnName = Node.returns.id + elif isinstance(Node.returns, ast.Attribute): + RetAnnName = Node.returns.attr + elif isinstance(Node.returns, ast.BinOp) and isinstance(Node.returns.op, ast.BitOr): + left = Node.returns.left + if isinstance(left, ast.Name): + RetAnnName = left.id + elif isinstance(left, ast.Attribute): + RetAnnName = left.attr + if RetAnnName and RetAnnName in Gen.structs: + ReturnType = ir.PointerType(Gen.structs[RetAnnName]) + elif IsPtr or RetAnnName: + ReturnType = ir.PointerType(ir.IntType(8)) + # __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致 + # .pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName* + if '.__new__' in FuncName: + _np = FuncName.split('.') + if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]): + _new_class_name = _np[1] + else: + _new_class_name = _np[0] + # 治本修复:优先用 source sha1 查找正确的 struct,避免同名类冲突时返回错误类型 + _new_struct = None + _new_src_sha1: str | None = None + if source_module_name and hasattr(Gen, 'ModuleSha1Map'): + _new_src_sha1 = Gen.ModuleSha1Map.get(source_module_name) + if not _new_src_sha1: + _init_name = f"{source_module_name}.__init__" + if _init_name in Gen.ModuleSha1Map: + _new_src_sha1 = Gen.ModuleSha1Map[_init_name] + if _new_src_sha1 and _new_class_name: + _new_struct = Gen.structs.get(f"{_new_src_sha1}.{_new_class_name}") + if _new_struct is None: + _new_struct = Gen.structs.get(_new_class_name) + if _new_struct is None: + _full_name = FuncName.split('.__new__')[0] + _new_struct = Gen.structs.get(_full_name) + if _new_struct: + if isinstance(_new_struct, ir.PointerType): + ReturnType = _new_struct + else: + ReturnType = ir.PointerType(_new_struct) + ParamTypes = [] + is_method = is_class_method or '.__' in FuncName + class_name_for_method = FuncName.split('.')[0] if '.' in FuncName else None + class_is_cpython = class_name_for_method and self.Trans.SymbolTable.is_struct(class_name_for_method) + # 治本修复:通过 source_module_name 获取源模块 sha1,查找正确的 struct。 + # 当存在同名类冲突(如 stmts.py:Module 和 __module.py:Module)时, + # Gen.structs[short_name] 可能指向错误模块的 struct(set_body 已锁定错误布局)。 + # 优先查找以 sha1 为前缀的完整 key,避免使用错误的 struct 作为 self 参数类型。 + _self_struct_sha1: str | None = None + if source_module_name and hasattr(Gen, 'ModuleSha1Map'): + _self_struct_sha1 = Gen.ModuleSha1Map.get(source_module_name) + if not _self_struct_sha1: + _init_name = f"{source_module_name}.__init__" + if _init_name in Gen.ModuleSha1Map: + _self_struct_sha1 = Gen.ModuleSha1Map[_init_name] + for i, Arg in enumerate(Node.args.args): + if i == 0 and is_method: + # self parameter of a method should always be a pointer to the struct + _self_struct = None + if _self_struct_sha1 and class_name_for_method: + _full_key = f"{_self_struct_sha1}.{class_name_for_method}" + _self_struct = Gen.structs.get(_full_key) + if _self_struct is None: + _self_struct = Gen.structs.get(class_name_for_method or FuncName.split('.')[0], ir.IntType(8)) + ParamType = ir.PointerType(_self_struct) + elif Arg.annotation: + try: + ParamTypeInfo = self.ResolveAnnotationType(Arg.annotation) + if ParamTypeInfo is None: + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CInt() + ParamIsPtr = ParamTypeInfo.IsPtr + if i == 0 and is_method and Arg.arg == 'self' and class_is_cpython: + ParamIsPtr = True + ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) + # 仅当指针层数 <=1 时才压平为 i8*(裸 str/bytes)。 + # `str | t.CPtr` 合并后 IsStr=True 但 PtrCount=2(i8**,如 argv 数组), + # 不能压平,否则形参类型错误导致 _adjust_args 多 load 一层崩溃。 + if (ParamTypeInfo.IsStr and ParamTypeInfo.PtrCount <= 1) or (isinstance(Arg.annotation, ast.Name) and Arg.annotation.id in ('str', 'bytes')): + ParamTypeInfo = CTypeInfo() + ParamTypeInfo.BaseType = t.CChar() + ParamTypeInfo.PtrCount = 1 + ParamIsPtr = True + # C 语言中数组参数退化为指针:t.CArray[type, N] → type* + if ParamTypeInfo.ArrayDims: + ParamTypeInfo.ArrayDims = [] + ParamTypeInfo.PtrCount = max(ParamTypeInfo.PtrCount, 1) + ParamType = Gen._ctype_to_llvm(ParamTypeInfo) except RecursionError: # 家族口径(HandlesBase.py:1060-1068):RecursionError 是类型解析链成环的 # 真缺陷信号,不允许退化成"默认 i32"(形参错型会让 _adjust_args 行为错乱)。 raise - except Exception as e: # 回退:参数类型解析失败时使用默认 i32 - # NoneType 异常是数据缺失的正常兜底,静默不 WARN(干扰太多) - if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)): - _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) - ParamType = ir.IntType(32) - if isinstance(ParamType, ir.VoidType) or (isinstance(ParamType, ir.PointerType) and isinstance(ParamType.pointee, ir.VoidType)): - AnnName = None - if isinstance(Arg.annotation, ast.Name): - AnnName = Arg.annotation.id - elif isinstance(Arg.annotation, ast.Attribute): - AnnName = Arg.annotation.attr - elif isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr): - AnnName = ExtractTypeNameFromBinOp(Arg.annotation) - if AnnName and AnnName in Gen.structs: - ParamType = ir.PointerType(Gen.structs[AnnName]) - else: - ParamType = ir.PointerType(ir.IntType(8)) - else: - ParamType = ir.IntType(32) - ParamTypes.append(ParamType) - IsVariadic = Node.args.vararg is not None - FuncType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) - _is_export = False - if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.IsState: - _is_export = True - if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExport) and not ReturnTypeInfo.IsState: - Gen._export_funcs.add(FuncName) - _is_export = True - if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExtern): - Gen._export_funcs.add(FuncName) - _is_export = True - if not _is_export: - if Node.returns and self._check_annotation_for_state(Node.returns): - _is_export = True - elif self._check_decorators_for_state(Node): - _is_export = True - MangledName = Gen._mangle_func_name(FuncName, module_name=source_module_name) - func = Gen._get_or_declare_function(MangledName, FuncType) - Gen.functions[MangledName] = func - Gen.functions[FuncName] = func - # 跨模块自动取址模式登记(ref/temp/always):以**真实发射名** func.name 为主键, - # 与查表侧 FuncGen._adjust_args 的 func.name 逐字一致;MangledName / FuncName - # 保留为别名键。ParamTypes 与 Node.args.args 逐位对齐(含方法 self)。 - # (_auto_modes 已在函数开头"早退之前"处计算,此处复用。) - if any(m is not None for m in _auto_modes): - Gen._auto_addr_params[func.name] = _auto_modes - Gen._auto_addr_params[MangledName] = _auto_modes - Gen._auto_addr_params[FuncName] = _auto_modes - func_meta = FuncMeta.NONE - if Node.decorator_list: - for d in Node.decorator_list: - if isinstance(d, ast.Name): - if d.id == 'staticmethod': - func_meta |= FuncMeta.STATIC_METHOD - elif d.id == 'property': - func_meta |= FuncMeta.PROPERTY_GETTER - elif d.id == 'classmethod': - func_meta |= FuncMeta.CLASS_METHOD - elif isinstance(d, ast.Attribute): - if isinstance(d.value, ast.Name): - # 支持 @property.setter 和 @propname.setter 两种形式 - if d.value.id == 'property' or d.attr in ('setter', 'getter', 'deleter'): - if d.attr == 'setter': - func_meta |= FuncMeta.PROPERTY_SETTER - elif d.attr == 'getter': - func_meta |= FuncMeta.PROPERTY_GETTER - elif d.attr == 'deleter': - func_meta |= FuncMeta.PROPERTY_DELETER - if not self.Trans.SymbolTable.has(FuncName): - FuncInfo = CTypeInfo() - FuncInfo.Name = FuncName - FuncInfo.IsFunction = True - FuncInfo.MetaList = func_meta - self.Trans.SymbolTable.insert(FuncName, FuncInfo) - else: - existing = self.Trans.SymbolTable[FuncName] - if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE: - existing.MetaList = func_meta - # 同时注册带模块前缀的符号,以便 module.func_name 查找能命中 - if source_module_name and source_module_name not in ('c', 't'): - FullSymKey = f"{source_module_name}.{FuncName}" - if not self.Trans.SymbolTable.has(FullSymKey): - FullFuncInfo = CTypeInfo() - FullFuncInfo.Name = FullSymKey - FullFuncInfo.IsFunction = True - FullFuncInfo.MetaList = func_meta - self.Trans.SymbolTable.insert(FullSymKey, FullFuncInfo) - # property setter/deleter: 在原始 PropKey(不带后缀)下注册 MetaList - if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta: - BasePropKey = FuncName.replace('$set', '').replace('$del', '') - base_existing = self.Trans.SymbolTable.lookup(BasePropKey) - if base_existing: - if func_meta != FuncMeta.NONE: - base_existing.MetaList = base_existing.MetaList | func_meta - else: - PropInfo = CTypeInfo() - PropInfo.Name = BasePropKey - PropInfo.IsFunction = True - PropInfo.MetaList = func_meta - self.Trans.SymbolTable.insert(BasePropKey, PropInfo) - if register_module_name and register_module_name != source_module_name: - ReexportName = Gen._mangle_func_name(FuncName, module_name=register_module_name) - Gen.functions[ReexportName] = func - if reexport_module_names: - for reexport_mod in reexport_module_names: - if reexport_mod and reexport_mod != source_module_name: - ReexportName2 = Gen._mangle_func_name(FuncName, module_name=reexport_mod) - if ReexportName2 not in Gen.functions: - Gen.functions[ReexportName2] = func - - def _ClassifyExternalClass(self, Node: ast.ClassDef) -> tuple: - """遍历 Node.bases/decorator_list 做类别判断(纯函数、与 Gen 无关)。 - - 结果由 _EmitExternalClassDeclLlvm 按 (source_sha1, class_name) 类级缓存, - 同一 worker 进程内多个任务对同一跨模块类重复调用时跳过本遍历。 - 返回 (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, IsCVTable, - IsNoVTable, IsPacked)。 - - PEP 695 泛型类(如 GSListNode[T])特殊:跨模块继承展平需要从泛型基类 - 获取字段(如 Next),否则子类(如 Value(GSListNode[Value]))的 - class_members 会缺少继承字段;但泛型类的方法声明必须跳过(方法参数 - 类型 T 会被解析为 opaque struct,LLVM 不允许 opaque struct 作为值参数)。 - """ - IsGenericClass: bool = hasattr(Node, 'type_params') and bool(Node.type_params) - IsCenum = False - IsRenum = False - IsCpythonObject = False - IsCVTable = False - IsNoVTable = False - if Node.bases: - for base in Node.bases: - if getattr(base, 'attr', None): - if base.attr == 'REnum': - IsRenum = True - break - elif base.attr == 'CEnum' or base.attr == 'Enum': - IsCenum = True - break - elif getattr(base, 'id', None): - if base.id == 'REnum': - IsRenum = True - break - elif base.id == 'CEnum' or base.id == 'Enum': - IsCenum = True - break - if not IsCenum and not IsRenum and getattr(Node, 'decorator_list', None): - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Attribute): - if getattr(decorator.value, 'id', None) == 't': - if decorator.attr == 'Object': - IsCpythonObject = True - elif decorator.attr == 'CVTable': - IsCVTable = True - elif decorator.attr == 'NoVTable': - IsNoVTable = True - elif decorator.attr == 'REnum': - IsRenum = True - elif decorator.attr == 'CEnum' or decorator.attr == 'Enum': - IsCenum = True - elif isinstance(decorator, ast.Call): - if isinstance(decorator.func, ast.Attribute): - if getattr(decorator.func.value, 'id', None) == 't': - if decorator.func.attr == 'Object': - IsCpythonObject = True - elif decorator.func.attr == 'CVTable': - IsCVTable = True - elif decorator.func.attr == 'NoVTable': - IsNoVTable = True - elif isinstance(decorator.func, ast.Name): - if decorator.func.id == 'Object': - IsCpythonObject = True - elif decorator.func.id == 'CVTable': - IsCVTable = True - elif decorator.func.id == 'NoVTable': - IsNoVTable = True - IsPacked = False - if getattr(Node, 'decorator_list', None): - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Call): - if isinstance(decorator.func, ast.Attribute): - if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute': - for arg in decorator.args: - if isinstance(arg, ast.Attribute): - if isinstance(arg.value, ast.Attribute): - if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed': - IsPacked = True - # 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable: - # 与 HandlesClassDef._resolve_class_flags / TPV _detect_vtable_status 的 - # `_has_any_func_cvtable` 分支对齐(跨模块这里只能判"类级 NoVTable / - # 无真实父类"两种可判定形态;父类 vtable 状态由 .pyi 按需注册路径解析)。 - if not IsCVTable and ClassHasFuncLevelCVTable(Node): - if IsNoVTable or not _HasRealParentClass(Node): - IsCVTable = True - return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, - IsCVTable, IsNoVTable, IsPacked) - - def _EmitExternalClassDeclLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, module_name: str | None = None, actual_module_name: str | None = None, source_sha1: str | None = None) -> None: - ClassName = Node.name - # Compute source_sha1 up front (moved from mid-function) so it can serve as - # the key of the class-level classification cache. Prefer the explicit - # source_sha1 passed by _TryLoadClassMembersFromPyi (module_name lookup may - # fail when the current module does not import the defining module directly). - if not source_sha1: - if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map: - source_sha1 = Gen.ModuleSha1Map[actual_module_name] - elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map: - source_sha1 = Gen.ModuleSha1Map[module_name] - # Class-level classification cache: the bases/decorator_list walk below is pure - # Gen-independent computation. Repeated calls on the same (source_sha1, class_name) - # across worker tasks (same process) skip the walk. When source_sha1 is None - # (lookup failed) neither read nor write the cache, to avoid mismatching - # same-named classes from different modules under a shared None key. - _classify_key = (source_sha1, ClassName) if source_sha1 else None - _cached_classify = self._extern_class_classify_cache.get(_classify_key) if _classify_key else None - if _cached_classify is None: - _cached_classify = self._ClassifyExternalClass(Node) - if _classify_key is not None: - self._extern_class_classify_cache[_classify_key] = _cached_classify - (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, - IsCVTable, IsNoVTable, IsPacked) = _cached_classify - if ClassName.startswith('__') and not IsCpythonObject and not IsCenum and not IsRenum: - return - if IsPacked: - Gen.class_packed.add(ClassName) - if IsCpythonObject: - CInfo = CTypeInfo() - CInfo.Name = ClassName - CInfo.IsCpythonObject = True - CInfo.IsStruct = True - if module_name: - FullName = f"{module_name}.{ClassName}" - self.Trans.SymbolTable.insert(FullName, CInfo) - self.Trans.SymbolTable.insert(ClassName, CInfo) - if IsCenum: - EnumTypeNode = CTypeInfo() - EnumTypeNode.Name = ClassName - EnumTypeNode.BaseType = t.CEnum(ClassName) - EnumTypeNode.IsEnum = True - # [跨模块枚举宽度] 读侧重推导:.vpi/.pyi 保留成员注解(`Name: T`), - # 故在本处按"最宽成员类型"重算(与定义端 HandlesClassDef 同一规则源 - # EnumClassIntWidth),使跨模块使用同一枚举时宽度一致。本函数会以短名 - # 覆盖共享符号表(_shared_sym.json pickle)中的同名条目,若不在此登记 - # 宽度,覆盖后即退化为默认 i32(= 上一轮遗留缺口)。旧产物缺注解时 - # 自然回落 32(Default),不崩。 - EnumWidth: int = EnumClassIntWidth(Node.body, self.Trans.SymbolTable) - EnumTypeNode.set('enum_width', EnumWidth) - self.Trans.SymbolTable.insert(ClassName, EnumTypeNode) - next_enum_value: int = 0 - for item in Node.body: - VarName = None - ItemValue = None - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - VarName = item.target.id - ItemValue = item.value - elif isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name): - VarName = target.id - break - ItemValue = item.value - if VarName is None: - continue - # 提取枚举值:优先用显式赋值,否则自动编号 - if ItemValue is not None and isinstance(ItemValue, ast.Constant) and isinstance(ItemValue.value, int): - value = ItemValue.value - next_enum_value = ItemValue.value + 1 - else: - value = next_enum_value - next_enum_value += 1 - MemberNode = CTypeInfo() - MemberNode.Name = VarName - MemberNode.BaseType = t.CEnum(ClassName) - MemberNode.value = value - MemberNode.EnumName = ClassName - MemberNode.Lineno = item.lineno - MemberNode.IsEnumMember = True - MemberNode.set('enum_width', EnumWidth) - self.Trans.SymbolTable.insert(VarName, MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode) - return - if IsRenum: - # REnum 跨模块导入:注册 REnum 类型 + 变体成员到 SymbolTable - # 对齐同模块 _EmitREnumLlvm 的注册逻辑 - RenumTypeNode = CTypeInfo() - RenumTypeNode.Name = ClassName - RenumTypeNode.IsRenum = True - RenumTypeNode.IsEnum = True # 兼容 IsEnum 检查 - # REnum 在内存布局上是 struct,必须设置 IsStruct=True 才能让 - # HandlesTypeMerge 的 CPtr 吸收逻辑生效(REnumType | t.CPtr = REnumType*) - RenumTypeNode.IsStruct = True - if module_name: - FullName = f"{module_name}.{ClassName}" - self.Trans.SymbolTable.insert(FullName, RenumTypeNode) - self.Trans.SymbolTable.insert(ClassName, RenumTypeNode) - # 从 stub.ll 加载 REnum 结构体布局到 Gen.structs - self._TryLoadStructFromStub(ClassName, Gen) - # 记录 class_sha1_map(跨模块名修饰) - source_sha1 = None - if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map: - source_sha1 = Gen.ModuleSha1Map[actual_module_name] - elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map: - source_sha1 = Gen.ModuleSha1Map[module_name] - if source_sha1: - if not hasattr(Gen, 'class_sha1_map'): - Gen.class_sha1_map = {} - Gen.class_sha1_map[ClassName] = source_sha1 - # 遍历嵌套 ClassDef 注册变体成员 - # REnum body 是嵌套 ClassDef(变体定义),不是 AnnAssign/Assign - next_tag: int = 0 - variant_names: list[str] = [] - for item in Node.body: - if isinstance(item, ast.ClassDef): - VariantName = item.name - MemberNode = CTypeInfo() - MemberNode.Name = VariantName - MemberNode.value = next_tag - MemberNode.EnumName = ClassName - MemberNode.Lineno = item.lineno - MemberNode.IsEnumMember = True - self.Trans.SymbolTable.insert(VariantName, MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode) - variant_names.append(VariantName) - next_tag += 1 - # 加载嵌套变体结构体(如 LLVMType_Int)到 Gen.structs, - # 并注册 class_members 以支持 _HandleREnumConstructLlvm 的 payload 存储和 - # _HandleRenumMatchLlvm 的 binding 提取。 - NestedStructName: str = f"{ClassName}_{VariantName}" - self._TryLoadStructFromStub(NestedStructName, Gen) - if NestedStructName not in Gen.class_members: - Gen.class_members[NestedStructName] = [] - if NestedStructName not in Gen.class_member_signeds: - Gen.class_member_signeds[NestedStructName] = {} - if Gen.class_members[NestedStructName]: - # 已有成员(_TryLoadStructFromStub 可能已加载),跳过 - pass - else: - Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32))) - Gen.class_member_signeds[NestedStructName]['__tag'] = None - for nested_item in item.body: - # 只把 AnnAssign 当载荷声明;变体体里的 pass / docstring 等 - # 非声明语句合法跳过(如 `class Void: pass`)。 - if not isinstance(nested_item, ast.AnnAssign): - continue - _member_txt: str = ast.unparse(nested_item) - try: - parsed = self.CollectAnnAssignMember(nested_item, Gen) - except Exception as _pe: - # 历史上此处异常被上层吞掉 ⇒ 变体只剩 __tag ⇒ 变体构造静默 - # 丢载荷、match 模式捕获绑不上(现场 LLVMType_Ptr / pointee_cs), - # 且循环中断使后续变体(Func/Array/...)整批缺失。改为响亮失败。 - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - raise _FFE( - f"REnum 变体 '{ClassName}.{VariantName}' 的载荷成员 " - f"'{_member_txt}' 解析失败:{type(_pe).__name__}: {_pe} " - f"—— 拒绝留下只有 __tag 的 class_members" - f"(会导致变体构造丢载荷、match 捕获绑定缺失、后续变体整批缺失)" - ) from _pe - if not parsed: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - raise _FFE( - f"REnum 变体 '{ClassName}.{VariantName}' 的载荷成员 " - f"'{_member_txt}' 未解析出类型(CollectAnnAssignMember 返回 None)" - f"—— 同上,拒绝静默降级为无载荷" - ) - MName, MType, MTypeInfo = parsed - Gen.class_members[NestedStructName].append((MName, MType)) - Gen.class_member_signeds[NestedStructName][MName] = None - # 当 stub 中没有嵌套变体结构体定义时(_TryLoadStructFromStub 失败), - # 使用主 REnum 结构体的 body (max_variant_struct) 创建嵌套变体结构体, - # 确保跨模块布局一致。否则 _generate_structs() Step 4 会从 class_members - # 设置最小布局 body,导致 GEP 偏移量与定义模块不匹配(offset 4 vs offset 8)。 - NestedStructExisting = Gen.structs.get(NestedStructName) - NestedHasBody = (isinstance(NestedStructExisting, ir.IdentifiedStructType) - and NestedStructExisting.elements is not None - and len(NestedStructExisting.elements) > 0) - if not NestedHasBody: - MainRenumStruct = Gen.structs.get(ClassName) - if (isinstance(MainRenumStruct, ir.IdentifiedStructType) - and MainRenumStruct.elements is not None - and len(MainRenumStruct.elements) > 0): - NestedStruct = Gen._get_or_create_struct(NestedStructName, source_sha1=source_sha1) - if isinstance(NestedStruct, ir.IdentifiedStructType) and NestedStruct.is_opaque: - NestedStruct.set_body(*MainRenumStruct.elements) - elif isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - # 显式值变体(无 payload) - VariantName = item.target.id - value = next_tag - if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - value = item.value.value - MemberNode = CTypeInfo() - MemberNode.Name = VariantName - MemberNode.value = value - MemberNode.EnumName = ClassName - MemberNode.Lineno = item.lineno - MemberNode.IsEnumMember = True - self.Trans.SymbolTable.insert(VariantName, MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode) - self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode) - variant_names.append(VariantName) - next_tag = value + 1 - RenumTypeNode.RenumVariants = variant_names - return - # 【声明发射重入标记】本函数主体会重入自身:成员收集(CollectAnnAssignMember - # → ResolveAnnotationType → 泛型特化 list[T]/GSListNode[T] → _EmitClassLlvm - # → StructGen._generate_structs → _TryLoadStructFromStub(X) - # → _TryLoadClassMembersFromPyi(X) → 本函数(X))。首字段即指针/容器的类必现 - # (llvmlite.Param 的 Attrs、match.MatchOr 的 patterns、stmts.Interactive 的 - # body…),且每轮仅 4 帧:本函数 → _TryLoadStructFromStub → 成员加载 → 本函数。 - # 两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同,失败时键还会被 - # 清除(_TryLoadStructFromStub 的"仍 opaque ⇒ 删键重试"),缓存守卫挡不住 - # ⇒ 无限递归(RecursionError 被上层 except 吞掉,表现为类型解析静默退化)。 - # 登记 (ClassName, source_sha1);由 `_TryLoadStructFromStub` 里的"顺带加载成员" - # 那一步短路(该边是递归环的唯一闭合边:结构体 body 加载会连带成员加载 → 又回到 - # 本函数);本函数自身**不**短路——重入的成员收集是"把表填起来"的唯一途径, - # 消费者(重入编译的其他模块方法体按名查字段)必须能拿到它。 - # 放在 CEnum/REnum 早退(上方 return)之后:那两个分支不加载成员/结构体。 - _emit_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None) - if _emit_inprog is None: - _emit_inprog = set() - Gen._extern_decl_emit_in_progress = _emit_inprog - _emit_key: tuple = (ClassName, source_sha1) - _emit_inprog.add(_emit_key) - # 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub - # 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods) - # 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32) - if ClassName not in Gen.class_members: - Gen.class_members[ClassName] = [] - if ClassName not in Gen.class_member_defaults: - Gen.class_member_defaults[ClassName] = {} - if ClassName not in Gen.class_member_signeds: - Gen.class_member_signeds[ClassName] = {} - if ClassName not in Gen.class_member_bitfields: - Gen.class_member_bitfields[ClassName] = {} - if ClassName not in Gen.class_member_byteorders: - Gen.class_member_byteorders[ClassName] = {} - if ClassName not in Gen.class_member_bitoffsets: - Gen.class_member_bitoffsets[ClassName] = {} - if ClassName not in Gen.class_methods: - Gen.class_methods[ClassName] = [] - # source_sha1 已在函数开头统一计算(作为分类缓存键),此处直接使用。 - # 注意:REnum 分支(上方)会按 actual_module_name 优先重新推导并已 return。 - # 提前注册 struct:使用导入的 source_sha1 创建 opaque struct, - # 避免后续 _TryLoadStructFromStub 触发嵌套编译时,StructGen 用本地 sha1 - # 创建缺少基类字段(如 GSListNode.Next)的本地类型定义。 - Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked) - # Track which module defines this class (for cross-module name mangling) - if source_sha1: - if not hasattr(Gen, 'class_sha1_map'): - Gen.class_sha1_map = {} - Gen.class_sha1_map[ClassName] = source_sha1 - # --- 预扫描预注册:修复编译时序问题 --- - # _TryLoadStructFromStub → _parse_llvm_type → _specialize_generic_class - # 可能触发 list[str].__new__ 编译(其中调用 pool.alloc(48)), - # 此时 MemManager 尚未注册到虚表,导致直接调用返回 NULL → 段错误。 - # 修复:在 _TryLoadStructFromStub 之前预扫描方法列表并提前注册虚表。 - _has_methods_prescan = False - # PEP 695 泛型类的方法声明必须跳过:方法参数类型 T(如 list[T].append(item: T)) - # 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。 - # 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。 - if not IsGenericClass: - # 虚表槽筛选:方法级 `novirtual def`(⇒ @t.NoVTable)不进槽、方法级 - # `virtual def`(⇒ @t.CVTable)进槽——判据为 SymbolUtils.IsVtableSlotMethod - # (唯一规则源,与定义端 HandlesClassDef / TPV _should_method_be_virtual 同口径), - # 保证跨模块虚表槽序与定义模块逐槽一致。 - _VtByClassLevel: bool = _VtableByClassLevelForImport(Node, Gen) - for _item in Node.body: - if isinstance(_item, ast.FunctionDef): - _has_methods_prescan = True - _MethodName = _item.name - # 构造函数(__new__/__init__/__before_init__)不放入 vtable, - # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移 - # (如 Module 有 __new__/__init__ 使 vtable=7,AST 无它们 vtable=5, - # 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。 - if _MethodName in ('__new__', '__init__', '__before_init__'): - continue - _FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}" - _IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(_item, _VtByClassLevel) - if _IsSlot and _FuncFullName not in Gen.class_methods[ClassName]: - Gen.class_methods[ClassName].append(_FuncFullName) - if _has_methods_prescan and IsCVTable: - Gen._cross_module_vtable_classes.add(ClassName) - Gen.class_vtable.add(ClassName) - # 首次加载就传入 source_sha1,避免从其他模块的 output stub 中加载到错误的结构体定义 - # (如 HandlesExprCall.py 的 output stub 中 43f27dda2e5b5923.Value 只有 5 字段, - # 而正确的 aaf8ba449d1e64c1.Value 有 6 字段) - self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1) - # 如果 _get_or_create_struct 因 sha1 冲突创建了新的 opaque struct, - # 需用 source_sha1 重新加载正确的 stub body。 - if source_sha1: - self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1) - has_methods = False - # 解析 __provides__/__requires__/__require_must__ 编译期元数据(跨模块加载) - for item in Node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - field_name: str = item.target.id - if field_name in ('__provides__', '__requires__', '__require_must__') and item.value: - values: list[str] = [] - if isinstance(item.value, ast.List): - for elt in item.value.elts: - if isinstance(elt, ast.Constant) and isinstance(elt.value, str): - values.append(elt.value) - if field_name == '__provides__': - Gen.class_provides[ClassName] = values - elif field_name == '__requires__': - Gen.class_requires[ClassName] = values - elif field_name == '__require_must__': - Gen.class_require_must[ClassName] = values - # 字段收集中保护(与 _EmitClassLlvm 的 _collecting_members_set 同源): - # 本节的成员解析会重入结构体发射 —— CollectAnnAssignMember → - # ResolveAnnotationType → HandlesTypeMerge._HandleSubscript → CollectAll → - # _ResolveGenericSlice → _specialize_generic_class → _EmitClassLlvm → - # _generate_structs(),而此刻 class_members[ClassName] 只含"已扫到的 - # 自有字段"(继承字段展平尚未执行,见下方 _all_inherited 段), - # 且 llvmlite 的 set_body 只允许调用一次 → 残缺布局被永久固化: - # BasicBlock.Lines: rawptr[GSList[Line]] → body 只到 Name 即止 - # Function.Params: rawptr[GSList[Param]] → body 只到 RetTy 即止 - # Line/Param 等则整类丢失继承字段 Next(GEP 索引整体前移一位)。 - # 置入保护后重入的 _generate_structs 会跳过本类(StructGen.py:461), - # 待本节收集+展平完成后由下方显式 _generate_structs() 一次性 set_body。 - Gen._collecting_members_set.add(ClassName) - # 【防重 · 实时判据(2026-09-30)】原实现在此**快照一次**名字集合,循环内只 - # 更新自己追加的项;而本循环每轮都可能重入同一张类表(CollectAnnAssignMember - # → ResolveAnnotationType → 泛型特化 → _generate_structs → - # _TryLoadClassMembersFromPyi(force=True) → 本文件 :3415 清空并按 .vpi 重填) - # ⇒ 快照即时失效,本循环会把**嵌套帧已经登记过的同一字段**再追加一条 - # (现场:AST 的最后一个字段 `children` 因注解含泛型容器而触发该重入, - # 表变 8 项/去重 7 项;该表再被展平原样拷给 AST 的每个子类 ⇒ StructGen - # "[结构体布局] 成员表含重名条目"在大量 AST 子类上齐发)。 - # 故改为**每轮实时读表**:判据语义("名字已在表里就不追加")与改前逐字相同, - # 只多看见循环中途出现的名字。 - for item in Node.body: - # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - if item.target.id in ('__provides__', '__requires__', '__require_must__'): - continue - parsed = self.CollectAnnAssignMember(item, Gen) - if parsed: - VarName, MemberType, TypeInfo = parsed - if TypeInfo is None: - if VarName not in {m[0] for m in Gen.class_members[ClassName]}: - Gen.class_members[ClassName].append((VarName, MemberType)) - Gen.class_member_signeds[ClassName][VarName] = None - Gen.class_member_bitfields[ClassName][VarName] = 0 - else: - if TypeInfo.IsBitField: - Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth - else: - if VarName not in {m[0] for m in Gen.class_members[ClassName]}: - Gen.class_members[ClassName].append((VarName, MemberType)) - Gen.class_member_bitfields[ClassName][VarName] = 0 - # 与 HandlesClassDef 对齐:联合类型字段(如 `list[str] | t.CPtr`) - # 记录泛型特化名到 class_member_element_class。否则跨模块 import - # 的类(如 TypeGenBase.CTypedefNames)缺此记录,_HandleLenLlvm - # 无法识别字段为 list → len() 误编译为 strlen → stdint 崩溃。 - if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8: - if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): + except Exception as e: # 回退:参数类型解析失败时使用默认 i32 + # NoneType 异常是数据缺失的正常兜底,静默不 WARN(干扰太多) + if not (isinstance(e, AttributeError) and "'NoneType'" in str(e)): + _vlog().warning(f"HandlesImports: 忽略异常 {e}", exc_info=e) + ParamType = ir.IntType(32) + if isinstance(ParamType, ir.VoidType) or (isinstance(ParamType, ir.PointerType) and isinstance(ParamType.pointee, ir.VoidType)): + AnnName = None + if isinstance(Arg.annotation, ast.Name): + AnnName = Arg.annotation.id + elif isinstance(Arg.annotation, ast.Attribute): + AnnName = Arg.annotation.attr + elif isinstance(Arg.annotation, ast.BinOp) and isinstance(Arg.annotation.op, ast.BitOr): + AnnName = ExtractTypeNameFromBinOp(Arg.annotation) + if AnnName and AnnName in Gen.structs: + ParamType = ir.PointerType(Gen.structs[AnnName]) + else: + ParamType = ir.PointerType(ir.IntType(8)) + else: + ParamType = ir.IntType(32) + ParamTypes.append(ParamType) + IsVariadic = Node.args.vararg is not None + FuncType = ir.FunctionType(ReturnType, ParamTypes, var_arg=IsVariadic) + _is_export = False + if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.IsState: + _is_export = True + if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExport) and not ReturnTypeInfo.IsState: + Gen._export_funcs.add(FuncName) + _is_export = True + if isinstance(ReturnTypeInfo, CTypeInfo) and ReturnTypeInfo.Storage and isinstance(ReturnTypeInfo.Storage, t.CExtern): + Gen._export_funcs.add(FuncName) + _is_export = True + if not _is_export: + if Node.returns and self._check_annotation_for_state(Node.returns): + _is_export = True + elif self._check_decorators_for_state(Node): + _is_export = True + MangledName = Gen._mangle_func_name(FuncName, module_name=source_module_name) + func = Gen._get_or_declare_function(MangledName, FuncType) + Gen.functions[MangledName] = func + Gen.functions[FuncName] = func + # 跨模块自动取址模式登记(ref/temp/always):以**真实发射名** func.name 为主键, + # 与查表侧 FuncGen._adjust_args 的 func.name 逐字一致;MangledName / FuncName + # 保留为别名键。ParamTypes 与 Node.args.args 逐位对齐(含方法 self)。 + # (_auto_modes 已在函数开头"早退之前"处计算,此处复用。) + if any(m is not None for m in _auto_modes): + Gen._auto_addr_params[func.name] = _auto_modes + Gen._auto_addr_params[MangledName] = _auto_modes + Gen._auto_addr_params[FuncName] = _auto_modes + func_meta = FuncMeta.NONE + if Node.decorator_list: + for d in Node.decorator_list: + if isinstance(d, ast.Name): + if d.id == 'staticmethod': + func_meta |= FuncMeta.STATIC_METHOD + elif d.id == 'property': + func_meta |= FuncMeta.PROPERTY_GETTER + elif d.id == 'classmethod': + func_meta |= FuncMeta.CLASS_METHOD + elif isinstance(d, ast.Attribute): + if isinstance(d.value, ast.Name): + # 支持 @property.setter 和 @propname.setter 两种形式 + if d.value.id == 'property' or d.attr in ('setter', 'getter', 'deleter'): + if d.attr == 'setter': + func_meta |= FuncMeta.PROPERTY_SETTER + elif d.attr == 'getter': + func_meta |= FuncMeta.PROPERTY_GETTER + elif d.attr == 'deleter': + func_meta |= FuncMeta.PROPERTY_DELETER + if not self.Trans.SymbolTable.has(FuncName): + FuncInfo = CTypeInfo() + FuncInfo.Name = FuncName + FuncInfo.IsFunction = True + FuncInfo.MetaList = func_meta + self.Trans.SymbolTable.insert(FuncName, FuncInfo) + else: + existing = self.Trans.SymbolTable[FuncName] + if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE: + existing.MetaList = func_meta + # 同时注册带模块前缀的符号,以便 module.func_name 查找能命中 + if source_module_name and source_module_name not in ('c', 't'): + FullSymKey = f"{source_module_name}.{FuncName}" + if not self.Trans.SymbolTable.has(FullSymKey): + FullFuncInfo = CTypeInfo() + FullFuncInfo.Name = FullSymKey + FullFuncInfo.IsFunction = True + FullFuncInfo.MetaList = func_meta + self.Trans.SymbolTable.insert(FullSymKey, FullFuncInfo) + # property setter/deleter: 在原始 PropKey(不带后缀)下注册 MetaList + if FuncMeta.PROPERTY_SETTER in func_meta or FuncMeta.PROPERTY_DELETER in func_meta: + BasePropKey = FuncName.replace('$set', '').replace('$del', '') + base_existing = self.Trans.SymbolTable.lookup(BasePropKey) + if base_existing: + if func_meta != FuncMeta.NONE: + base_existing.MetaList = base_existing.MetaList | func_meta + else: + PropInfo = CTypeInfo() + PropInfo.Name = BasePropKey + PropInfo.IsFunction = True + PropInfo.MetaList = func_meta + self.Trans.SymbolTable.insert(BasePropKey, PropInfo) + if register_module_name and register_module_name != source_module_name: + ReexportName = Gen._mangle_func_name(FuncName, module_name=register_module_name) + Gen.functions[ReexportName] = func + if reexport_module_names: + for reexport_mod in reexport_module_names: + if reexport_mod and reexport_mod != source_module_name: + ReexportName2 = Gen._mangle_func_name(FuncName, module_name=reexport_mod) + if ReexportName2 not in Gen.functions: + Gen.functions[ReexportName2] = func + + def _ClassifyExternalClass(self, Node: ast.ClassDef) -> tuple: + """遍历 Node.bases/decorator_list 做类别判断(纯函数、与 Gen 无关)。 + + 结果由 _EmitExternalClassDeclLlvm 按 (source_sha1, class_name) 类级缓存, + 同一 worker 进程内多个任务对同一跨模块类重复调用时跳过本遍历。 + 返回 (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, IsCVTable, + IsNoVTable, IsPacked)。 + + PEP 695 泛型类(如 GSListNode[T])特殊:跨模块继承展平需要从泛型基类 + 获取字段(如 Next),否则子类(如 Value(GSListNode[Value]))的 + class_members 会缺少继承字段;但泛型类的方法声明必须跳过(方法参数 + 类型 T 会被解析为 opaque struct,LLVM 不允许 opaque struct 作为值参数)。 + """ + IsGenericClass: bool = hasattr(Node, 'type_params') and bool(Node.type_params) + IsCenum = False + IsRenum = False + IsCpythonObject = False + IsCVTable = False + IsNoVTable = False + if Node.bases: + for base in Node.bases: + if getattr(base, 'attr', None): + if base.attr == 'REnum': + IsRenum = True + break + elif base.attr == 'CEnum' or base.attr == 'Enum': + IsCenum = True + break + elif getattr(base, 'id', None): + if base.id == 'REnum': + IsRenum = True + break + elif base.id == 'CEnum' or base.id == 'Enum': + IsCenum = True + break + if not IsCenum and not IsRenum and getattr(Node, 'decorator_list', None): + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Attribute): + if getattr(decorator.value, 'id', None) == 't': + if decorator.attr == 'Object': + IsCpythonObject = True + elif decorator.attr == 'CVTable': + IsCVTable = True + elif decorator.attr == 'NoVTable': + IsNoVTable = True + elif decorator.attr == 'REnum': + IsRenum = True + elif decorator.attr == 'CEnum' or decorator.attr == 'Enum': + IsCenum = True + elif isinstance(decorator, ast.Call): + if isinstance(decorator.func, ast.Attribute): + if getattr(decorator.func.value, 'id', None) == 't': + if decorator.func.attr == 'Object': + IsCpythonObject = True + elif decorator.func.attr == 'CVTable': + IsCVTable = True + elif decorator.func.attr == 'NoVTable': + IsNoVTable = True + elif isinstance(decorator.func, ast.Name): + if decorator.func.id == 'Object': + IsCpythonObject = True + elif decorator.func.id == 'CVTable': + IsCVTable = True + elif decorator.func.id == 'NoVTable': + IsNoVTable = True + IsPacked = False + if getattr(Node, 'decorator_list', None): + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Call): + if isinstance(decorator.func, ast.Attribute): + if getattr(decorator.func.value, 'id', None) == 'c' and decorator.func.attr == 'Attribute': + for arg in decorator.args: + if isinstance(arg, ast.Attribute): + if isinstance(arg.value, ast.Attribute): + if getattr(arg.value.value, 'id', None) == 't' and arg.value.attr == 'attr' and arg.attr == 'packed': + IsPacked = True + # 方法级 virtual(`virtual def m(...)` ⇒ 方法装饰器 @t.CVTable)触发类级 vtable: + # 与 HandlesClassDef._resolve_class_flags / VPC _detect_vtable_status 的 + # `_has_any_func_cvtable` 分支对齐(跨模块这里只能判"类级 NoVTable / + # 无真实父类"两种可判定形态;父类 vtable 状态由 .pyi 按需注册路径解析)。 + if not IsCVTable and ClassHasFuncLevelCVTable(Node): + if IsNoVTable or not _HasRealParentClass(Node): + IsCVTable = True + return (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, + IsCVTable, IsNoVTable, IsPacked) + + def _EmitExternalClassDeclLlvm(self, Node: ast.ClassDef, Gen: LlvmGeneratorMixin, module_name: str | None = None, actual_module_name: str | None = None, source_sha1: str | None = None) -> None: + ClassName = Node.name + # Compute source_sha1 up front (moved from mid-function) so it can serve as + # the key of the class-level classification cache. Prefer the explicit + # source_sha1 passed by _TryLoadClassMembersFromPyi (module_name lookup may + # fail when the current module does not import the defining module directly). + if not source_sha1: + if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map: + source_sha1 = Gen.ModuleSha1Map[actual_module_name] + elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map: + source_sha1 = Gen.ModuleSha1Map[module_name] + # Class-level classification cache: the bases/decorator_list walk below is pure + # Gen-independent computation. Repeated calls on the same (source_sha1, class_name) + # across worker tasks (same process) skip the walk. When source_sha1 is None + # (lookup failed) neither read nor write the cache, to avoid mismatching + # same-named classes from different modules under a shared None key. + _classify_key = (source_sha1, ClassName) if source_sha1 else None + _cached_classify = self._extern_class_classify_cache.get(_classify_key) if _classify_key else None + if _cached_classify is None: + _cached_classify = self._ClassifyExternalClass(Node) + if _classify_key is not None: + self._extern_class_classify_cache[_classify_key] = _cached_classify + (IsGenericClass, IsCenum, IsRenum, IsCpythonObject, + IsCVTable, IsNoVTable, IsPacked) = _cached_classify + if ClassName.startswith('__') and not IsCpythonObject and not IsCenum and not IsRenum: + return + if IsPacked: + Gen.class_packed.add(ClassName) + if IsCpythonObject: + CInfo = CTypeInfo() + CInfo.Name = ClassName + CInfo.IsCpythonObject = True + CInfo.IsStruct = True + if module_name: + FullName = f"{module_name}.{ClassName}" + self.Trans.SymbolTable.insert(FullName, CInfo) + self.Trans.SymbolTable.insert(ClassName, CInfo) + if IsCenum: + EnumTypeNode = CTypeInfo() + EnumTypeNode.Name = ClassName + EnumTypeNode.BaseType = t.CEnum(ClassName) + EnumTypeNode.IsEnum = True + # [跨模块枚举宽度] 读侧重推导:.vpi/.pyi 保留成员注解(`Name: T`), + # 故在本处按"最宽成员类型"重算(与定义端 HandlesClassDef 同一规则源 + # EnumClassIntWidth),使跨模块使用同一枚举时宽度一致。本函数会以短名 + # 覆盖共享符号表(_shared_sym.json pickle)中的同名条目,若不在此登记 + # 宽度,覆盖后即退化为默认 i32(= 上一轮遗留缺口)。旧产物缺注解时 + # 自然回落 32(Default),不崩。 + EnumWidth: int = EnumClassIntWidth(Node.body, self.Trans.SymbolTable) + EnumTypeNode.set('enum_width', EnumWidth) + self.Trans.SymbolTable.insert(ClassName, EnumTypeNode) + next_enum_value: int = 0 + for item in Node.body: + VarName = None + ItemValue = None + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + VarName = item.target.id + ItemValue = item.value + elif isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + VarName = target.id + break + ItemValue = item.value + if VarName is None: + continue + # 提取枚举值:优先用显式赋值,否则自动编号 + if ItemValue is not None and isinstance(ItemValue, ast.Constant) and isinstance(ItemValue.value, int): + value = ItemValue.value + next_enum_value = ItemValue.value + 1 + else: + value = next_enum_value + next_enum_value += 1 + MemberNode = CTypeInfo() + MemberNode.Name = VarName + MemberNode.BaseType = t.CEnum(ClassName) + MemberNode.value = value + MemberNode.EnumName = ClassName + MemberNode.Lineno = item.lineno + MemberNode.IsEnumMember = True + MemberNode.set('enum_width', EnumWidth) + self.Trans.SymbolTable.insert(VarName, MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}.{VarName}", MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}_{VarName}", MemberNode) + return + if IsRenum: + # REnum 跨模块导入:注册 REnum 类型 + 变体成员到 SymbolTable + # 对齐同模块 _EmitREnumLlvm 的注册逻辑 + RenumTypeNode = CTypeInfo() + RenumTypeNode.Name = ClassName + RenumTypeNode.IsRenum = True + RenumTypeNode.IsEnum = True # 兼容 IsEnum 检查 + # REnum 在内存布局上是 struct,必须设置 IsStruct=True 才能让 + # HandlesTypeMerge 的 CPtr 吸收逻辑生效(REnumType | t.CPtr = REnumType*) + RenumTypeNode.IsStruct = True + if module_name: + FullName = f"{module_name}.{ClassName}" + self.Trans.SymbolTable.insert(FullName, RenumTypeNode) + self.Trans.SymbolTable.insert(ClassName, RenumTypeNode) + # 从 stub.ll 加载 REnum 结构体布局到 Gen.structs + self._TryLoadStructFromStub(ClassName, Gen) + # 记录 class_sha1_map(跨模块名修饰) + source_sha1 = None + if actual_module_name and hasattr(Gen, 'ModuleSha1Map') and actual_module_name in Gen.ModuleSha1Map: + source_sha1 = Gen.ModuleSha1Map[actual_module_name] + elif module_name and hasattr(Gen, 'ModuleSha1Map') and module_name in Gen.ModuleSha1Map: + source_sha1 = Gen.ModuleSha1Map[module_name] + if source_sha1: + if not hasattr(Gen, 'class_sha1_map'): + Gen.class_sha1_map = {} + Gen.class_sha1_map[ClassName] = source_sha1 + # 遍历嵌套 ClassDef 注册变体成员 + # REnum body 是嵌套 ClassDef(变体定义),不是 AnnAssign/Assign + next_tag: int = 0 + variant_names: list[str] = [] + for item in Node.body: + if isinstance(item, ast.ClassDef): + VariantName = item.name + MemberNode = CTypeInfo() + MemberNode.Name = VariantName + MemberNode.value = next_tag + MemberNode.EnumName = ClassName + MemberNode.Lineno = item.lineno + MemberNode.IsEnumMember = True + self.Trans.SymbolTable.insert(VariantName, MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode) + variant_names.append(VariantName) + next_tag += 1 + # 加载嵌套变体结构体(如 LLVMType_Int)到 Gen.structs, + # 并注册 class_members 以支持 _HandleREnumConstructLlvm 的 payload 存储和 + # _HandleRenumMatchLlvm 的 binding 提取。 + NestedStructName: str = f"{ClassName}_{VariantName}" + self._TryLoadStructFromStub(NestedStructName, Gen) + if NestedStructName not in Gen.class_members: + Gen.class_members[NestedStructName] = [] + if NestedStructName not in Gen.class_member_signeds: + Gen.class_member_signeds[NestedStructName] = {} + if Gen.class_members[NestedStructName]: + # 已有成员(_TryLoadStructFromStub 可能已加载),跳过 + pass + else: + Gen.class_members[NestedStructName].append(('__tag', ir.IntType(32))) + Gen.class_member_signeds[NestedStructName]['__tag'] = None + for nested_item in item.body: + # 只把 AnnAssign 当载荷声明;变体体里的 pass / docstring 等 + # 非声明语句合法跳过(如 `class Void: pass`)。 + if not isinstance(nested_item, ast.AnnAssign): + continue + _member_txt: str = ast.unparse(nested_item) + try: + parsed = self.CollectAnnAssignMember(nested_item, Gen) + except Exception as _pe: + # 历史上此处异常被上层吞掉 ⇒ 变体只剩 __tag ⇒ 变体构造静默 + # 丢载荷、match 模式捕获绑不上(现场 LLVMType_Ptr / pointee_cs), + # 且循环中断使后续变体(Func/Array/...)整批缺失。改为响亮失败。 + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + raise _FFE( + f"REnum 变体 '{ClassName}.{VariantName}' 的载荷成员 " + f"'{_member_txt}' 解析失败:{type(_pe).__name__}: {_pe} " + f"—— 拒绝留下只有 __tag 的 class_members" + f"(会导致变体构造丢载荷、match 捕获绑定缺失、后续变体整批缺失)" + ) from _pe + if not parsed: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + raise _FFE( + f"REnum 变体 '{ClassName}.{VariantName}' 的载荷成员 " + f"'{_member_txt}' 未解析出类型(CollectAnnAssignMember 返回 None)" + f"—— 同上,拒绝静默降级为无载荷" + ) + MName, MType, MTypeInfo = parsed + Gen.class_members[NestedStructName].append((MName, MType)) + Gen.class_member_signeds[NestedStructName][MName] = None + # 当 stub 中没有嵌套变体结构体定义时(_TryLoadStructFromStub 失败), + # 使用主 REnum 结构体的 body (max_variant_struct) 创建嵌套变体结构体, + # 确保跨模块布局一致。否则 _generate_structs() Step 4 会从 class_members + # 设置最小布局 body,导致 GEP 偏移量与定义模块不匹配(offset 4 vs offset 8)。 + NestedStructExisting = Gen.structs.get(NestedStructName) + NestedHasBody = (isinstance(NestedStructExisting, ir.IdentifiedStructType) + and NestedStructExisting.elements is not None + and len(NestedStructExisting.elements) > 0) + if not NestedHasBody: + MainRenumStruct = Gen.structs.get(ClassName) + if (isinstance(MainRenumStruct, ir.IdentifiedStructType) + and MainRenumStruct.elements is not None + and len(MainRenumStruct.elements) > 0): + NestedStruct = Gen._get_or_create_struct(NestedStructName, source_sha1=source_sha1) + if isinstance(NestedStruct, ir.IdentifiedStructType) and NestedStruct.is_opaque: + NestedStruct.set_body(*MainRenumStruct.elements) + elif isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + # 显式值变体(无 payload) + VariantName = item.target.id + value = next_tag + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + value = item.value.value + MemberNode = CTypeInfo() + MemberNode.Name = VariantName + MemberNode.value = value + MemberNode.EnumName = ClassName + MemberNode.Lineno = item.lineno + MemberNode.IsEnumMember = True + self.Trans.SymbolTable.insert(VariantName, MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}.{VariantName}", MemberNode) + self.Trans.SymbolTable.insert(f"{ClassName}_{VariantName}", MemberNode) + variant_names.append(VariantName) + next_tag = value + 1 + RenumTypeNode.RenumVariants = variant_names + return + # 【声明发射重入标记】本函数主体会重入自身:成员收集(CollectAnnAssignMember + # → ResolveAnnotationType → 泛型特化 list[T]/GSListNode[T] → _EmitClassLlvm + # → StructGen._generate_structs → _TryLoadStructFromStub(X) + # → _TryLoadClassMembersFromPyi(X) → 本函数(X))。首字段即指针/容器的类必现 + # (llvmlite.Param 的 Attrs、match.MatchOr 的 patterns、stmts.Interactive 的 + # body…),且每轮仅 4 帧:本函数 → _TryLoadStructFromStub → 成员加载 → 本函数。 + # 两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同,失败时键还会被 + # 清除(_TryLoadStructFromStub 的"仍 opaque ⇒ 删键重试"),缓存守卫挡不住 + # ⇒ 无限递归(RecursionError 被上层 except 吞掉,表现为类型解析静默退化)。 + # 登记 (ClassName, source_sha1);由 `_TryLoadStructFromStub` 里的"顺带加载成员" + # 那一步短路(该边是递归环的唯一闭合边:结构体 body 加载会连带成员加载 → 又回到 + # 本函数);本函数自身**不**短路——重入的成员收集是"把表填起来"的唯一途径, + # 消费者(重入编译的其他模块方法体按名查字段)必须能拿到它。 + # 放在 CEnum/REnum 早退(上方 return)之后:那两个分支不加载成员/结构体。 + _emit_inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None) + if _emit_inprog is None: + _emit_inprog = set() + Gen._extern_decl_emit_in_progress = _emit_inprog + _emit_key: tuple = (ClassName, source_sha1) + _emit_inprog.add(_emit_key) + # 先初始化 class_methods/class_members 等容器,确保 _TryLoadStructFromStub + # 和 _get_or_create_struct 中的 IsEnum 守卫(clean_name not in class_methods) + # 能正确识别 AST 节点类名(避免 ASTKind 枚举名冲突时把节点类当枚举返回 i32) + if ClassName not in Gen.class_members: + Gen.class_members[ClassName] = [] + if ClassName not in Gen.class_member_defaults: + Gen.class_member_defaults[ClassName] = {} + if ClassName not in Gen.class_member_signeds: + Gen.class_member_signeds[ClassName] = {} + if ClassName not in Gen.class_member_bitfields: + Gen.class_member_bitfields[ClassName] = {} + if ClassName not in Gen.class_member_byteorders: + Gen.class_member_byteorders[ClassName] = {} + if ClassName not in Gen.class_member_bitoffsets: + Gen.class_member_bitoffsets[ClassName] = {} + if ClassName not in Gen.class_methods: + Gen.class_methods[ClassName] = [] + # source_sha1 已在函数开头统一计算(作为分类缓存键),此处直接使用。 + # 注意:REnum 分支(上方)会按 actual_module_name 优先重新推导并已 return。 + # 提前注册 struct:使用导入的 source_sha1 创建 opaque struct, + # 避免后续 _TryLoadStructFromStub 触发嵌套编译时,StructGen 用本地 sha1 + # 创建缺少基类字段(如 GSListNode.Next)的本地类型定义。 + Gen._get_or_create_struct(ClassName, source_sha1=source_sha1, packed=IsPacked) + # Track which module defines this class (for cross-module name mangling) + if source_sha1: + if not hasattr(Gen, 'class_sha1_map'): + Gen.class_sha1_map = {} + Gen.class_sha1_map[ClassName] = source_sha1 + # --- 预扫描预注册:修复编译时序问题 --- + # _TryLoadStructFromStub → _parse_llvm_type → _specialize_generic_class + # 可能触发 list[str].__new__ 编译(其中调用 pool.alloc(48)), + # 此时 MemManager 尚未注册到虚表,导致直接调用返回 NULL → 段错误。 + # 修复:在 _TryLoadStructFromStub 之前预扫描方法列表并提前注册虚表。 + _has_methods_prescan = False + # PEP 695 泛型类的方法声明必须跳过:方法参数类型 T(如 list[T].append(item: T)) + # 会被解析为 opaque struct,LLVM 报 "invalid type for function argument"。 + # 字段(AnnAssign)仍正常加载,确保继承字段(如 GSListNode.Next)可被展平。 + if not IsGenericClass: + # 虚表槽筛选:方法级 `novirtual def`(⇒ @t.NoVTable)不进槽、方法级 + # `virtual def`(⇒ @t.CVTable)进槽——判据为 SymbolUtils.IsVtableSlotMethod + # (唯一规则源,与定义端 HandlesClassDef / VPC _should_method_be_virtual 同口径), + # 保证跨模块虚表槽序与定义模块逐槽一致。 + _VtByClassLevel: bool = _VtableByClassLevelForImport(Node, Gen) + for _item in Node.body: + if isinstance(_item, ast.FunctionDef): + _has_methods_prescan = True + _MethodName = _item.name + # 构造函数(__new__/__init__/__before_init__)不放入 vtable, + # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移 + # (如 Module 有 __new__/__init__ 使 vtable=7,AST 无它们 vtable=5, + # 调用方按 AST 布局取 append@idx4 实际取到 Module.dump@idx4)。 + if _MethodName in ('__new__', '__init__', '__before_init__'): + continue + _FuncFullName = f"{ClassName}.__init__" if _MethodName == "__init__" else f"{ClassName}.__call__" if _MethodName == "__call__" else f"{ClassName}.{_MethodName}" + _IsSlot: bool = (not IsCVTable) or IsVtableSlotMethod(_item, _VtByClassLevel) + if _IsSlot and _FuncFullName not in Gen.class_methods[ClassName]: + Gen.class_methods[ClassName].append(_FuncFullName) + if _has_methods_prescan and IsCVTable: + Gen._cross_module_vtable_classes.add(ClassName) + Gen.class_vtable.add(ClassName) + # 首次加载就传入 source_sha1,避免从其他模块的 output stub 中加载到错误的结构体定义 + # (如 HandlesExprCall.py 的 output stub 中 43f27dda2e5b5923.Value 只有 5 字段, + # 而正确的 aaf8ba449d1e64c1.Value 有 6 字段) + self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1) + # 如果 _get_or_create_struct 因 sha1 冲突创建了新的 opaque struct, + # 需用 source_sha1 重新加载正确的 stub body。 + if source_sha1: + self._TryLoadStructFromStub(ClassName, Gen, source_sha1=source_sha1) + has_methods = False + # 解析 __provides__/__requires__/__require_must__ 编译期元数据(跨模块加载) + for item in Node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + field_name: str = item.target.id + if field_name in ('__provides__', '__requires__', '__require_must__') and item.value: + values: list[str] = [] + if isinstance(item.value, ast.List): + for elt in item.value.elts: + if isinstance(elt, ast.Constant) and isinstance(elt.value, str): + values.append(elt.value) + if field_name == '__provides__': + Gen.class_provides[ClassName] = values + elif field_name == '__requires__': + Gen.class_requires[ClassName] = values + elif field_name == '__require_must__': + Gen.class_require_must[ClassName] = values + # 字段收集中保护(与 _EmitClassLlvm 的 _collecting_members_set 同源): + # 本节的成员解析会重入结构体发射 —— CollectAnnAssignMember → + # ResolveAnnotationType → HandlesTypeMerge._HandleSubscript → CollectAll → + # _ResolveGenericSlice → _specialize_generic_class → _EmitClassLlvm → + # _generate_structs(),而此刻 class_members[ClassName] 只含"已扫到的 + # 自有字段"(继承字段展平尚未执行,见下方 _all_inherited 段), + # 且 llvmlite 的 set_body 只允许调用一次 → 残缺布局被永久固化: + # BasicBlock.Lines: rawptr[GSList[Line]] → body 只到 Name 即止 + # Function.Params: rawptr[GSList[Param]] → body 只到 RetTy 即止 + # Line/Param 等则整类丢失继承字段 Next(GEP 索引整体前移一位)。 + # 置入保护后重入的 _generate_structs 会跳过本类(StructGen.py:461), + # 待本节收集+展平完成后由下方显式 _generate_structs() 一次性 set_body。 + Gen._collecting_members_set.add(ClassName) + # 【防重 · 实时判据(2026-09-30)】原实现在此**快照一次**名字集合,循环内只 + # 更新自己追加的项;而本循环每轮都可能重入同一张类表(CollectAnnAssignMember + # → ResolveAnnotationType → 泛型特化 → _generate_structs → + # _TryLoadClassMembersFromPyi(force=True) → 本文件 :3415 清空并按 .vpi 重填) + # ⇒ 快照即时失效,本循环会把**嵌套帧已经登记过的同一字段**再追加一条 + # (现场:AST 的最后一个字段 `children` 因注解含泛型容器而触发该重入, + # 表变 8 项/去重 7 项;该表再被展平原样拷给 AST 的每个子类 ⇒ StructGen + # "[结构体布局] 成员表含重名条目"在大量 AST 子类上齐发)。 + # 故改为**每轮实时读表**:判据语义("名字已在表里就不追加")与改前逐字相同, + # 只多看见循环中途出现的名字。 + for item in Node.body: + # 跳过编译期元数据字段(__provides__/__requires__/__require_must__) + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + if item.target.id in ('__provides__', '__requires__', '__require_must__'): + continue + parsed = self.CollectAnnAssignMember(item, Gen) + if parsed: + VarName, MemberType, TypeInfo = parsed + if TypeInfo is None: + if VarName not in {m[0] for m in Gen.class_members[ClassName]}: + Gen.class_members[ClassName].append((VarName, MemberType)) + Gen.class_member_signeds[ClassName][VarName] = None + Gen.class_member_bitfields[ClassName][VarName] = 0 + else: + if TypeInfo.IsBitField: + Gen.class_member_bitfields[ClassName][VarName] = TypeInfo.BitWidth + else: + if VarName not in {m[0] for m in Gen.class_members[ClassName]}: + Gen.class_members[ClassName].append((VarName, MemberType)) + Gen.class_member_bitfields[ClassName][VarName] = 0 + # 与 HandlesClassDef 对齐:联合类型字段(如 `list[str] | t.CPtr`) + # 记录泛型特化名到 class_member_element_class。否则跨模块 import + # 的类(如 TypeGenBase.CTypedefNames)缺此记录,_HandleLenLlvm + # 无法识别字段为 list → len() 误编译为 strlen → stdint 崩溃。 + if isinstance(MemberType, ir.PointerType) and isinstance(MemberType.pointee, ir.IntType) and MemberType.pointee.width == 8: + if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): element_class: str | None = FindStructNameInAnnotationWithContext( - item.annotation, self.Trans, Gen) - if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: - element_class = TypeInfo.Name - if element_class: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = element_class - elif (isinstance(MemberType, ir.PointerType) - and isinstance(MemberType.pointee, ir.IdentifiedStructType) - and isinstance(item.annotation, ast.BinOp) - and isinstance(item.annotation.op, ast.BitOr)): + item.annotation, self.Trans, Gen) + if not element_class and TypeInfo.IsStruct and TypeInfo.Name and TypeInfo.Name in Gen.structs: + element_class = TypeInfo.Name + if element_class: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = element_class + elif (isinstance(MemberType, ir.PointerType) + and isinstance(MemberType.pointee, ir.IdentifiedStructType) + and isinstance(item.annotation, ast.BinOp) + and isinstance(item.annotation.op, ast.BitOr)): spec_in_anno: str | None = FindStructNameInAnnotationWithContext( - item.annotation, self.Trans, Gen) - if spec_in_anno and '[' in spec_in_anno: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = spec_in_anno - # Subscript 容器字段(list[X] / rawptr[list[X]],如 includes/ast - # 的 Call.args / Import.names: rawptr[list[rawptr[AST]]]):非 BinOp - # 联合,上面两分支不命中 → 容器身份丢失 → 字段方法调用(get/len…) - # 分派失败报 "Undefined method 'get' in class 'rawptr'"(Converter.vp)。 - # 用与特化生成端同源的 _resolve_type_arg_str 构造 element_class, - # 键名与 Gen.structs 中 list 特化名保持一致。 - if isinstance(item.annotation, ast.Subscript): - _ann_sub_s: ast.Subscript = item.annotation - _subv_s: ast.AST = _ann_sub_s.value - _subname_s: str = getattr(_subv_s, 'id', None) or getattr(_subv_s, 'attr', '') - _elem_slice_s: ast.AST | None = None - if _subname_s == 'list': - _elem_slice_s = _ann_sub_s.slice - elif _subname_s == 'rawptr': - _slice_s: ast.AST = _ann_sub_s.slice - if isinstance(_slice_s, ast.Subscript): - # rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]): - # 容器身份 = 泛型特化名。list 继续走 _elem_slice_s 统一登记; - # 其余泛型容器直接登记 element_class(阻塞 B 根因链一环: - # 原实现只认内层 list,rawptr[GSList[Param]] 在此空转并跳过 - # 裸结构分支 → element_class 缺失 → 字段/局部变量方法分派 - # 丢失容器身份,FakeDuck 错绑 LinkedNode.append 写穿)。 - _inner_sub_s: ast.Subscript = _slice_s - _iname_s: str = getattr(_inner_sub_s.value, 'id', None) or getattr(_inner_sub_s.value, 'attr', '') - if _iname_s == 'list': - _elem_slice_s = _inner_sub_s.slice - else: - _ec_gs: str | None = None - if _iname_s: - try: - _ec_gs = f'{_iname_s}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_s.slice)}]' - except Exception: - _ec_gs = None - if _ec_gs: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = _ec_gs - else: - # rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内 - # 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复 - # struct 身份走 char*→struct cast。HEAD 态 `Token | t.CPtr` 联合 - # 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。 - # 与 HandlesClassDef 的 rawptr 分支对称。 - _slc_name_s: str | None = None - if isinstance(_slice_s, ast.Name): - _slc_name_s = _slice_s.id - elif isinstance(_slice_s, ast.Attribute): - _slc_name_s = _slice_s.attr - _ec_target_s: str | None = None - if _slc_name_s: - if _slc_name_s in Gen.structs: - _ec_target_s = _slc_name_s - else: - _st_sha1_s: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_slc_name_s) - if _st_sha1_s and f'{_st_sha1_s}.{_slc_name_s}' in Gen.structs: - _ec_target_s = f'{_st_sha1_s}.{_slc_name_s}' - else: - # 短名模糊扫描(对齐 HandlesAnnAssign 变量侧 - # 补偿 L597-602):跨模块 `mod.Class` 形态的 - # rawptr[Class] 字段在 class_sha1_map 尚无该短名 - # 时上面两路全 miss → element_class 静默缺失 → - # 字段方法调用(factory.PermPool.alloc)链尾类名 - # 丢失 → TPC 调用分派返回 i32 0 哨兵 → 误报 - # "Unknown module: ...PermPool"。 - for _ec_k_s in Gen.structs: - _ec_short_s: str = _ec_k_s.split('.')[-1] if '.' in _ec_k_s else _ec_k_s - if _ec_short_s == _slc_name_s: - _ec_target_s = _ec_k_s - break - if _ec_target_s: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = _ec_target_s - if _elem_slice_s is not None: - _ec_s: str | None = None - try: - _ec_s = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_s)}]' - except Exception: - _ec_s = None - if _ec_s: - if ClassName not in Gen.class_member_element_class: - Gen.class_member_element_class[ClassName] = {} - Gen.class_member_element_class[ClassName][VarName] = _ec_s - if TypeInfo.ByteOrder: - Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder - else: - Gen.class_member_byteorders[ClassName][VarName] = "" - if item.value: - const = self._BuildScalarConstant(item.value, MemberType, Gen) - if const: - Gen.class_member_defaults[ClassName][VarName] = const - elif isinstance(item, ast.FunctionDef) and not IsGenericClass: - # PEP 695 泛型类跳过方法声明:T 参数会变成 opaque struct 触发 - # "invalid type for function argument"。字段已由上面的 AnnAssign - # 分支加载,继承字段(如 GSListNode.Next)由后面的展平逻辑补充。 - has_methods = True - MethodName = item.name - FuncFullName = f"{ClassName}.__init__" if MethodName == "__init__" else f"{ClassName}.__call__" if MethodName == "__call__" else f"{ClassName}.{MethodName}" - # 构造函数(__new__/__init__/__before_init__)不放入 vtable, - # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。 - # register_method 内部也会过滤,此处仅跳过 class_methods.append。 - # 方法级修饰符(virtual def/novirtual def)⇒ 是否占槽按 - # SymbolUtils.IsVtableSlotMethod(唯一规则源)判定,与定义模块逐槽一致。 - _IsSlot2: bool = (not IsCVTable) or IsVtableSlotMethod( - item, _VtableByClassLevelForImport(Node, Gen)) - if MethodName not in ('__new__', '__init__', '__before_init__'): - if _IsSlot2 and FuncFullName not in Gen.class_methods[ClassName]: - Gen.class_methods[ClassName].append(FuncFullName) - Gen.register_method(ClassName, FuncFullName, _IsSlot2) - is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list) - is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list) - is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list) - is_item_prop_setter = any(isinstance(d, ast.Attribute) and d.attr == 'setter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list) - is_item_prop_getter = any(isinstance(d, ast.Attribute) and d.attr == 'getter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list) - is_item_prop_deleter = any(isinstance(d, ast.Attribute) and d.attr == 'deleter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list) - # setter/deleter 使用不同的函数名后缀 - DeclFuncName = FuncFullName - if is_item_prop_setter: - DeclFuncName = FuncFullName + '$set' - elif is_item_prop_deleter: - DeclFuncName = FuncFullName + '$del' - # 无条件调用:即使 DeclFuncName 已在 Gen.functions 中,也必须让 - # _EmitExternalFuncDeclLlvm 走到其**早退之前**的自动取址登记逻辑 - # (该函数在早退前完成登记后即返回,对已存在符号无其它副作用); - # 否则下面的守卫会让整个调用被跳过,stub 方法的取址模式永不生效。 - FuncDeclNode = ast.FunctionDef( - name=DeclFuncName, - args=item.args, - body=item.body, - decorator_list=item.decorator_list, - returns=item.returns - ) - try: - self._EmitExternalFuncDeclLlvm(FuncDeclNode, Gen, is_class_method=not is_item_static and not is_item_classmethod, source_module_name=module_name) - except Exception as e: - raise - func_meta = FuncMeta.NONE - if is_item_static: - func_meta |= FuncMeta.STATIC_METHOD - if is_item_property: - func_meta |= FuncMeta.PROPERTY_GETTER - if is_item_classmethod: - func_meta |= FuncMeta.CLASS_METHOD - if is_item_prop_setter: - func_meta |= FuncMeta.PROPERTY_SETTER - if is_item_prop_getter: - func_meta |= FuncMeta.PROPERTY_GETTER - if is_item_prop_deleter: - func_meta |= FuncMeta.PROPERTY_DELETER - # setter/deleter 的 SymKey 使用带后缀的函数名 - SymKey = DeclFuncName - if not self.Trans.SymbolTable.has(SymKey): - FuncInfo = CTypeInfo() - FuncInfo.Name = SymKey - FuncInfo.IsFunction = True - FuncInfo.MetaList = func_meta - self.Trans.SymbolTable.insert(SymKey, FuncInfo) - else: - existing = self.Trans.SymbolTable[SymKey] - if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE: - existing.MetaList = func_meta - # setter/deleter: 同时在原始 PropKey(不带后缀)下注册 MetaList - if is_item_prop_setter or is_item_prop_deleter: - base_existing = self.Trans.SymbolTable.lookup(FuncFullName) - if base_existing: - if func_meta != FuncMeta.NONE: - base_existing.MetaList = base_existing.MetaList | func_meta - else: - PropInfo = CTypeInfo() - PropInfo.Name = FuncFullName - PropInfo.IsFunction = True - PropInfo.MetaList = func_meta - self.Trans.SymbolTable.insert(FuncFullName, PropInfo) - # --- 跨模块继承字段展平 --- - # _EmitExternalClassDeclLlvm 只添加了类自身定义的字段(来自 .pyi), - # 没有展平父类(如 GSListNode[T])的继承字段,导致 class_members 缺少 - # 继承字段,_get_member_offset 返回错误偏移。 - # 修复:先尝试从已加载的父类获取真实字段名;若父类未加载, - # 则用 struct body 元素数推断继承字段数并补充占位条目 __inherited_N。 - _ParentClass: str | None = None - _ParentClasses: list[str] = [] - if Node.bases: - for _base in Node.bases: - _base_name: str | None = None - if hasattr(_base, 'id'): - _base_name = _base.id - elif hasattr(_base, 'attr'): - _base_name = _base.attr - elif isinstance(_base, ast.Subscript): - _spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(_base) - if _spec_name: - _base_name = _spec_name - else: - # _ResolveGenericSlice 失败(泛型基类未在当前模块注册为模板, - # 常见于跨模块导入:如 HandlesExprCall.py 导入 Value(GSListNode[Value]), - # 但 GSListNode 定义在 includes/linkedlist.py,尚未编译)。 - # 直接从 AST 提取泛型基类名(如 GSListNode[Value] -> GSListNode), - # 后续继承字段展平逻辑会从 .pyi 加载该基类的字段。 - if isinstance(_base.value, ast.Name): - _base_short_name: str = _base.value.id - elif isinstance(_base.value, ast.Attribute): - _base_short_name: str = _base.value.attr - else: - continue - # 尝试手动构造特化名并检查是否已有特化 class_members。 - # 这解决了 _ResolveGenericSlice 失败但特化已通过其他路径 - # (如 _parse_llvm_type)触发的情况。特化 class_members 中 - # 的 T 已被正确替换为具体类型(如 BasicBlock),避免使用 - # 未特化的 GSListNode 导致 T* 占位类型残留。 - _slice_name: str | None = None - if isinstance(_base.slice, ast.Name): - _slice_name = _base.slice.id - elif isinstance(_base.slice, ast.Attribute): - _slice_name = _base.slice.attr - elif isinstance(_base.slice, ast.Subscript): - # 嵌套泛型(如 GSList[GSListNode[T]]),暂不处理 - pass - _manual_spec_name: str | None = None - if _slice_name is not None: - _manual_spec_name = f"{_base_short_name}[{_slice_name}]" - if _manual_spec_name is not None and _manual_spec_name in Gen.class_members: - # 特化 class_members 已存在,直接使用(T 已被替换) - _base_name = _manual_spec_name - else: - _base_name = _base_short_name - else: - continue - if _base_name == ClassName: - continue - if _base_name in ('CVTable', 'Object', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): - continue - # 多继承:收集所有真实父类(声明顺序),不再 break。 - # 总是记录父类名,即使父类 class_members 未加载。 - # _get_member_offset 会沿 class_parent 链回退查找继承字段。 - _ParentClasses.append(_base_name) - if _ParentClass is None: - _ParentClass = _base_name - if not hasattr(Gen, 'class_parent'): - Gen.class_parent = {} - Gen.class_parent[ClassName] = _ParentClass - if _ParentClasses: - if not hasattr(Gen, 'class_parents'): - Gen.class_parents = {} - Gen.class_parents[ClassName] = _ParentClasses - # 治本修复:在继承展开前,尝试从 stub/.pyi 加载父类的 class_members。 - # 确保子类 class_members 能正确展平父类真实字段名(而非 __inherited_N 占位符), - # 避免 struct body 从不完整 class_members 生成导致 GEP 越界。 - # 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。 - _any_parent_loaded: bool = False - _all_inherited: list[tuple] = [] - # 展平完整性判定(供 `_members_flattened` 登记):仅当"每个声明父类都已在 - # class_members 中就绪且其自身已展平",或"占位符路径已按 struct 槽位补齐 - # 继承前缀"时才为 True。未完成(典型:声明期 struct 尚 opaque / 父类所在 - # 模块 stub 未就绪)时不登记,后续查询会重跑本节把继承前缀补齐 - # —— 使展平结果与"父类/struct 就绪的先后顺序"无关。 - _flat_complete: bool = not _ParentClasses - _placeholder_applied: bool = False - # 按继承源去重的跨父类共享 seen 集合(必须在 for 循环外,跨所有父类 - # 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个 - # 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段) - _seen_flat_cls: set[str] = set() - for _ParentClass in _ParentClasses: - if not _ParentClass: - continue - _parent_members_check: list = Gen.class_members.get(_ParentClass, []) - _parent_all_placeholders: bool = bool(_parent_members_check) and all( - n.startswith('__inherited_') for n, _ in _parent_members_check - ) - # 2026-08-18 关键修复:无条件 force 重载父类成员(此前仅当成员为空/ - # 全占位时才加载)。并行 worker 中父类可能已存在"不完整"的成员表 - # (此前某次展平时祖先未加载完成即冻结部分字段,guard 又阻止重载), - # 导致 class_members[Generator] 83~106 vs 114 不确定 → this_adjust - # 错位 → 继承方法 self 偏移错误 → 运行时越界崩溃。force=True 会先 - # 清空再重建(缺啥补啥、顺序与定义模块一致),且 _EmitExternalClassDeclLlvm - # 对其祖先递归同样强制重载,最终所有 worker 展平结果确定性一致。 - _old_parent_members: list = _parent_members_check - if _parent_all_placeholders: - Gen.class_members[_ParentClass] = [] - _sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) or {} - _parent_found: bool = False - # 已加载过的父类直接用其定义模块 sha1 强制重载(O(1)), - # 避免对每个父类线性扫描全部 ModuleSha1Map。 - _parent_sha1: str | None = getattr(Gen, 'class_sha1_map', {}).get(_ParentClass) - if _parent_sha1: - try: - _parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, f"{_parent_sha1}.stub.ll", Gen, force=True) - except Exception: - _parent_found = False - else: - for _mod_name, _mod_sha1 in _sha1_map.items(): - _stub_name: str = f"{_mod_sha1}.stub.ll" - try: - _parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen, force=True) - except Exception: - _parent_found = False - if _parent_found: - break - _parent_members_check = Gen.class_members.get(_ParentClass, []) - if not _parent_found and _old_parent_members: - # 加载失败(父类不在任何 pyi 中,如泛型特化基类),回滚旧条目(有总比没有好) - Gen.class_members[_ParentClass] = _old_parent_members - if _ParentClass in Gen.class_members: - # 按继承源去重(与 HandlesClassDef._FlattenInherited 一致,2026-08-18): - # 菱形继承(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase, - # 而它们都继承 ClassGenBase)若直接拼接会重复展平 → 与 DeclarationGenerator - # stub struct 布局不一致 → 并行 worker 里 this_adjust 算不出偏移 → 继承 - # 方法 self 不调整 → 运行时错位崩溃(register_reexport 写 0x3f)。 - # 递归每类只处理一次,每类只贡献非祖先字段。 - def _FlattenExt(cls_name: str) -> None: - if cls_name in _seen_flat_cls: - return - _seen_flat_cls.add(cls_name) - for _gp in Gen.class_parents.get(cls_name, []) or []: - _FlattenExt(_gp) - _anc_flat: set[str] = set() - _stk: list[str] = [cls_name] - _vis: set[str] = set() - while _stk: - _c = _stk.pop() - for _gp2 in Gen.class_parents.get(_c, []) or []: - if _gp2 in _vis: - continue - _vis.add(_gp2) - _stk.append(_gp2) - for _mn2, _mt2 in Gen.class_members.get(_gp2, []) or []: - _anc_flat.add(_mn2) - for _mn3, _mt3 in Gen.class_members.get(cls_name, []) or []: - if _mn3 == '__vtable__': - continue - if _mn3 in _anc_flat: - continue - _all_inherited.append((_mn3, _mt3)) - _pd3: dict = dict(Gen.class_member_defaults.get(cls_name, {})) - if _mn3 in _pd3: - Gen.class_member_defaults[ClassName][_mn3] = _pd3[_mn3] - _FlattenExt(_ParentClass) - if _all_inherited: - # 防重复注册(2026-08-10):_EmitExternalClassDeclLlvm 可能被多个模块 - # 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm - # / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次 - # 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...]) - # → 与 struct elements 数量不匹配 → 属性偏移错乱。 - # 【判据改为"前置段是否已等于祖先块"(2026-09-30)】旧判据是 - # `nm2 not in {整表所有名字}`——它会把**与子类自有字段重名的继承项**删掉, - # 于是继承段与 body 的继承前缀失去一一对应:被删项**之后**的继承字段表内 - # 下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标` - # (FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。现场: - # TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult - # 下标 32→27,写到 i32 槽 → "指针隐式转整数已禁止"。 - # 新判据(幂等且不删块内条目):只问"表头前 len(块) 项的名字序列是否等于 - # 祖先块的名字序列"——相等 ⇒ 整块已前置(本次是重复调用)⇒ 不再前置; - # 否则整块前置。**任何情况下都不删块内条目**,因此"同名自有字段 + 同名继承项" - # 会并存(body 里本来就是两个槽,定义端 HandlesClassDef 同样两者都在)。 - PrependInheritedBlock(Gen, ClassName, _all_inherited) - # 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加 - _any_parent_loaded = True - if not _any_parent_loaded and _ParentClasses: - # 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value], - # 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。 - _st = Gen.structs.get(ClassName) - # 【必须先取快照】`elements` 不是稳定值:`StructGen._generate_structs` 判定 - # "struct body 与 class_members 不一致"时会**主动置 `elements = None`** - # (StructGen.py:579/:590,为强制重新派生的既有机制)。本分支在下方 - # `_ResolveInheritedFieldNames`(加载父类成员 → _TryLoadClassMembersFromPyi - # → _EmitExternalClassDeclLlvm → _generate_structs)里会重入,重入期间本类 - # 的 `elements` 可能被置回 None ⇒ 若在配对时(:2526)才读 `_st.elements`, - # 就是对 None 下标:裸 `TypeError: 'NoneType' object is not subscriptable` - # (2026-09-29 现场:astaux.vp 编译期 `_EmitExternalClassDeclLlvm` 崩, - # 外层 :647 `except Exception` 只 WARN "忽略节点 ClassDef" 就 continue ⇒ - # 该类声明被整条静默丢弃)。 - # 快照与 `_num_elements` 同源(都是 guard 通过的这一份 body),二者必须 - # 一致:槽位数与槽类型取自同一时刻的布局,不能一个用旧、一个用新。 - if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: - _elements_ph: list = list(_st.elements) - _num_elements = len(_elements_ph) - # 如果类有 vtable ptr(struct 首元素),需减去 1 - # 否则 vtable ptr 会被误当成 __inherited_0 占位条目, - # 导致子类继承时多出一个字段(如 Name(AST) 从 9 字段变成 10) - _has_vtable_ptr = IsCVTable or (has_methods and not IsNoVTable) - if _has_vtable_ptr and _num_elements > 0: - _num_elements -= 1 - # struct 槽位起点:有 vtable 指针时首元素是 vtable,继承字段从元素 1 起 - # (上面已把 vtable 从 _num_elements 扣掉,配对槽位必须同步右移一位, - # 否则会把 vtable 指针的类型当成第一个继承字段的类型)。 - _slot0_ph: int = 1 if _has_vtable_ptr else 0 - _num_members = len(Gen.class_members[ClassName]) - if _num_elements > _num_members: - _num_inherited = _num_elements - _num_members - # 继承字段的**真实名字**:按声明顺序沿继承链取父类声明中的字段名 - # (父类可能是泛型特化 `GSListNode[ParamNode]` —— 特化体不在任何 - # .vpi 中,回退用模板名 `GSListNode` 加载其成员),与 struct 槽位 - # 按序配对。这样展平结果直接是 `('Next', <槽位类型>)`,属性访问 - # 命中 class_members 真实条目,**无需任何查找期回退**。 - # 名字取不到时(父类声明也不可得)才退回 __inherited_N 占位名, - # 仅保证偏移正确。 - _inh_names: list[str] = self._ResolveInheritedFieldNames(Gen, _ParentClasses, _num_inherited) - _inherited_ph: list[tuple] = [] - for _i_ph in range(_num_inherited): - _nm_ph: str = _inh_names[_i_ph] if _i_ph < len(_inh_names) else f'__inherited_{_i_ph}' - _inherited_ph.append((_nm_ph, _elements_ph[_slot0_ph + _i_ph])) - # 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致 - Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName] - _placeholder_applied = True - if _ParentClasses: - # 完整展平的判据:占位符路径已按 struct 槽位补齐继承前缀,或 - # 每个声明父类都已在 class_members 中就绪且其自身已展平。 - _flat_complete = _placeholder_applied or all( - (_pc in Gen.class_members and _pc in Gen._members_flattened) - for _pc in _ParentClasses if _pc - ) - # 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。 - # 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的 - # _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque - # (opaque 结构体上的 GEP 是非法 IR,llvm-as 会直接报错),而残缺布局正是 - # 本保护要消除的目标。 - Gen._collecting_members_set.discard(ClassName) - Gen._generate_structs() - if _flat_complete: - # 登记"该表已按父类展平",供 _TryLoadClassMembersFromPyi 的 guard 与 - # _member_base 的查询前补齐使用。未登记 ⇒ 后续任一查询都可重跑本节补齐。 - Gen._members_flattened.add(ClassName) - # ============================================================ - # 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位), - # 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只 - # 注册自身方法(_has_methods_prescan),vtable 槽位会错位 → - # 虚分派调用错误函数(如 MemBuddy.alloc 被解析到槽 13=_fl_find_and_remove - # → 返回 None → "Generator alloc failed")。 - # ============================================================ - if _ParentClasses: - _all_parent_methods: list[str] = [] - _parent_method_names_seen: set[str] = set() - for _pcls in _ParentClasses: - for _pm in list(Gen.class_methods.get(_pcls, [])): - _pm_name: str = _pm.split('.')[-1] if '.' in _pm else _pm - if _pm_name in ('__new__', '__init__', '__before_init__'): - continue - if _pm_name in _parent_method_names_seen: - continue - _parent_method_names_seen.add(_pm_name) - _all_parent_methods.append(_pm) - if _all_parent_methods: - _child_method_map: dict[str, str] = {} - for _m in Gen.class_methods.get(ClassName, []): - _mn: str = _m.split('.')[-1] if '.' in _m else _m - _child_method_map[_mn] = _m - # 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)。 - # 方法级 `novirtual def`(⇒ @t.NoVTable)不占槽,**不得**用它覆盖父类槽位 - # ——与 TPV _collect_vtable_methods 的 child_nvt 分支同口径(子类标记 - # NoVTable 时槽位仍指向实际定义类的实现)。 - _VtByClassLevelC: bool = _VtableByClassLevelForImport(Node, Gen) - for _item_m in getattr(Node, 'body', []): - if isinstance(_item_m, ast.FunctionDef): - _mn2: str = _item_m.name - if _mn2 in ('__new__', '__init__', '__before_init__'): - continue - if not IsVtableSlotMethod(_item_m, _VtByClassLevelC): - continue - _child_method_map[_mn2] = f"{ClassName}.{_mn2}" - _new_methods: list[str] = [] - _covered_names: set[str] = set() - for _pm2 in _all_parent_methods: - _mname2: str = _pm2.split('.')[-1] if '.' in _pm2 else _pm2 - if _mname2 in _child_method_map: - _new_methods.append(_child_method_map[_mname2]) - else: - _new_methods.append(_pm2) - _covered_names.add(_mname2) - for _m3 in list(Gen.class_methods.get(ClassName, [])): - _mn3: str = _m3.split('.')[-1] if '.' in _m3 else _m3 - if _mn3 not in _covered_names: - _new_methods.append(_m3) - Gen.class_methods[ClassName] = _new_methods - if has_methods: - if IsCVTable: - Gen._cross_module_vtable_classes.add(ClassName) - Gen.class_vtable.add(ClassName) - if IsNoVTable: - Gen._cross_module_novtable.add(ClassName) - # __before_init__ 对所有跨模块结构体声明(定义侧无条件生成,导入侧也应无条件声明) - NewFuncName = f'{ClassName}.__before_init__' - if not Gen._has_function(NewFuncName): - # 复用前面已查找的 source_sha1(优先 actual_module_name 再 module_name), - # 通过 class_sha1_map 获取,避免此处只查 module_name 导致子模块(如 w32.win32file)查找失败 - source_sha1 = getattr(Gen, 'class_sha1_map', {}).get(ClassName) - MangledName = Gen._mangle_name(NewFuncName) if not source_sha1 else f"{source_sha1}.{NewFuncName}" - StructType = Gen.structs.get(ClassName) - if StructType is not None: - StructPtrType = ir.PointerType(StructType) - NewFuncType = ir.FunctionType(ir.VoidType(), [StructPtrType]) - # 走唯一幂等入口:查的是 NewFuncName 而建的是 MangledName,二者不同名 - # ⇒ 此处若裸建,MangledName 已被 stub / 调用点占住时必抛 - # llvmlite DuplicatedNameError。 - NewFunc = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType) - Gen.functions[NewFuncName] = NewFunc - bitfields = Gen.class_member_bitfields.get(ClassName, {}) - if any(v > 0 for v in bitfields.values()): - current_bit_offset = 0 - for name, _ in Gen.class_members.get(ClassName, []): - bw = bitfields.get(name, 0) - Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset - current_bit_offset += bw - # 声明发射结束:解除重入标记(与上方 add 配对)。 - _emit_inprog.discard(_emit_key) - - def _build_stub_index(self) -> None: - """扫描所有 .stub.ll 文件构建 class_name → 条目列表索引 - - 一次扫描替代 _TryLoadStructFromStub 的 609 次全量扫描。 - 增量策略:记录已解析文件集合 _stub_index_scanned,单次编译进程内 - stub 文件只增不减(output 逐步填充),只增量解析新增文件; - 无新增文件时仅做一次目录集合比较即返回(省去原签名失效后的全量重扫)。 - - 零 syscall 快路径:本函数由 _TryLoadStructFromStub 的**每次索引 miss** - 调用(profile:238k 次),而"stub 集合是否变化"由写入方显式置脏 - (见 MarkStubSetDirty)⇒ 未置脏时直接返回,不再做 - 2× isdir + 2× listdir + 全量 endswith/join(这三项在 profile 里合计 - 约占总时长 30%:nt._path_isdir 491s、nt.listdir 183s、 - np.join/splitroot/endswith ≈ 180s)。 - """ - if not ImportHandle._stub_set_dirty: - return - ProjectRoot = self._find_project_root() - temp_dir = os.path.join(ProjectRoot, 'temp') - output_dir = os.path.join(ProjectRoot, 'output') - if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir): - return - - # 收集当前 stub 文件集合(绝对路径,仅文件名不读内容) - current_files: set[str] = set() - for scan_dir in (output_dir, temp_dir): - if not os.path.isdir(scan_dir): - continue - for filename in os.listdir(scan_dir): - if filename.endswith('.stub.ll'): - current_files.add(os.path.join(scan_dir, filename)) - - # 无新增文件 → 索引最新,清脏(下次 miss 走零 syscall 快路径) - if current_files <= self._stub_index_scanned: - ImportHandle._stub_set_dirty = False - return - - # 只增量解析新增文件(首次调用时全量) - for stub_path in sorted(current_files - self._stub_index_scanned): - filename = os.path.basename(stub_path) - # 预检查文件存在性:os.listdir 与 open 之间文件可能被 --clean 或 - # 并发写入删除(独立 profile 脚本曾触发 FileNotFoundError) - if not os.path.isfile(stub_path): - continue - is_output = stub_path.startswith(os.path.join(output_dir, '')) - file_sha1 = filename.replace('.stub.ll', '') - try: - with open(stub_path, 'r', encoding='utf-8') as f: - content = f.read() - for line in content.splitlines(): - stripped = line.strip() - match = _RE_STRUCT_TYPE_DEF.match(stripped) - if match: - full_name = match.group(1) - short_name = match.group(2) - struct_body = match.group(3).strip() - # 索引键:short_name 和 full_name 都映射到同一条目 - self._stub_index.setdefault(short_name, []).append((full_name, struct_body, file_sha1, is_output)) - if full_name != short_name: - self._stub_index.setdefault(full_name, []).append((full_name, struct_body, file_sha1, is_output)) - except FileNotFoundError: - # 竞态:isfile 检查后文件被删除,跳过即可(不应触发 strict raise) - continue - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"构建 stub 索引失败: {_e}", "Exception") + item.annotation, self.Trans, Gen) + if spec_in_anno and '[' in spec_in_anno: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = spec_in_anno + # Subscript 容器字段(list[X] / rawptr[list[X]],如 includes/ast + # 的 Call.args / Import.names: rawptr[list[rawptr[AST]]]):非 BinOp + # 联合,上面两分支不命中 → 容器身份丢失 → 字段方法调用(get/len…) + # 分派失败报 "Undefined method 'get' in class 'rawptr'"(Converter.vp)。 + # 用与特化生成端同源的 _resolve_type_arg_str 构造 element_class, + # 键名与 Gen.structs 中 list 特化名保持一致。 + if isinstance(item.annotation, ast.Subscript): + _ann_sub_s: ast.Subscript = item.annotation + _subv_s: ast.AST = _ann_sub_s.value + _subname_s: str = getattr(_subv_s, 'id', None) or getattr(_subv_s, 'attr', '') + _elem_slice_s: ast.AST | None = None + if _subname_s == 'list': + _elem_slice_s = _ann_sub_s.slice + elif _subname_s == 'rawptr': + _slice_s: ast.AST = _ann_sub_s.slice + if isinstance(_slice_s, ast.Subscript): + # rawptr[泛型容器](rawptr[list[X]] / rawptr[GSList[Param]]): + # 容器身份 = 泛型特化名。list 继续走 _elem_slice_s 统一登记; + # 其余泛型容器直接登记 element_class(阻塞 B 根因链一环: + # 原实现只认内层 list,rawptr[GSList[Param]] 在此空转并跳过 + # 裸结构分支 → element_class 缺失 → 字段/局部变量方法分派 + # 丢失容器身份,FakeDuck 错绑 LinkedNode.append 写穿)。 + _inner_sub_s: ast.Subscript = _slice_s + _iname_s: str = getattr(_inner_sub_s.value, 'id', None) or getattr(_inner_sub_s.value, 'attr', '') + if _iname_s == 'list': + _elem_slice_s = _inner_sub_s.slice + else: + _ec_gs: str | None = None + if _iname_s: + try: + _ec_gs = f'{_iname_s}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_s.slice)}]' + except Exception: + _ec_gs = None + if _ec_gs: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = _ec_gs + else: + # rawptr[裸结构](如 rawptr[Token]):跨模块 stub 会把自引用/类内 + # 指针字段退化为 i8*,链式成员访问(a.b.c)需 element_class 恢复 + # struct 身份走 char*→struct cast。HEAD 态 `Token | t.CPtr` 联合 + # 由上方 BinOp 分支登记;rawptr 重构后此分支补齐(阻塞 A 根因)。 + # 与 HandlesClassDef 的 rawptr 分支对称。 + _slc_name_s: str | None = None + if isinstance(_slice_s, ast.Name): + _slc_name_s = _slice_s.id + elif isinstance(_slice_s, ast.Attribute): + _slc_name_s = _slice_s.attr + _ec_target_s: str | None = None + if _slc_name_s: + if _slc_name_s in Gen.structs: + _ec_target_s = _slc_name_s + else: + _st_sha1_s: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(_slc_name_s) + if _st_sha1_s and f'{_st_sha1_s}.{_slc_name_s}' in Gen.structs: + _ec_target_s = f'{_st_sha1_s}.{_slc_name_s}' + else: + # 短名模糊扫描(对齐 HandlesAnnAssign 变量侧 + # 补偿 L597-602):跨模块 `mod.Class` 形态的 + # rawptr[Class] 字段在 class_sha1_map 尚无该短名 + # 时上面两路全 miss → element_class 静默缺失 → + # 字段方法调用(factory.PermPool.alloc)链尾类名 + # 丢失 → TPC 调用分派返回 i32 0 哨兵 → 误报 + # "Unknown module: ...PermPool"。 + for _ec_k_s in Gen.structs: + _ec_short_s: str = _ec_k_s.split('.')[-1] if '.' in _ec_k_s else _ec_k_s + if _ec_short_s == _slc_name_s: + _ec_target_s = _ec_k_s + break + if _ec_target_s: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = _ec_target_s + if _elem_slice_s is not None: + _ec_s: str | None = None + try: + _ec_s = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_s)}]' + except Exception: + _ec_s = None + if _ec_s: + if ClassName not in Gen.class_member_element_class: + Gen.class_member_element_class[ClassName] = {} + Gen.class_member_element_class[ClassName][VarName] = _ec_s + if TypeInfo.ByteOrder: + Gen.class_member_byteorders[ClassName][VarName] = TypeInfo.ByteOrder + else: + Gen.class_member_byteorders[ClassName][VarName] = "" + if item.value: + const = self._BuildScalarConstant(item.value, MemberType, Gen) + if const: + Gen.class_member_defaults[ClassName][VarName] = const + elif isinstance(item, ast.FunctionDef) and not IsGenericClass: + # PEP 695 泛型类跳过方法声明:T 参数会变成 opaque struct 触发 + # "invalid type for function argument"。字段已由上面的 AnnAssign + # 分支加载,继承字段(如 GSListNode.Next)由后面的展平逻辑补充。 + has_methods = True + MethodName = item.name + FuncFullName = f"{ClassName}.__init__" if MethodName == "__init__" else f"{ClassName}.__call__" if MethodName == "__call__" else f"{ClassName}.{MethodName}" + # 构造函数(__new__/__init__/__before_init__)不放入 vtable, + # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移。 + # register_method 内部也会过滤,此处仅跳过 class_methods.append。 + # 方法级修饰符(virtual def/novirtual def)⇒ 是否占槽按 + # SymbolUtils.IsVtableSlotMethod(唯一规则源)判定,与定义模块逐槽一致。 + _IsSlot2: bool = (not IsCVTable) or IsVtableSlotMethod( + item, _VtableByClassLevelForImport(Node, Gen)) + if MethodName not in ('__new__', '__init__', '__before_init__'): + if _IsSlot2 and FuncFullName not in Gen.class_methods[ClassName]: + Gen.class_methods[ClassName].append(FuncFullName) + Gen.register_method(ClassName, FuncFullName, _IsSlot2) + is_item_static = any(isinstance(d, ast.Name) and d.id == 'staticmethod' for d in item.decorator_list) + is_item_property = any(isinstance(d, ast.Name) and d.id == 'property' for d in item.decorator_list) + is_item_classmethod = any(isinstance(d, ast.Name) and d.id == 'classmethod' for d in item.decorator_list) + is_item_prop_setter = any(isinstance(d, ast.Attribute) and d.attr == 'setter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list) + is_item_prop_getter = any(isinstance(d, ast.Attribute) and d.attr == 'getter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list) + is_item_prop_deleter = any(isinstance(d, ast.Attribute) and d.attr == 'deleter' and isinstance(d.value, ast.Name) and d.value.id == 'property' for d in item.decorator_list) + # setter/deleter 使用不同的函数名后缀 + DeclFuncName = FuncFullName + if is_item_prop_setter: + DeclFuncName = FuncFullName + '$set' + elif is_item_prop_deleter: + DeclFuncName = FuncFullName + '$del' + # 无条件调用:即使 DeclFuncName 已在 Gen.functions 中,也必须让 + # _EmitExternalFuncDeclLlvm 走到其**早退之前**的自动取址登记逻辑 + # (该函数在早退前完成登记后即返回,对已存在符号无其它副作用); + # 否则下面的守卫会让整个调用被跳过,stub 方法的取址模式永不生效。 + FuncDeclNode = ast.FunctionDef( + name=DeclFuncName, + args=item.args, + body=item.body, + decorator_list=item.decorator_list, + returns=item.returns + ) + try: + self._EmitExternalFuncDeclLlvm(FuncDeclNode, Gen, is_class_method=not is_item_static and not is_item_classmethod, source_module_name=module_name) + except Exception as e: + raise + func_meta = FuncMeta.NONE + if is_item_static: + func_meta |= FuncMeta.STATIC_METHOD + if is_item_property: + func_meta |= FuncMeta.PROPERTY_GETTER + if is_item_classmethod: + func_meta |= FuncMeta.CLASS_METHOD + if is_item_prop_setter: + func_meta |= FuncMeta.PROPERTY_SETTER + if is_item_prop_getter: + func_meta |= FuncMeta.PROPERTY_GETTER + if is_item_prop_deleter: + func_meta |= FuncMeta.PROPERTY_DELETER + # setter/deleter 的 SymKey 使用带后缀的函数名 + SymKey = DeclFuncName + if not self.Trans.SymbolTable.has(SymKey): + FuncInfo = CTypeInfo() + FuncInfo.Name = SymKey + FuncInfo.IsFunction = True + FuncInfo.MetaList = func_meta + self.Trans.SymbolTable.insert(SymKey, FuncInfo) + else: + existing = self.Trans.SymbolTable[SymKey] + if existing.MetaList == FuncMeta.NONE and func_meta != FuncMeta.NONE: + existing.MetaList = func_meta + # setter/deleter: 同时在原始 PropKey(不带后缀)下注册 MetaList + if is_item_prop_setter or is_item_prop_deleter: + base_existing = self.Trans.SymbolTable.lookup(FuncFullName) + if base_existing: + if func_meta != FuncMeta.NONE: + base_existing.MetaList = base_existing.MetaList | func_meta + else: + PropInfo = CTypeInfo() + PropInfo.Name = FuncFullName + PropInfo.IsFunction = True + PropInfo.MetaList = func_meta + self.Trans.SymbolTable.insert(FuncFullName, PropInfo) + # --- 跨模块继承字段展平 --- + # _EmitExternalClassDeclLlvm 只添加了类自身定义的字段(来自 .pyi), + # 没有展平父类(如 GSListNode[T])的继承字段,导致 class_members 缺少 + # 继承字段,_get_member_offset 返回错误偏移。 + # 修复:先尝试从已加载的父类获取真实字段名;若父类未加载, + # 则用 struct body 元素数推断继承字段数并补充占位条目 __inherited_N。 + _ParentClass: str | None = None + _ParentClasses: list[str] = [] + if Node.bases: + for _base in Node.bases: + _base_name: str | None = None + if hasattr(_base, 'id'): + _base_name = _base.id + elif hasattr(_base, 'attr'): + _base_name = _base.attr + elif isinstance(_base, ast.Subscript): + _spec_name: str | None = self.Trans.ExprCallHandle._ResolveGenericSlice(_base) + if _spec_name: + _base_name = _spec_name + else: + # _ResolveGenericSlice 失败(泛型基类未在当前模块注册为模板, + # 常见于跨模块导入:如 HandlesExprCall.py 导入 Value(GSListNode[Value]), + # 但 GSListNode 定义在 includes/linkedlist.py,尚未编译)。 + # 直接从 AST 提取泛型基类名(如 GSListNode[Value] -> GSListNode), + # 后续继承字段展平逻辑会从 .pyi 加载该基类的字段。 + if isinstance(_base.value, ast.Name): + _base_short_name: str = _base.value.id + elif isinstance(_base.value, ast.Attribute): + _base_short_name: str = _base.value.attr + else: + continue + # 尝试手动构造特化名并检查是否已有特化 class_members。 + # 这解决了 _ResolveGenericSlice 失败但特化已通过其他路径 + # (如 _parse_llvm_type)触发的情况。特化 class_members 中 + # 的 T 已被正确替换为具体类型(如 BasicBlock),避免使用 + # 未特化的 GSListNode 导致 T* 占位类型残留。 + _slice_name: str | None = None + if isinstance(_base.slice, ast.Name): + _slice_name = _base.slice.id + elif isinstance(_base.slice, ast.Attribute): + _slice_name = _base.slice.attr + elif isinstance(_base.slice, ast.Subscript): + # 嵌套泛型(如 GSList[GSListNode[T]]),暂不处理 + pass + _manual_spec_name: str | None = None + if _slice_name is not None: + _manual_spec_name = f"{_base_short_name}[{_slice_name}]" + if _manual_spec_name is not None and _manual_spec_name in Gen.class_members: + # 特化 class_members 已存在,直接使用(T 已被替换) + _base_name = _manual_spec_name + else: + _base_name = _base_short_name + else: + continue + if _base_name == ClassName: + continue + if _base_name in ('CVTable', 'Object', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): + continue + # 多继承:收集所有真实父类(声明顺序),不再 break。 + # 总是记录父类名,即使父类 class_members 未加载。 + # _get_member_offset 会沿 class_parent 链回退查找继承字段。 + _ParentClasses.append(_base_name) + if _ParentClass is None: + _ParentClass = _base_name + if not hasattr(Gen, 'class_parent'): + Gen.class_parent = {} + Gen.class_parent[ClassName] = _ParentClass + if _ParentClasses: + if not hasattr(Gen, 'class_parents'): + Gen.class_parents = {} + Gen.class_parents[ClassName] = _ParentClasses + # 治本修复:在继承展开前,尝试从 stub/.pyi 加载父类的 class_members。 + # 确保子类 class_members 能正确展平父类真实字段名(而非 __inherited_N 占位符), + # 避免 struct body 从不完整 class_members 生成导致 GEP 越界。 + # 同时处理父类 class_members 全为占位符的情况(清除后重新加载)。 + _any_parent_loaded: bool = False + _all_inherited: list[tuple] = [] + # 展平完整性判定(供 `_members_flattened` 登记):仅当"每个声明父类都已在 + # class_members 中就绪且其自身已展平",或"占位符路径已按 struct 槽位补齐 + # 继承前缀"时才为 True。未完成(典型:声明期 struct 尚 opaque / 父类所在 + # 模块 stub 未就绪)时不登记,后续查询会重跑本节把继承前缀补齐 + # —— 使展平结果与"父类/struct 就绪的先后顺序"无关。 + _flat_complete: bool = not _ParentClasses + _placeholder_applied: bool = False + # 按继承源去重的跨父类共享 seen 集合(必须在 for 循环外,跨所有父类 + # 共享——否则每个父类新建 seen,菱形祖先(如 ClassGenBase)被每个 + # 继承它的 ClassGen* 父类各展平一次 → 重复(Generator 曾 707 字段) + _seen_flat_cls: set[str] = set() + for _ParentClass in _ParentClasses: + if not _ParentClass: + continue + _parent_members_check: list = Gen.class_members.get(_ParentClass, []) + _parent_all_placeholders: bool = bool(_parent_members_check) and all( + n.startswith('__inherited_') for n, _ in _parent_members_check + ) + # 2026-08-18 关键修复:无条件 force 重载父类成员(此前仅当成员为空/ + # 全占位时才加载)。并行 worker 中父类可能已存在"不完整"的成员表 + # (此前某次展平时祖先未加载完成即冻结部分字段,guard 又阻止重载), + # 导致 class_members[Generator] 83~106 vs 114 不确定 → this_adjust + # 错位 → 继承方法 self 偏移错误 → 运行时越界崩溃。force=True 会先 + # 清空再重建(缺啥补啥、顺序与定义模块一致),且 _EmitExternalClassDeclLlvm + # 对其祖先递归同样强制重载,最终所有 worker 展平结果确定性一致。 + _old_parent_members: list = _parent_members_check + if _parent_all_placeholders: + Gen.class_members[_ParentClass] = [] + _sha1_map: dict[str, str] = getattr(Gen, 'ModuleSha1Map', {}) or {} + _parent_found: bool = False + # 已加载过的父类直接用其定义模块 sha1 强制重载(O(1)), + # 避免对每个父类线性扫描全部 ModuleSha1Map。 + _parent_sha1: str | None = getattr(Gen, 'class_sha1_map', {}).get(_ParentClass) + if _parent_sha1: + try: + _parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, f"{_parent_sha1}.stub.ll", Gen, force=True) + except Exception: + _parent_found = False + else: + for _mod_name, _mod_sha1 in _sha1_map.items(): + _stub_name: str = f"{_mod_sha1}.stub.ll" + try: + _parent_found = self._TryLoadClassMembersFromPyi(_ParentClass, _stub_name, Gen, force=True) + except Exception: + _parent_found = False + if _parent_found: + break + _parent_members_check = Gen.class_members.get(_ParentClass, []) + if not _parent_found and _old_parent_members: + # 加载失败(父类不在任何 pyi 中,如泛型特化基类),回滚旧条目(有总比没有好) + Gen.class_members[_ParentClass] = _old_parent_members + if _ParentClass in Gen.class_members: + # 按继承源去重(与 HandlesClassDef._FlattenInherited 一致,2026-08-18): + # 菱形继承(Generator 同时继承 ClassGenMixin/ClassGenEnum/.../ClassGenBase, + # 而它们都继承 ClassGenBase)若直接拼接会重复展平 → 与 DeclarationGenerator + # stub struct 布局不一致 → 并行 worker 里 this_adjust 算不出偏移 → 继承 + # 方法 self 不调整 → 运行时错位崩溃(register_reexport 写 0x3f)。 + # 递归每类只处理一次,每类只贡献非祖先字段。 + def _FlattenExt(cls_name: str) -> None: + if cls_name in _seen_flat_cls: + return + _seen_flat_cls.add(cls_name) + for _gp in Gen.class_parents.get(cls_name, []) or []: + _FlattenExt(_gp) + _anc_flat: set[str] = set() + _stk: list[str] = [cls_name] + _vis: set[str] = set() + while _stk: + _c = _stk.pop() + for _gp2 in Gen.class_parents.get(_c, []) or []: + if _gp2 in _vis: + continue + _vis.add(_gp2) + _stk.append(_gp2) + for _mn2, _mt2 in Gen.class_members.get(_gp2, []) or []: + _anc_flat.add(_mn2) + for _mn3, _mt3 in Gen.class_members.get(cls_name, []) or []: + if _mn3 == '__vtable__': + continue + if _mn3 in _anc_flat: + continue + _all_inherited.append((_mn3, _mt3)) + _pd3: dict = dict(Gen.class_member_defaults.get(cls_name, {})) + if _mn3 in _pd3: + Gen.class_member_defaults[ClassName][_mn3] = _pd3[_mn3] + _FlattenExt(_ParentClass) + if _all_inherited: + # 防重复注册(2026-08-10):_EmitExternalClassDeclLlvm 可能被多个模块 + # 加载路径重复调用(_LoadModuleDeclarationsFromFile / _LoadDeclarationsFromStubLlvm + # / _TryLoadClassMembersFromPyi 懒加载),此前调用已前置的父类字段会被再次 + # 前置 → class_members 出现重复字段(如 BasicBlock: [Next, Next, Name, ...]) + # → 与 struct elements 数量不匹配 → 属性偏移错乱。 + # 【判据改为"前置段是否已等于祖先块"(2026-09-30)】旧判据是 + # `nm2 not in {整表所有名字}`——它会把**与子类自有字段重名的继承项**删掉, + # 于是继承段与 body 的继承前缀失去一一对应:被删项**之后**的继承字段表内 + # 下标整体前移 k(k = 被删项数),而查询侧回退路径用 `base + 表内下标` + # (FuncGen._get_member_offset)⇒ 落到**类型完全不同的槽**。现场: + # TypeGenCType 重复声明 5 个父类已有字段后,继承字段 LastBuiltinsTypedefResult + # 下标 32→27,写到 i32 槽 → "指针隐式转整数已禁止"。 + # 新判据(幂等且不删块内条目):只问"表头前 len(块) 项的名字序列是否等于 + # 祖先块的名字序列"——相等 ⇒ 整块已前置(本次是重复调用)⇒ 不再前置; + # 否则整块前置。**任何情况下都不删块内条目**,因此"同名自有字段 + 同名继承项" + # 会并存(body 里本来就是两个槽,定义端 HandlesClassDef 同样两者都在)。 + PrependInheritedBlock(Gen, ClassName, _all_inherited) + # 父类字段已处理(可能此前已前置),跳过占位符路径避免叠加 + _any_parent_loaded = True + if not _any_parent_loaded and _ParentClasses: + # 父类未加载到 class_members(常见于泛型特化基类如 GSListNode[Value], + # 其特化不在 .pyi 中)。用 struct body 元素数推断继承字段数。 + _st = Gen.structs.get(ClassName) + # 【必须先取快照】`elements` 不是稳定值:`StructGen._generate_structs` 判定 + # "struct body 与 class_members 不一致"时会**主动置 `elements = None`** + # (StructGen.py:579/:590,为强制重新派生的既有机制)。本分支在下方 + # `_ResolveInheritedFieldNames`(加载父类成员 → _TryLoadClassMembersFromPyi + # → _EmitExternalClassDeclLlvm → _generate_structs)里会重入,重入期间本类 + # 的 `elements` 可能被置回 None ⇒ 若在配对时(:2526)才读 `_st.elements`, + # 就是对 None 下标:裸 `TypeError: 'NoneType' object is not subscriptable` + # (2026-09-29 现场:astaux.vp 编译期 `_EmitExternalClassDeclLlvm` 崩, + # 外层 :647 `except Exception` 只 WARN "忽略节点 ClassDef" 就 continue ⇒ + # 该类声明被整条静默丢弃)。 + # 快照与 `_num_elements` 同源(都是 guard 通过的这一份 body),二者必须 + # 一致:槽位数与槽类型取自同一时刻的布局,不能一个用旧、一个用新。 + if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: + _elements_ph: list = list(_st.elements) + _num_elements = len(_elements_ph) + # 如果类有 vtable ptr(struct 首元素),需减去 1 + # 否则 vtable ptr 会被误当成 __inherited_0 占位条目, + # 导致子类继承时多出一个字段(如 Name(AST) 从 9 字段变成 10) + _has_vtable_ptr = IsCVTable or (has_methods and not IsNoVTable) + if _has_vtable_ptr and _num_elements > 0: + _num_elements -= 1 + # struct 槽位起点:有 vtable 指针时首元素是 vtable,继承字段从元素 1 起 + # (上面已把 vtable 从 _num_elements 扣掉,配对槽位必须同步右移一位, + # 否则会把 vtable 指针的类型当成第一个继承字段的类型)。 + _slot0_ph: int = 1 if _has_vtable_ptr else 0 + _num_members = len(Gen.class_members[ClassName]) + if _num_elements > _num_members: + _num_inherited = _num_elements - _num_members + # 继承字段的**真实名字**:按声明顺序沿继承链取父类声明中的字段名 + # (父类可能是泛型特化 `GSListNode[ParamNode]` —— 特化体不在任何 + # .vpi 中,回退用模板名 `GSListNode` 加载其成员),与 struct 槽位 + # 按序配对。这样展平结果直接是 `('Next', <槽位类型>)`,属性访问 + # 命中 class_members 真实条目,**无需任何查找期回退**。 + # 名字取不到时(父类声明也不可得)才退回 __inherited_N 占位名, + # 仅保证偏移正确。 + _inh_names: list[str] = self._ResolveInheritedFieldNames(Gen, _ParentClasses, _num_inherited) + _inherited_ph: list[tuple] = [] + for _i_ph in range(_num_inherited): + _nm_ph: str = _inh_names[_i_ph] if _i_ph < len(_inh_names) else f'__inherited_{_i_ph}' + _inherited_ph.append((_nm_ph, _elements_ph[_slot0_ph + _i_ph])) + # 占位符路径同样整体前置(继承字段在 struct 头部),与主路径保持一致 + Gen.class_members[ClassName] = _inherited_ph + Gen.class_members[ClassName] + _placeholder_applied = True + if _ParentClasses: + # 完整展平的判据:占位符路径已按 struct 槽位补齐继承前缀,或 + # 每个声明父类都已在 class_members 中就绪且其自身已展平。 + _flat_complete = _placeholder_applied or all( + (_pc in Gen.class_members and _pc in Gen._members_flattened) + for _pc in _ParentClasses if _pc + ) + # 成员收集 + 继承展平完成 → 解除收集中保护,并用完整 class_members 设置 body。 + # 必须在此显式触发一次 _generate_structs():保护期内(上方 add)所有重入的 + # _generate_structs 都跳过了本类,若此处不再触发,本类 struct 将保持 opaque + # (opaque 结构体上的 GEP 是非法 IR,llvm-as 会直接报错),而残缺布局正是 + # 本保护要消除的目标。 + Gen._collecting_members_set.discard(ClassName) + Gen._generate_structs() + if _flat_complete: + # 登记"该表已按父类展平",供 _TryLoadClassMembersFromPyi 的 guard 与 + # _member_base 的查询前补齐使用。未登记 ⇒ 后续任一查询都可重跑本节补齐。 + Gen._members_flattened.add(ClassName) + # ============================================================ + # 跨模块 vtable 槽布局与定义模块一致:父类方法在前(子类覆盖放父类槽位), + # 子类新增方法在后。定义模块(HandlesClassDef)已按此合并,跨模块若只 + # 注册自身方法(_has_methods_prescan),vtable 槽位会错位 → + # 虚分派调用错误函数(如 MemBuddy.alloc 被解析到槽 13=_fl_find_and_remove + # → 返回 None → "Generator alloc failed")。 + # ============================================================ + if _ParentClasses: + _all_parent_methods: list[str] = [] + _parent_method_names_seen: set[str] = set() + for _pcls in _ParentClasses: + for _pm in list(Gen.class_methods.get(_pcls, [])): + _pm_name: str = _pm.split('.')[-1] if '.' in _pm else _pm + if _pm_name in ('__new__', '__init__', '__before_init__'): + continue + if _pm_name in _parent_method_names_seen: + continue + _parent_method_names_seen.add(_pm_name) + _all_parent_methods.append(_pm) + if _all_parent_methods: + _child_method_map: dict[str, str] = {} + for _m in Gen.class_methods.get(ClassName, []): + _mn: str = _m.split('.')[-1] if '.' in _m else _m + _child_method_map[_mn] = _m + # 从 .vpi 的 FunctionDef 补充子类方法映射(覆盖方法可能尚未注册)。 + # 方法级 `novirtual def`(⇒ @t.NoVTable)不占槽,**不得**用它覆盖父类槽位 + # ——与 VPC _collect_vtable_methods 的 child_nvt 分支同口径(子类标记 + # NoVTable 时槽位仍指向实际定义类的实现)。 + _VtByClassLevelC: bool = _VtableByClassLevelForImport(Node, Gen) + for _item_m in getattr(Node, 'body', []): + if isinstance(_item_m, ast.FunctionDef): + _mn2: str = _item_m.name + if _mn2 in ('__new__', '__init__', '__before_init__'): + continue + if not IsVtableSlotMethod(_item_m, _VtByClassLevelC): + continue + _child_method_map[_mn2] = f"{ClassName}.{_mn2}" + _new_methods: list[str] = [] + _covered_names: set[str] = set() + for _pm2 in _all_parent_methods: + _mname2: str = _pm2.split('.')[-1] if '.' in _pm2 else _pm2 + if _mname2 in _child_method_map: + _new_methods.append(_child_method_map[_mname2]) + else: + _new_methods.append(_pm2) + _covered_names.add(_mname2) + for _m3 in list(Gen.class_methods.get(ClassName, [])): + _mn3: str = _m3.split('.')[-1] if '.' in _m3 else _m3 + if _mn3 not in _covered_names: + _new_methods.append(_m3) + Gen.class_methods[ClassName] = _new_methods + if has_methods: + if IsCVTable: + Gen._cross_module_vtable_classes.add(ClassName) + Gen.class_vtable.add(ClassName) + if IsNoVTable: + Gen._cross_module_novtable.add(ClassName) + # __before_init__ 对所有跨模块结构体声明(定义侧无条件生成,导入侧也应无条件声明) + NewFuncName = f'{ClassName}.__before_init__' + if not Gen._has_function(NewFuncName): + # 复用前面已查找的 source_sha1(优先 actual_module_name 再 module_name), + # 通过 class_sha1_map 获取,避免此处只查 module_name 导致子模块(如 w32.win32file)查找失败 + source_sha1 = getattr(Gen, 'class_sha1_map', {}).get(ClassName) + MangledName = Gen._mangle_name(NewFuncName) if not source_sha1 else f"{source_sha1}.{NewFuncName}" + StructType = Gen.structs.get(ClassName) + if StructType is not None: + StructPtrType = ir.PointerType(StructType) + NewFuncType = ir.FunctionType(ir.VoidType(), [StructPtrType]) + # 走唯一幂等入口:查的是 NewFuncName 而建的是 MangledName,二者不同名 + # ⇒ 此处若裸建,MangledName 已被 stub / 调用点占住时必抛 + # llvmlite DuplicatedNameError。 + NewFunc = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType) + Gen.functions[NewFuncName] = NewFunc + bitfields = Gen.class_member_bitfields.get(ClassName, {}) + if any(v > 0 for v in bitfields.values()): + current_bit_offset = 0 + for name, _ in Gen.class_members.get(ClassName, []): + bw = bitfields.get(name, 0) + Gen.class_member_bitoffsets[ClassName][name] = current_bit_offset + current_bit_offset += bw + # 声明发射结束:解除重入标记(与上方 add 配对)。 + _emit_inprog.discard(_emit_key) + + def _build_stub_index(self) -> None: + """扫描所有 .stub.ll 文件构建 class_name → 条目列表索引 + + 一次扫描替代 _TryLoadStructFromStub 的 609 次全量扫描。 + 增量策略:记录已解析文件集合 _stub_index_scanned,单次编译进程内 + stub 文件只增不减(output 逐步填充),只增量解析新增文件; + 无新增文件时仅做一次目录集合比较即返回(省去原签名失效后的全量重扫)。 + + 零 syscall 快路径:本函数由 _TryLoadStructFromStub 的**每次索引 miss** + 调用(profile:238k 次),而"stub 集合是否变化"由写入方显式置脏 + (见 MarkStubSetDirty)⇒ 未置脏时直接返回,不再做 + 2× isdir + 2× listdir + 全量 endswith/join(这三项在 profile 里合计 + 约占总时长 30%:nt._path_isdir 491s、nt.listdir 183s、 + np.join/splitroot/endswith ≈ 180s)。 + """ + if not ImportHandle._stub_set_dirty: + return + ProjectRoot = self._find_project_root() + temp_dir = os.path.join(ProjectRoot, 'temp') + output_dir = os.path.join(ProjectRoot, 'output') + if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir): + return + + # 收集当前 stub 文件集合(绝对路径,仅文件名不读内容) + current_files: set[str] = set() + for scan_dir in (output_dir, temp_dir): + if not os.path.isdir(scan_dir): + continue + for filename in os.listdir(scan_dir): + if filename.endswith('.stub.ll'): + current_files.add(os.path.join(scan_dir, filename)) + + # 无新增文件 → 索引最新,清脏(下次 miss 走零 syscall 快路径) + if current_files <= self._stub_index_scanned: + ImportHandle._stub_set_dirty = False + return + + # 只增量解析新增文件(首次调用时全量) + for stub_path in sorted(current_files - self._stub_index_scanned): + filename = os.path.basename(stub_path) + # 预检查文件存在性:os.listdir 与 open 之间文件可能被 --clean 或 + # 并发写入删除(独立 profile 脚本曾触发 FileNotFoundError) + if not os.path.isfile(stub_path): + continue + is_output = stub_path.startswith(os.path.join(output_dir, '')) + file_sha1 = filename.replace('.stub.ll', '') + try: + with open(stub_path, 'r', encoding='utf-8') as f: + content = f.read() + for line in content.splitlines(): + stripped = line.strip() + match = _RE_STRUCT_TYPE_DEF.match(stripped) + if match: + full_name = match.group(1) + short_name = match.group(2) + struct_body = match.group(3).strip() + # 索引键:short_name 和 full_name 都映射到同一条目 + self._stub_index.setdefault(short_name, []).append((full_name, struct_body, file_sha1, is_output)) + if full_name != short_name: + self._stub_index.setdefault(full_name, []).append((full_name, struct_body, file_sha1, is_output)) + except FileNotFoundError: + # 竞态:isfile 检查后文件被删除,跳过即可(不应触发 strict raise) + continue + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"构建 stub 索引失败: {_e}", "Exception") self._stub_index_scanned.add(stub_path) # 增量解析完成 → 索引与目录一致,清脏(下次 miss 走零 syscall 快路径) - ImportHandle._stub_set_dirty = False - - @staticmethod - def _ExternDeclEmitting(Gen: LlvmGeneratorMixin, class_name: str, source_sha1: str | None) -> bool: - """该类(同名同源)的跨模块声明发射是否**正在进行中**(递归重入判定)。 - - 标记由 `_EmitExternalClassDeclLlvm` 主体开头登记、末尾解除(键 - `(ClassName, source_sha1)`)。定义模块 sha1 未知(None)时按类名保守判定: - None 与 '' 两种形态可能指同一个类,宁可本次漏加载(由最外层收尾) - 也不递归——`_EmitExternalClassDeclLlvm` → `_TryLoadStructFromStub(X)` - → `_TryLoadClassMembersFromPyi(X)` → `_EmitExternalClassDeclLlvm(X)` - 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 sha1 前缀)互不相同、 - 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住, - 会一直递归到 RecursionError(被上层 except 吞掉 ⇒ 类型解析静默退化)。 - """ - _inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None) - if not _inprog: - return False - if (class_name, source_sha1) in _inprog: - return True - if source_sha1 is None: - return any(_k[0] == class_name for _k in _inprog) - return False - + ImportHandle._stub_set_dirty = False + + @staticmethod + def _ExternDeclEmitting(Gen: LlvmGeneratorMixin, class_name: str, source_sha1: str | None) -> bool: + """该类(同名同源)的跨模块声明发射是否**正在进行中**(递归重入判定)。 + + 标记由 `_EmitExternalClassDeclLlvm` 主体开头登记、末尾解除(键 + `(ClassName, source_sha1)`)。定义模块 sha1 未知(None)时按类名保守判定: + None 与 '' 两种形态可能指同一个类,宁可本次漏加载(由最外层收尾) + 也不递归——`_EmitExternalClassDeclLlvm` → `_TryLoadStructFromStub(X)` + → `_TryLoadClassMembersFromPyi(X)` → `_EmitExternalClassDeclLlvm(X)` + 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 sha1 前缀)互不相同、 + 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住, + 会一直递归到 RecursionError(被上层 except 吞掉 ⇒ 类型解析静默退化)。 + """ + _inprog: set | None = getattr(Gen, '_extern_decl_emit_in_progress', None) + if not _inprog: + return False + if (class_name, source_sha1) in _inprog: + return True + if source_sha1 is None: + return any(_k[0] == class_name for _k in _inprog) + return False + def _ReReadStructBodyFromDisk(self, full_name: str, file_sha1: str, from_output: bool) -> str | None: """按**磁盘当前内容**重取一个结构体的 body 文本(索引陈旧时的定点再读)。 @@ -3063,218 +3063,218 @@ class ImportHandle(BaseHandle): return _m2.group(2).strip() return None - def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None: - # 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义, - # 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致 - # 跨模块 struct 类型对象无法分别 set_body(每个模块有独立的 self.structs)。 - # 治本修复:当 source_sha1 指定时(同名类冲突情况),使用 sha1 前缀全名作为 - # 缓存键,确保不同模块的同名类(如 stmts.py:Module 和 __module.py:Module) - # 不会互相阻止 stub 加载。 - cache_key = (id(Gen), f"{source_sha1}.{class_name}" if source_sha1 else class_name) - if cache_key in self._struct_Load_cache: - return - # 先设置缓存防止递归重入,但如果加载失败则清除缓存允许后续重试 - self._struct_Load_cache[cache_key] = True - - # 使用 stub 索引替代全量扫描(609 次 × 100 文件 → 1 次扫描 + O(1) 查询)。 - # 命中索引直接返回,不触发 _build_stub_index(避免每次调用做目录扫描); - # 仅在 miss 时才增量扫描(无新增文件时仅一次集合比较)。 - entries = self._stub_index.get(class_name, []) - if not entries: - self._build_stub_index() - entries = self._stub_index.get(class_name, []) - - if not entries: - del self._struct_Load_cache[cache_key] - return - - # 选择最佳条目: - # 优先级 0: 当 source_sha1 指定时,优先选择 type_sha1 匹配的条目(治本修复: - # 同名类冲突时确保选择正确模块的 struct 定义) - # 优先级 1: 类型定义模块自身的 stub(file_sha1 == full_name 中的 sha1 前缀) - # 定义模块的 stub 包含完整的字段展平(含跨模块继承字段) - # 优先级 2: output(Phase2 生成)优于 temp(Phase1 生成) - # 优先级 3: 非 opaque(具体定义)优于 opaque - def _entry_priority(e: tuple[str, str, str, bool]) -> tuple[int, int, int, int]: - full_name, struct_body, file_sha1, from_output = e - type_sha1 = file_sha1 - if '.' in full_name: - parts = full_name.split('.', 1) - if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): - type_sha1 = parts[0] - sha1_match = 0 if (source_sha1 and type_sha1 == source_sha1) else 1 - is_defining = 0 if type_sha1 == file_sha1 else 1 - is_temp = 0 if from_output else 1 - is_opaque = 0 if (struct_body and struct_body != 'opaque') else 1 - # 【is_opaque 前置于 is_temp(2026-09-30)】原顺序 (…, is_temp, is_opaque) 让 - # **陈旧的 output 占位条目压住 temp 里已有的好 body**(现场:TypeGenCType - # output 条目在索引里是 opaque / temp 是 41 槽 —— 只因 output 排在前面就被选中, - # 该类在本 Gen 内永远 opaque ⇒ Repl.vp:802 的基座匹配失败)。有 body 的一侧 - # 永远优先于"占位/陈旧"的一侧;两侧都有 body 时仍按 output > temp 的原判据。 - return (sha1_match, is_defining, is_opaque, is_temp) - - best_entry = min(entries, key=_entry_priority) - full_name, struct_body, file_sha1, from_output = best_entry - # 索引条目**没有 body**(空 / 'opaque')⇒ 可能是"索引陈旧":同名 stub 被**原地 - # 重写**过(见 _ReReadStructBodyFromDisk 的成因说明)。此处按磁盘当前内容重取一次 - # (只读这一个文件;取不到即维持原状,不兜底)。 - if not struct_body or struct_body == 'opaque': - _fresh_body: str | None = self._ReReadStructBodyFromDisk(full_name, file_sha1, from_output) - if _fresh_body and _fresh_body != 'opaque': - struct_body = _fresh_body - - # Bug 1 修复:从 full_name 提取 sha1 前缀(类型定义模块的 sha1), - # 而非使用 file_sha1(stub 文件所在模块的 sha1,可能只是导入方而非定义方)。 - # 例如 output/35ffd4b9fbd082d1.stub.ll (main.py) 中包含 - # %"5cde8c6813a2b6f3.Function" = type {...}, - # 此时 file_sha1='35ffd4b9fbd082d1' (main.py) 但类型实际定义在 - # __function.py (sha1='5cde8c6813a2b6f3'),必须用后者作为 source_sha1。 - actual_sha1 = file_sha1 - if '.' in full_name: - parts = full_name.split('.', 1) - if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): - actual_sha1 = parts[0] - - is_packed = struct_body is not None and struct_body.startswith('<{') and struct_body.endswith('}>') - if struct_body and struct_body != 'opaque': - elem_types = [] - if is_packed: - inner = struct_body[2:-1].strip() - else: - inner = struct_body.strip('{}').strip() - if inner: - for elem in inner.split(','): - elem = elem.strip() - if elem: - et = self._parse_llvm_type(elem, Gen, source_sha1=actual_sha1) - elem_types.append(et if et else ir.IntType(32)) - try: - st = Gen._get_or_create_struct(class_name, source_sha1=actual_sha1, packed=is_packed) - if isinstance(st, ir.IdentifiedStructType) and st.is_opaque: - st.set_body(*elem_types) - if is_packed: - Gen.class_packed.add(class_name) - # 同步设置短名 struct 的 body:当存在 sha1 冲突时,_get_or_create_struct - # 返回 full_key struct,但代码可能通过短名引用旧 struct(由 setup_from_symbol_table - # 早期创建的 opaque 占位符,使用本地 sha1 前缀)。需要同步设置短名 struct - # 的 body,确保 GEP 指令能正确访问字段(修复跨模块类型定义冲突导致的 - # 字段索引错位问题)。 - short_st = Gen.structs.get(class_name) - if short_st is not None and short_st is not st and isinstance(short_st, ir.IdentifiedStructType) and short_st.is_opaque: - try: - short_st.set_body(*elem_types) - except Exception: - pass # set_body 可能因已设置而失败,忽略 - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - stub_filename = actual_sha1 + '.stub.ll' - # 【递归环闭合边 · 重入短路】该类的声明发射尚未结束(`_EmitExternalClassDeclLlvm` - # 正在跑:见其主体开头的 `_extern_decl_emit_in_progress` 登记)⇒ 本调用是环内一环: - # 发射(X) → _TryLoadStructFromStub(X) →(此处)成员加载(X) → 发射(X) … - # 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同、 - # 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住 ⇒ 无限递归。 - # 短路对象**只限"顺带加载成员"这一步**:上面的 struct body 解析照常执行 - # (发射主体依赖它提前 set_body —— 否则 llvmlite `__len__` 的 - # `assert self.elements is not None` 直接炸,见 _EmitExternalFuncDeclLlvm - # 的 `if _new_struct:`);成员由外层发射收尾统一收集。 - if not ImportHandle._ExternDeclEmitting(Gen, class_name, source_sha1): - self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen) - # 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、 - # 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久 - # 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。 - # 失败状态可重试,待 stub 索引刷新 / 定义文件就绪后再加载。 - # 【键缺失也算失败(2026-09-30)】原判据只在"**有**实例且 opaque"时删键; - # 若索引里**有该类的条目**、而该键此刻根本没有实例(`Gen.structs.get` 为 None, - # 例如只落了 sha1 全名键、短名键尚未建立),这一轮的成功缓存就被永久留下 ⇒ - # 之后每次同名查询都在 :2855 直接 return,短名那份永远补不上 body - # (现场:Repl.vp:802 的基座实例从未匹配上,而唯一同短名候选一直是 opaque)。 - _final_st = Gen.structs.get(class_name) - _HasUsableBody: bool = (isinstance(_final_st, ir.IdentifiedStructType) - and not _final_st.is_opaque) - if not _HasUsableBody and entries: - try: - del self._struct_Load_cache[cache_key] - except KeyError: - pass - - def EnsureClassMembersFlattened(self, class_name: str, Gen: LlvmGeneratorMixin) -> None: - """查询前补齐:确保 `class_members[class_name]` 已按父类展平(幂等、顺序无关)。 - - 背景:`_EmitExternalClassDeclLlvm` 的展平只在"父类成员表 + 声明期 struct - body 都已就绪"时才能算出继承前缀;若当时 struct 仍 opaque(定义模块 stub - 尚未生成)或父类未就绪,表就停在"自有字段",而长度随之与 struct 元素数 - 不再自洽(delta = 缺失槽数)。此后 `_TryLoadClassMembersFromPyi` 的原 - guard(`len>0` 即短路)会把这份残缺表永久冻结 ⇒ 该类**自有字段**的 GEP - 下标缺了继承前缀而整体前移(`ExceptHandler.type` 返回 1 而非 8)。 - - 本方法是该冻结的解除点(与 `FuncGen._member_base` 的查询侧调用配套): - 只有"未登记已展平 且 该类有父类 且 struct 与成员表长度不自洽(含 struct - 尚未登记)"才重跑展平;对已展平类、无父类类(含 REnum 变体等非继承键) - 是纯内存判断直接返回,真正重跑的类另按依赖签名去重(见下)。 - - 以 `(struct 元素数, 成员表长度)` 作为"依赖状态签名":同一签名下补齐无效 - 时不再重复尝试(避免依赖长期不就绪时在热路径反复重建);依赖一旦变化 - (struct 被 `_generate_structs` 修正、或表被别处改动)签名变化即重试。 - """ - if Gen is None: - return - _flat: set | None = getattr(Gen, '_members_flattened', None) - if _flat is None or class_name in _flat: - return - _inprog: set | None = getattr(Gen, '_members_flatten_in_progress', None) - if _inprog is None or class_name in _inprog: - return - if class_name in (getattr(Gen, '_collecting_members_set', None) or ()): - # 该类的字段正在收集中(重入路径)⇒ 不在此处重跑展平, - # 交由收集完成后的 `_EmitExternalClassDeclLlvm` 正常收尾。 - return - _parents: list = (getattr(Gen, 'class_parents', {}) or {}).get(class_name) or [] - if not _parents: - # 无父类 ⇒ 展平平凡成立。表非空时登记(后续查询直接短路);表为空时 - # 不登记、也不在此补齐(那是"表未加载",由 _get_member_offset 既有的 - # 懒加载路径负责,不在本专项范围内)。 - if Gen.class_members.get(class_name): - _flat.add(class_name) - return - _tbl: list = Gen.class_members.get(class_name) or [] - _st = Gen.structs.get(class_name) - _num_elements: int = -1 - if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: - _num_elements = len(_st.elements) - if (_num_elements - len(_tbl)) in (0, 1): - # struct 布局与成员表长度自洽(无 vtable 槽 / 恰一个 vtable 槽) - # ⇒ 已展平:登记,后续查询直接短路。 - _flat.add(class_name) - return - _sig: tuple[int, int] = (_num_elements, len(_tbl)) - _sigs: dict = getattr(Gen, '_members_flatten_repair_sig', None) - if _sigs is None: - _sigs = {} - Gen._members_flatten_repair_sig = _sigs - if _sigs.get(class_name) == _sig: - # 同一依赖状态下补齐无效(父类/struct 仍未就绪)⇒ 不重复尝试。 - return - _sigs[class_name] = _sig - if not hasattr(self, 'Trans') or self.Trans is None: - return - _sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(class_name) - _cands: list[str] = [f"{_sha1}.stub.ll"] if _sha1 else [] - if not _cands: - _sha1_map: dict = getattr(Gen, 'ModuleSha1Map', {}) or {} - _cands = [f"{_s}.stub.ll" for _s in _sha1_map.values()] - _inprog.add(class_name) - try: - for _stub in _cands: - try: - if self._TryLoadClassMembersFromPyi(class_name, _stub, Gen): - break - except Exception: - continue - finally: - _inprog.discard(class_name) - + def _TryLoadStructFromStub(self, class_name: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None) -> None: + # 缓存键包含 Gen 实例标识,确保每个模块的 Gen 都能独立加载 struct 定义, + # 同时仍能防止同一 Gen 内的递归重入。原先使用纯 class_name 作为键会导致 + # 跨模块 struct 类型对象无法分别 set_body(每个模块有独立的 self.structs)。 + # 治本修复:当 source_sha1 指定时(同名类冲突情况),使用 sha1 前缀全名作为 + # 缓存键,确保不同模块的同名类(如 stmts.py:Module 和 __module.py:Module) + # 不会互相阻止 stub 加载。 + cache_key = (id(Gen), f"{source_sha1}.{class_name}" if source_sha1 else class_name) + if cache_key in self._struct_Load_cache: + return + # 先设置缓存防止递归重入,但如果加载失败则清除缓存允许后续重试 + self._struct_Load_cache[cache_key] = True + + # 使用 stub 索引替代全量扫描(609 次 × 100 文件 → 1 次扫描 + O(1) 查询)。 + # 命中索引直接返回,不触发 _build_stub_index(避免每次调用做目录扫描); + # 仅在 miss 时才增量扫描(无新增文件时仅一次集合比较)。 + entries = self._stub_index.get(class_name, []) + if not entries: + self._build_stub_index() + entries = self._stub_index.get(class_name, []) + + if not entries: + del self._struct_Load_cache[cache_key] + return + + # 选择最佳条目: + # 优先级 0: 当 source_sha1 指定时,优先选择 type_sha1 匹配的条目(治本修复: + # 同名类冲突时确保选择正确模块的 struct 定义) + # 优先级 1: 类型定义模块自身的 stub(file_sha1 == full_name 中的 sha1 前缀) + # 定义模块的 stub 包含完整的字段展平(含跨模块继承字段) + # 优先级 2: output(Phase2 生成)优于 temp(Phase1 生成) + # 优先级 3: 非 opaque(具体定义)优于 opaque + def _entry_priority(e: tuple[str, str, str, bool]) -> tuple[int, int, int, int]: + full_name, struct_body, file_sha1, from_output = e + type_sha1 = file_sha1 + if '.' in full_name: + parts = full_name.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + type_sha1 = parts[0] + sha1_match = 0 if (source_sha1 and type_sha1 == source_sha1) else 1 + is_defining = 0 if type_sha1 == file_sha1 else 1 + is_temp = 0 if from_output else 1 + is_opaque = 0 if (struct_body and struct_body != 'opaque') else 1 + # 【is_opaque 前置于 is_temp(2026-09-30)】原顺序 (…, is_temp, is_opaque) 让 + # **陈旧的 output 占位条目压住 temp 里已有的好 body**(现场:TypeGenCType + # output 条目在索引里是 opaque / temp 是 41 槽 —— 只因 output 排在前面就被选中, + # 该类在本 Gen 内永远 opaque ⇒ Repl.vp:802 的基座匹配失败)。有 body 的一侧 + # 永远优先于"占位/陈旧"的一侧;两侧都有 body 时仍按 output > temp 的原判据。 + return (sha1_match, is_defining, is_opaque, is_temp) + + best_entry = min(entries, key=_entry_priority) + full_name, struct_body, file_sha1, from_output = best_entry + # 索引条目**没有 body**(空 / 'opaque')⇒ 可能是"索引陈旧":同名 stub 被**原地 + # 重写**过(见 _ReReadStructBodyFromDisk 的成因说明)。此处按磁盘当前内容重取一次 + # (只读这一个文件;取不到即维持原状,不兜底)。 + if not struct_body or struct_body == 'opaque': + _fresh_body: str | None = self._ReReadStructBodyFromDisk(full_name, file_sha1, from_output) + if _fresh_body and _fresh_body != 'opaque': + struct_body = _fresh_body + + # Bug 1 修复:从 full_name 提取 sha1 前缀(类型定义模块的 sha1), + # 而非使用 file_sha1(stub 文件所在模块的 sha1,可能只是导入方而非定义方)。 + # 例如 output/35ffd4b9fbd082d1.stub.ll (main.py) 中包含 + # %"5cde8c6813a2b6f3.Function" = type {...}, + # 此时 file_sha1='35ffd4b9fbd082d1' (main.py) 但类型实际定义在 + # __function.py (sha1='5cde8c6813a2b6f3'),必须用后者作为 source_sha1。 + actual_sha1 = file_sha1 + if '.' in full_name: + parts = full_name.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + actual_sha1 = parts[0] + + is_packed = struct_body is not None and struct_body.startswith('<{') and struct_body.endswith('}>') + if struct_body and struct_body != 'opaque': + elem_types = [] + if is_packed: + inner = struct_body[2:-1].strip() + else: + inner = struct_body.strip('{}').strip() + if inner: + for elem in inner.split(','): + elem = elem.strip() + if elem: + et = self._parse_llvm_type(elem, Gen, source_sha1=actual_sha1) + elem_types.append(et if et else ir.IntType(32)) + try: + st = Gen._get_or_create_struct(class_name, source_sha1=actual_sha1, packed=is_packed) + if isinstance(st, ir.IdentifiedStructType) and st.is_opaque: + st.set_body(*elem_types) + if is_packed: + Gen.class_packed.add(class_name) + # 同步设置短名 struct 的 body:当存在 sha1 冲突时,_get_or_create_struct + # 返回 full_key struct,但代码可能通过短名引用旧 struct(由 setup_from_symbol_table + # 早期创建的 opaque 占位符,使用本地 sha1 前缀)。需要同步设置短名 struct + # 的 body,确保 GEP 指令能正确访问字段(修复跨模块类型定义冲突导致的 + # 字段索引错位问题)。 + short_st = Gen.structs.get(class_name) + if short_st is not None and short_st is not st and isinstance(short_st, ir.IdentifiedStructType) and short_st.is_opaque: + try: + short_st.set_body(*elem_types) + except Exception: + pass # set_body 可能因已设置而失败,忽略 + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + stub_filename = actual_sha1 + '.stub.ll' + # 【递归环闭合边 · 重入短路】该类的声明发射尚未结束(`_EmitExternalClassDeclLlvm` + # 正在跑:见其主体开头的 `_extern_decl_emit_in_progress` 登记)⇒ 本调用是环内一环: + # 发射(X) → _TryLoadStructFromStub(X) →(此处)成员加载(X) → 发射(X) … + # 每轮仅 4 帧,且两条加载路径的缓存键(带/不带 source_sha1 前缀)互不相同、 + # 失败时键还会被清除("仍 opaque ⇒ 删键重试")⇒ 缓存守卫挡不住 ⇒ 无限递归。 + # 短路对象**只限"顺带加载成员"这一步**:上面的 struct body 解析照常执行 + # (发射主体依赖它提前 set_body —— 否则 llvmlite `__len__` 的 + # `assert self.elements is not None` 直接炸,见 _EmitExternalFuncDeclLlvm + # 的 `if _new_struct:`);成员由外层发射收尾统一收集。 + if not ImportHandle._ExternDeclEmitting(Gen, class_name, source_sha1): + self._TryLoadClassMembersFromPyi(class_name, stub_filename, Gen) + # 根本修复:加载后 struct 仍为 opaque 时(如选中 'type opaque' 占位条目、 + # 或 set_body 未生效),不保留成功缓存。保留会导致同 Gen 内后续调用永久 + # 跳过补载,opaque 状态被固化,vtable 分派 GEP 直接崩溃。 + # 失败状态可重试,待 stub 索引刷新 / 定义文件就绪后再加载。 + # 【键缺失也算失败(2026-09-30)】原判据只在"**有**实例且 opaque"时删键; + # 若索引里**有该类的条目**、而该键此刻根本没有实例(`Gen.structs.get` 为 None, + # 例如只落了 sha1 全名键、短名键尚未建立),这一轮的成功缓存就被永久留下 ⇒ + # 之后每次同名查询都在 :2855 直接 return,短名那份永远补不上 body + # (现场:Repl.vp:802 的基座实例从未匹配上,而唯一同短名候选一直是 opaque)。 + _final_st = Gen.structs.get(class_name) + _HasUsableBody: bool = (isinstance(_final_st, ir.IdentifiedStructType) + and not _final_st.is_opaque) + if not _HasUsableBody and entries: + try: + del self._struct_Load_cache[cache_key] + except KeyError: + pass + + def EnsureClassMembersFlattened(self, class_name: str, Gen: LlvmGeneratorMixin) -> None: + """查询前补齐:确保 `class_members[class_name]` 已按父类展平(幂等、顺序无关)。 + + 背景:`_EmitExternalClassDeclLlvm` 的展平只在"父类成员表 + 声明期 struct + body 都已就绪"时才能算出继承前缀;若当时 struct 仍 opaque(定义模块 stub + 尚未生成)或父类未就绪,表就停在"自有字段",而长度随之与 struct 元素数 + 不再自洽(delta = 缺失槽数)。此后 `_TryLoadClassMembersFromPyi` 的原 + guard(`len>0` 即短路)会把这份残缺表永久冻结 ⇒ 该类**自有字段**的 GEP + 下标缺了继承前缀而整体前移(`ExceptHandler.type` 返回 1 而非 8)。 + + 本方法是该冻结的解除点(与 `FuncGen._member_base` 的查询侧调用配套): + 只有"未登记已展平 且 该类有父类 且 struct 与成员表长度不自洽(含 struct + 尚未登记)"才重跑展平;对已展平类、无父类类(含 REnum 变体等非继承键) + 是纯内存判断直接返回,真正重跑的类另按依赖签名去重(见下)。 + + 以 `(struct 元素数, 成员表长度)` 作为"依赖状态签名":同一签名下补齐无效 + 时不再重复尝试(避免依赖长期不就绪时在热路径反复重建);依赖一旦变化 + (struct 被 `_generate_structs` 修正、或表被别处改动)签名变化即重试。 + """ + if Gen is None: + return + _flat: set | None = getattr(Gen, '_members_flattened', None) + if _flat is None or class_name in _flat: + return + _inprog: set | None = getattr(Gen, '_members_flatten_in_progress', None) + if _inprog is None or class_name in _inprog: + return + if class_name in (getattr(Gen, '_collecting_members_set', None) or ()): + # 该类的字段正在收集中(重入路径)⇒ 不在此处重跑展平, + # 交由收集完成后的 `_EmitExternalClassDeclLlvm` 正常收尾。 + return + _parents: list = (getattr(Gen, 'class_parents', {}) or {}).get(class_name) or [] + if not _parents: + # 无父类 ⇒ 展平平凡成立。表非空时登记(后续查询直接短路);表为空时 + # 不登记、也不在此补齐(那是"表未加载",由 _get_member_offset 既有的 + # 懒加载路径负责,不在本专项范围内)。 + if Gen.class_members.get(class_name): + _flat.add(class_name) + return + _tbl: list = Gen.class_members.get(class_name) or [] + _st = Gen.structs.get(class_name) + _num_elements: int = -1 + if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: + _num_elements = len(_st.elements) + if (_num_elements - len(_tbl)) in (0, 1): + # struct 布局与成员表长度自洽(无 vtable 槽 / 恰一个 vtable 槽) + # ⇒ 已展平:登记,后续查询直接短路。 + _flat.add(class_name) + return + _sig: tuple[int, int] = (_num_elements, len(_tbl)) + _sigs: dict = getattr(Gen, '_members_flatten_repair_sig', None) + if _sigs is None: + _sigs = {} + Gen._members_flatten_repair_sig = _sigs + if _sigs.get(class_name) == _sig: + # 同一依赖状态下补齐无效(父类/struct 仍未就绪)⇒ 不重复尝试。 + return + _sigs[class_name] = _sig + if not hasattr(self, 'Trans') or self.Trans is None: + return + _sha1: str | None = (getattr(Gen, 'class_sha1_map', {}) or {}).get(class_name) + _cands: list[str] = [f"{_sha1}.stub.ll"] if _sha1 else [] + if not _cands: + _sha1_map: dict = getattr(Gen, 'ModuleSha1Map', {}) or {} + _cands = [f"{_s}.stub.ll" for _s in _sha1_map.values()] + _inprog.add(class_name) + try: + for _stub in _cands: + try: + if self._TryLoadClassMembersFromPyi(class_name, _stub, Gen): + break + except Exception: + continue + finally: + _inprog.discard(class_name) + def _ResolveInheritedFieldNames(self, Gen: LlvmGeneratorMixin, ParentClasses: list[str], Limit: int) -> list[str]: """沿继承链取父类声明中的**真实字段名**(按声明顺序,跨父类去重)。 @@ -3334,245 +3334,245 @@ class ImportHandle(BaseHandle): break return Names - def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin, - force: bool = False) -> bool: - """从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。 - - force=True 时(跨模块展平父类的强制重载):先清空该类的成员数据再重建, - 保证并行 worker 间展平结果确定性一致(缺啥补啥、字段顺序与定义模块一致), - 避免此前"部分成员被冻结、guard 阻止重载"导致的 class_members[Generator] - 83~106 vs 114 不确定变体(this_adjust 错位崩溃)。 - """ - ProjectRoot = self._find_project_root() - temp_dir = os.path.join(ProjectRoot, 'temp') - # guard(2026-xx 收紧):只在"表非空 **且已登记为已展平**"时短路。原实现 - # 只看 `len>0`,于是"停在自有字段"的残缺表(展平未完成)一旦生成就再也不能 - # 补齐(顺序敏感)——这正是自有字段下标缺继承前缀的成因。未展平的表允许 - # 重跑 `_EmitExternalClassDeclLlvm`(其自有字段收集按名去重、继承前缀按名 - # 去重,重跑幂等),从而与父类/struct 的就绪顺序无关。 - # 【重入短路 · 与成员表长度解耦】该类**正在**被 `_EmitExternalClassDeclLlvm` - # 收集字段(`_collecting_members_set` 置位于本文件 :2054、解除于 :2481)时, - # 本次调用必是递归重入,直接返回、交由最外层收尾。 - # 为什么判据不能带 `len(class_members)>0`:类内**第一个**字段的注解解析就会 - # 重入本方法 —— CollectAnnAssignMember → ResolveAnnotationType → 泛型特化 - # (list[T] 等)→ _EmitClassLlvm → StructGen._generate_structs - # → _TryLoadStructFromStub(X) → 本方法(X)。此刻 class_members[X] 仍为空 - # (第一个字段尚未收集完),旧判据(要求 len>0)放行 ⇒ 无限递归(每轮 4 帧): - # _TryLoadClassMembersFromPyi(X) → _EmitExternalClassDeclLlvm(X) - # → _TryLoadStructFromStub(X) → _TryLoadClassMembersFromPyi(X) … - # 现场(2026-09-24 深度探针):match.MatchOr 的 patterns、stmts.Interactive 的 - # body 等"首字段即指针/容器"的类;RecursionError 被上层 except 吞掉后表现为 - # `WARN: … 忽略异常 maximum recursion depth exceeded` + 类型解析静默退化 - # (失败的 typedef 报文、注解赋值 RHS 求值为空等次生现象)。 - _collecting: set | None = getattr(Gen, '_collecting_members_set', None) - if ((not force) and ( - (_collecting is not None and class_name in _collecting) - or (class_name in Gen.class_members - and len(Gen.class_members[class_name]) > 0 - and class_name in Gen._members_flattened))): - # guard 命中:class_members 已存在(可能从别处预置、不完整)或该类正在 - # 收集中;但 class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names - # 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114) - # → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载 - # class_members,避免覆盖可能更完整的已有数据)。 - _path0: str = os.path.join(temp_dir, stub_filename.replace('.stub.ll', '.vpi')) - try: - _tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0) - if _tree0 is None and os.path.isfile(_path0): - with open(_path0, 'r', encoding='utf-8') as _fh0: - _tree0 = ast.parse(_fh0.read(), _path0) - getattr(self, '_pyi_cache', {})[_path0] = _tree0 - if _tree0 is not None: - for _n0 in _tree0.body: - if isinstance(_n0, ast.ClassDef) and _n0.name == class_name: - _b0: list[str] = [] - for _bb in _n0.bases: - _bn0: str | None = getattr(_bb, 'id', None) or getattr(_bb, 'attr', None) - if _bn0 and _bn0 not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): - _b0.append(_bn0) - if _b0: - if not hasattr(Gen, 'class_parents'): - Gen.class_parents = {} - Gen.class_parents[class_name] = _b0 - break - except Exception as _e0: - # 同族口径:此处原为 `except Exception: pass`(静默)。class_parents - # 未回填 ⇒ 多继承/菱形展平不完整 ⇒ class_members 长度不确定 ⇒ - # this_adjust 错位。改为显式诊断(strict fail-fast / 非严格告警)。 - _ReportDeclarationParseFailure( - _path0, _e0, - f"类 '{class_name}' 的 class_parents 未能回填," - "多继承展平可能不完整(字段下标/this_adjust 可能错位)") - return False - pyi_basename = stub_filename.replace('.stub.ll', '.vpi') - pyi_path = os.path.join(temp_dir, pyi_basename) - # 已探测过的路径(_pyi_cache 含键,值为树或 None=不存在)不再重复 - # os.path.isfile(Windows stat ~0.5ms/次;本函数 28543 次调用 × 探测 - # ≈ 12.8s,占 _path_isfile 总耗时 63%)。 - if pyi_path not in self._pyi_cache and not os.path.isfile(pyi_path): - # 回退读取旧缓存 .pyi - pyi_basename = stub_filename.replace('.stub.ll', '.pyi') - pyi_path = os.path.join(temp_dir, pyi_basename) - - if self._pyi_cache_dir != temp_dir: - self._pyi_cache.clear() - self._pyi_cache_dir = temp_dir - - if pyi_path not in self._pyi_cache: - if not os.path.isfile(pyi_path): - self._pyi_cache[pyi_path] = None - return False - try: - with open(pyi_path, 'r', encoding='utf-8') as f: - self._pyi_cache[pyi_path] = ast.parse(f.read(), pyi_path) - except Exception as e: - # 同族口径:原告警不带文件名("忽略异常"),改为显式诊断 - # (strict fail-fast / 非严格告警)。.vpi 是编译器无糖产物, - # 解析失败属内部不变量被破坏,非严格模式仍缓存 None 并返回 False。 - _ReportDeclarationParseFailure( - pyi_path, e, - f"类 '{class_name}' 的成员/父类信息不会加载," - "class_members 可能残缺(字段下标/this_adjust 可能错位)") - self._pyi_cache[pyi_path] = None - return False - - pyi_tree = self._pyi_cache[pyi_path] - if pyi_tree is None: - return False - source_sha1 = stub_filename.replace('.stub.ll', '') - module_name = None - if hasattr(Gen, 'ModuleSha1Map'): - for mod_name, mod_sha1 in Gen.ModuleSha1Map.items(): - if mod_sha1 == source_sha1 and '.' not in mod_name: - module_name = mod_name - break - if module_name is None: - for mod_name, mod_sha1 in Gen.ModuleSha1Map.items(): - if mod_sha1 == source_sha1: - module_name = mod_name - break - if module_name is None and hasattr(Gen, 'module_sha1') and Gen.module_sha1 == source_sha1: - module_name = '__self__' - # module_name 反查失败时(当前模块未直接导入定义模块), - # 仍需注册类成员和 vtable 信息。传 source_sha1 让 _EmitExternalClassDeclLlvm - # 正确设置 class_sha1_map 和注册 vtable。 - # 类级 pyi 树节点索引:同一 (source_sha1, class_name) 的 ClassDef 节点在进程内 - # 唯一(_pyi_cache 以 pyi_path 为键只 parse 一次),避免跨 worker 任务反复 - # 线性扫描 pyi_tree.body 查找同一定义类。 - found = False - node = self._extern_class_decl_cache.get((source_sha1, class_name)) - if node is not None: - found = True - else: - for node in pyi_tree.body: - if isinstance(node, ast.ClassDef) and node.name == class_name: - self._extern_class_decl_cache[(source_sha1, class_name)] = node - found = True - break - if not found: - return False - if force: - # 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。 - # 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。 - Gen.class_members[class_name] = [] - # 同步撤销"已展平"登记:重建若因依赖未就绪而不完整,须允许后续查询再补齐。 - Gen._members_flattened.discard(class_name) - for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields', - 'class_member_byteorders', 'class_member_bitoffsets'): - _gd: dict | None = getattr(Gen, _gd_attr, None) - if _gd is not None: - _gd.pop(class_name, None) - if hasattr(Gen, 'class_parents'): - Gen.class_parents.pop(class_name, None) - if hasattr(Gen, 'class_parent'): - Gen.class_parent.pop(class_name, None) + def _TryLoadClassMembersFromPyi(self, class_name: str, stub_filename: str, Gen: LlvmGeneratorMixin, + force: bool = False) -> bool: + """从 .vpi/.pyi 声明文件加载类成员。返回 True 表示在 pyi 中找到该类并完成处理。 + + force=True 时(跨模块展平父类的强制重载):先清空该类的成员数据再重建, + 保证并行 worker 间展平结果确定性一致(缺啥补啥、字段顺序与定义模块一致), + 避免此前"部分成员被冻结、guard 阻止重载"导致的 class_members[Generator] + 83~106 vs 114 不确定变体(this_adjust 错位崩溃)。 + """ + ProjectRoot = self._find_project_root() + temp_dir = os.path.join(ProjectRoot, 'temp') + # guard(2026-xx 收紧):只在"表非空 **且已登记为已展平**"时短路。原实现 + # 只看 `len>0`,于是"停在自有字段"的残缺表(展平未完成)一旦生成就再也不能 + # 补齐(顺序敏感)——这正是自有字段下标缺继承前缀的成因。未展平的表允许 + # 重跑 `_EmitExternalClassDeclLlvm`(其自有字段收集按名去重、继承前缀按名 + # 去重,重跑幂等),从而与父类/struct 的就绪顺序无关。 + # 【重入短路 · 与成员表长度解耦】该类**正在**被 `_EmitExternalClassDeclLlvm` + # 收集字段(`_collecting_members_set` 置位于本文件 :2054、解除于 :2481)时, + # 本次调用必是递归重入,直接返回、交由最外层收尾。 + # 为什么判据不能带 `len(class_members)>0`:类内**第一个**字段的注解解析就会 + # 重入本方法 —— CollectAnnAssignMember → ResolveAnnotationType → 泛型特化 + # (list[T] 等)→ _EmitClassLlvm → StructGen._generate_structs + # → _TryLoadStructFromStub(X) → 本方法(X)。此刻 class_members[X] 仍为空 + # (第一个字段尚未收集完),旧判据(要求 len>0)放行 ⇒ 无限递归(每轮 4 帧): + # _TryLoadClassMembersFromPyi(X) → _EmitExternalClassDeclLlvm(X) + # → _TryLoadStructFromStub(X) → _TryLoadClassMembersFromPyi(X) … + # 现场(2026-09-24 深度探针):match.MatchOr 的 patterns、stmts.Interactive 的 + # body 等"首字段即指针/容器"的类;RecursionError 被上层 except 吞掉后表现为 + # `WARN: … 忽略异常 maximum recursion depth exceeded` + 类型解析静默退化 + # (失败的 typedef 报文、注解赋值 RHS 求值为空等次生现象)。 + _collecting: set | None = getattr(Gen, '_collecting_members_set', None) + if ((not force) and ( + (_collecting is not None and class_name in _collecting) + or (class_name in Gen.class_members + and len(Gen.class_members[class_name]) > 0 + and class_name in Gen._members_flattened))): + # guard 命中:class_members 已存在(可能从别处预置、不完整)或该类正在 + # 收集中;但 class_parents 可能未设置 → 多继承展平(_FlattenExt)的递归/anc_names + # 失效 → 菱形去重失败 → class_members 不完整(Generator 89/83 vs 114) + # → this_adjust 错位崩溃。此处补解析 .vpi 设置 class_parents(不重载 + # class_members,避免覆盖可能更完整的已有数据)。 + _path0: str = os.path.join(temp_dir, stub_filename.replace('.stub.ll', '.vpi')) + try: + _tree0: ast.AST | None = getattr(self, '_pyi_cache', {}).get(_path0) + if _tree0 is None and os.path.isfile(_path0): + with open(_path0, 'r', encoding='utf-8') as _fh0: + _tree0 = ast.parse(_fh0.read(), _path0) + getattr(self, '_pyi_cache', {})[_path0] = _tree0 + if _tree0 is not None: + for _n0 in _tree0.body: + if isinstance(_n0, ast.ClassDef) and _n0.name == class_name: + _b0: list[str] = [] + for _bb in _n0.bases: + _bn0: str | None = getattr(_bb, 'id', None) or getattr(_bb, 'attr', None) + if _bn0 and _bn0 not in ('Object', 'CVTable', 'NoVTable', 'CStruct', 'CUnion', 'CEnum', 'REnum', 'Exception', 'Enum'): + _b0.append(_bn0) + if _b0: + if not hasattr(Gen, 'class_parents'): + Gen.class_parents = {} + Gen.class_parents[class_name] = _b0 + break + except Exception as _e0: + # 同族口径:此处原为 `except Exception: pass`(静默)。class_parents + # 未回填 ⇒ 多继承/菱形展平不完整 ⇒ class_members 长度不确定 ⇒ + # this_adjust 错位。改为显式诊断(strict fail-fast / 非严格告警)。 + _ReportDeclarationParseFailure( + _path0, _e0, + f"类 '{class_name}' 的 class_parents 未能回填," + "多继承展平可能不完整(字段下标/this_adjust 可能错位)") + return False + pyi_basename = stub_filename.replace('.stub.ll', '.vpi') + pyi_path = os.path.join(temp_dir, pyi_basename) + # 已探测过的路径(_pyi_cache 含键,值为树或 None=不存在)不再重复 + # os.path.isfile(Windows stat ~0.5ms/次;本函数 28543 次调用 × 探测 + # ≈ 12.8s,占 _path_isfile 总耗时 63%)。 + if pyi_path not in self._pyi_cache and not os.path.isfile(pyi_path): + # 回退读取旧缓存 .pyi + pyi_basename = stub_filename.replace('.stub.ll', '.pyi') + pyi_path = os.path.join(temp_dir, pyi_basename) + + if self._pyi_cache_dir != temp_dir: + self._pyi_cache.clear() + self._pyi_cache_dir = temp_dir + + if pyi_path not in self._pyi_cache: + if not os.path.isfile(pyi_path): + self._pyi_cache[pyi_path] = None + return False + try: + with open(pyi_path, 'r', encoding='utf-8') as f: + self._pyi_cache[pyi_path] = ast.parse(f.read(), pyi_path) + except Exception as e: + # 同族口径:原告警不带文件名("忽略异常"),改为显式诊断 + # (strict fail-fast / 非严格告警)。.vpi 是编译器无糖产物, + # 解析失败属内部不变量被破坏,非严格模式仍缓存 None 并返回 False。 + _ReportDeclarationParseFailure( + pyi_path, e, + f"类 '{class_name}' 的成员/父类信息不会加载," + "class_members 可能残缺(字段下标/this_adjust 可能错位)") + self._pyi_cache[pyi_path] = None + return False + + pyi_tree = self._pyi_cache[pyi_path] + if pyi_tree is None: + return False + source_sha1 = stub_filename.replace('.stub.ll', '') + module_name = None + if hasattr(Gen, 'ModuleSha1Map'): + for mod_name, mod_sha1 in Gen.ModuleSha1Map.items(): + if mod_sha1 == source_sha1 and '.' not in mod_name: + module_name = mod_name + break + if module_name is None: + for mod_name, mod_sha1 in Gen.ModuleSha1Map.items(): + if mod_sha1 == source_sha1: + module_name = mod_name + break + if module_name is None and hasattr(Gen, 'module_sha1') and Gen.module_sha1 == source_sha1: + module_name = '__self__' + # module_name 反查失败时(当前模块未直接导入定义模块), + # 仍需注册类成员和 vtable 信息。传 source_sha1 让 _EmitExternalClassDeclLlvm + # 正确设置 class_sha1_map 和注册 vtable。 + # 类级 pyi 树节点索引:同一 (source_sha1, class_name) 的 ClassDef 节点在进程内 + # 唯一(_pyi_cache 以 pyi_path 为键只 parse 一次),避免跨 worker 任务反复 + # 线性扫描 pyi_tree.body 查找同一定义类。 + found = False + node = self._extern_class_decl_cache.get((source_sha1, class_name)) + if node is not None: + found = True + else: + for node in pyi_tree.body: + if isinstance(node, ast.ClassDef) and node.name == class_name: + self._extern_class_decl_cache[(source_sha1, class_name)] = node + found = True + break + if not found: + return False + if force: + # 强制重载:清空按类缓存的成员数据,重建出与定义模块一致的完整布局。 + # 不清空则 dedup 只在末尾追加缺失字段 → 字段顺序与 struct elements 错位。 + Gen.class_members[class_name] = [] + # 同步撤销"已展平"登记:重建若因依赖未就绪而不完整,须允许后续查询再补齐。 + Gen._members_flattened.discard(class_name) + for _gd_attr in ('class_member_defaults', 'class_member_signeds', 'class_member_bitfields', + 'class_member_byteorders', 'class_member_bitoffsets'): + _gd: dict | None = getattr(Gen, _gd_attr, None) + if _gd is not None: + _gd.pop(class_name, None) + if hasattr(Gen, 'class_parents'): + Gen.class_parents.pop(class_name, None) + if hasattr(Gen, 'class_parent'): + Gen.class_parent.pop(class_name, None) # 去重登记(force=True 时不登记,见下);在 try 之外先绑定, # 便于发射失败时统一撤销登记。 _sigmap: dict | None = None _sig_key: tuple | None = None _sig: tuple | None = None - if not force: - # 【稳定态去重 · 依赖签名】guard(上方)只在"表非空 **且已登记已展平**" - # 时短路;而"父类是泛型特化/始终不可得"的类**永远不会**被登记为已展平 - # ⇒ guard 永不命中 ⇒ 每次重入都重跑整套 `_EmitExternalClassDeclLlvm` - # (其内部又触发 _generate_structs → _TryLoadStructFromStub → 本方法, - # 形成每轮 4 帧的环)。profile(2026-09-29 main_prof_72472)实测: - # _TryLoadClassMembersFromPyi 142,888 次 → 6,683 次落到 _EmitExternalClassDeclLlvm - # (cum 103.1s)→ _generate_structs 8,059 次(cum 128.7s) - # → _TryLoadStructFromStub 88,970 次 → _parse_llvm_type 909,382 次 - # 即"环上重复重跑"而非"首次发射"是主开销。 - # 依赖签名(与 EnsureClassMembersFlattened 的"(struct 元素数, 成员表长度) - # 签名"同一思路):签名不变 ⇒ 上一次发射已经把该状态下能填的都填完了, - # 重跑是纯 no-op,直接短路。**只在签名变化时才允许重跑** ⇒ 与"父类/struct - # 就绪顺序无关"这条设计目标不冲突(依赖一变,签名就变,立刻放行)。 - # 保守量:全局"已登记类数 / 已展平类数"——声明加载只要有任何进展就会变, - # 因此不会把"别的类补齐后本类该重跑"的情形漏掉。 - # 键含 source_sha1:同名类可能分属不同模块,发射是 per-(sha1, 类名) 的。 - _sigmap: dict | None = getattr(Gen, '_class_members_reload_sig', None) - if _sigmap is None: - _sigmap = {} - Gen._class_members_reload_sig = _sigmap - _sig_key: tuple = (source_sha1, class_name) - _sig: tuple = self._ClassMembersReloadSignature(Gen, class_name) - if _sigmap.get(_sig_key) == _sig: - return True - # 先登记、再发射:**登记必须在发射之前**,否则环上第一个内层重入 - # (发射进行中,状态已变)仍会看到"无签名"而再发一次,去重被推迟一层; - # 先登记后内层重入即可在同状态下直接短路(环深 2 层即收敛)。 - _sigmap[_sig_key] = _sig - try: - self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name, source_sha1=source_sha1) - except BaseException: - # 发射失败(fail-fast 或将被上层 except 吞掉):撤销本次签名登记, - # 保证"失败可重试"这一既有语义不被去重挡死——否则失败态会被签名 - # 永久冻结(正是本仓反复踩过的"残缺表冻结"类缺陷)。 - if _sigmap is not None and _sigmap.get(_sig_key) == _sig: - del _sigmap[_sig_key] - raise - return True - - def _ClassMembersReloadSignature(self, Gen: LlvmGeneratorMixin, class_name: str) -> tuple: - """`_TryLoadClassMembersFromPyi` 重跑的依赖签名(纯读,不产生副作用)。 - - 收录"重跑能否产生新结果"的全部输入: - - 全局进度:**逐类**(`class_members` 键, 该表长度) 的完整元组(不是"类数"—— - 只看键数会在"某已有类的成员表继续增长"(父类成员后到)时看不出变化, - 而父类成员正是继承前缀的来源);`_members_flattened` 长度; - `class_parents` / `class_parent` 的条目数(父链登记本身也是输入) - - 本类自身:成员表长度、是否已登记已展平、struct body 元素数(None ⇒ -1) - - 各父类:成员表长度 + 是否已展平(父类从 `class_parents` / `class_parent` 取; - 两者都缺时用空元组 —— 此时本类依赖尚未建立,签名仍稳定,不影响收敛) - """ - _parents: list = list((getattr(Gen, 'class_parents', {}) or {}).get(class_name) or []) - if not _parents: - _p1: str | None = (getattr(Gen, 'class_parent', {}) or {}).get(class_name) - if _p1: - _parents = [_p1] - _flat: set = getattr(Gen, '_members_flattened', None) or set() - _st: object = Gen.structs.get(class_name) - _st_len: int = -1 - if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: - _st_len = len(_st.elements) - _parent_sig: tuple = tuple( - (_p, len(Gen.class_members.get(_p, []) or []), _p in _flat) - for _p in _parents - ) - _cm_sig: tuple = tuple((_k, len(_v)) for _k, _v in Gen.class_members.items()) - return ( - _cm_sig, - len(_flat), - len(getattr(Gen, 'class_parents', {}) or {}), - len(getattr(Gen, 'class_parent', {}) or {}), - len(Gen.class_members.get(class_name, []) or []), - class_name in _flat, - _st_len, - _parent_sig, - ) - - def _BuildScalarConstant(self, value_node: ast.AST, target_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Constant | None: - """Build a scalar constant value from AST node, delegated to unified method.""" - return self.Trans._BuildScalarConstantUnified(value_node, target_type, Gen) - + if not force: + # 【稳定态去重 · 依赖签名】guard(上方)只在"表非空 **且已登记已展平**" + # 时短路;而"父类是泛型特化/始终不可得"的类**永远不会**被登记为已展平 + # ⇒ guard 永不命中 ⇒ 每次重入都重跑整套 `_EmitExternalClassDeclLlvm` + # (其内部又触发 _generate_structs → _TryLoadStructFromStub → 本方法, + # 形成每轮 4 帧的环)。profile(2026-09-29 main_prof_72472)实测: + # _TryLoadClassMembersFromPyi 142,888 次 → 6,683 次落到 _EmitExternalClassDeclLlvm + # (cum 103.1s)→ _generate_structs 8,059 次(cum 128.7s) + # → _TryLoadStructFromStub 88,970 次 → _parse_llvm_type 909,382 次 + # 即"环上重复重跑"而非"首次发射"是主开销。 + # 依赖签名(与 EnsureClassMembersFlattened 的"(struct 元素数, 成员表长度) + # 签名"同一思路):签名不变 ⇒ 上一次发射已经把该状态下能填的都填完了, + # 重跑是纯 no-op,直接短路。**只在签名变化时才允许重跑** ⇒ 与"父类/struct + # 就绪顺序无关"这条设计目标不冲突(依赖一变,签名就变,立刻放行)。 + # 保守量:全局"已登记类数 / 已展平类数"——声明加载只要有任何进展就会变, + # 因此不会把"别的类补齐后本类该重跑"的情形漏掉。 + # 键含 source_sha1:同名类可能分属不同模块,发射是 per-(sha1, 类名) 的。 + _sigmap: dict | None = getattr(Gen, '_class_members_reload_sig', None) + if _sigmap is None: + _sigmap = {} + Gen._class_members_reload_sig = _sigmap + _sig_key: tuple = (source_sha1, class_name) + _sig: tuple = self._ClassMembersReloadSignature(Gen, class_name) + if _sigmap.get(_sig_key) == _sig: + return True + # 先登记、再发射:**登记必须在发射之前**,否则环上第一个内层重入 + # (发射进行中,状态已变)仍会看到"无签名"而再发一次,去重被推迟一层; + # 先登记后内层重入即可在同状态下直接短路(环深 2 层即收敛)。 + _sigmap[_sig_key] = _sig + try: + self._EmitExternalClassDeclLlvm(node, Gen, module_name=module_name, source_sha1=source_sha1) + except BaseException: + # 发射失败(fail-fast 或将被上层 except 吞掉):撤销本次签名登记, + # 保证"失败可重试"这一既有语义不被去重挡死——否则失败态会被签名 + # 永久冻结(正是本仓反复踩过的"残缺表冻结"类缺陷)。 + if _sigmap is not None and _sigmap.get(_sig_key) == _sig: + del _sigmap[_sig_key] + raise + return True + + def _ClassMembersReloadSignature(self, Gen: LlvmGeneratorMixin, class_name: str) -> tuple: + """`_TryLoadClassMembersFromPyi` 重跑的依赖签名(纯读,不产生副作用)。 + + 收录"重跑能否产生新结果"的全部输入: + - 全局进度:**逐类**(`class_members` 键, 该表长度) 的完整元组(不是"类数"—— + 只看键数会在"某已有类的成员表继续增长"(父类成员后到)时看不出变化, + 而父类成员正是继承前缀的来源);`_members_flattened` 长度; + `class_parents` / `class_parent` 的条目数(父链登记本身也是输入) + - 本类自身:成员表长度、是否已登记已展平、struct body 元素数(None ⇒ -1) + - 各父类:成员表长度 + 是否已展平(父类从 `class_parents` / `class_parent` 取; + 两者都缺时用空元组 —— 此时本类依赖尚未建立,签名仍稳定,不影响收敛) + """ + _parents: list = list((getattr(Gen, 'class_parents', {}) or {}).get(class_name) or []) + if not _parents: + _p1: str | None = (getattr(Gen, 'class_parent', {}) or {}).get(class_name) + if _p1: + _parents = [_p1] + _flat: set = getattr(Gen, '_members_flattened', None) or set() + _st: object = Gen.structs.get(class_name) + _st_len: int = -1 + if isinstance(_st, ir.IdentifiedStructType) and _st.elements is not None: + _st_len = len(_st.elements) + _parent_sig: tuple = tuple( + (_p, len(Gen.class_members.get(_p, []) or []), _p in _flat) + for _p in _parents + ) + _cm_sig: tuple = tuple((_k, len(_v)) for _k, _v in Gen.class_members.items()) + return ( + _cm_sig, + len(_flat), + len(getattr(Gen, 'class_parents', {}) or {}), + len(getattr(Gen, 'class_parent', {}) or {}), + len(Gen.class_members.get(class_name, []) or []), + class_name in _flat, + _st_len, + _parent_sig, + ) + + def _BuildScalarConstant(self, value_node: ast.AST, target_type: ir.Type, Gen: LlvmGeneratorMixin) -> ir.Constant | None: + """Build a scalar constant value from AST node, delegated to unified method.""" + return self.Trans._BuildScalarConstantUnified(value_node, target_type, Gen) + @staticmethod def _CountStubFilesInDir(DirPath: str) -> int: """目录下 `*.stub.ll` 的数量(同一「目录 + 目录 mtime」只 listdir 一次)。 @@ -3600,707 +3600,707 @@ class ImportHandle(BaseHandle): ImportHandle._stub_dir_count_cache[DirPath] = (_Mtime, _Count) return _Count - def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None: - """从 stub.ll 文件中查找指定函数的 LLVM 类型 - - 扫描 output/ 和 temp/ 两个目录的 .stub.ll 文件。 - 关键修复:output/(Phase 2 完整结果)必须覆盖 temp/(Phase 1 不完整结果), - 否则继承包装方法(在 Phase 2 的 _generate_inherited_method_wrappers 中生成) - 将无法被解析,导致跨模块调用使用默认 i32 返回类型 → 64 位指针截断。 - """ - - ProjectRoot = self._find_project_root() - temp_dir = os.path.join(ProjectRoot, 'temp') - output_dir = os.path.join(ProjectRoot, 'output') - - if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir): - return None - - # 文件数签名:检测 stub 新增(增量编译时 output 逐步填充完整签名)。 - # 类级缓存(ImportHandle._stub_func_cache)跨 worker 任务共享;签名变化 - # 只增量解析新增 stub 文件(编译进程内 stub 只增不改,旧文件不重扫, - # 省略每文件 os.path.isfile ——Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。 - # 覆盖语义不变:先扫 temp/ 后扫 output/,output 同名声明覆盖 temp。 - # 计数本身也不重复 listdir:走 _CountStubFilesInDir 的「目录 mtime 命中即复用」 - # 缓存(原实现每次调用都 listdir + 全量 endswith 判定,纯浪费)。 - output_count = self._CountStubFilesInDir(output_dir) - temp_count = self._CountStubFilesInDir(temp_dir) - signature = (output_count, temp_count) - if ImportHandle._stub_func_cache_signature != signature: - ImportHandle._stub_func_cache_signature = signature - for scan_dir in [temp_dir, output_dir]: - if not os.path.isdir(scan_dir): - continue - for filename in os.listdir(scan_dir): - if not filename.endswith('.stub.ll'): - continue - - stub_path = os.path.join(scan_dir, filename) - if stub_path in ImportHandle._stub_func_scanned: - continue - ImportHandle._stub_func_scanned.add(stub_path) - source_sha1 = filename.replace('.stub.ll', '') - try: - with open(stub_path, 'r', encoding='utf-8') as f: - content = f.read() - - for line in content.splitlines(): - stripped = line.strip() - if not stripped.startswith('declare '): - continue - - name_match = stripped.split('@', 1) - if len(name_match) < 2: - continue - - stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"') - - match = _RE_DECLARE_SIMPLE.match(stripped) - if match: - stub_ret = match.group(1).strip() - stub_params = match.group(3).strip() - ImportHandle._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1) - except FileNotFoundError: - # listdir 与 open 之间文件被并发删除:跳过(等效旧 isfile 预检查) - continue - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - - if func_name not in self._stub_func_cache: - return None - - ret_str, params_str, is_variadic, source_sha1 = self._stub_func_cache[func_name] - ret_type = self._parse_llvm_type(ret_str, Gen, source_sha1=source_sha1, create_structs=False) - if ret_type is None: - ret_type = ir.IntType(32) - param_types = [] - if params_str and params_str != 'void': - depth = 0 - current = '' - for ch in params_str: - if ch == ',' and depth == 0: - p = self._strip_llvm_param_name(current.strip()) - if p and p != '...': - pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False) - if pt is not None: - param_types.append(pt) - else: - param_types.append(ir.PointerType(ir.IntType(8))) - current = '' - else: - if ch == '{': - depth += 1 - elif ch == '}': - depth -= 1 - current += ch - p = self._strip_llvm_param_name(current.strip()) - if p and p != '...': - pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False) - if pt is not None: - param_types.append(pt) - else: - param_types.append(ir.PointerType(ir.IntType(8))) - return ir.FunctionType(ret_type, param_types, var_arg=is_variadic) - - def _strip_llvm_param_name(self, param_str: str) -> str: - """剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分""" - param_str = param_str.strip() - # 支持 %name 和 %"quoted.name" 两种参数名形式 - result = _RE_PARAM_NAME_TRAILING.sub('', param_str) - return result.strip() - - def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None: - """解析简单的 LLVM 类型字符串(不创建结构体,避免副作用)""" - - type_str = type_str.strip() - - type_map = { - 'void': ir.VoidType(), - 'i1': ir.IntType(1), - 'i8': ir.IntType(8), - 'i16': ir.IntType(16), - 'i32': ir.IntType(32), - 'i64': ir.IntType(64), - 'float': ir.FloatType(), - 'double': ir.DoubleType(), - } - - if type_str in type_map: - return type_map[type_str] - - if type_str.endswith('*'): - pointee = type_str[:-1].strip() - pointee_type = self._parse_simple_llvm_type(pointee) - if pointee_type: - return ir.PointerType(pointee_type) - return ir.PointerType(ir.IntType(8)) - - if type_str.startswith('[') and ']' in type_str: - arr_match = _RE_ARRAY_TYPE.match(type_str) - if arr_match: - size = int(arr_match.group(1)) - elem_type = self._parse_simple_llvm_type(arr_match.group(2).strip()) - if elem_type: - return ir.ArrayType(elem_type, size) - - if type_str.startswith('%'): - return ir.PointerType(ir.IntType(8)) - - return None - - def _LoadDeclarationsFromStubLlvm(self, module_name: str, Gen: LlvmGeneratorMixin) -> None: - """从 stub.ll 文件加载函数声明和结构体定义到 Gen""" - ProjectRoot = self._find_project_root() - temp_dir = os.path.join(ProjectRoot, 'temp') - - if not os.path.isdir(temp_dir): - return - - all_stub_files = [f for f in os.listdir(temp_dir) if f.endswith('.stub.ll')] - - source_sig_files = getattr(self.Trans, '_source_module_sig_files', {}) - target_sha1 = None - if module_name: - for key, val in source_sig_files.items(): - if key == module_name or key.endswith('.' + module_name): - target_sha1 = os.path.basename(val).replace('.vpi', '').replace('.pyi', '') - break - if not target_sha1: - ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) - target_sha1 = ModuleSha1Map.get(module_name) - - if target_sha1: - target_stub = f"{target_sha1}.stub.ll" - if target_stub in all_stub_files: - stub_files = [target_stub] - else: - stub_files = [] - else: - stub_files = [] - - needed_sha1s = set() - for filename in stub_files: - stub_path = os.path.join(temp_dir, filename) - try: - with open(stub_path, 'r', encoding='utf-8') as f: - content = f.read() - for line in content.splitlines(): - stripped = line.strip() - if stripped.startswith('%') and '=' in stripped and 'type' in stripped: - for m in _RE_SHA1_PREFIX_REF.finditer(stripped): - needed_sha1s.add(m.group(1)) - elif stripped.startswith('declare '): - for m in _RE_SHA1_PREFIX_REF.finditer(stripped): - needed_sha1s.add(m.group(1)) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - - preLoad_files = [] - for sha1 in needed_sha1s: - dep_stub = f"{sha1}.stub.ll" - if dep_stub in all_stub_files and dep_stub not in stub_files: - preLoad_files.append(dep_stub) - - Load_order = preLoad_files + stub_files - - for filename in Load_order: - stub_path = os.path.join(temp_dir, filename) - source_sha1 = filename.replace('.stub.ll', '') - is_preLoad = filename in preLoad_files - try: - with open(stub_path, 'r', encoding='utf-8') as f: - content = f.read() - - for line in content.splitlines(): - stripped = line.strip() - - if stripped.startswith('%') and '=' in stripped and 'type' in stripped: - struct_def_match = _RE_STRUCT_TYPE_DEF.match(stripped) - if struct_def_match: - raw_name = struct_def_match.group(1) - # 治本修复:从 raw_name 提取类型定义的实际 sha1 前缀, - # 而非使用 stub 文件的 sha1。同一 stub 文件可能包含多个模块的 - # struct 定义(前向引用),必须用类型名中的 sha1 作为 source_sha1, - # 否则同名类冲突检测会失效(如 __module.py stub 中的 - # e64295c03b564a53.Module 前向引用会被错误地用 6e3f70566907c6cf 作为 sha1)。 - actual_type_sha1 = source_sha1 - if '.' in raw_name: - _rn_parts = raw_name.split('.', 1) - if len(_rn_parts[0]) == 16 and all(_c in '0123456789abcdef' for _c in _rn_parts[0]): - actual_type_sha1 = _rn_parts[0] - clean_name = _rn_parts[1] - else: - clean_name = raw_name - struct_body = struct_def_match.group(3).strip() - if struct_body and struct_body != 'opaque': - elem_types = [] - is_packed = struct_body is not None and struct_body.startswith('<{') and struct_body.endswith('}>') - if is_packed: - inner = struct_body[2:-1].strip() - else: - inner = struct_body.strip('{}') - if inner: - for elem in inner.split(','): - elem = elem.strip() - if elem: - elem_type = self._parse_llvm_type(elem, Gen, source_sha1=actual_type_sha1) - if elem_type is not None: - elem_types.append(elem_type) - else: - elem_types.append(ir.IntType(32)) - st = Gen._get_or_create_struct(clean_name, source_sha1=actual_type_sha1, packed=is_packed) - if isinstance(st, ir.IdentifiedStructType) and st.elements is None: - try: - st.set_body(*elem_types) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - continue - - # 全局变量声明:@name = external global TYPE(stub.ll 实际写入格式, - # 跨模块全局变量为裸名,如 @_sha1_store_arr;原正则只匹配 - # @sha1.name 前缀格式,实际从未命中 → TPC 编译 TPV 时跨模块 - # 全局变量(如 StubMerger._sha1_store_count)从未嵌入 - # Gen.module.globals → _lookup_module_global 恒 None → store/load - # 全部丢失 → memset(null) 崩溃。此处兼容裸名 + sha1 前缀两种格式, - # 名称与定义一致(temp/*.text.ll 均为裸名)。 - _gv_match = _RE_EXTERNAL_GLOBAL_DEF.match(stripped) - if _gv_match: - _gv_name: str = _gv_match.group(3) - _gv_sha1: str | None = _gv_match.group(2) - _gv_type_str: str = _gv_match.group(4).strip() - # 去掉尾部修饰(zeroinitializer / , align N),只保留类型 - _gv_type_str = _RE_STRIP_ZEROINIT.sub('', _gv_type_str) - _gv_type_str = _RE_STRIP_ALIGN.sub('', _gv_type_str) - if _gv_name not in Gen.module.globals: - _gv_ty: ir.Type | None = self._parse_llvm_type(_gv_type_str, Gen, source_sha1=source_sha1) - if _gv_ty is not None: - _gv = ir.GlobalVariable(Gen.module, _gv_ty, name=_gv_name) - _gv.linkage = 'external' - continue - - if not stripped.startswith('declare '): - continue - - if is_preLoad: - continue - - func_name_match = stripped.split('@', 1) - if len(func_name_match) < 2: - continue - - func_name = func_name_match[1].split('(', 1)[0].strip().strip('"') - - if not module_name: - continue - - # bare 名(无 16 位 hex SHA1 前缀)表示 CExport,调用点需用纯名 - _Prefix: str = func_name.split('.', 1)[0] - if not (len(_Prefix) == 16 and all(_c in '0123456789abcdef' for _c in _Prefix)): - Gen._export_funcs.add(func_name) - - try: - func_type = self._parse_llvm_declare(stripped, Gen) - if func_type is not None: - # __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致 - # .stub.ll 由 .pyi 生成,.pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName* - if '.__new__' in func_name: - _np = func_name.split('.') - if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]): - _new_class = _np[1] - else: - _new_class = _np[0] - _new_struct = Gen.structs.get(_new_class) - if _new_struct is None: - _full_name = func_name.split('.__new__')[0] - _new_struct = Gen.structs.get(_full_name) - if _new_struct: - _ret_type = _new_struct if isinstance(_new_struct, ir.PointerType) else ir.PointerType(_new_struct) - func_type = ir.FunctionType(_ret_type, func_type.args, var_arg=func_type.var_arg) - if func_name in Gen.functions: - existing_func = Gen.functions[func_name] - existing_ftype = getattr(existing_func, 'ftype', None) or existing_func.type.pointee - if str(existing_ftype) != str(func_type): - # 类型不一致:清理 module.globals + module.scope._useset 后重建, - # 避免 DuplicatedNameError。必须同时清 existing_func.name(真名) - # 与 func_name(字典键):别名场景下两者不同(如 - # Gen.functions['ExprGenAttr.find_global_in_module'] 指向名为 - # 'dddc595175f00c66.ExprGenAttr.find_global_in_module' 的对象), - # 只删真名会留下字典键仍占住 module.scope(llvmlite 按名字去重) - # → 重建仍抛 DuplicatedNameError(报文只有名字,不含冲突双方)。 - module_scope = getattr(Gen.module, 'scope', None) - _useset = getattr(module_scope, '_useset', None) - for _stale_name in {existing_func.name, func_name}: - _stale = Gen.module.globals.get(_stale_name) - if isinstance(_stale, ir.Function): - del Gen.module.globals[_stale_name] - if _useset is not None: - _useset.discard(_stale_name) - func = Gen._CreateOrReuseFuncDecl(func_name, func_type) - Gen.functions[func_name] = func - if '.' in func_name: - short_name = func_name.split('.', 1)[1] - Gen.functions[short_name] = func - else: - func = Gen._get_or_declare_function(func_name, func_type) - Gen.functions[func_name] = func - if '.' in func_name: - short_name = func_name.split('.', 1)[1] - Gen.functions[short_name] = func - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - # 声明创建/签名的 fail-fast 必须上抛(见 _LoadModuleDeclarationsFromFile - # 同款说明):吞掉它会让该声明静默缺失、并把半注册名字留给下一轮。 - raise - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - raise - if _config_mode == "strict": - raise - _vlog().warning(f"处理导入失败: {_e}", "Exception") - - def _TryLoadFuncDeclsFromOutputStub(self, struct_name: str, Gen: LlvmGeneratorMixin) -> bool: - """按需从 output 目录的 stub.ll 加载泛型特化方法声明。 - - 当 temp stub 不包含泛型特化(如 list[str].__iter__)时, - 从 output stub(由 _split_ll 从完整 IR 生成)中按需加载。 - - struct_name: 结构体名,如 '9163064cf3eb88f4.list[str]' 或 'list[str]' - 返回 True 如果找到了并加载了新函数声明。 - """ - ProjectRoot = self._find_project_root() - output_dir = os.path.join(ProjectRoot, 'output') - if not os.path.isdir(output_dir): - return False - - # 提取短名称和 SHA1 前缀 - sha1_prefix: str | None = None - if '.' in struct_name: - parts: list[str] = struct_name.split('.', 1) - if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): - sha1_prefix = parts[0] - short_name: str = parts[1] - else: - short_name = struct_name - else: - short_name = struct_name - - # 确定要搜索的 stub 文件列表 - stub_files_to_search: list[str] = [] - if sha1_prefix: - target_stub = f'{sha1_prefix}.stub.ll' - target_path = os.path.join(output_dir, target_stub) - if os.path.isfile(target_path): - stub_files_to_search.append(target_stub) - else: - # 没有SHA1前缀,搜索所有 output stub - for fname in os.listdir(output_dir): - if fname.endswith('.stub.ll'): - stub_files_to_search.append(fname) - - loaded_any: bool = False - for fname in stub_files_to_search: - stub_path = os.path.join(output_dir, fname) - try: - with open(stub_path, 'r', encoding='utf-8') as f: - content = f.read() - for line in content.splitlines(): - stripped = line.strip() - if not stripped.startswith('declare '): - continue - # 检查是否是目标结构体的方法 - if f'.{short_name}.' not in stripped: - continue - try: - func_type = self._parse_llvm_declare(stripped, Gen) - if func_type is None: - continue - # 提取函数名:最后一个 @ 到第一个 ( 之间的内容 - paren_pos: int = stripped.index('(') - at_pos: int = stripped.rindex('@', 0, paren_pos) - func_name: str = stripped[at_pos + 1:paren_pos].strip().strip('"') - if func_name not in Gen.functions: - # module.globals 已有同名声明 → 复用(llvmlite NameScope - # 按名字去重,重复 ir.Function 必抛 DuplicatedNameError; - # 此前该异常被下方 except 静默吞掉 → 声明未登记 → 后续 - # 调用点找不到签名) - g_ex_stub = Gen.module.globals.get(func_name) - if g_ex_stub is not None: - func = g_ex_stub - else: - func = Gen._CreateOrReuseFuncDecl(func_name, func_type) - Gen.functions[func_name] = func - if '.' in func_name: - short_fn = func_name.split('.', 1)[1] - Gen.functions[short_fn] = func - loaded_any = True - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - # 见 _LoadModuleDeclarationsFromFile 同款说明:fail-fast 必须上抛。 - # 原实现是裸 `except Exception: pass` —— 创建中途失败(半注册名字) - # 会在这里被彻底吞掉,后续同名创建只能报"名字悬空占用"。 - raise - _vlog().warning( - f"按需加载 output stub 声明失败: '{stripped}' → {_e}", "Exception") - except Exception as _e: - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - if isinstance(_e, _FFE): - raise - if _config_mode == "strict": - raise - _vlog().warning(f"按需加载 output stub 失败: {_e}", "Exception") - return loaded_any - - def _parse_llvm_declare(self, declare_line: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None: - """解析 LLVM declare 语句""" - match = _RE_DECLARE_GENERIC.match(declare_line) - if not match: - return None - - ret_type_str = match.group(1).strip() - params_str = match.group(3).strip() - - ret_type = self._parse_llvm_type(ret_type_str, Gen) - if ret_type is None: - return None - - param_types = [] - if params_str and params_str != 'void': - depth = 0 - current = '' - for ch in params_str: - if ch == ',' and depth == 0: - p = self._strip_llvm_param_name(current.strip()) - if p and p != '...': - pt = self._parse_llvm_type(p, Gen) - if pt is not None: - param_types.append(pt) - current = '' - else: - if ch == '{': - depth += 1 - elif ch == '}': - depth -= 1 - current += ch - p = self._strip_llvm_param_name(current.strip()) - if p and p != '...': - pt = self._parse_llvm_type(p, Gen) - if pt is not None: - param_types.append(pt) - - is_variadic = '...' in params_str - return ir.FunctionType(ret_type, param_types, var_arg=is_variadic) - - @staticmethod - def _IsGenIndependentLlvmType(T: ir.Type) -> bool: - """该 llvm 类型是否**不含具名结构体**(⇒ 结果与 `Gen.structs` 状态无关,可缓存)。 - - 具名结构体(`IdentifiedStructType`)由 `Gen._get_or_create_struct` 创建、 - 会被 `_generate_structs` 清除重建(instance 与注册表状态绑定),且 - `create_structs=False` 分支返回的 None 表示"此刻未加载"(状态依赖) - ⇒ 这两类结果一律不进 memo。 - """ - if isinstance(T, ir.IdentifiedStructType): - return False - if isinstance(T, ir.PointerType): - return ImportHandle._IsGenIndependentLlvmType(T.pointee) - if isinstance(T, ir.ArrayType): - return ImportHandle._IsGenIndependentLlvmType(T.element) - if isinstance(T, ir.VectorType): - return ImportHandle._IsGenIndependentLlvmType(T.element) - if isinstance(T, ir.LiteralStructType): - return all(ImportHandle._IsGenIndependentLlvmType(_e) for _e in T.elements) - if isinstance(T, ir.FunctionType): - return (ImportHandle._IsGenIndependentLlvmType(T.return_type) - and all(ImportHandle._IsGenIndependentLlvmType(_a) for _a in T.args)) - return True - - def _parse_llvm_type(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None: - """`_parse_llvm_type_impl` 的 memo 包装(只缓存与 Gen 状态无关的结果)。 - - profile(2026-09-29 main_prof_72472):本函数被调 1,689,809 次 - (cum 46.7s,其中 909,382 次来自 `_TryLoadStructFromStub`、57,525 次来自 - `_LoadDeclarationsFromStubLlvm`、683,127 次是自递归),并且每次 `X*` 都要 - 新建 `ir.PointerType` —— 全程序 `_TypedPointerType.__init__` 4,596,463 次 - (≈12–15s)主要来自这里。而 `i8*`/`i32`/`[N x i8]` 这些**纯字面类型串**的 - 解析结果与任何 Gen 状态无关,重复解析纯属浪费 ⇒ 加 memo。 - """ - _key_str: str = type_str.strip() - _key: tuple = (_key_str, source_sha1, create_structs) - _memo: dict = self._llvm_type_memo - try: - return _memo[_key] - except KeyError: - pass - _res: ir.Type | None = self._parse_llvm_type_impl( - _key_str, Gen, source_sha1=source_sha1, create_structs=create_structs) - if _res is not None and ImportHandle._IsGenIndependentLlvmType(_res): - _memo[_key] = _res - return _res - - def _parse_llvm_type_impl(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None: - """解析 LLVM 类型字符串""" - - type_str = type_str.strip() - - # 模块级常量(原为每调用重建的字典字面量,见 _LLVM_PRIMITIVE_TYPE_MAP 注释) - type_map: dict[str, ir.Type] = _LLVM_PRIMITIVE_TYPE_MAP - - if type_str in type_map: - return type_map[type_str] - - if type_str.startswith('{') and type_str.endswith('}'): - inner = type_str[1:-1].strip() - field_types = [] - depth = 0 - current = '' - for ch in inner: - if ch == ',' and depth == 0: - field_types.append(current.strip()) - current = '' - else: - if ch == '{': - depth += 1 - elif ch == '}': - depth -= 1 - current += ch - if current.strip(): - field_types.append(current.strip()) - llvm_fields = [] - for ft in field_types: - parsed = self._parse_llvm_type(ft, Gen, source_sha1=source_sha1, create_structs=create_structs) - if parsed is not None: - llvm_fields.append(parsed) - else: - return None - if llvm_fields: - return ir.LiteralStructType(llvm_fields) - return None - - if type_str.endswith('*'): - pointee = type_str[:-1].strip() - pointee_type = self._parse_llvm_type(pointee, Gen, source_sha1=source_sha1, create_structs=create_structs) - if pointee_type is not None: - return ir.PointerType(pointee_type) - # 被指类型是**具名聚合**(`%...`)却在"允许建结构体"模式下解析不出来: - # 具名类型必然可由 _get_or_create_struct 兜出,取不到即内部不变量已被破坏 - # (成员表/布局两链不同源、名字带多余前缀等)。静默退化成 i8* 会把 - # 「结构体指针」悄悄变成通用指针,外层再套一层即成 i8**(现场: - # Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。 - # create_structs=False 时未加载不属异常 ⇒ 只在该模式下响亮失败。 - if create_structs and pointee.startswith('%'): - from lib.core.LLVMCG.FuncGen import FailFastError as _FFE - raise _FFE( - f"具名类型指针的被指类型解析失败:'{type_str}' 的被指部分 '{pointee}'" - f" 在 create_structs=True 下仍取不到结构体(source_sha1={source_sha1})" - f"—— 拒绝静默退化为 i8*(会使结构体指针变通用指针,再套一层即 i8**)") - return ir.PointerType(ir.IntType(8)) - - if type_str.startswith('[') and ']' in type_str: - arr_match = _RE_ARRAY_TYPE.match(type_str) - if arr_match: - size = int(arr_match.group(1)) - elem_type = self._parse_llvm_type(arr_match.group(2).strip(), Gen, source_sha1=source_sha1, create_structs=create_structs) - if elem_type is not None: - return ir.ArrayType(elem_type, size) - - typedef_names = {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'} + def _LookupStubFuncType(self, func_name: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None: + """从 stub.ll 文件中查找指定函数的 LLVM 类型 + + 扫描 output/ 和 temp/ 两个目录的 .stub.ll 文件。 + 关键修复:output/(Phase 2 完整结果)必须覆盖 temp/(Phase 1 不完整结果), + 否则继承包装方法(在 Phase 2 的 _generate_inherited_method_wrappers 中生成) + 将无法被解析,导致跨模块调用使用默认 i32 返回类型 → 64 位指针截断。 + """ + + ProjectRoot = self._find_project_root() + temp_dir = os.path.join(ProjectRoot, 'temp') + output_dir = os.path.join(ProjectRoot, 'output') + + if not os.path.isdir(temp_dir) and not os.path.isdir(output_dir): + return None + + # 文件数签名:检测 stub 新增(增量编译时 output 逐步填充完整签名)。 + # 类级缓存(ImportHandle._stub_func_cache)跨 worker 任务共享;签名变化 + # 只增量解析新增 stub 文件(编译进程内 stub 只增不改,旧文件不重扫, + # 省略每文件 os.path.isfile ——Windows stat ~0.5ms/次,原 5840 次 ≈ 6.5s)。 + # 覆盖语义不变:先扫 temp/ 后扫 output/,output 同名声明覆盖 temp。 + # 计数本身也不重复 listdir:走 _CountStubFilesInDir 的「目录 mtime 命中即复用」 + # 缓存(原实现每次调用都 listdir + 全量 endswith 判定,纯浪费)。 + output_count = self._CountStubFilesInDir(output_dir) + temp_count = self._CountStubFilesInDir(temp_dir) + signature = (output_count, temp_count) + if ImportHandle._stub_func_cache_signature != signature: + ImportHandle._stub_func_cache_signature = signature + for scan_dir in [temp_dir, output_dir]: + if not os.path.isdir(scan_dir): + continue + for filename in os.listdir(scan_dir): + if not filename.endswith('.stub.ll'): + continue + + stub_path = os.path.join(scan_dir, filename) + if stub_path in ImportHandle._stub_func_scanned: + continue + ImportHandle._stub_func_scanned.add(stub_path) + source_sha1 = filename.replace('.stub.ll', '') + try: + with open(stub_path, 'r', encoding='utf-8') as f: + content = f.read() + + for line in content.splitlines(): + stripped = line.strip() + if not stripped.startswith('declare '): + continue + + name_match = stripped.split('@', 1) + if len(name_match) < 2: + continue + + stub_func_name = name_match[1].split('(', 1)[0].strip().strip('"') + + match = _RE_DECLARE_SIMPLE.match(stripped) + if match: + stub_ret = match.group(1).strip() + stub_params = match.group(3).strip() + ImportHandle._stub_func_cache[stub_func_name] = (stub_ret, stub_params, '...' in stub_params, source_sha1) + except FileNotFoundError: + # listdir 与 open 之间文件被并发删除:跳过(等效旧 isfile 预检查) + continue + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + + if func_name not in self._stub_func_cache: + return None + + ret_str, params_str, is_variadic, source_sha1 = self._stub_func_cache[func_name] + ret_type = self._parse_llvm_type(ret_str, Gen, source_sha1=source_sha1, create_structs=False) + if ret_type is None: + ret_type = ir.IntType(32) + param_types = [] + if params_str and params_str != 'void': + depth = 0 + current = '' + for ch in params_str: + if ch == ',' and depth == 0: + p = self._strip_llvm_param_name(current.strip()) + if p and p != '...': + pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False) + if pt is not None: + param_types.append(pt) + else: + param_types.append(ir.PointerType(ir.IntType(8))) + current = '' + else: + if ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + current += ch + p = self._strip_llvm_param_name(current.strip()) + if p and p != '...': + pt = self._parse_llvm_type(p, Gen, source_sha1=source_sha1, create_structs=False) + if pt is not None: + param_types.append(pt) + else: + param_types.append(ir.PointerType(ir.IntType(8))) + return ir.FunctionType(ret_type, param_types, var_arg=is_variadic) + + def _strip_llvm_param_name(self, param_str: str) -> str: + """剥离 LLVM IR 参数字符串中的参数名,仅保留类型部分""" + param_str = param_str.strip() + # 支持 %name 和 %"quoted.name" 两种参数名形式 + result = _RE_PARAM_NAME_TRAILING.sub('', param_str) + return result.strip() + + def _parse_simple_llvm_type(self, type_str: str) -> ir.Type | None: + """解析简单的 LLVM 类型字符串(不创建结构体,避免副作用)""" + + type_str = type_str.strip() + + type_map = { + 'void': ir.VoidType(), + 'i1': ir.IntType(1), + 'i8': ir.IntType(8), + 'i16': ir.IntType(16), + 'i32': ir.IntType(32), + 'i64': ir.IntType(64), + 'float': ir.FloatType(), + 'double': ir.DoubleType(), + } + + if type_str in type_map: + return type_map[type_str] + + if type_str.endswith('*'): + pointee = type_str[:-1].strip() + pointee_type = self._parse_simple_llvm_type(pointee) + if pointee_type: + return ir.PointerType(pointee_type) + return ir.PointerType(ir.IntType(8)) + + if type_str.startswith('[') and ']' in type_str: + arr_match = _RE_ARRAY_TYPE.match(type_str) + if arr_match: + size = int(arr_match.group(1)) + elem_type = self._parse_simple_llvm_type(arr_match.group(2).strip()) + if elem_type: + return ir.ArrayType(elem_type, size) + + if type_str.startswith('%'): + return ir.PointerType(ir.IntType(8)) + + return None + + def _LoadDeclarationsFromStubLlvm(self, module_name: str, Gen: LlvmGeneratorMixin) -> None: + """从 stub.ll 文件加载函数声明和结构体定义到 Gen""" + ProjectRoot = self._find_project_root() + temp_dir = os.path.join(ProjectRoot, 'temp') + + if not os.path.isdir(temp_dir): + return + + all_stub_files = [f for f in os.listdir(temp_dir) if f.endswith('.stub.ll')] + + source_sig_files = getattr(self.Trans, '_source_module_sig_files', {}) + target_sha1 = None + if module_name: + for key, val in source_sig_files.items(): + if key == module_name or key.endswith('.' + module_name): + target_sha1 = os.path.basename(val).replace('.vpi', '').replace('.pyi', '') + break + if not target_sha1: + ModuleSha1Map = getattr(Gen, 'ModuleSha1Map', {}) + target_sha1 = ModuleSha1Map.get(module_name) + + if target_sha1: + target_stub = f"{target_sha1}.stub.ll" + if target_stub in all_stub_files: + stub_files = [target_stub] + else: + stub_files = [] + else: + stub_files = [] + + needed_sha1s = set() + for filename in stub_files: + stub_path = os.path.join(temp_dir, filename) + try: + with open(stub_path, 'r', encoding='utf-8') as f: + content = f.read() + for line in content.splitlines(): + stripped = line.strip() + if stripped.startswith('%') and '=' in stripped and 'type' in stripped: + for m in _RE_SHA1_PREFIX_REF.finditer(stripped): + needed_sha1s.add(m.group(1)) + elif stripped.startswith('declare '): + for m in _RE_SHA1_PREFIX_REF.finditer(stripped): + needed_sha1s.add(m.group(1)) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + + preLoad_files = [] + for sha1 in needed_sha1s: + dep_stub = f"{sha1}.stub.ll" + if dep_stub in all_stub_files and dep_stub not in stub_files: + preLoad_files.append(dep_stub) + + Load_order = preLoad_files + stub_files + + for filename in Load_order: + stub_path = os.path.join(temp_dir, filename) + source_sha1 = filename.replace('.stub.ll', '') + is_preLoad = filename in preLoad_files + try: + with open(stub_path, 'r', encoding='utf-8') as f: + content = f.read() + + for line in content.splitlines(): + stripped = line.strip() + + if stripped.startswith('%') and '=' in stripped and 'type' in stripped: + struct_def_match = _RE_STRUCT_TYPE_DEF.match(stripped) + if struct_def_match: + raw_name = struct_def_match.group(1) + # 治本修复:从 raw_name 提取类型定义的实际 sha1 前缀, + # 而非使用 stub 文件的 sha1。同一 stub 文件可能包含多个模块的 + # struct 定义(前向引用),必须用类型名中的 sha1 作为 source_sha1, + # 否则同名类冲突检测会失效(如 __module.py stub 中的 + # e64295c03b564a53.Module 前向引用会被错误地用 6e3f70566907c6cf 作为 sha1)。 + actual_type_sha1 = source_sha1 + if '.' in raw_name: + _rn_parts = raw_name.split('.', 1) + if len(_rn_parts[0]) == 16 and all(_c in '0123456789abcdef' for _c in _rn_parts[0]): + actual_type_sha1 = _rn_parts[0] + clean_name = _rn_parts[1] + else: + clean_name = raw_name + struct_body = struct_def_match.group(3).strip() + if struct_body and struct_body != 'opaque': + elem_types = [] + is_packed = struct_body is not None and struct_body.startswith('<{') and struct_body.endswith('}>') + if is_packed: + inner = struct_body[2:-1].strip() + else: + inner = struct_body.strip('{}') + if inner: + for elem in inner.split(','): + elem = elem.strip() + if elem: + elem_type = self._parse_llvm_type(elem, Gen, source_sha1=actual_type_sha1) + if elem_type is not None: + elem_types.append(elem_type) + else: + elem_types.append(ir.IntType(32)) + st = Gen._get_or_create_struct(clean_name, source_sha1=actual_type_sha1, packed=is_packed) + if isinstance(st, ir.IdentifiedStructType) and st.elements is None: + try: + st.set_body(*elem_types) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + continue + + # 全局变量声明:@name = external global TYPE(stub.ll 实际写入格式, + # 跨模块全局变量为裸名,如 @_sha1_store_arr;原正则只匹配 + # @sha1.name 前缀格式,实际从未命中 → TPC 编译 VPC 时跨模块 + # 全局变量(如 StubMerger._sha1_store_count)从未嵌入 + # Gen.module.globals → _lookup_module_global 恒 None → store/load + # 全部丢失 → memset(null) 崩溃。此处兼容裸名 + sha1 前缀两种格式, + # 名称与定义一致(temp/*.text.ll 均为裸名)。 + _gv_match = _RE_EXTERNAL_GLOBAL_DEF.match(stripped) + if _gv_match: + _gv_name: str = _gv_match.group(3) + _gv_sha1: str | None = _gv_match.group(2) + _gv_type_str: str = _gv_match.group(4).strip() + # 去掉尾部修饰(zeroinitializer / , align N),只保留类型 + _gv_type_str = _RE_STRIP_ZEROINIT.sub('', _gv_type_str) + _gv_type_str = _RE_STRIP_ALIGN.sub('', _gv_type_str) + if _gv_name not in Gen.module.globals: + _gv_ty: ir.Type | None = self._parse_llvm_type(_gv_type_str, Gen, source_sha1=source_sha1) + if _gv_ty is not None: + _gv = ir.GlobalVariable(Gen.module, _gv_ty, name=_gv_name) + _gv.linkage = 'external' + continue + + if not stripped.startswith('declare '): + continue + + if is_preLoad: + continue + + func_name_match = stripped.split('@', 1) + if len(func_name_match) < 2: + continue + + func_name = func_name_match[1].split('(', 1)[0].strip().strip('"') + + if not module_name: + continue + + # bare 名(无 16 位 hex SHA1 前缀)表示 CExport,调用点需用纯名 + _Prefix: str = func_name.split('.', 1)[0] + if not (len(_Prefix) == 16 and all(_c in '0123456789abcdef' for _c in _Prefix)): + Gen._export_funcs.add(func_name) + + try: + func_type = self._parse_llvm_declare(stripped, Gen) + if func_type is not None: + # __new__ 方法应返回类指针类型(堆分配替换),与 _GetFunctionSignatureLlvm/_EmitFunctionLlvm 保持一致 + # .stub.ll 由 .pyi 生成,.pyi 生成器对无返回类型注解的 __new__ 默认使用 t.CInt,这里修正为 ClassName* + if '.__new__' in func_name: + _np = func_name.split('.') + if len(_np[0]) == 16 and all(_c in '0123456789abcdef' for _c in _np[0]): + _new_class = _np[1] + else: + _new_class = _np[0] + _new_struct = Gen.structs.get(_new_class) + if _new_struct is None: + _full_name = func_name.split('.__new__')[0] + _new_struct = Gen.structs.get(_full_name) + if _new_struct: + _ret_type = _new_struct if isinstance(_new_struct, ir.PointerType) else ir.PointerType(_new_struct) + func_type = ir.FunctionType(_ret_type, func_type.args, var_arg=func_type.var_arg) + if func_name in Gen.functions: + existing_func = Gen.functions[func_name] + existing_ftype = getattr(existing_func, 'ftype', None) or existing_func.type.pointee + if str(existing_ftype) != str(func_type): + # 类型不一致:清理 module.globals + module.scope._useset 后重建, + # 避免 DuplicatedNameError。必须同时清 existing_func.name(真名) + # 与 func_name(字典键):别名场景下两者不同(如 + # Gen.functions['ExprGenAttr.find_global_in_module'] 指向名为 + # 'dddc595175f00c66.ExprGenAttr.find_global_in_module' 的对象), + # 只删真名会留下字典键仍占住 module.scope(llvmlite 按名字去重) + # → 重建仍抛 DuplicatedNameError(报文只有名字,不含冲突双方)。 + module_scope = getattr(Gen.module, 'scope', None) + _useset = getattr(module_scope, '_useset', None) + for _stale_name in {existing_func.name, func_name}: + _stale = Gen.module.globals.get(_stale_name) + if isinstance(_stale, ir.Function): + del Gen.module.globals[_stale_name] + if _useset is not None: + _useset.discard(_stale_name) + func = Gen._CreateOrReuseFuncDecl(func_name, func_type) + Gen.functions[func_name] = func + if '.' in func_name: + short_name = func_name.split('.', 1)[1] + Gen.functions[short_name] = func + else: + func = Gen._get_or_declare_function(func_name, func_type) + Gen.functions[func_name] = func + if '.' in func_name: + short_name = func_name.split('.', 1)[1] + Gen.functions[short_name] = func + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # 声明创建/签名的 fail-fast 必须上抛(见 _LoadModuleDeclarationsFromFile + # 同款说明):吞掉它会让该声明静默缺失、并把半注册名字留给下一轮。 + raise + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise + if _config_mode == "strict": + raise + _vlog().warning(f"处理导入失败: {_e}", "Exception") + + def _TryLoadFuncDeclsFromOutputStub(self, struct_name: str, Gen: LlvmGeneratorMixin) -> bool: + """按需从 output 目录的 stub.ll 加载泛型特化方法声明。 + + 当 temp stub 不包含泛型特化(如 list[str].__iter__)时, + 从 output stub(由 _split_ll 从完整 IR 生成)中按需加载。 + + struct_name: 结构体名,如 '9163064cf3eb88f4.list[str]' 或 'list[str]' + 返回 True 如果找到了并加载了新函数声明。 + """ + ProjectRoot = self._find_project_root() + output_dir = os.path.join(ProjectRoot, 'output') + if not os.path.isdir(output_dir): + return False + + # 提取短名称和 SHA1 前缀 + sha1_prefix: str | None = None + if '.' in struct_name: + parts: list[str] = struct_name.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + sha1_prefix = parts[0] + short_name: str = parts[1] + else: + short_name = struct_name + else: + short_name = struct_name + + # 确定要搜索的 stub 文件列表 + stub_files_to_search: list[str] = [] + if sha1_prefix: + target_stub = f'{sha1_prefix}.stub.ll' + target_path = os.path.join(output_dir, target_stub) + if os.path.isfile(target_path): + stub_files_to_search.append(target_stub) + else: + # 没有SHA1前缀,搜索所有 output stub + for fname in os.listdir(output_dir): + if fname.endswith('.stub.ll'): + stub_files_to_search.append(fname) + + loaded_any: bool = False + for fname in stub_files_to_search: + stub_path = os.path.join(output_dir, fname) + try: + with open(stub_path, 'r', encoding='utf-8') as f: + content = f.read() + for line in content.splitlines(): + stripped = line.strip() + if not stripped.startswith('declare '): + continue + # 检查是否是目标结构体的方法 + if f'.{short_name}.' not in stripped: + continue + try: + func_type = self._parse_llvm_declare(stripped, Gen) + if func_type is None: + continue + # 提取函数名:最后一个 @ 到第一个 ( 之间的内容 + paren_pos: int = stripped.index('(') + at_pos: int = stripped.rindex('@', 0, paren_pos) + func_name: str = stripped[at_pos + 1:paren_pos].strip().strip('"') + if func_name not in Gen.functions: + # module.globals 已有同名声明 → 复用(llvmlite NameScope + # 按名字去重,重复 ir.Function 必抛 DuplicatedNameError; + # 此前该异常被下方 except 静默吞掉 → 声明未登记 → 后续 + # 调用点找不到签名) + g_ex_stub = Gen.module.globals.get(func_name) + if g_ex_stub is not None: + func = g_ex_stub + else: + func = Gen._CreateOrReuseFuncDecl(func_name, func_type) + Gen.functions[func_name] = func + if '.' in func_name: + short_fn = func_name.split('.', 1)[1] + Gen.functions[short_fn] = func + loaded_any = True + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + # 见 _LoadModuleDeclarationsFromFile 同款说明:fail-fast 必须上抛。 + # 原实现是裸 `except Exception: pass` —— 创建中途失败(半注册名字) + # 会在这里被彻底吞掉,后续同名创建只能报"名字悬空占用"。 + raise + _vlog().warning( + f"按需加载 output stub 声明失败: '{stripped}' → {_e}", "Exception") + except Exception as _e: + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + if isinstance(_e, _FFE): + raise + if _config_mode == "strict": + raise + _vlog().warning(f"按需加载 output stub 失败: {_e}", "Exception") + return loaded_any + + def _parse_llvm_declare(self, declare_line: str, Gen: LlvmGeneratorMixin) -> ir.FunctionType | None: + """解析 LLVM declare 语句""" + match = _RE_DECLARE_GENERIC.match(declare_line) + if not match: + return None + + ret_type_str = match.group(1).strip() + params_str = match.group(3).strip() + + ret_type = self._parse_llvm_type(ret_type_str, Gen) + if ret_type is None: + return None + + param_types = [] + if params_str and params_str != 'void': + depth = 0 + current = '' + for ch in params_str: + if ch == ',' and depth == 0: + p = self._strip_llvm_param_name(current.strip()) + if p and p != '...': + pt = self._parse_llvm_type(p, Gen) + if pt is not None: + param_types.append(pt) + current = '' + else: + if ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + current += ch + p = self._strip_llvm_param_name(current.strip()) + if p and p != '...': + pt = self._parse_llvm_type(p, Gen) + if pt is not None: + param_types.append(pt) + + is_variadic = '...' in params_str + return ir.FunctionType(ret_type, param_types, var_arg=is_variadic) + + @staticmethod + def _IsGenIndependentLlvmType(T: ir.Type) -> bool: + """该 llvm 类型是否**不含具名结构体**(⇒ 结果与 `Gen.structs` 状态无关,可缓存)。 + + 具名结构体(`IdentifiedStructType`)由 `Gen._get_or_create_struct` 创建、 + 会被 `_generate_structs` 清除重建(instance 与注册表状态绑定),且 + `create_structs=False` 分支返回的 None 表示"此刻未加载"(状态依赖) + ⇒ 这两类结果一律不进 memo。 + """ + if isinstance(T, ir.IdentifiedStructType): + return False + if isinstance(T, ir.PointerType): + return ImportHandle._IsGenIndependentLlvmType(T.pointee) + if isinstance(T, ir.ArrayType): + return ImportHandle._IsGenIndependentLlvmType(T.element) + if isinstance(T, ir.VectorType): + return ImportHandle._IsGenIndependentLlvmType(T.element) + if isinstance(T, ir.LiteralStructType): + return all(ImportHandle._IsGenIndependentLlvmType(_e) for _e in T.elements) + if isinstance(T, ir.FunctionType): + return (ImportHandle._IsGenIndependentLlvmType(T.return_type) + and all(ImportHandle._IsGenIndependentLlvmType(_a) for _a in T.args)) + return True + + def _parse_llvm_type(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None: + """`_parse_llvm_type_impl` 的 memo 包装(只缓存与 Gen 状态无关的结果)。 + + profile(2026-09-29 main_prof_72472):本函数被调 1,689,809 次 + (cum 46.7s,其中 909,382 次来自 `_TryLoadStructFromStub`、57,525 次来自 + `_LoadDeclarationsFromStubLlvm`、683,127 次是自递归),并且每次 `X*` 都要 + 新建 `ir.PointerType` —— 全程序 `_TypedPointerType.__init__` 4,596,463 次 + (≈12–15s)主要来自这里。而 `i8*`/`i32`/`[N x i8]` 这些**纯字面类型串**的 + 解析结果与任何 Gen 状态无关,重复解析纯属浪费 ⇒ 加 memo。 + """ + _key_str: str = type_str.strip() + _key: tuple = (_key_str, source_sha1, create_structs) + _memo: dict = self._llvm_type_memo + try: + return _memo[_key] + except KeyError: + pass + _res: ir.Type | None = self._parse_llvm_type_impl( + _key_str, Gen, source_sha1=source_sha1, create_structs=create_structs) + if _res is not None and ImportHandle._IsGenIndependentLlvmType(_res): + _memo[_key] = _res + return _res + + def _parse_llvm_type_impl(self, type_str: str, Gen: LlvmGeneratorMixin, source_sha1: str | None = None, create_structs: bool = True) -> ir.Type | None: + """解析 LLVM 类型字符串""" + + type_str = type_str.strip() + + # 模块级常量(原为每调用重建的字典字面量,见 _LLVM_PRIMITIVE_TYPE_MAP 注释) + type_map: dict[str, ir.Type] = _LLVM_PRIMITIVE_TYPE_MAP + + if type_str in type_map: + return type_map[type_str] + + if type_str.startswith('{') and type_str.endswith('}'): + inner = type_str[1:-1].strip() + field_types = [] + depth = 0 + current = '' + for ch in inner: + if ch == ',' and depth == 0: + field_types.append(current.strip()) + current = '' + else: + if ch == '{': + depth += 1 + elif ch == '}': + depth -= 1 + current += ch + if current.strip(): + field_types.append(current.strip()) + llvm_fields = [] + for ft in field_types: + parsed = self._parse_llvm_type(ft, Gen, source_sha1=source_sha1, create_structs=create_structs) + if parsed is not None: + llvm_fields.append(parsed) + else: + return None + if llvm_fields: + return ir.LiteralStructType(llvm_fields) + return None + + if type_str.endswith('*'): + pointee = type_str[:-1].strip() + pointee_type = self._parse_llvm_type(pointee, Gen, source_sha1=source_sha1, create_structs=create_structs) + if pointee_type is not None: + return ir.PointerType(pointee_type) + # 被指类型是**具名聚合**(`%...`)却在"允许建结构体"模式下解析不出来: + # 具名类型必然可由 _get_or_create_struct 兜出,取不到即内部不变量已被破坏 + # (成员表/布局两链不同源、名字带多余前缀等)。静默退化成 i8* 会把 + # 「结构体指针」悄悄变成通用指针,外层再套一层即成 i8**(现场: + # Generator.ExprGenConst 声明 %"...ExprGenConst"* 却落到 i8**)。 + # create_structs=False 时未加载不属异常 ⇒ 只在该模式下响亮失败。 + if create_structs and pointee.startswith('%'): + from lib.core.LLVMCG.FuncGen import FailFastError as _FFE + raise _FFE( + f"具名类型指针的被指类型解析失败:'{type_str}' 的被指部分 '{pointee}'" + f" 在 create_structs=True 下仍取不到结构体(source_sha1={source_sha1})" + f"—— 拒绝静默退化为 i8*(会使结构体指针变通用指针,再套一层即 i8**)") + return ir.PointerType(ir.IntType(8)) + + if type_str.startswith('[') and ']' in type_str: + arr_match = _RE_ARRAY_TYPE.match(type_str) + if arr_match: + size = int(arr_match.group(1)) + elem_type = self._parse_llvm_type(arr_match.group(2).strip(), Gen, source_sha1=source_sha1, create_structs=create_structs) + if elem_type is not None: + return ir.ArrayType(elem_type, size) + + typedef_names = {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum'} # 类型名内允许 '*':泛型特化名可带指针层实参(如 # %"<模sha1>.list[.AST*]"*)。此前字符集缺 '*' 会把名字截断在 # 内层 '*' 处(丢掉尾随 ']'),随后 gc_base/gc_args 重解析出 # list[.AST](少一层指针)并 declare_only 特化出无方法体的 # 假类型 → 调用点引用未定义的 <模sha1>.list[.AST].__len__ # (链接期 undefined symbol 根因)。 - struct_match = _RE_STRUCT_NAME.match(type_str) - if struct_match: - raw_name = struct_match.group(1) - actual_source_sha1 = source_sha1 - if '.' in raw_name: - parts = raw_name.split('.', 1) - if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): - actual_source_sha1 = parts[0] - struct_name = parts[1] - else: - struct_name = raw_name - if struct_name not in typedef_names: - # 泛型类特化(如 list[str]):触发特化创建结构体和方法声明 - # 使用 declare_only=True 避免在引用模块中生成方法体(定义由定义模块提供) - if '[' in struct_name and actual_source_sha1: - if struct_name in Gen.structs: - return Gen.structs[struct_name] - gc_base: str = struct_name.split('[')[0] - gc_args_raw: str = struct_name[len(gc_base):] - gc_type_args: list[str] = [] - for gc_arg_part in gc_args_raw.split(']['): - gc_arg_part = gc_arg_part.strip('[]') - if gc_arg_part: - gc_type_args.append(gc_arg_part) - if (gc_type_args - and hasattr(self.Trans, 'ClassHandler') - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and gc_base in self.Trans.ClassHandler._generic_class_templates): - gc_type_names: list[str] = [] - for gc_ta in gc_type_args: - gc_tn: str = gc_ta - if gc_ta == 'int': - gc_tn = 'CInt' - elif gc_ta == 'double': - gc_tn = 'CDouble' - elif gc_ta == 'float': - gc_tn = 'CFloat' - elif gc_ta == 'char': - gc_tn = 'CChar' - elif gc_ta == 'bool': - gc_tn = 'CBool' - gc_type_names.append(gc_tn) - saved_module_sha1: str | None = getattr(Gen, 'module_sha1', None) - saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None) - Gen.module_sha1 = actual_source_sha1 - self.Trans._module_sha1 = actual_source_sha1 - try: - gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class( - gc_base, gc_type_args, Gen, type_names=gc_type_names, - declare_only=True) - except Exception: - gc_spec = None - Gen.module_sha1 = saved_module_sha1 - self.Trans._module_sha1 = saved_trans_sha1 - if gc_spec and gc_spec in Gen.structs: - return Gen.structs[gc_spec] - if create_structs: - return Gen._get_or_create_struct(struct_name, source_sha1=actual_source_sha1) - else: - existing = Gen.structs.get(struct_name) - if existing is not None: - return existing - # 尝试用 source_sha1 前缀的全名查找 - if actual_source_sha1: - full_key = f"{actual_source_sha1}.{struct_name}" - existing_full = Gen.structs.get(full_key) - if existing_full is not None: - return existing_full - return None - return ir.IntType(32) - - return None - - def _strip_param_name(self, param_str: str) -> str: - """从 LLVM 参数字符串中剥离参数名,只保留类型部分 - 例如: '%"sha1.struct_name"* %param_name' -> '%"sha1.struct_name"*' - 'i8* %path' -> 'i8*' - 'i32' -> 'i32' - """ - param_str = param_str.strip() - stripped = _RE_PARAM_NAME_PLAIN.sub('', param_str) - return stripped.strip() + struct_match = _RE_STRUCT_NAME.match(type_str) + if struct_match: + raw_name = struct_match.group(1) + actual_source_sha1 = source_sha1 + if '.' in raw_name: + parts = raw_name.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + actual_source_sha1 = parts[0] + struct_name = parts[1] + else: + struct_name = raw_name + if struct_name not in typedef_names: + # 泛型类特化(如 list[str]):触发特化创建结构体和方法声明 + # 使用 declare_only=True 避免在引用模块中生成方法体(定义由定义模块提供) + if '[' in struct_name and actual_source_sha1: + if struct_name in Gen.structs: + return Gen.structs[struct_name] + gc_base: str = struct_name.split('[')[0] + gc_args_raw: str = struct_name[len(gc_base):] + gc_type_args: list[str] = [] + for gc_arg_part in gc_args_raw.split(']['): + gc_arg_part = gc_arg_part.strip('[]') + if gc_arg_part: + gc_type_args.append(gc_arg_part) + if (gc_type_args + and hasattr(self.Trans, 'ClassHandler') + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and gc_base in self.Trans.ClassHandler._generic_class_templates): + gc_type_names: list[str] = [] + for gc_ta in gc_type_args: + gc_tn: str = gc_ta + if gc_ta == 'int': + gc_tn = 'CInt' + elif gc_ta == 'double': + gc_tn = 'CDouble' + elif gc_ta == 'float': + gc_tn = 'CFloat' + elif gc_ta == 'char': + gc_tn = 'CChar' + elif gc_ta == 'bool': + gc_tn = 'CBool' + gc_type_names.append(gc_tn) + saved_module_sha1: str | None = getattr(Gen, 'module_sha1', None) + saved_trans_sha1: str | None = getattr(self.Trans, '_module_sha1', None) + Gen.module_sha1 = actual_source_sha1 + self.Trans._module_sha1 = actual_source_sha1 + try: + gc_spec: str | None = self.Trans.ClassHandler._specialize_generic_class( + gc_base, gc_type_args, Gen, type_names=gc_type_names, + declare_only=True) + except Exception: + gc_spec = None + Gen.module_sha1 = saved_module_sha1 + self.Trans._module_sha1 = saved_trans_sha1 + if gc_spec and gc_spec in Gen.structs: + return Gen.structs[gc_spec] + if create_structs: + return Gen._get_or_create_struct(struct_name, source_sha1=actual_source_sha1) + else: + existing = Gen.structs.get(struct_name) + if existing is not None: + return existing + # 尝试用 source_sha1 前缀的全名查找 + if actual_source_sha1: + full_key = f"{actual_source_sha1}.{struct_name}" + existing_full = Gen.structs.get(full_key) + if existing_full is not None: + return existing_full + return None + return ir.IntType(32) + + return None + + def _strip_param_name(self, param_str: str) -> str: + """从 LLVM 参数字符串中剥离参数名,只保留类型部分 + 例如: '%"sha1.struct_name"* %param_name' -> '%"sha1.struct_name"*' + 'i8* %path' -> 'i8*' + 'i32' -> 'i32' + """ + param_str = param_str.strip() + stripped = _RE_PARAM_NAME_PLAIN.sub('', param_str) + return stripped.strip() diff --git a/lib/core/Handles/HandlesTypeMerge.py b/lib/core/Handles/HandlesTypeMerge.py index f40a737..1c8fc82 100644 --- a/lib/core/Handles/HandlesTypeMerge.py +++ b/lib/core/Handles/HandlesTypeMerge.py @@ -1,1144 +1,1144 @@ -from __future__ import annotations -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.translator import Translator -from typing import List -from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo +from __future__ import annotations +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.translator import Translator +from typing import List +from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo from lib.core.SymbolUtils import ( IsTModule, IsListAnnotation, IsPtrSubscriptNode, IsCPtrMarkerNode, - DesugarPtrSubscriptToUnion) -from lib.includes import t -import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) -import vast as ast - - -class HandlesTypeMerge(BaseHandle): - """处理类型合并逻辑 - 所有类型分析的核心模块""" - - def _MakeVoidCTypeInfo(self) -> CTypeInfo: - Info = CTypeInfo() - Info.BaseType = t.CVoid - return Info - - @staticmethod - def _MakeCTypeInfoFromName(TypeName: str) -> CTypeInfo: - """直接从类型名构造 CTypeInfo,不经过 FromStr/字符串解析""" - return CTypeInfo.FromTypeName(TypeName) - - def GetCTypeInfo(self, Node: ast.AST) -> CTypeInfo: - """获取 CTypeInfo 对象""" - Result = self._GetCTypeInfoInternal(Node) - if isinstance(Result, CTypeInfo): - return Result - elif isinstance(Result, str): - return self._MakeCTypeInfoFromName(Result) - else: - return self._MakeVoidCTypeInfo() - - def _GetCTypeInfoInternal(self, Node: ast.AST) -> CTypeInfo | str | None: - """鍐呴儴鏂规硶锛岃繑鍥?CTypeInfo""" - LineNum = getattr(Node, 'lineno', None) - - if getattr(self.Trans, 'DebugFile', None): - # 仅 debug 模式构造参数(ast.dump 昂贵,非 debug 时无条件执行是隐藏热点) - self.Trans.DebugPrint(f"[GetCTypeInfo] Node type: {type(Node).__name__}, dump: {ast.dump(Node)[:60]}...") - - if isinstance(Node, ast.Constant): - TypeName = Node.value - return self._MakeCTypeInfoFromName(TypeName) - - if isinstance(Node, ast.Name): - TypeName = Node.id - t_c_imported = getattr(self.Trans, '_t_c_imported_names', {}) - if TypeName in t_c_imported: - src_module, src_name = t_c_imported[TypeName] - virtual_attr = ast.Attribute( - value=ast.Name(id=src_module, ctx=ast.Load()), - attr=src_name, - ctx=ast.Load() - ) - ast.copy_location(virtual_attr, Node) - return self.GetCTypeInfo(virtual_attr) - Entry = self.Trans.SymbolTable.lookup(TypeName) - if Entry and Entry.IsTypedef: - if isinstance(Entry, CTypeInfo) and Entry.BaseType: - if not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0: - Result = Entry.Copy() - Result.IsTypedef = True - Result.Name = TypeName - return Result - if Entry.OriginalType: - if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] - Result.IsTypedef = True - Result.Name = TypeName - return Result - elif isinstance(Entry.OriginalType, CTypeInfo): - Resolved = Entry.OriginalType.Copy() - Resolved.IsTypedef = True - Resolved.Name = TypeName - return Resolved - elif isinstance(Entry.OriginalType, str): - # 字符串 OriginalType(如 'void *')需解析为 CTypeInfo, - # 否则返回的 Entry BaseType=None 会被下游误判为 i32 - Resolved = CTypeInfo.FromTypeName(Entry.OriginalType) - if Resolved and Resolved.BaseType: - Resolved.IsTypedef = True - Resolved.Name = TypeName - return Resolved - return Entry - # REnum 优先于 IsEnum 处理(REnum 同时有 IsEnum=True 和 IsStruct=True) - # 必须设置 Name 和 PtrCount=1,否则 BinOp `LLVMType | t.CPtr` 合并后丢失 REnum 语义 - if isinstance(Entry, CTypeInfo) and Entry.IsRenum: - Result = Entry.Copy() - Result.Name = TypeName - Result.PtrCount = 1 # REnum 变量默认为指针(和 struct 一致) - return Result - # 类名与枚举成员名冲突时(如 Constant 既是 ASTKind.Constant 枚举成员又是 - # 结构体类名),类型注解应解析为结构体而非枚举值。判据是"该名**有类定义**", - # 两个来源都要查(与下方 Attribute 分支 L241-269 的判据**同一条规则源**, - # 也与 TypeConvert._type_str_to_llvm_base 的 `type_str in self.structs or - # type_str in self._struct_sha1_map` 守卫同口径): - # ① Gen.structs:本模块已创建的结构体(原判据); - # ② Gen._struct_sha1_map:预扫描登记了"类名 → 模块 sha1"、但本模块尚未 - # 建出结构体的类(跨模块 import 的类)。 - # 只查①漏②时的现场:`rawptr[Constant]`(includes/ast/exprs.vp:59 的 - # accessor,`class Constant` 声明在同文件更后面)在导入侧解析时该名先命中 - # 平坦符号表里的枚举成员 `Constant: t.State`(ast/base.vp)→ 走下方 - # `Entry.IsEnum` 分支返回枚举 → 与 t.CPtr 合并后经 _ctype_to_llvm 的 - # IsEnum 分支(枚举名未登记 ⇒ 宽度退化为 i32)得 **i32\***,与调用点实参 - # %".Constant"* 分裂 → FuncGen._adjust_args fail-fast。 - # 查②后 BaseType=CStruct('Constant'),_get_or_create_struct 会按 - # `_struct_sha1_map` 拼出 `".Constant"`(BaseGen.py:527-528), - # 与声明端/实参同一结构体对象。 - # 但必须排除**枚举类型本身**(如 `class ASTKind(t.CEnum)` 的注解 `x: ASTKind`): - # _struct_sha1_map 是按 .vpi 里全部 ClassDef 建的,枚举类也在其中;若一并 - # 当结构体,枚举注解会被建成 opaque struct,与 i32 错位 - # (现场:'IntType' object has no attribute 'is_opaque')。 - # 判据必须按 **IsEnumMember** 区分(现场实测):枚举**成员**条目的标志组合是 - # IsEnum=True + IsEnumMember=True + BaseType=CEnum - # (即成员的 IsEnum 也被置真),只判 IsEnum 会把成员误当枚举类型 ⇒ 落 i32*; - # 而枚举**类型**条目是 IsEnum=True + IsEnumMember=False。 - # 故:枚举类型 = IsEnum and not IsEnumMember ⇒ 走下方 IsEnum 分支; - # 成员(IsEnumMember)⇒ 不是类型 ⇒ 让位给类定义。 - _is_enum_type: bool = ( - isinstance(Entry, CTypeInfo) and Entry.IsEnum - and not getattr(Entry, 'IsEnumMember', False)) - _Gen = getattr(self.Trans, 'LlvmGen', None) - if _Gen and not _is_enum_type and ( - TypeName in _Gen.structs - or TypeName in getattr(_Gen, '_struct_sha1_map', {})): - Result = CTypeInfo() - Result.BaseType = t.CStruct(name=TypeName) - Result.IsStruct = True - Result.Name = TypeName - Result.PtrCount = 1 - return Result - if isinstance(Entry, CTypeInfo) and Entry.IsEnum: - return Entry.Copy() - if isinstance(Entry, dict) and Entry.get('IsEnum'): - Result = CTypeInfo() - Result.BaseType = t.CEnum(TypeName) - Result.IsEnum = True - return Result - # 检测结构体类型 (CTypeInfo with IsStruct=True, 来自 _InsertClassSymbol) - if isinstance(Entry, CTypeInfo) and Entry.IsStruct: - Result = Entry.Copy() - if Result.BaseType is None: - Result.BaseType = t.CStruct(name=TypeName) - Result.Name = TypeName - Result.PtrCount = 1 # struct 变量默认为指针 - return Result - # [builtins 最后匹配] 常规路径全部 miss 后查 includes/builtins.vp 的 - # CTypedef(size_t → t.CSizeT、rawptr → t.CPtr 内置等),对齐 - # TypeAnnotationResolver._from_node_name 兜底,避免裸名被误判为 - # 命名结构体(main2.vp `a: size_t` → alloca %...size_t 根因)。 - try: - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR - builtins_info: CTypeInfo | None = _TAR._lookup_builtins_typedef(TypeName, self.Trans.SymbolTable) - if builtins_info is not None: - return builtins_info - except Exception: - pass - return self._MakeCTypeInfoFromName(TypeName) - - elif isinstance(Node, ast.Attribute): - ModuleParts = [] - Current = Node - while isinstance(Current, ast.Attribute): - ModuleParts.insert(0, Current.attr) - Current = Current.value - - if isinstance(Current, ast.Name): - ModuleParts.insert(0, Current.id) - - if len(ModuleParts) >= 2: - TypeName = ModuleParts[-1] - ModulePath = '.'.join(ModuleParts[:-1]) - elif len(ModuleParts) == 1: - TypeName = ModuleParts[0] - ModulePath = None - else: - return self._MakeVoidCTypeInfo() - - # 解析 import 别名: 如 import fat32_types as types -> types -> fat32_types - if ModulePath: - import_aliases = self.Trans.SymbolTable.import_aliases - if ModulePath in import_aliases: - ModulePath = import_aliases[ModulePath] - else: - # 也尝试在符号表中查找模块别名 - entry = self.Trans.SymbolTable.lookup(ModulePath) - if isinstance(entry, CTypeInfo) and entry.IsModuleAlias: - resolved = entry.ResolvedModule - if resolved: - ModulePath = resolved - - if getattr(self.Trans, 'DebugFile', None): - self.Trans.DebugPrint(f"[GetCTypeInfo-Attribute] 查询: '{TypeName}' ModulePath: {ModulePath}") - - if ModulePath == 't' and TypeName == 'State': - Info = CTypeInfo() - Info.BaseType = t.CVoid - Info.IsState = True - return Info - - if ModulePath == 't' or (ModulePath and ModulePath.startswith('t.')): - # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):与 - # t.CPtr 同形的「一层指针」,使 `T | t.CRef` 合并为 T*(pointer 层 +1)。 - if TypeName in ('CRef', 'CTempRef'): - Info = CTypeInfo() - Info.PtrCount = 1 - Info.BaseType = t.CVoid - return Info - if self._IsTModuleType(TypeName): - TypeClass = CTypeHelper.GetTModuleCType(TypeName) or getattr(t, TypeName, None) - if TypeClass == t.CArrayPtr: - Info = CTypeInfo() - Info.IsArrayPtr = True - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CPtr: - Info = CTypeInfo() - Info.PtrCount = 1 - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CConst: - Info = CTypeInfo() - Info.DataConst = True - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CVolatile: - Info = CTypeInfo() - Info.DataVolatile = True - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CInline: - Info = CTypeInfo() - Info.Storage = t.CInline() - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CStatic: - Info = CTypeInfo() - Info.Storage = t.CStatic() - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CExtern: - Info = CTypeInfo() - Info.Storage = t.CExtern() - Info.BaseType = t.CVoid - return Info - elif TypeClass == t.CExport: - Info = CTypeInfo() - Info.Storage = t.CExport() - Info.BaseType = t.CVoid - return Info - elif TypeClass in (t.BigEndian, t.LittleEndian): - Info = CTypeInfo() - Info.BaseType = t.CInt() - Info.ByteOrder = 'big' if TypeClass == t.BigEndian else 'little' - return Info - elif TypeClass: - Info = CTypeInfo() - Info.BaseType = TypeClass - # 治本修复:t.CDefine 注解必须设置 IsDefine=True, - # 否则预扫描 (LlvmGenerator.py) 和主处理 (HandlesAnnAssign.py) - # 都无法识别 CDefine 常量,导致 FOREGROUND_GREEN 等解析为 0 - if isinstance(TypeClass, type) and issubclass(TypeClass, t.CDefine): - Info.IsDefine = True - return Info - elif CTypeHelper.GetCName(TypeName): - return self._MakeCTypeInfoFromName(CTypeHelper.GetCName(TypeName)) - - FullName = f"{ModulePath}.{TypeName}" if ModulePath else TypeName - - # 优先检查 Gen.structs:类名与枚举成员名冲突时(如 Import 既是 ASTKind.Import - # 枚举成员又是 ast.Import 结构体类名),类型注解应解析为结构体而非枚举值 - # (与 ast.Name 路径 line 100-107 的逻辑一致) - _Gen = getattr(self.Trans, 'LlvmGen', None) - if _Gen and TypeName in _Gen.structs: - Result = CTypeInfo() - Result.BaseType = t.CStruct(name=TypeName) - Result.IsStruct = True - Result.Name = TypeName - Result.PtrCount = 1 - return Result - - # 跨模块类引用(`.CLS` / `ast.CLS`):按类名查 _struct_sha1_map, - # 与 ast.Name 路径(FromTypeName → _get_or_create_struct 的 - # `clean_name in self._struct_sha1_map` 分支,BaseGen.py:421)、声明端 - # DeclarationGenerator._get_type_str(`attr_name in struct_names`, - # DeclarationGenerator.py:1185)以及名称域 SymbolUtils._ResolveAttributeArgStr - # (SymbolUtils.py:491)**同一条"类名 → SHA1"规则**。 - # 此前此处只查 Gen.structs(仅本模块已创建的结构体)→ 本模块未 import - # 的类(如 rawptr[e4c92b87b98424db.AST])落空 → 返回 BaseType=None → - # 裸引用抛"无法识别的参数类型注解"/脱糖合并后 CVoid/1(i8*), - # 与声明端 %".CLS"* 分裂。 - if _Gen is not None and TypeName in getattr(_Gen, '_struct_sha1_map', {}): - Result = CTypeInfo() - Result.BaseType = t.CStruct(name=TypeName) - Result.IsStruct = True - Result.Name = TypeName - Result.PtrCount = 1 - return Result - - Entry = self.Trans.SymbolTable.lookup(TypeName) - if Entry and Entry.IsTypedef: - if isinstance(Entry, CTypeInfo) and Entry.BaseType: - if not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0: - Result = Entry.Copy() - Result.IsTypedef = True - Result.Name = TypeName - return Result - if Entry.OriginalType: - if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] - Result.IsTypedef = True - Result.Name = TypeName - return Result - elif isinstance(Entry.OriginalType, CTypeInfo): - Resolved = Entry.OriginalType.Copy() - Resolved.IsTypedef = True - Resolved.Name = TypeName - return Resolved - elif isinstance(Entry.OriginalType, str): - # 字符串 OriginalType(如 'void *')需解析为 CTypeInfo, - # 否则返回的 Result BaseType=_CTypedef 会被下游误判为 i32 - Resolved = CTypeInfo.FromTypeName(Entry.OriginalType) - if Resolved and Resolved.BaseType: - Resolved.IsTypedef = True - Resolved.Name = TypeName - return Resolved - Result = CTypeInfo() - Result.BaseType = t._CTypedef(TypeName) - Result.IsTypedef = True - Result.Name = TypeName - return Result - if Entry: - # If BaseType is None but this is a class/struct, set BaseType to CStruct - Result = Entry.Copy() - if Result.BaseType is None: - Result.BaseType = t.CStruct(name=TypeName) - Result.IsStruct = True - # struct 变量默认为指针(与 ast.Name 路径一致,见 line 121) - # 同时确保 Name 字段被设置,供 ToString() 使用 - if Result.IsStruct: - if Result.PtrCount == 0: - Result.PtrCount = 1 - if not Result.Name: - Result.Name = TypeName - return Result - return CTypeInfo() - - elif isinstance(Node, ast.Call): - if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': - ReturnTypeInfo = self._GetCTypeInfoInternal(Node.args[0]) if Node.args else None - if isinstance(ReturnTypeInfo, CTypeInfo): - ReturnType = ReturnTypeInfo - else: - ReturnType = CTypeInfo() - ReturnType.BaseType = t.CVoid() - - ParamTypes = [] - ParamNames = [] - for kw in Node.keywords: - ParamTypeInfo = self._GetCTypeInfoInternal(kw.value) - if not isinstance(ParamTypeInfo, CTypeInfo): - ParamTypeInfo = self._MakeCTypeInfoFromName('int') - ParamTypes.append(ParamTypeInfo) - ParamNames.append(kw.arg) - - for arg in Node.args[1:]: - if isinstance(arg, ast.Tuple): - for elt in arg.elts: - ParamTypeInfo = self._GetCTypeInfoInternal(elt) - if not isinstance(ParamTypeInfo, CTypeInfo): - ParamTypeInfo = self._MakeCTypeInfoFromName('int') - ParamTypes.append(ParamTypeInfo) - ParamNames.append('') - else: - ParamTypeInfo = self._GetCTypeInfoInternal(arg) - if not isinstance(ParamTypeInfo, CTypeInfo): - ParamTypeInfo = self._MakeCTypeInfoFromName('int') - ParamTypes.append(ParamTypeInfo) - ParamNames.append('') - - if not ParamTypes: - ParamTypes.append(self._MakeCTypeInfoFromName('void')) - ParamNames.append('') - - Info = CTypeInfo() - Info.IsFuncPtr = True - Info.FuncPtrReturn = ReturnType - Info.FuncPtrParams = list(zip(ParamNames, ParamTypes)) - return Info - - if isinstance(Node.func, ast.Attribute) and isinstance(Node.func.value, ast.Name): - ModuleName = Node.func.value.id - TypeName = Node.func.attr - - if ModuleName == 't' and CTypeHelper.GetTModuleCType(TypeName) == t._CTypedef and Node.args: - TypeArg = Node.args[0] - if isinstance(TypeArg, ast.Name): - return TypeArg.id - elif isinstance(TypeArg, ast.Attribute): - AttrModule = TypeArg.value.id if isinstance(TypeArg.value, ast.Name) else None - AttrName = TypeArg.attr - if AttrModule == 't' and getattr(t, AttrName, None): - TypeObj = getattr(t, AttrName) - if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType): - return TypeObj.__name__ - return AttrName - elif isinstance(TypeArg, ast.Constant): - return str(TypeArg.value) - - if ModuleName in self.Trans._UserTypeModules: - module = self.Trans._UserTypeModules[ModuleName] - if getattr(module, TypeName, None): - TypeObj = getattr(module, TypeName) - if isinstance(TypeObj, type): - if issubclass(TypeObj, t.CType): - return TypeObj.__name__ - - if ModuleName == 't': - TypeObj = CTypeHelper.GetTModuleCType(TypeName) - if TypeObj is None: - TypeObj = getattr(t, TypeName, None) - if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType): - Result = CTypeInfo() - Result.BaseType = TypeObj - return Result - - if ModuleName == 't' and TypeName == 'State': - Result = CTypeInfo() - Result.IsState = True - return Result - - if ModuleName == 't' and TypeName == 'Bit': - if Node.args and isinstance(Node.args[0], ast.Constant): - BitWidth = Node.args[0].value - Result = CTypeInfo() - Result.IsBitField = True - Result.BitWidth = BitWidth - Result.BaseType = t.CInt - return Result - - Result = CTypeInfo() - Result.BaseType = t.CVoid - return Result - - elif isinstance(Node, ast.Subscript): - if (isinstance(Node.value, ast.Attribute) - and isinstance(Node.value.value, ast.Name) - and IsTModule(Node.value.value.id, self.Trans.SymbolTable) - and Node.value.attr == 'Bit'): - if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): - Result = CTypeInfo() - Result.IsBitField = True - Result.BitWidth = Node.slice.value - Result.BaseType = t.CInt - return Result - return self._HandleSubscript(Node) - - elif isinstance(Node, ast.BinOp): - return self.MergeTypes(Node) - - elif isinstance(Node, ast.List): - if Node.elts: - BaseTypes = [] - for elt in Node.elts: - EltType = self._GetCTypeInfoInternal(elt) - if isinstance(EltType, CTypeInfo) and EltType.BaseType: - BaseTypes.append(EltType.BaseType) - if BaseTypes: - Result = CTypeInfo() - Result.BaseType = tuple(BaseTypes) - return Result - return self._MakeVoidCTypeInfo() - - return self._MakeVoidCTypeInfo() - - def _HandleSubscript(self, Node: ast.Subscript) -> CTypeInfo: - """澶勭悊 Subscript 鑺傜偣锛屼娇鐢?CTypeInfo 鏍戝舰缁撴瀯鍒嗘瀽 AST - - 例如? - t.CVoid[5] -> void *[5] - t.CPtr[t.CPtr[t.CPtr]][5][6] -> void ***[5][6] - t.CArrayPtr[t.CArrayPtr] -> void *(*)[] - t.CPtr[t.CArrayPtr[t.CArrayPtr]] -> void *(*(*))[] - """ - # 指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] 下标指针,含裸名 CPtr[T]) - # 折叠为等价的联合写法 `T | t.CPtr` 后交给 MergeTypes 统一解析:内层 T - # 因此走通用解析路径(含跨模块类经 Gen.structs 反查 SHA1 前缀),指针 - # 层数与源码 `T | t.CPtr` 完全一致(此前 t.CPtr[ast.AST] 在此得 - # AST**(比 declare/`AST | t.CPtr` 多一层)、rawptr[ast.AST] 退化为 - # CVoid/1 → i8*)。 - if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): - return self.MergeTypes(DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable)) - - # 判据自洽守卫(fail-fast):base 是指针标记(`rawptr` / `t.CPtr` / 裸名 - # `CPtr` / 别名 `X[...]`,见 IsPtrSubscriptNode)却被上面那条判据漏掉 ⇒ - # 两处判据分裂(内部不变量被破坏)。历史行为是落到下方 CollectAll 当成 - # 「数组下标」解析,**静默**产出"基座 void + 多层指针"(如 i8**),与 - # declare 端的结构体指针分裂(现场:Generator.ExprGenConst 声明 - # `%"...ExprGenConst"*` 却落到 i8**)。按「不许静默降级」改为响亮失败, - # 暴露上游根因。 - if (isinstance(Node.value, ast.Name) and Node.value.id == 'rawptr') \ - or IsCPtrMarkerNode(Node.value, self.Trans.SymbolTable): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"指针下标判据分裂:注解 '{ast.unparse(Node)}' 的 base 是指针标记," - f"但 IsPtrSubscriptNode 未命中 —— 拒绝落到 CollectAll 静默产出" - f"「基座 void 的多层指针」(结构体指针会被悄悄变成 i8**)") - - def CollectAll(node: ast.AST, ParentIsArrayPtr: bool = False) -> CTypeInfo: - """鏀堕泦绫诲瀷淇℃伅锛岃繑鍥?CTypeInfo""" - if isinstance(node, ast.Subscript): - value_node = node.value - slice_node = node.slice - - if isinstance(value_node, ast.Attribute): - VtInfo = self.GetCTypeInfo(value_node) - - if isinstance(value_node, ast.Attribute) and value_node.attr == 'Callable' and isinstance(value_node.value, ast.Name) and IsTModule(value_node.value.id, self.Trans.SymbolTable): - if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: - params_list = slice_node.elts[0] - return_node = slice_node.elts[1] - ParamTypes = [] - ParamNames = [] - if isinstance(params_list, ast.List): - for elt in params_list.elts: - ParamTypeInfo = self._GetCTypeInfoInternal(elt) - if isinstance(ParamTypeInfo, CTypeInfo): - ParamTypes.append(ParamTypeInfo) - ParamNames.append('') - else: - ParamTypes.append(self._MakeCTypeInfoFromName('int')) - ParamNames.append('') - if not ParamTypes: - ParamTypes.append(self._MakeCTypeInfoFromName('void')) - ParamNames.append('') - ReturnTypeInfo = self._GetCTypeInfoInternal(return_node) - if isinstance(ReturnTypeInfo, CTypeInfo): - ReturnType = ReturnTypeInfo - else: - ReturnType = CTypeInfo() - ReturnType.BaseType = t.CVoid() - Info = CTypeInfo() - Info.IsFuncPtr = True - Info.FuncPtrReturn = ReturnType - Info.FuncPtrParams = list(zip(ParamNames, ParamTypes)) - return Info - - if VtInfo and VtInfo.IsArrayPtr: - ResultInfo = CTypeInfo() - ResultInfo.PtrCount = VtInfo.PtrCount - ResultInfo.IsArrayPtr = True - if isinstance(slice_node, ast.Attribute): - SliceVtInfo = self.GetCTypeInfo(slice_node) - if SliceVtInfo and SliceVtInfo.IsArrayPtr: - ResultInfo.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr.IsArrayPtr = True - ResultInfo.ArrayPtr.PtrCount = SliceVtInfo.PtrCount - if SliceVtInfo.ArrayPtr: - ResultInfo.ArrayPtr.ArrayPtr = SliceVtInfo.ArrayPtr - elif SliceVtInfo and SliceVtInfo.PtrCount > 0: - ResultInfo.ArrayPtr = SliceVtInfo - else: - ResultInfo.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr.IsArrayPtr = True - elif isinstance(slice_node, ast.Subscript): - ResultInfo.ArrayPtr = CollectAll(slice_node) - elif isinstance(slice_node, ast.Name): - ResultInfo.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr.BaseType = CTypeInfo.CreateFromTypeName(slice_node.id).BaseType - elif isinstance(slice_node, ast.BinOp): - ResultInfo.ArrayPtr = CTypeInfo() - try: - ResultInfo.ArrayPtr.ArrayDims.append(ast.unparse(slice_node)) - except Exception: # 回退:unparse 失败时用 'N' - ResultInfo.ArrayPtr.ArrayDims.append('N') - elif isinstance(slice_node, ast.Constant): - ResultInfo.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr.ArrayDims.append(str(slice_node.value)) - else: - ResultInfo.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr.IsArrayPtr = True - elif VtInfo and VtInfo.PtrCount == 1 and not VtInfo.ArrayPtr: - ResultInfo = CTypeInfo() - ResultInfo.BaseType = t.CVoid - ResultInfo.PtrCount = 1 - if isinstance(slice_node, ast.Subscript): - inner_info = CollectAll(slice_node) - if inner_info and inner_info.PtrCount > 0: - ResultInfo.PtrCount += inner_info.PtrCount - if inner_info.BaseType and not isinstance(inner_info.BaseType, t.CVoid): - ResultInfo.BaseType = inner_info.BaseType - elif inner_info and inner_info.ArrayPtr: - ResultInfo.ArrayPtr = inner_info - elif inner_info and inner_info.BaseType and not isinstance(inner_info.BaseType, t.CVoid): - ResultInfo.BaseType = inner_info.BaseType - elif isinstance(slice_node, ast.Attribute): - SliceVtInfo = self.GetCTypeInfo(slice_node) - if SliceVtInfo and SliceVtInfo.PtrCount == 1 and not SliceVtInfo.ArrayPtr: - ResultInfo.PtrCount = 2 - if SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): - ResultInfo.BaseType = SliceVtInfo.BaseType - elif SliceVtInfo and SliceVtInfo.IsArrayPtr: - inner = CTypeInfo() - inner.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr = inner - elif SliceVtInfo and SliceVtInfo.ArrayPtr: - ResultInfo.ArrayPtr = SliceVtInfo - elif SliceVtInfo and SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): - ResultInfo.BaseType = SliceVtInfo.BaseType - elif isinstance(slice_node, ast.Name): - SliceType = getattr(t, slice_node.id, None) - if SliceType == t.CPtr: - ResultInfo.PtrCount += 1 - elif SliceType == t.CArrayPtr: - inner = CTypeInfo() - inner.ArrayPtr = CTypeInfo() - ResultInfo.ArrayPtr = inner - else: - ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(slice_node.id).BaseType - elif isinstance(slice_node, ast.BinOp): - merged = self.MergeTypes(slice_node) - if merged: - ResultInfo.PtrCount += merged.PtrCount - if merged.BaseType and not isinstance(merged.BaseType, t.CVoid): - ResultInfo.BaseType = merged.BaseType - if merged.ArrayPtr: - ResultInfo.ArrayPtr = merged.ArrayPtr - elif isinstance(slice_node, ast.Constant): - ResultInfo.ArrayDims.append(str(slice_node.value)) - elif VtInfo: - ResultInfo = CTypeInfo() - ResultInfo.BaseType = VtInfo.BaseType - if isinstance(slice_node, ast.Constant): - ResultInfo.ArrayDims.append(str(slice_node.value)) - elif isinstance(slice_node, ast.Name): - ResultInfo.ArrayDims.append(slice_node.id) - elif isinstance(slice_node, ast.BinOp): - try: - ResultInfo.ArrayDims.append(ast.unparse(slice_node)) - except Exception: # 回退:unparse 失败时用 'N' - ResultInfo.ArrayDims.append('N') - elif isinstance(slice_node, ast.Subscript): - ResultInfo.ArrayPtr = CollectAll(slice_node) - else: - ResultInfo = CTypeInfo() - if isinstance(slice_node, ast.Constant): - ResultInfo.ArrayDims.append(str(slice_node.value)) - elif isinstance(slice_node, ast.Name): - SliceType = getattr(t, slice_node.id, None) - if SliceType == t.CPtr: - ResultInfo.PtrCount += 1 - elif SliceType == t.CArrayPtr: - ResultInfo.ArrayPtr = CTypeInfo() - else: - ResultInfo.ArrayDims.append(slice_node.id) - elif isinstance(value_node, ast.Subscript): - ResultInfo = CollectAll(value_node) - if isinstance(slice_node, ast.Attribute): - SliceVtInfo = self.GetCTypeInfo(slice_node) - if SliceVtInfo and SliceVtInfo.PtrCount >= 1 and not SliceVtInfo.ArrayPtr: - ResultInfo.PtrCount += SliceVtInfo.PtrCount - if SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): - ResultInfo.BaseType = SliceVtInfo.BaseType - elif SliceVtInfo and SliceVtInfo.IsArrayPtr: - NewInfo = CTypeInfo() - NewInfo.ArrayPtr = ResultInfo - ResultInfo = NewInfo - elif SliceVtInfo and SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): - ResultInfo.BaseType = SliceVtInfo.BaseType - elif isinstance(slice_node, ast.Subscript): - InnerNode = CollectAll(slice_node) - if ResultInfo.ArrayPtr is None: - ResultInfo.ArrayPtr = InnerNode - else: - current = ResultInfo - while current.ArrayPtr is not None: - current = current.ArrayPtr - current.ArrayPtr = InnerNode - elif isinstance(slice_node, ast.Constant): - ResultInfo.ArrayDims.append(str(slice_node.value)) - elif isinstance(slice_node, ast.Name): - SliceType = getattr(t, slice_node.id, None) - if SliceType == t.CPtr: - ResultInfo.PtrCount += 1 - elif SliceType == t.CArrayPtr: - ResultInfo.ArrayPtr = CTypeInfo() - else: - ResultInfo.ArrayDims.append(slice_node.id) - elif isinstance(slice_node, ast.BinOp): - try: - ResultInfo.ArrayDims.append(ast.unparse(slice_node)) - except Exception: # 回退:unparse 失败时用 'N' - ResultInfo.ArrayDims.append('N') - elif isinstance(value_node, ast.Name): - ResultInfo = CTypeInfo() - if IsListAnnotation(node): - if not isinstance(slice_node, ast.Tuple): - ElementType = self._GetCTypeInfoInternal(slice_node) - if ElementType and isinstance(ElementType, CTypeInfo): - ResultInfo.BaseType = ElementType.BaseType if ElementType.BaseType else t.CVoid() - ResultInfo.PtrCount = ElementType.PtrCount + 1 - ResultInfo.IsFuncPtr = ElementType.IsFuncPtr - ResultInfo.FuncPtrReturn = ElementType.FuncPtrReturn - ResultInfo.FuncPtrParams = ElementType.FuncPtrParams - else: - ResultInfo.BaseType = t.CVoid() - ResultInfo.PtrCount = 1 - elif isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1: - ElementType = self._GetCTypeInfoInternal(slice_node.elts[0]) - if ElementType and isinstance(ElementType, CTypeInfo): - ResultInfo.BaseType = ElementType.BaseType if ElementType.BaseType else t.CVoid() - ResultInfo.PtrCount = ElementType.PtrCount - ResultInfo.IsFuncPtr = ElementType.IsFuncPtr - ResultInfo.FuncPtrReturn = ElementType.FuncPtrReturn - ResultInfo.FuncPtrParams = ElementType.FuncPtrParams - for elt in slice_node.elts[1:]: - if isinstance(elt, ast.Constant): - ResultInfo.ArrayDims.append(str(elt.value)) - elif isinstance(elt, ast.Name): - ResultInfo.ArrayDims.append(elt.id) - else: - try: - ResultInfo.ArrayDims.append(ast.unparse(elt)) - except Exception: # 回退:unparse 失败时用 'N' - ResultInfo.ArrayDims.append('N') - else: - ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(value_node.id).BaseType - else: - # 检查是否为泛型类特化(如 list[str]) - _is_generic_template: bool = ( - hasattr(self.Trans, 'ClassHandler') - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and value_node.id in self.Trans.ClassHandler._generic_class_templates - ) - if _is_generic_template: - # 走和 _ResolveGenericSlice 同样的规范化路径(_resolve_generic_slice_type), - # 确保字段类型名与特化名完全一致: - # - ast.Name: 用原始名(如 'Param' → 'Param',不加 SHA1 前缀), - # 与 _resolve_generic_slice_type 的 ast.Name 分支一致 - # - BinOp/Attribute: 走 _resolve_type_arg_str(加 SHA1 前缀 / 'ptr' 标记) - # 避免字段类型用 'GSList[sha1.Param]' 而特化名用 'GSList[Param]' 导致 - # find_struct_by_pointee 匹配到 opaque 副本,方法分派失败 - type_arg_str: str - if isinstance(slice_node, ast.Tuple): - arg_parts: list[str] = [] - for elt in slice_node.elts: - slice_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(elt) - arg_parts.append(slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(elt)) - # 必须用 '][' 分隔以匹配 _mangle_generic_class_name 的格式 - # (class_name + '[' + ']['.join(mangled_args) + ']') - type_arg_str = ']['.join(arg_parts) - else: - slice_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) - type_arg_str = slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(slice_node) - full_spec_name: str = f'{value_node.id}[{type_arg_str}]' - ResultInfo.BaseType = t.CStruct(name=full_spec_name) - ResultInfo.Name = full_spec_name - ResultInfo.IsStruct = True - ResultInfo.PtrCount = 1 - # 触发泛型特化发射:确保特化的方法被发射 + struct sha1 注册 - # 与局部变量 AnnAssign 路径一致;否则函数参数注解路径 - # 只构建 mangled 名,方法调用分派时 class_sha1=None 导致 - # "Undefined method" 错误(如 lst.__len__() 在 list[T] | t.CPtr 参数上) - # _specialize_generic_class 内部有 spec_key 去重,重复调用安全 - if hasattr(self.Trans, 'ExprCallHandle') and self.Trans.ExprCallHandle is not None: - self.Trans.ExprCallHandle._ResolveGenericSlice(node) - else: - # [修复-阻塞B] rawptr 包裹泛型特化(rawptr[GSList[Param]]): - # slice 是 Subscript 且内层 base 命中泛型模板时,若委托 TAR, - # 内层 GSList[Param] 会被折叠成 i8* → declare_only 特化从不触发、 - # 变量/字段容器身份丢失 → 方法调用 FakeDuck 兜底错绑兄弟类 - # (LinkedNode.append 48B 语义写穿 24B GSList)。此处按裸泛型 - # 特化(GSList[Param])同语义解析:PtrCount=1(rawptr 与裸名 - # 同为 1 层指针,对齐 element_class 侧 rawptr[list[X]]==list[X] - # 约定),并触发 declare_only 特化(struct + sha1 注册;方法体 - # 由方法调用时 _try_complete_generic_specialization 补发)。 - _inner_gsub: ast.Subscript | None = None - if (isinstance(slice_node, ast.Subscript) - and isinstance(slice_node.value, ast.Name) - and hasattr(self.Trans, 'ClassHandler') - and hasattr(self.Trans.ClassHandler, '_generic_class_templates') - and slice_node.value.id in self.Trans.ClassHandler._generic_class_templates - and getattr(self.Trans, 'ExprCallHandle', None) is not None): - _inner_gsub = slice_node - if _inner_gsub is not None: - _ig_slice: ast.AST = _inner_gsub.slice - _ig_slices: list[ast.AST] = list(_ig_slice.elts) if isinstance(_ig_slice, ast.Tuple) else [_ig_slice] - _ig_args: list[str] = [] - for _ig_elt in _ig_slices: - _ig_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(_ig_elt) - _ig_args.append(_ig_res[0] if _ig_res else self.Trans.ExprCallHandle._resolve_type_arg_str(_ig_elt)) - _ig_spec: str = f'{_inner_gsub.value.id}[' + ']['.join(_ig_args) + ']' - ResultInfo.BaseType = t.CStruct(name=_ig_spec) - ResultInfo.Name = _ig_spec - ResultInfo.IsStruct = True - ResultInfo.PtrCount = 1 - self.Trans.ExprCallHandle._ResolveGenericSlice(_inner_gsub) - return ResultInfo - # [builtins 最后匹配] base 为 builtins 指针 typedef - # (rawptr → t.CPtr):rawptr[X] 等价 X*(对齐 TPV - # Subscript 语义),委托 TypeAnnotationResolver 统一解析, - # 避免被误当作 'rawptr[X]' 结构体/数组维度处理。 - try: - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR - _tar_sub: CTypeInfo = _TAR.from_node(node, self.Trans.SymbolTable) - if (_tar_sub is not None and _tar_sub.PtrCount > 0 - and _tar_sub.BaseType is not None): - return _tar_sub - except Exception: - pass - ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(value_node.id).BaseType - if isinstance(slice_node, ast.Constant): - ResultInfo.ArrayDims.append(str(slice_node.value)) - elif isinstance(slice_node, ast.Name): - SliceType = getattr(t, slice_node.id, None) - if SliceType == t.CPtr: - ResultInfo.PtrCount += 1 - elif SliceType == t.CArrayPtr: - ResultInfo.ArrayPtr = CTypeInfo() - else: - ResultInfo.ArrayDims.append(slice_node.id) - elif isinstance(slice_node, ast.BinOp): - try: - ResultInfo.ArrayDims.append(ast.unparse(slice_node)) - except Exception: # 回退:unparse 失败时用 'N' - ResultInfo.ArrayDims.append('N') - elif isinstance(slice_node, ast.Subscript): - InnerNode = CollectAll(slice_node) - ResultInfo.ArrayDims.extend(InnerNode.ArrayDims) - else: - ResultInfo = CTypeInfo() - - return ResultInfo - elif isinstance(node, ast.Attribute): - VtInfo = self.GetCTypeInfo(node) - result = CTypeInfo() - if VtInfo and VtInfo.IsArrayPtr: - result.ArrayPtr = CTypeInfo() - return result - elif VtInfo and VtInfo.PtrCount == 1 and not VtInfo.ArrayPtr: - result.BaseType = t.CVoid - result.PtrCount = 1 - return result - elif VtInfo and VtInfo.BaseType: - result.BaseType = VtInfo.BaseType - return result - else: - return CTypeInfo() - elif isinstance(node, ast.Name): - result = CTypeInfo() - result.BaseType = CTypeInfo.CreateFromTypeName(node.id).BaseType - return result - else: - return CTypeInfo() - - return CollectAll(Node) - - def _CollectAllOperandTypes(self, Node: ast.AST) -> List: - """递归收集所有操作数的类型(用于一次性合并)""" - Types = [] - - # 指针下标(rawptr[T] / t.CPtr[T])先折叠成等价的 `T | t.CPtr`:使 - # `rawptr[T] | t.CPtr` 与 `T | t.CPtr | t.CPtr` 拉平成同一组操作数 - # (否则折叠结果作为单个 Subscript 操作数参与合并,指针层数少算一层); - # t.CPtr[T] 与 rawptr[T] 同一条规则(见 SymbolUtils.IsPtrSubscriptNode)。 - if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): - Node = DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable) - - if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): - Types.extend(self._CollectAllOperandTypes(Node.left)) - Types.extend(self._CollectAllOperandTypes(Node.right)) - else: - Types.append(Node) - - return Types - - def _GetTypeFromNode(self, Node: ast.AST) -> CTypeInfo: - """浠?AST 鑺傜偣鑾峰彇绫诲瀷""" - if isinstance(Node, ast.Call): - if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': - return self.GetCTypeInfo(Node) - elif isinstance(Node.func, ast.Attribute) and CTypeHelper.GetTModuleCType(Node.func.attr) == t._CTypedef: - return self.GetCTypeInfo(Node) - elif isinstance(Node.func, ast.Attribute) and Node.func.attr == 'Bit': - if Node.args and isinstance(Node.args[0], ast.Constant): - Result = CTypeInfo() - Result.BaseType = t.CInt - Result.IsBitField = True - Result.BitWidth = Node.args[0].value - return Result - return self._MakeVoidCTypeInfo() - elif isinstance(Node, ast.Subscript): - if (isinstance(Node.value, ast.Attribute) - and isinstance(Node.value.value, ast.Name) - and IsTModule(Node.value.value.id, self.Trans.SymbolTable) - and Node.value.attr == 'Bit'): - if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): - Result = CTypeInfo() - Result.BaseType = t.CInt - Result.IsBitField = True - Result.BitWidth = Node.slice.value - return Result - return self.GetCTypeInfo(Node) - else: - return self.GetCTypeInfo(Node) - - def MergeTypes(self, Node: ast.BinOp): - """处理类型组合,如 t.CInt | t.CPtr ?t.CStatic | t.CInt - - 这是 | 运算符的类型合并处理函数 - 收集所有操作数,一次性合? - """ - if not isinstance(Node, ast.BinOp) or not isinstance(Node.op, ast.BitOr): - return None - - AllOperands = self._CollectAllOperandTypes(Node) - - AllTypes = [] - for Operand in AllOperands: - TypeItem = self._GetTypeFromNode(Operand) - AllTypes.append(TypeItem) - - if isinstance(Node.left, ast.Attribute): - LeftParts = [] - current = Node.left - while isinstance(current, ast.Attribute): - attr_type = getattr(t, current.attr, None) - if attr_type and isinstance(attr_type, type) and issubclass(attr_type, t.CType): - LeftParts.insert(0, attr_type) - current = current.value - if isinstance(current, ast.Name): - name_type = getattr(t, current.id, None) - if name_type and isinstance(name_type, type) and issubclass(name_type, t.CType): - LeftParts.insert(0, name_type) - if LeftParts and LeftParts[-1] == t._CTypedef: - for type_item in AllTypes: - if isinstance(type_item, CTypeInfo) and type_item.IsFuncPtr: - type_item.IsTypedef = True - type_item.OriginalType = CTypeInfo() - type_item.OriginalType.IsFuncPtr = True - type_item.OriginalType.FuncPtrReturn = type_item.FuncPtrReturn or CTypeInfo.VoidTypeInfo() - type_item.OriginalType.FuncPtrParams = list(type_item.FuncPtrParams) if type_item.FuncPtrParams else [] - return type_item - Result = CTypeInfo() - if isinstance(AllTypes[-1], CTypeInfo): - Result.BaseType = AllTypes[-1].BaseType - else: - Result.BaseType = t.CInt - Result.IsTypedef = True - return Result - - return self._MergeAllComponentTypes(*AllTypes) - - def _MergeAllComponentTypes(self, *Types: CTypeInfo) -> CTypeInfo: - """合并多个类型 - 直接使用 CTypeInfo 类""" - if not Types: - return CTypeInfo() - - Result = CTypeInfo() - # 统计 t.CPtr(CVoid + PtrCount=1)的个数,用于正确累加指针层数 - # 因为 (AST | t.CPtr) | t.CPtr 展开后会包含多个 t.CPtr,每个都需要累加 - cptr_count: int = 0 - - for TypeItem in Types: - if TypeItem is None: - continue - if TypeItem.IsDefine: - # t.CDefine 标记在 | 合并中传播(对齐 IsState): - # `int | t.CDefine`(define 宏函数返回注解)合并后仍保持 - # IsDefine=True,下游才能识别为编译期宏(alwaysinline/#define) - Result.IsDefine = True - if TypeItem.IsState: - Result.IsState = True - if not Result.Storage: - Result.Storage = t.CExport() - continue - if TypeItem.IsBitField: - Result.BaseType = t.CInt - Result.IsBitField = True - Result.BitWidth = TypeItem.BitWidth - return Result - if TypeItem.IsFuncPtr: - Result.BaseType = t.CVoid - Result.IsFuncPtr = True - Result.FuncPtrParams = TypeItem.FuncPtrParams - Result.FuncPtrReturn = TypeItem.FuncPtrReturn - return Result - if TypeItem.IsTypedef: - if TypeItem.BaseType and (not isinstance(TypeItem.BaseType, (t._CTypedef,)) or TypeItem.PtrCount > 0): - if Result.BaseType is None or isinstance(Result.BaseType, (t.CVoid, t._CTypedef)): - Result.BaseType = TypeItem.BaseType - Result.Name = TypeItem.Name - Result.IsTypedef = True - Result.PtrCount = max(Result.PtrCount, TypeItem.PtrCount) - if TypeItem.OriginalType: - Result.OriginalType = TypeItem.OriginalType - else: - Result.IsTypedef = True - Result.Name = TypeItem.Name - if TypeItem.OriginalType: - Result.OriginalType = TypeItem.OriginalType - continue - # 统计 t.CPtr(CVoid + PtrCount>0) - # 注意: t.CPtr[t.CPtr] 的 PtrCount=2(双重指针),需要完整累加 - if TypeItem.PtrCount > 0 and TypeItem.BaseType and (TypeItem.BaseType is t.CVoid or isinstance(TypeItem.BaseType, t.CVoid)): - cptr_count += TypeItem.PtrCount - # BaseType 保持不变,只计数,后续统一累加 - if TypeItem.IsRenum and TypeItem.Name: - Result.IsRenum = True - Result.IsEnum = True - Result.IsStruct = True - if not Result.Name: - Result.Name = TypeItem.Name - continue - self._MergeCTypeInfoInto(Result, TypeItem) - - # 对所有 t.CPtr 统一累加指针层数 - # 仅对 struct 类型吸收第一个 CPtr(struct 变量默认是指针,AST | t.CPtr = AST*)。 - # 对于本身已是指针的类型(如 bytes=CChar*),bytes | t.CPtr = i8**(不吸收)。 - base_ptr_count: int = Result.PtrCount - Result.PtrCount += cptr_count - if Result.IsStruct and base_ptr_count > 0 and cptr_count > 0: - Result.PtrCount -= 1 - - if Result.BaseType is None: - Result.BaseType = t.CVoid - - return Result - - def _MergeCTypeInfoInto(self, Target: CTypeInfo, Source: CTypeInfo): - if Source.PtrCount > 0 and Source.BaseType and (Source.BaseType is t.CVoid or isinstance(Source.BaseType, t.CVoid)): - # t.CPtr 的处理已提升到 _MergeAllComponentTypes 中统一计数累加, - # 此处保留仅作为兜底(从其他路径直接调用 _MergeCTypeInfoInto 时) - if Target.PtrCount < Source.PtrCount: - Target.PtrCount = Source.PtrCount - elif Source.PtrCount > 0 and Source.BaseType and Source.BaseType is not t.CVoid and not isinstance(Source.BaseType, t.CVoid): - if Target.BaseType is None or Target.BaseType is t.CVoid or isinstance(Target.BaseType, t.CVoid): - Target.BaseType = Source.BaseType - # 当 Target 已有 PtrCount(来自之前的 t.CPtr),累加而非替换 - Target.PtrCount = Source.PtrCount + Target.PtrCount - elif Target.PtrCount == 0: - Target.PtrCount = Source.PtrCount - elif Source.PtrCount > Target.PtrCount: - Target.PtrCount = Source.PtrCount - elif Source.PtrCount > Target.PtrCount: - Target.PtrCount = Source.PtrCount - if Source.BaseType and Source.BaseType is not t.CVoid and not isinstance(Source.BaseType, (t.CVoid, t._CTypedef)): - if Target.BaseType is None: - Target.BaseType = Source.BaseType - elif (Target.BaseType is t.CVoid or isinstance(Target.BaseType, t.CVoid)) and Target.PtrCount == 0: - Target.BaseType = Source.BaseType - elif (Source.BaseType is t.CVoid or isinstance(Source.BaseType, t.CVoid)) and Target.BaseType is None: - Target.BaseType = Source.BaseType - # Struct 名称保留:BinOp `list[AST | t.CPtr] | t.CPtr` 合并时, - # _HandleSubscript 已在 Source.Name 设置了特化名(如 'list[5dab8cb390496d22.ASTptr]'), - # 合并后 Target.Name 必须保留此名称,否则 class_member_element_class - # 无法获取正确的特化类名 → 方法分派 fallback 到错误的类 - if Source.IsStruct and Source.Name and not Target.Name: - Target.Name = Source.Name - # 【IsStruct 必须随 Name 一起传播】`_MergeAllComponentTypes` 的 - # "struct 吸收第一个 t.CPtr"规则(见该函数 :1022-1027 的注释: - # struct 变量默认是指针 ⇒ `AST | t.CPtr = AST*`)以 `Result.IsStruct` 为**闸门**: - # 此前本函数只复制 Name、从不置 IsStruct ⇒ 闸门恒假 ⇒ 闸门后的 - # `Result.PtrCount -= 1` 永不执行 ⇒ 「类 + CPtr」的联合(如 - # `list[str] | t.CPtr`)合并后指针层数**多算一层**(得 `list[str]**`,应为 - # `list[str]*`)⇒ 调用点/字段拿到的值类型 pointee 是指针而不是结构体 ⇒ - # 类名在下游被当作通用指针处理(现场:`parts = s1.split(',')` 的返回值 - # pointee 非结构体 ⇒ HandlesAssign 的类名推断整块跳过 ⇒ 随后 `parts[0]` - # 退化成按字节索引并得到整数)。 - # 语义上这两个标志同源(合并结果就是那个 struct),故成对传播。 - if Source.IsStruct: - Target.IsStruct = True - # REnum 标志和名称保留(BinOp `LLVMType | t.CPtr` 合并时不能丢失 REnum 语义) - # 同时传播 IsStruct=True,让 CPtr 吸收逻辑生效(REnum 在内存布局上是 struct) - if Source.IsRenum and Source.Name: - Target.IsRenum = True - Target.IsEnum = True - Target.IsStruct = True - if not Target.Name: - Target.Name = Source.Name - if Source.IsTypedef and Source.Name: - if Source.BaseType and (not isinstance(Source.BaseType, (t._CTypedef,)) or Source.PtrCount > 0): - if Target.BaseType is None or isinstance(Target.BaseType, (t.CVoid, t._CTypedef)): - Target.BaseType = Source.BaseType - Target.IsTypedef = True - Target.Name = Source.Name - if Source.OriginalType: - Target.OriginalType = Source.OriginalType - elif Target.BaseType is None or isinstance(Target.BaseType, (t.CVoid, t._CTypedef)): - if Source.OriginalType: - if isinstance(Source.OriginalType, CTypeInfo) and Source.OriginalType.BaseType: - Target.BaseType = Source.OriginalType.BaseType - Target.PtrCount = max(Target.PtrCount, Source.OriginalType.PtrCount) - Target.IsTypedef = True - Target.Name = Source.Name - Target.OriginalType = Source.OriginalType - elif isinstance(Source.OriginalType, CTypeInfo): - Target.IsTypedef = True - Target.Name = Source.Name - Target.OriginalType = Source.OriginalType - else: - Target.BaseType = t.CStruct(name=Source.Name) - Target.IsStruct = True - else: - Target.BaseType = t.CStruct(name=Source.Name) - Target.IsStruct = True - if Source.ArrayPtr: - if Target.ArrayPtr is None: - Target.ArrayPtr = Source.ArrayPtr - else: - if Source.PtrCount > Target.PtrCount: - Target.PtrCount = Source.PtrCount - Target.ArrayPtr = Source.ArrayPtr - if Source.PtrCount > 0: - Target.ArrayDims = [] - elif Source.ArrayDims: - Target.ArrayDims.extend(Source.ArrayDims) - if Source.VarConst: - Target.VarConst = True - if Source.VarVolatile: - Target.VarVolatile = True - if Source.DataConst: - Target.DataConst = True - if Source.DataVolatile: - Target.DataVolatile = True - if Source.IsFuncPtr: - Target.IsFuncPtr = True - Target.FuncPtrParams = Source.FuncPtrParams - Target.FuncPtrReturn = Source.FuncPtrReturn - if Source.Storage and not Target.Storage: - Target.Storage = Source.Storage - if Source.ByteOrder and not Target.ByteOrder: - Target.ByteOrder = Source.ByteOrder + DesugarPtrSubscriptToUnion) +from lib.includes import t +import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib/includes) +import vast as ast + + +class HandlesTypeMerge(BaseHandle): + """处理类型合并逻辑 - 所有类型分析的核心模块""" + + def _MakeVoidCTypeInfo(self) -> CTypeInfo: + Info = CTypeInfo() + Info.BaseType = t.CVoid + return Info + + @staticmethod + def _MakeCTypeInfoFromName(TypeName: str) -> CTypeInfo: + """直接从类型名构造 CTypeInfo,不经过 FromStr/字符串解析""" + return CTypeInfo.FromTypeName(TypeName) + + def GetCTypeInfo(self, Node: ast.AST) -> CTypeInfo: + """获取 CTypeInfo 对象""" + Result = self._GetCTypeInfoInternal(Node) + if isinstance(Result, CTypeInfo): + return Result + elif isinstance(Result, str): + return self._MakeCTypeInfoFromName(Result) + else: + return self._MakeVoidCTypeInfo() + + def _GetCTypeInfoInternal(self, Node: ast.AST) -> CTypeInfo | str | None: + """鍐呴儴鏂规硶锛岃繑鍥?CTypeInfo""" + LineNum = getattr(Node, 'lineno', None) + + if getattr(self.Trans, 'DebugFile', None): + # 仅 debug 模式构造参数(ast.dump 昂贵,非 debug 时无条件执行是隐藏热点) + self.Trans.DebugPrint(f"[GetCTypeInfo] Node type: {type(Node).__name__}, dump: {ast.dump(Node)[:60]}...") + + if isinstance(Node, ast.Constant): + TypeName = Node.value + return self._MakeCTypeInfoFromName(TypeName) + + if isinstance(Node, ast.Name): + TypeName = Node.id + t_c_imported = getattr(self.Trans, '_t_c_imported_names', {}) + if TypeName in t_c_imported: + src_module, src_name = t_c_imported[TypeName] + virtual_attr = ast.Attribute( + value=ast.Name(id=src_module, ctx=ast.Load()), + attr=src_name, + ctx=ast.Load() + ) + ast.copy_location(virtual_attr, Node) + return self.GetCTypeInfo(virtual_attr) + Entry = self.Trans.SymbolTable.lookup(TypeName) + if Entry and Entry.IsTypedef: + if isinstance(Entry, CTypeInfo) and Entry.BaseType: + if not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0: + Result = Entry.Copy() + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry.OriginalType: + if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, CTypeInfo): + Resolved = Entry.OriginalType.Copy() + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + elif isinstance(Entry.OriginalType, str): + # 字符串 OriginalType(如 'void *')需解析为 CTypeInfo, + # 否则返回的 Entry BaseType=None 会被下游误判为 i32 + Resolved = CTypeInfo.FromTypeName(Entry.OriginalType) + if Resolved and Resolved.BaseType: + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + return Entry + # REnum 优先于 IsEnum 处理(REnum 同时有 IsEnum=True 和 IsStruct=True) + # 必须设置 Name 和 PtrCount=1,否则 BinOp `LLVMType | t.CPtr` 合并后丢失 REnum 语义 + if isinstance(Entry, CTypeInfo) and Entry.IsRenum: + Result = Entry.Copy() + Result.Name = TypeName + Result.PtrCount = 1 # REnum 变量默认为指针(和 struct 一致) + return Result + # 类名与枚举成员名冲突时(如 Constant 既是 ASTKind.Constant 枚举成员又是 + # 结构体类名),类型注解应解析为结构体而非枚举值。判据是"该名**有类定义**", + # 两个来源都要查(与下方 Attribute 分支 L241-269 的判据**同一条规则源**, + # 也与 TypeConvert._type_str_to_llvm_base 的 `type_str in self.structs or + # type_str in self._struct_sha1_map` 守卫同口径): + # ① Gen.structs:本模块已创建的结构体(原判据); + # ② Gen._struct_sha1_map:预扫描登记了"类名 → 模块 sha1"、但本模块尚未 + # 建出结构体的类(跨模块 import 的类)。 + # 只查①漏②时的现场:`rawptr[Constant]`(includes/ast/exprs.vp:59 的 + # accessor,`class Constant` 声明在同文件更后面)在导入侧解析时该名先命中 + # 平坦符号表里的枚举成员 `Constant: t.State`(ast/base.vp)→ 走下方 + # `Entry.IsEnum` 分支返回枚举 → 与 t.CPtr 合并后经 _ctype_to_llvm 的 + # IsEnum 分支(枚举名未登记 ⇒ 宽度退化为 i32)得 **i32\***,与调用点实参 + # %".Constant"* 分裂 → FuncGen._adjust_args fail-fast。 + # 查②后 BaseType=CStruct('Constant'),_get_or_create_struct 会按 + # `_struct_sha1_map` 拼出 `".Constant"`(BaseGen.py:527-528), + # 与声明端/实参同一结构体对象。 + # 但必须排除**枚举类型本身**(如 `class ASTKind(t.CEnum)` 的注解 `x: ASTKind`): + # _struct_sha1_map 是按 .vpi 里全部 ClassDef 建的,枚举类也在其中;若一并 + # 当结构体,枚举注解会被建成 opaque struct,与 i32 错位 + # (现场:'IntType' object has no attribute 'is_opaque')。 + # 判据必须按 **IsEnumMember** 区分(现场实测):枚举**成员**条目的标志组合是 + # IsEnum=True + IsEnumMember=True + BaseType=CEnum + # (即成员的 IsEnum 也被置真),只判 IsEnum 会把成员误当枚举类型 ⇒ 落 i32*; + # 而枚举**类型**条目是 IsEnum=True + IsEnumMember=False。 + # 故:枚举类型 = IsEnum and not IsEnumMember ⇒ 走下方 IsEnum 分支; + # 成员(IsEnumMember)⇒ 不是类型 ⇒ 让位给类定义。 + _is_enum_type: bool = ( + isinstance(Entry, CTypeInfo) and Entry.IsEnum + and not getattr(Entry, 'IsEnumMember', False)) + _Gen = getattr(self.Trans, 'LlvmGen', None) + if _Gen and not _is_enum_type and ( + TypeName in _Gen.structs + or TypeName in getattr(_Gen, '_struct_sha1_map', {})): + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + Result.Name = TypeName + Result.PtrCount = 1 + return Result + if isinstance(Entry, CTypeInfo) and Entry.IsEnum: + return Entry.Copy() + if isinstance(Entry, dict) and Entry.get('IsEnum'): + Result = CTypeInfo() + Result.BaseType = t.CEnum(TypeName) + Result.IsEnum = True + return Result + # 检测结构体类型 (CTypeInfo with IsStruct=True, 来自 _InsertClassSymbol) + if isinstance(Entry, CTypeInfo) and Entry.IsStruct: + Result = Entry.Copy() + if Result.BaseType is None: + Result.BaseType = t.CStruct(name=TypeName) + Result.Name = TypeName + Result.PtrCount = 1 # struct 变量默认为指针 + return Result + # [builtins 最后匹配] 常规路径全部 miss 后查 includes/builtins.vp 的 + # CTypedef(size_t → t.CSizeT、rawptr → t.CPtr 内置等),对齐 + # TypeAnnotationResolver._from_node_name 兜底,避免裸名被误判为 + # 命名结构体(main2.vp `a: size_t` → alloca %...size_t 根因)。 + try: + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR + builtins_info: CTypeInfo | None = _TAR._lookup_builtins_typedef(TypeName, self.Trans.SymbolTable) + if builtins_info is not None: + return builtins_info + except Exception: + pass + return self._MakeCTypeInfoFromName(TypeName) + + elif isinstance(Node, ast.Attribute): + ModuleParts = [] + Current = Node + while isinstance(Current, ast.Attribute): + ModuleParts.insert(0, Current.attr) + Current = Current.value + + if isinstance(Current, ast.Name): + ModuleParts.insert(0, Current.id) + + if len(ModuleParts) >= 2: + TypeName = ModuleParts[-1] + ModulePath = '.'.join(ModuleParts[:-1]) + elif len(ModuleParts) == 1: + TypeName = ModuleParts[0] + ModulePath = None + else: + return self._MakeVoidCTypeInfo() + + # 解析 import 别名: 如 import fat32_types as types -> types -> fat32_types + if ModulePath: + import_aliases = self.Trans.SymbolTable.import_aliases + if ModulePath in import_aliases: + ModulePath = import_aliases[ModulePath] + else: + # 也尝试在符号表中查找模块别名 + entry = self.Trans.SymbolTable.lookup(ModulePath) + if isinstance(entry, CTypeInfo) and entry.IsModuleAlias: + resolved = entry.ResolvedModule + if resolved: + ModulePath = resolved + + if getattr(self.Trans, 'DebugFile', None): + self.Trans.DebugPrint(f"[GetCTypeInfo-Attribute] 查询: '{TypeName}' ModulePath: {ModulePath}") + + if ModulePath == 't' and TypeName == 'State': + Info = CTypeInfo() + Info.BaseType = t.CVoid + Info.IsState = True + return Info + + if ModulePath == 't' or (ModulePath and ModulePath.startswith('t.')): + # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`,非真实类):与 + # t.CPtr 同形的「一层指针」,使 `T | t.CRef` 合并为 T*(pointer 层 +1)。 + if TypeName in ('CRef', 'CTempRef'): + Info = CTypeInfo() + Info.PtrCount = 1 + Info.BaseType = t.CVoid + return Info + if self._IsTModuleType(TypeName): + TypeClass = CTypeHelper.GetTModuleCType(TypeName) or getattr(t, TypeName, None) + if TypeClass == t.CArrayPtr: + Info = CTypeInfo() + Info.IsArrayPtr = True + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CPtr: + Info = CTypeInfo() + Info.PtrCount = 1 + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CConst: + Info = CTypeInfo() + Info.DataConst = True + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CVolatile: + Info = CTypeInfo() + Info.DataVolatile = True + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CInline: + Info = CTypeInfo() + Info.Storage = t.CInline() + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CStatic: + Info = CTypeInfo() + Info.Storage = t.CStatic() + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CExtern: + Info = CTypeInfo() + Info.Storage = t.CExtern() + Info.BaseType = t.CVoid + return Info + elif TypeClass == t.CExport: + Info = CTypeInfo() + Info.Storage = t.CExport() + Info.BaseType = t.CVoid + return Info + elif TypeClass in (t.BigEndian, t.LittleEndian): + Info = CTypeInfo() + Info.BaseType = t.CInt() + Info.ByteOrder = 'big' if TypeClass == t.BigEndian else 'little' + return Info + elif TypeClass: + Info = CTypeInfo() + Info.BaseType = TypeClass + # 治本修复:t.CDefine 注解必须设置 IsDefine=True, + # 否则预扫描 (LlvmGenerator.py) 和主处理 (HandlesAnnAssign.py) + # 都无法识别 CDefine 常量,导致 FOREGROUND_GREEN 等解析为 0 + if isinstance(TypeClass, type) and issubclass(TypeClass, t.CDefine): + Info.IsDefine = True + return Info + elif CTypeHelper.GetCName(TypeName): + return self._MakeCTypeInfoFromName(CTypeHelper.GetCName(TypeName)) + + FullName = f"{ModulePath}.{TypeName}" if ModulePath else TypeName + + # 优先检查 Gen.structs:类名与枚举成员名冲突时(如 Import 既是 ASTKind.Import + # 枚举成员又是 ast.Import 结构体类名),类型注解应解析为结构体而非枚举值 + # (与 ast.Name 路径 line 100-107 的逻辑一致) + _Gen = getattr(self.Trans, 'LlvmGen', None) + if _Gen and TypeName in _Gen.structs: + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + Result.Name = TypeName + Result.PtrCount = 1 + return Result + + # 跨模块类引用(`.CLS` / `ast.CLS`):按类名查 _struct_sha1_map, + # 与 ast.Name 路径(FromTypeName → _get_or_create_struct 的 + # `clean_name in self._struct_sha1_map` 分支,BaseGen.py:421)、声明端 + # DeclarationGenerator._get_type_str(`attr_name in struct_names`, + # DeclarationGenerator.py:1185)以及名称域 SymbolUtils._ResolveAttributeArgStr + # (SymbolUtils.py:491)**同一条"类名 → SHA1"规则**。 + # 此前此处只查 Gen.structs(仅本模块已创建的结构体)→ 本模块未 import + # 的类(如 rawptr[e4c92b87b98424db.AST])落空 → 返回 BaseType=None → + # 裸引用抛"无法识别的参数类型注解"/脱糖合并后 CVoid/1(i8*), + # 与声明端 %".CLS"* 分裂。 + if _Gen is not None and TypeName in getattr(_Gen, '_struct_sha1_map', {}): + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + Result.Name = TypeName + Result.PtrCount = 1 + return Result + + Entry = self.Trans.SymbolTable.lookup(TypeName) + if Entry and Entry.IsTypedef: + if isinstance(Entry, CTypeInfo) and Entry.BaseType: + if not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0: + Result = Entry.Copy() + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry.OriginalType: + if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, CTypeInfo): + Resolved = Entry.OriginalType.Copy() + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + elif isinstance(Entry.OriginalType, str): + # 字符串 OriginalType(如 'void *')需解析为 CTypeInfo, + # 否则返回的 Result BaseType=_CTypedef 会被下游误判为 i32 + Resolved = CTypeInfo.FromTypeName(Entry.OriginalType) + if Resolved and Resolved.BaseType: + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + Result = CTypeInfo() + Result.BaseType = t._CTypedef(TypeName) + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry: + # If BaseType is None but this is a class/struct, set BaseType to CStruct + Result = Entry.Copy() + if Result.BaseType is None: + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + # struct 变量默认为指针(与 ast.Name 路径一致,见 line 121) + # 同时确保 Name 字段被设置,供 ToString() 使用 + if Result.IsStruct: + if Result.PtrCount == 0: + Result.PtrCount = 1 + if not Result.Name: + Result.Name = TypeName + return Result + return CTypeInfo() + + elif isinstance(Node, ast.Call): + if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': + ReturnTypeInfo = self._GetCTypeInfoInternal(Node.args[0]) if Node.args else None + if isinstance(ReturnTypeInfo, CTypeInfo): + ReturnType = ReturnTypeInfo + else: + ReturnType = CTypeInfo() + ReturnType.BaseType = t.CVoid() + + ParamTypes = [] + ParamNames = [] + for kw in Node.keywords: + ParamTypeInfo = self._GetCTypeInfoInternal(kw.value) + if not isinstance(ParamTypeInfo, CTypeInfo): + ParamTypeInfo = self._MakeCTypeInfoFromName('int') + ParamTypes.append(ParamTypeInfo) + ParamNames.append(kw.arg) + + for arg in Node.args[1:]: + if isinstance(arg, ast.Tuple): + for elt in arg.elts: + ParamTypeInfo = self._GetCTypeInfoInternal(elt) + if not isinstance(ParamTypeInfo, CTypeInfo): + ParamTypeInfo = self._MakeCTypeInfoFromName('int') + ParamTypes.append(ParamTypeInfo) + ParamNames.append('') + else: + ParamTypeInfo = self._GetCTypeInfoInternal(arg) + if not isinstance(ParamTypeInfo, CTypeInfo): + ParamTypeInfo = self._MakeCTypeInfoFromName('int') + ParamTypes.append(ParamTypeInfo) + ParamNames.append('') + + if not ParamTypes: + ParamTypes.append(self._MakeCTypeInfoFromName('void')) + ParamNames.append('') + + Info = CTypeInfo() + Info.IsFuncPtr = True + Info.FuncPtrReturn = ReturnType + Info.FuncPtrParams = list(zip(ParamNames, ParamTypes)) + return Info + + if isinstance(Node.func, ast.Attribute) and isinstance(Node.func.value, ast.Name): + ModuleName = Node.func.value.id + TypeName = Node.func.attr + + if ModuleName == 't' and CTypeHelper.GetTModuleCType(TypeName) == t._CTypedef and Node.args: + TypeArg = Node.args[0] + if isinstance(TypeArg, ast.Name): + return TypeArg.id + elif isinstance(TypeArg, ast.Attribute): + AttrModule = TypeArg.value.id if isinstance(TypeArg.value, ast.Name) else None + AttrName = TypeArg.attr + if AttrModule == 't' and getattr(t, AttrName, None): + TypeObj = getattr(t, AttrName) + if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType): + return TypeObj.__name__ + return AttrName + elif isinstance(TypeArg, ast.Constant): + return str(TypeArg.value) + + if ModuleName in self.Trans._UserTypeModules: + module = self.Trans._UserTypeModules[ModuleName] + if getattr(module, TypeName, None): + TypeObj = getattr(module, TypeName) + if isinstance(TypeObj, type): + if issubclass(TypeObj, t.CType): + return TypeObj.__name__ + + if ModuleName == 't': + TypeObj = CTypeHelper.GetTModuleCType(TypeName) + if TypeObj is None: + TypeObj = getattr(t, TypeName, None) + if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType): + Result = CTypeInfo() + Result.BaseType = TypeObj + return Result + + if ModuleName == 't' and TypeName == 'State': + Result = CTypeInfo() + Result.IsState = True + return Result + + if ModuleName == 't' and TypeName == 'Bit': + if Node.args and isinstance(Node.args[0], ast.Constant): + BitWidth = Node.args[0].value + Result = CTypeInfo() + Result.IsBitField = True + Result.BitWidth = BitWidth + Result.BaseType = t.CInt + return Result + + Result = CTypeInfo() + Result.BaseType = t.CVoid + return Result + + elif isinstance(Node, ast.Subscript): + if (isinstance(Node.value, ast.Attribute) + and isinstance(Node.value.value, ast.Name) + and IsTModule(Node.value.value.id, self.Trans.SymbolTable) + and Node.value.attr == 'Bit'): + if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): + Result = CTypeInfo() + Result.IsBitField = True + Result.BitWidth = Node.slice.value + Result.BaseType = t.CInt + return Result + return self._HandleSubscript(Node) + + elif isinstance(Node, ast.BinOp): + return self.MergeTypes(Node) + + elif isinstance(Node, ast.List): + if Node.elts: + BaseTypes = [] + for elt in Node.elts: + EltType = self._GetCTypeInfoInternal(elt) + if isinstance(EltType, CTypeInfo) and EltType.BaseType: + BaseTypes.append(EltType.BaseType) + if BaseTypes: + Result = CTypeInfo() + Result.BaseType = tuple(BaseTypes) + return Result + return self._MakeVoidCTypeInfo() + + return self._MakeVoidCTypeInfo() + + def _HandleSubscript(self, Node: ast.Subscript) -> CTypeInfo: + """澶勭悊 Subscript 鑺傜偣锛屼娇鐢?CTypeInfo 鏍戝舰缁撴瀯鍒嗘瀽 AST + + 例如? + t.CVoid[5] -> void *[5] + t.CPtr[t.CPtr[t.CPtr]][5][6] -> void ***[5][6] + t.CArrayPtr[t.CArrayPtr] -> void *(*)[] + t.CPtr[t.CArrayPtr[t.CArrayPtr]] -> void *(*(*))[] + """ + # 指针下标(rawptr[T] 内置裸指针 / t.CPtr[T] 下标指针,含裸名 CPtr[T]) + # 折叠为等价的联合写法 `T | t.CPtr` 后交给 MergeTypes 统一解析:内层 T + # 因此走通用解析路径(含跨模块类经 Gen.structs 反查 SHA1 前缀),指针 + # 层数与源码 `T | t.CPtr` 完全一致(此前 t.CPtr[ast.AST] 在此得 + # AST**(比 declare/`AST | t.CPtr` 多一层)、rawptr[ast.AST] 退化为 + # CVoid/1 → i8*)。 + if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): + return self.MergeTypes(DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable)) + + # 判据自洽守卫(fail-fast):base 是指针标记(`rawptr` / `t.CPtr` / 裸名 + # `CPtr` / 别名 `X[...]`,见 IsPtrSubscriptNode)却被上面那条判据漏掉 ⇒ + # 两处判据分裂(内部不变量被破坏)。历史行为是落到下方 CollectAll 当成 + # 「数组下标」解析,**静默**产出"基座 void + 多层指针"(如 i8**),与 + # declare 端的结构体指针分裂(现场:Generator.ExprGenConst 声明 + # `%"...ExprGenConst"*` 却落到 i8**)。按「不许静默降级」改为响亮失败, + # 暴露上游根因。 + if (isinstance(Node.value, ast.Name) and Node.value.id == 'rawptr') \ + or IsCPtrMarkerNode(Node.value, self.Trans.SymbolTable): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"指针下标判据分裂:注解 '{ast.unparse(Node)}' 的 base 是指针标记," + f"但 IsPtrSubscriptNode 未命中 —— 拒绝落到 CollectAll 静默产出" + f"「基座 void 的多层指针」(结构体指针会被悄悄变成 i8**)") + + def CollectAll(node: ast.AST, ParentIsArrayPtr: bool = False) -> CTypeInfo: + """鏀堕泦绫诲瀷淇℃伅锛岃繑鍥?CTypeInfo""" + if isinstance(node, ast.Subscript): + value_node = node.value + slice_node = node.slice + + if isinstance(value_node, ast.Attribute): + VtInfo = self.GetCTypeInfo(value_node) + + if isinstance(value_node, ast.Attribute) and value_node.attr == 'Callable' and isinstance(value_node.value, ast.Name) and IsTModule(value_node.value.id, self.Trans.SymbolTable): + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + params_list = slice_node.elts[0] + return_node = slice_node.elts[1] + ParamTypes = [] + ParamNames = [] + if isinstance(params_list, ast.List): + for elt in params_list.elts: + ParamTypeInfo = self._GetCTypeInfoInternal(elt) + if isinstance(ParamTypeInfo, CTypeInfo): + ParamTypes.append(ParamTypeInfo) + ParamNames.append('') + else: + ParamTypes.append(self._MakeCTypeInfoFromName('int')) + ParamNames.append('') + if not ParamTypes: + ParamTypes.append(self._MakeCTypeInfoFromName('void')) + ParamNames.append('') + ReturnTypeInfo = self._GetCTypeInfoInternal(return_node) + if isinstance(ReturnTypeInfo, CTypeInfo): + ReturnType = ReturnTypeInfo + else: + ReturnType = CTypeInfo() + ReturnType.BaseType = t.CVoid() + Info = CTypeInfo() + Info.IsFuncPtr = True + Info.FuncPtrReturn = ReturnType + Info.FuncPtrParams = list(zip(ParamNames, ParamTypes)) + return Info + + if VtInfo and VtInfo.IsArrayPtr: + ResultInfo = CTypeInfo() + ResultInfo.PtrCount = VtInfo.PtrCount + ResultInfo.IsArrayPtr = True + if isinstance(slice_node, ast.Attribute): + SliceVtInfo = self.GetCTypeInfo(slice_node) + if SliceVtInfo and SliceVtInfo.IsArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.IsArrayPtr = True + ResultInfo.ArrayPtr.PtrCount = SliceVtInfo.PtrCount + if SliceVtInfo.ArrayPtr: + ResultInfo.ArrayPtr.ArrayPtr = SliceVtInfo.ArrayPtr + elif SliceVtInfo and SliceVtInfo.PtrCount > 0: + ResultInfo.ArrayPtr = SliceVtInfo + else: + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.IsArrayPtr = True + elif isinstance(slice_node, ast.Subscript): + ResultInfo.ArrayPtr = CollectAll(slice_node) + elif isinstance(slice_node, ast.Name): + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.BaseType = CTypeInfo.CreateFromTypeName(slice_node.id).BaseType + elif isinstance(slice_node, ast.BinOp): + ResultInfo.ArrayPtr = CTypeInfo() + try: + ResultInfo.ArrayPtr.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayPtr.ArrayDims.append('N') + elif isinstance(slice_node, ast.Constant): + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.ArrayDims.append(str(slice_node.value)) + else: + ResultInfo.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr.IsArrayPtr = True + elif VtInfo and VtInfo.PtrCount == 1 and not VtInfo.ArrayPtr: + ResultInfo = CTypeInfo() + ResultInfo.BaseType = t.CVoid + ResultInfo.PtrCount = 1 + if isinstance(slice_node, ast.Subscript): + inner_info = CollectAll(slice_node) + if inner_info and inner_info.PtrCount > 0: + ResultInfo.PtrCount += inner_info.PtrCount + if inner_info.BaseType and not isinstance(inner_info.BaseType, t.CVoid): + ResultInfo.BaseType = inner_info.BaseType + elif inner_info and inner_info.ArrayPtr: + ResultInfo.ArrayPtr = inner_info + elif inner_info and inner_info.BaseType and not isinstance(inner_info.BaseType, t.CVoid): + ResultInfo.BaseType = inner_info.BaseType + elif isinstance(slice_node, ast.Attribute): + SliceVtInfo = self.GetCTypeInfo(slice_node) + if SliceVtInfo and SliceVtInfo.PtrCount == 1 and not SliceVtInfo.ArrayPtr: + ResultInfo.PtrCount = 2 + if SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif SliceVtInfo and SliceVtInfo.IsArrayPtr: + inner = CTypeInfo() + inner.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr = inner + elif SliceVtInfo and SliceVtInfo.ArrayPtr: + ResultInfo.ArrayPtr = SliceVtInfo + elif SliceVtInfo and SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + inner = CTypeInfo() + inner.ArrayPtr = CTypeInfo() + ResultInfo.ArrayPtr = inner + else: + ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(slice_node.id).BaseType + elif isinstance(slice_node, ast.BinOp): + merged = self.MergeTypes(slice_node) + if merged: + ResultInfo.PtrCount += merged.PtrCount + if merged.BaseType and not isinstance(merged.BaseType, t.CVoid): + ResultInfo.BaseType = merged.BaseType + if merged.ArrayPtr: + ResultInfo.ArrayPtr = merged.ArrayPtr + elif isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif VtInfo: + ResultInfo = CTypeInfo() + ResultInfo.BaseType = VtInfo.BaseType + if isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(slice_node, ast.BinOp): + try: + ResultInfo.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + elif isinstance(slice_node, ast.Subscript): + ResultInfo.ArrayPtr = CollectAll(slice_node) + else: + ResultInfo = CTypeInfo() + if isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + else: + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(value_node, ast.Subscript): + ResultInfo = CollectAll(value_node) + if isinstance(slice_node, ast.Attribute): + SliceVtInfo = self.GetCTypeInfo(slice_node) + if SliceVtInfo and SliceVtInfo.PtrCount >= 1 and not SliceVtInfo.ArrayPtr: + ResultInfo.PtrCount += SliceVtInfo.PtrCount + if SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif SliceVtInfo and SliceVtInfo.IsArrayPtr: + NewInfo = CTypeInfo() + NewInfo.ArrayPtr = ResultInfo + ResultInfo = NewInfo + elif SliceVtInfo and SliceVtInfo.BaseType and not isinstance(SliceVtInfo.BaseType, t.CVoid): + ResultInfo.BaseType = SliceVtInfo.BaseType + elif isinstance(slice_node, ast.Subscript): + InnerNode = CollectAll(slice_node) + if ResultInfo.ArrayPtr is None: + ResultInfo.ArrayPtr = InnerNode + else: + current = ResultInfo + while current.ArrayPtr is not None: + current = current.ArrayPtr + current.ArrayPtr = InnerNode + elif isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + else: + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(slice_node, ast.BinOp): + try: + ResultInfo.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + elif isinstance(value_node, ast.Name): + ResultInfo = CTypeInfo() + if IsListAnnotation(node): + if not isinstance(slice_node, ast.Tuple): + ElementType = self._GetCTypeInfoInternal(slice_node) + if ElementType and isinstance(ElementType, CTypeInfo): + ResultInfo.BaseType = ElementType.BaseType if ElementType.BaseType else t.CVoid() + ResultInfo.PtrCount = ElementType.PtrCount + 1 + ResultInfo.IsFuncPtr = ElementType.IsFuncPtr + ResultInfo.FuncPtrReturn = ElementType.FuncPtrReturn + ResultInfo.FuncPtrParams = ElementType.FuncPtrParams + else: + ResultInfo.BaseType = t.CVoid() + ResultInfo.PtrCount = 1 + elif isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1: + ElementType = self._GetCTypeInfoInternal(slice_node.elts[0]) + if ElementType and isinstance(ElementType, CTypeInfo): + ResultInfo.BaseType = ElementType.BaseType if ElementType.BaseType else t.CVoid() + ResultInfo.PtrCount = ElementType.PtrCount + ResultInfo.IsFuncPtr = ElementType.IsFuncPtr + ResultInfo.FuncPtrReturn = ElementType.FuncPtrReturn + ResultInfo.FuncPtrParams = ElementType.FuncPtrParams + for elt in slice_node.elts[1:]: + if isinstance(elt, ast.Constant): + ResultInfo.ArrayDims.append(str(elt.value)) + elif isinstance(elt, ast.Name): + ResultInfo.ArrayDims.append(elt.id) + else: + try: + ResultInfo.ArrayDims.append(ast.unparse(elt)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + else: + ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(value_node.id).BaseType + else: + # 检查是否为泛型类特化(如 list[str]) + _is_generic_template: bool = ( + hasattr(self.Trans, 'ClassHandler') + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and value_node.id in self.Trans.ClassHandler._generic_class_templates + ) + if _is_generic_template: + # 走和 _ResolveGenericSlice 同样的规范化路径(_resolve_generic_slice_type), + # 确保字段类型名与特化名完全一致: + # - ast.Name: 用原始名(如 'Param' → 'Param',不加 SHA1 前缀), + # 与 _resolve_generic_slice_type 的 ast.Name 分支一致 + # - BinOp/Attribute: 走 _resolve_type_arg_str(加 SHA1 前缀 / 'ptr' 标记) + # 避免字段类型用 'GSList[sha1.Param]' 而特化名用 'GSList[Param]' 导致 + # find_struct_by_pointee 匹配到 opaque 副本,方法分派失败 + type_arg_str: str + if isinstance(slice_node, ast.Tuple): + arg_parts: list[str] = [] + for elt in slice_node.elts: + slice_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(elt) + arg_parts.append(slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(elt)) + # 必须用 '][' 分隔以匹配 _mangle_generic_class_name 的格式 + # (class_name + '[' + ']['.join(mangled_args) + ']') + type_arg_str = ']['.join(arg_parts) + else: + slice_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(slice_node) + type_arg_str = slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(slice_node) + full_spec_name: str = f'{value_node.id}[{type_arg_str}]' + ResultInfo.BaseType = t.CStruct(name=full_spec_name) + ResultInfo.Name = full_spec_name + ResultInfo.IsStruct = True + ResultInfo.PtrCount = 1 + # 触发泛型特化发射:确保特化的方法被发射 + struct sha1 注册 + # 与局部变量 AnnAssign 路径一致;否则函数参数注解路径 + # 只构建 mangled 名,方法调用分派时 class_sha1=None 导致 + # "Undefined method" 错误(如 lst.__len__() 在 list[T] | t.CPtr 参数上) + # _specialize_generic_class 内部有 spec_key 去重,重复调用安全 + if hasattr(self.Trans, 'ExprCallHandle') and self.Trans.ExprCallHandle is not None: + self.Trans.ExprCallHandle._ResolveGenericSlice(node) + else: + # [修复-阻塞B] rawptr 包裹泛型特化(rawptr[GSList[Param]]): + # slice 是 Subscript 且内层 base 命中泛型模板时,若委托 TAR, + # 内层 GSList[Param] 会被折叠成 i8* → declare_only 特化从不触发、 + # 变量/字段容器身份丢失 → 方法调用 FakeDuck 兜底错绑兄弟类 + # (LinkedNode.append 48B 语义写穿 24B GSList)。此处按裸泛型 + # 特化(GSList[Param])同语义解析:PtrCount=1(rawptr 与裸名 + # 同为 1 层指针,对齐 element_class 侧 rawptr[list[X]]==list[X] + # 约定),并触发 declare_only 特化(struct + sha1 注册;方法体 + # 由方法调用时 _try_complete_generic_specialization 补发)。 + _inner_gsub: ast.Subscript | None = None + if (isinstance(slice_node, ast.Subscript) + and isinstance(slice_node.value, ast.Name) + and hasattr(self.Trans, 'ClassHandler') + and hasattr(self.Trans.ClassHandler, '_generic_class_templates') + and slice_node.value.id in self.Trans.ClassHandler._generic_class_templates + and getattr(self.Trans, 'ExprCallHandle', None) is not None): + _inner_gsub = slice_node + if _inner_gsub is not None: + _ig_slice: ast.AST = _inner_gsub.slice + _ig_slices: list[ast.AST] = list(_ig_slice.elts) if isinstance(_ig_slice, ast.Tuple) else [_ig_slice] + _ig_args: list[str] = [] + for _ig_elt in _ig_slices: + _ig_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(_ig_elt) + _ig_args.append(_ig_res[0] if _ig_res else self.Trans.ExprCallHandle._resolve_type_arg_str(_ig_elt)) + _ig_spec: str = f'{_inner_gsub.value.id}[' + ']['.join(_ig_args) + ']' + ResultInfo.BaseType = t.CStruct(name=_ig_spec) + ResultInfo.Name = _ig_spec + ResultInfo.IsStruct = True + ResultInfo.PtrCount = 1 + self.Trans.ExprCallHandle._ResolveGenericSlice(_inner_gsub) + return ResultInfo + # [builtins 最后匹配] base 为 builtins 指针 typedef + # (rawptr → t.CPtr):rawptr[X] 等价 X*(对齐 VPC + # Subscript 语义),委托 TypeAnnotationResolver 统一解析, + # 避免被误当作 'rawptr[X]' 结构体/数组维度处理。 + try: + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR + _tar_sub: CTypeInfo = _TAR.from_node(node, self.Trans.SymbolTable) + if (_tar_sub is not None and _tar_sub.PtrCount > 0 + and _tar_sub.BaseType is not None): + return _tar_sub + except Exception: + pass + ResultInfo.BaseType = CTypeInfo.CreateFromTypeName(value_node.id).BaseType + if isinstance(slice_node, ast.Constant): + ResultInfo.ArrayDims.append(str(slice_node.value)) + elif isinstance(slice_node, ast.Name): + SliceType = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + ResultInfo.PtrCount += 1 + elif SliceType == t.CArrayPtr: + ResultInfo.ArrayPtr = CTypeInfo() + else: + ResultInfo.ArrayDims.append(slice_node.id) + elif isinstance(slice_node, ast.BinOp): + try: + ResultInfo.ArrayDims.append(ast.unparse(slice_node)) + except Exception: # 回退:unparse 失败时用 'N' + ResultInfo.ArrayDims.append('N') + elif isinstance(slice_node, ast.Subscript): + InnerNode = CollectAll(slice_node) + ResultInfo.ArrayDims.extend(InnerNode.ArrayDims) + else: + ResultInfo = CTypeInfo() + + return ResultInfo + elif isinstance(node, ast.Attribute): + VtInfo = self.GetCTypeInfo(node) + result = CTypeInfo() + if VtInfo and VtInfo.IsArrayPtr: + result.ArrayPtr = CTypeInfo() + return result + elif VtInfo and VtInfo.PtrCount == 1 and not VtInfo.ArrayPtr: + result.BaseType = t.CVoid + result.PtrCount = 1 + return result + elif VtInfo and VtInfo.BaseType: + result.BaseType = VtInfo.BaseType + return result + else: + return CTypeInfo() + elif isinstance(node, ast.Name): + result = CTypeInfo() + result.BaseType = CTypeInfo.CreateFromTypeName(node.id).BaseType + return result + else: + return CTypeInfo() + + return CollectAll(Node) + + def _CollectAllOperandTypes(self, Node: ast.AST) -> List: + """递归收集所有操作数的类型(用于一次性合并)""" + Types = [] + + # 指针下标(rawptr[T] / t.CPtr[T])先折叠成等价的 `T | t.CPtr`:使 + # `rawptr[T] | t.CPtr` 与 `T | t.CPtr | t.CPtr` 拉平成同一组操作数 + # (否则折叠结果作为单个 Subscript 操作数参与合并,指针层数少算一层); + # t.CPtr[T] 与 rawptr[T] 同一条规则(见 SymbolUtils.IsPtrSubscriptNode)。 + if IsPtrSubscriptNode(Node, self.Trans.SymbolTable): + Node = DesugarPtrSubscriptToUnion(Node, self.Trans.SymbolTable) + + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + Types.extend(self._CollectAllOperandTypes(Node.left)) + Types.extend(self._CollectAllOperandTypes(Node.right)) + else: + Types.append(Node) + + return Types + + def _GetTypeFromNode(self, Node: ast.AST) -> CTypeInfo: + """浠?AST 鑺傜偣鑾峰彇绫诲瀷""" + if isinstance(Node, ast.Call): + if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': + return self.GetCTypeInfo(Node) + elif isinstance(Node.func, ast.Attribute) and CTypeHelper.GetTModuleCType(Node.func.attr) == t._CTypedef: + return self.GetCTypeInfo(Node) + elif isinstance(Node.func, ast.Attribute) and Node.func.attr == 'Bit': + if Node.args and isinstance(Node.args[0], ast.Constant): + Result = CTypeInfo() + Result.BaseType = t.CInt + Result.IsBitField = True + Result.BitWidth = Node.args[0].value + return Result + return self._MakeVoidCTypeInfo() + elif isinstance(Node, ast.Subscript): + if (isinstance(Node.value, ast.Attribute) + and isinstance(Node.value.value, ast.Name) + and IsTModule(Node.value.value.id, self.Trans.SymbolTable) + and Node.value.attr == 'Bit'): + if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): + Result = CTypeInfo() + Result.BaseType = t.CInt + Result.IsBitField = True + Result.BitWidth = Node.slice.value + return Result + return self.GetCTypeInfo(Node) + else: + return self.GetCTypeInfo(Node) + + def MergeTypes(self, Node: ast.BinOp): + """处理类型组合,如 t.CInt | t.CPtr ?t.CStatic | t.CInt + + 这是 | 运算符的类型合并处理函数 + 收集所有操作数,一次性合? + """ + if not isinstance(Node, ast.BinOp) or not isinstance(Node.op, ast.BitOr): + return None + + AllOperands = self._CollectAllOperandTypes(Node) + + AllTypes = [] + for Operand in AllOperands: + TypeItem = self._GetTypeFromNode(Operand) + AllTypes.append(TypeItem) + + if isinstance(Node.left, ast.Attribute): + LeftParts = [] + current = Node.left + while isinstance(current, ast.Attribute): + attr_type = getattr(t, current.attr, None) + if attr_type and isinstance(attr_type, type) and issubclass(attr_type, t.CType): + LeftParts.insert(0, attr_type) + current = current.value + if isinstance(current, ast.Name): + name_type = getattr(t, current.id, None) + if name_type and isinstance(name_type, type) and issubclass(name_type, t.CType): + LeftParts.insert(0, name_type) + if LeftParts and LeftParts[-1] == t._CTypedef: + for type_item in AllTypes: + if isinstance(type_item, CTypeInfo) and type_item.IsFuncPtr: + type_item.IsTypedef = True + type_item.OriginalType = CTypeInfo() + type_item.OriginalType.IsFuncPtr = True + type_item.OriginalType.FuncPtrReturn = type_item.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + type_item.OriginalType.FuncPtrParams = list(type_item.FuncPtrParams) if type_item.FuncPtrParams else [] + return type_item + Result = CTypeInfo() + if isinstance(AllTypes[-1], CTypeInfo): + Result.BaseType = AllTypes[-1].BaseType + else: + Result.BaseType = t.CInt + Result.IsTypedef = True + return Result + + return self._MergeAllComponentTypes(*AllTypes) + + def _MergeAllComponentTypes(self, *Types: CTypeInfo) -> CTypeInfo: + """合并多个类型 - 直接使用 CTypeInfo 类""" + if not Types: + return CTypeInfo() + + Result = CTypeInfo() + # 统计 t.CPtr(CVoid + PtrCount=1)的个数,用于正确累加指针层数 + # 因为 (AST | t.CPtr) | t.CPtr 展开后会包含多个 t.CPtr,每个都需要累加 + cptr_count: int = 0 + + for TypeItem in Types: + if TypeItem is None: + continue + if TypeItem.IsDefine: + # t.CDefine 标记在 | 合并中传播(对齐 IsState): + # `int | t.CDefine`(define 宏函数返回注解)合并后仍保持 + # IsDefine=True,下游才能识别为编译期宏(alwaysinline/#define) + Result.IsDefine = True + if TypeItem.IsState: + Result.IsState = True + if not Result.Storage: + Result.Storage = t.CExport() + continue + if TypeItem.IsBitField: + Result.BaseType = t.CInt + Result.IsBitField = True + Result.BitWidth = TypeItem.BitWidth + return Result + if TypeItem.IsFuncPtr: + Result.BaseType = t.CVoid + Result.IsFuncPtr = True + Result.FuncPtrParams = TypeItem.FuncPtrParams + Result.FuncPtrReturn = TypeItem.FuncPtrReturn + return Result + if TypeItem.IsTypedef: + if TypeItem.BaseType and (not isinstance(TypeItem.BaseType, (t._CTypedef,)) or TypeItem.PtrCount > 0): + if Result.BaseType is None or isinstance(Result.BaseType, (t.CVoid, t._CTypedef)): + Result.BaseType = TypeItem.BaseType + Result.Name = TypeItem.Name + Result.IsTypedef = True + Result.PtrCount = max(Result.PtrCount, TypeItem.PtrCount) + if TypeItem.OriginalType: + Result.OriginalType = TypeItem.OriginalType + else: + Result.IsTypedef = True + Result.Name = TypeItem.Name + if TypeItem.OriginalType: + Result.OriginalType = TypeItem.OriginalType + continue + # 统计 t.CPtr(CVoid + PtrCount>0) + # 注意: t.CPtr[t.CPtr] 的 PtrCount=2(双重指针),需要完整累加 + if TypeItem.PtrCount > 0 and TypeItem.BaseType and (TypeItem.BaseType is t.CVoid or isinstance(TypeItem.BaseType, t.CVoid)): + cptr_count += TypeItem.PtrCount + # BaseType 保持不变,只计数,后续统一累加 + if TypeItem.IsRenum and TypeItem.Name: + Result.IsRenum = True + Result.IsEnum = True + Result.IsStruct = True + if not Result.Name: + Result.Name = TypeItem.Name + continue + self._MergeCTypeInfoInto(Result, TypeItem) + + # 对所有 t.CPtr 统一累加指针层数 + # 仅对 struct 类型吸收第一个 CPtr(struct 变量默认是指针,AST | t.CPtr = AST*)。 + # 对于本身已是指针的类型(如 bytes=CChar*),bytes | t.CPtr = i8**(不吸收)。 + base_ptr_count: int = Result.PtrCount + Result.PtrCount += cptr_count + if Result.IsStruct and base_ptr_count > 0 and cptr_count > 0: + Result.PtrCount -= 1 + + if Result.BaseType is None: + Result.BaseType = t.CVoid + + return Result + + def _MergeCTypeInfoInto(self, Target: CTypeInfo, Source: CTypeInfo): + if Source.PtrCount > 0 and Source.BaseType and (Source.BaseType is t.CVoid or isinstance(Source.BaseType, t.CVoid)): + # t.CPtr 的处理已提升到 _MergeAllComponentTypes 中统一计数累加, + # 此处保留仅作为兜底(从其他路径直接调用 _MergeCTypeInfoInto 时) + if Target.PtrCount < Source.PtrCount: + Target.PtrCount = Source.PtrCount + elif Source.PtrCount > 0 and Source.BaseType and Source.BaseType is not t.CVoid and not isinstance(Source.BaseType, t.CVoid): + if Target.BaseType is None or Target.BaseType is t.CVoid or isinstance(Target.BaseType, t.CVoid): + Target.BaseType = Source.BaseType + # 当 Target 已有 PtrCount(来自之前的 t.CPtr),累加而非替换 + Target.PtrCount = Source.PtrCount + Target.PtrCount + elif Target.PtrCount == 0: + Target.PtrCount = Source.PtrCount + elif Source.PtrCount > Target.PtrCount: + Target.PtrCount = Source.PtrCount + elif Source.PtrCount > Target.PtrCount: + Target.PtrCount = Source.PtrCount + if Source.BaseType and Source.BaseType is not t.CVoid and not isinstance(Source.BaseType, (t.CVoid, t._CTypedef)): + if Target.BaseType is None: + Target.BaseType = Source.BaseType + elif (Target.BaseType is t.CVoid or isinstance(Target.BaseType, t.CVoid)) and Target.PtrCount == 0: + Target.BaseType = Source.BaseType + elif (Source.BaseType is t.CVoid or isinstance(Source.BaseType, t.CVoid)) and Target.BaseType is None: + Target.BaseType = Source.BaseType + # Struct 名称保留:BinOp `list[AST | t.CPtr] | t.CPtr` 合并时, + # _HandleSubscript 已在 Source.Name 设置了特化名(如 'list[5dab8cb390496d22.ASTptr]'), + # 合并后 Target.Name 必须保留此名称,否则 class_member_element_class + # 无法获取正确的特化类名 → 方法分派 fallback 到错误的类 + if Source.IsStruct and Source.Name and not Target.Name: + Target.Name = Source.Name + # 【IsStruct 必须随 Name 一起传播】`_MergeAllComponentTypes` 的 + # "struct 吸收第一个 t.CPtr"规则(见该函数 :1022-1027 的注释: + # struct 变量默认是指针 ⇒ `AST | t.CPtr = AST*`)以 `Result.IsStruct` 为**闸门**: + # 此前本函数只复制 Name、从不置 IsStruct ⇒ 闸门恒假 ⇒ 闸门后的 + # `Result.PtrCount -= 1` 永不执行 ⇒ 「类 + CPtr」的联合(如 + # `list[str] | t.CPtr`)合并后指针层数**多算一层**(得 `list[str]**`,应为 + # `list[str]*`)⇒ 调用点/字段拿到的值类型 pointee 是指针而不是结构体 ⇒ + # 类名在下游被当作通用指针处理(现场:`parts = s1.split(',')` 的返回值 + # pointee 非结构体 ⇒ HandlesAssign 的类名推断整块跳过 ⇒ 随后 `parts[0]` + # 退化成按字节索引并得到整数)。 + # 语义上这两个标志同源(合并结果就是那个 struct),故成对传播。 + if Source.IsStruct: + Target.IsStruct = True + # REnum 标志和名称保留(BinOp `LLVMType | t.CPtr` 合并时不能丢失 REnum 语义) + # 同时传播 IsStruct=True,让 CPtr 吸收逻辑生效(REnum 在内存布局上是 struct) + if Source.IsRenum and Source.Name: + Target.IsRenum = True + Target.IsEnum = True + Target.IsStruct = True + if not Target.Name: + Target.Name = Source.Name + if Source.IsTypedef and Source.Name: + if Source.BaseType and (not isinstance(Source.BaseType, (t._CTypedef,)) or Source.PtrCount > 0): + if Target.BaseType is None or isinstance(Target.BaseType, (t.CVoid, t._CTypedef)): + Target.BaseType = Source.BaseType + Target.IsTypedef = True + Target.Name = Source.Name + if Source.OriginalType: + Target.OriginalType = Source.OriginalType + elif Target.BaseType is None or isinstance(Target.BaseType, (t.CVoid, t._CTypedef)): + if Source.OriginalType: + if isinstance(Source.OriginalType, CTypeInfo) and Source.OriginalType.BaseType: + Target.BaseType = Source.OriginalType.BaseType + Target.PtrCount = max(Target.PtrCount, Source.OriginalType.PtrCount) + Target.IsTypedef = True + Target.Name = Source.Name + Target.OriginalType = Source.OriginalType + elif isinstance(Source.OriginalType, CTypeInfo): + Target.IsTypedef = True + Target.Name = Source.Name + Target.OriginalType = Source.OriginalType + else: + Target.BaseType = t.CStruct(name=Source.Name) + Target.IsStruct = True + else: + Target.BaseType = t.CStruct(name=Source.Name) + Target.IsStruct = True + if Source.ArrayPtr: + if Target.ArrayPtr is None: + Target.ArrayPtr = Source.ArrayPtr + else: + if Source.PtrCount > Target.PtrCount: + Target.PtrCount = Source.PtrCount + Target.ArrayPtr = Source.ArrayPtr + if Source.PtrCount > 0: + Target.ArrayDims = [] + elif Source.ArrayDims: + Target.ArrayDims.extend(Source.ArrayDims) + if Source.VarConst: + Target.VarConst = True + if Source.VarVolatile: + Target.VarVolatile = True + if Source.DataConst: + Target.DataConst = True + if Source.DataVolatile: + Target.DataVolatile = True + if Source.IsFuncPtr: + Target.IsFuncPtr = True + Target.FuncPtrParams = Source.FuncPtrParams + Target.FuncPtrReturn = Source.FuncPtrReturn + if Source.Storage and not Target.Storage: + Target.Storage = Source.Storage + if Source.ByteOrder and not Target.ByteOrder: + Target.ByteOrder = Source.ByteOrder diff --git a/lib/core/LLVMCG/BaseGen.py b/lib/core/LLVMCG/BaseGen.py index c0c45d2..6ecd363 100644 --- a/lib/core/LLVMCG/BaseGen.py +++ b/lib/core/LLVMCG/BaseGen.py @@ -1,132 +1,132 @@ -from __future__ import annotations -from typing import Any -import vast as ast -import os -import re -import llvmlite.ir as ir -from lib.includes import t as _t -from lib.includes.t import CTypeRegistry -from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, BuiltinTypeMap -from lib.core.SymbolUtils import EnumIntWidth, _ENUM_DEFAULT_INT_WIDTH -from lib.constants.config import DEFAULT_HOTSPOT_CALLEES - -# `_current_node_info` 的形态:"line N[, col C][ (额外)]"(由 _set_node_info 生成)。 -# _get_node_info 用它把标签行号还原成磁盘行号(见 _DiskLineno)。 -_LINE_INFO_RE: re.Pattern[str] = re.compile(r'line (\d+)(.*)\Z', re.S) - - -class VaArgInstruction(ir.Instruction): - def descr(self, buf: list[str]) -> None: - ptr: ir.Value = self.operands[0] - buf.append("va_arg {0} {1}, {2}\n".format( - ptr.type, ptr.get_reference(), self.type)) - - -def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str = '', - RequireConcretePointee: bool = False) -> None: - """IR 边界守卫:指针类指令(GEP/load/store)的**指针操作数**必须是指针类型。 - - 为什么需要本守卫:llvmlite 的 `IRBuilder.gep` **不做任何类型校验**, - `GEPInstr.__init__` 直接读 `ptr.type.is_opaque`;`LoadInstr.__init__` 同族 - 亦同(见 llvmlite/ir/instructions.py:440/558,builder.py:777)。因此上游一旦 - 把一个**非指针值**(典型:i32)当指针传进来,抛出的就是裸 - `AttributeError: 'IntType' object has no attribute 'is_opaque'` —— - 报文里既没有指令名、没有操作数、也没有当前语句,无法定位发射点 - (现场:Phase2 翻译 TransPyV/App/lib/core/Gen/TranslatorGenMixin.vp 时报错 - 只带 713/787/788 三条语句位置,剩下的全被 llvmlite 内部吞掉)。 - - 本函数在 IR 边界先行判定,**不做任何兜底转换**:操作数不是指针立即抛 - FailFastError,报文给出指令名、操作数类型、操作数名、GEP 的 `name=`(即发射 - 点标识)与当前节点位置,让"上游把非指针值当指针使用"的根因一眼可见。 - - 合法路径下(操作数确实是指针)零副作用,只多一次 isinstance 判定。 - """ - if isinstance(getattr(Value, 'type', None), ir.PointerType): - if RequireConcretePointee: - # GEP 的第二层守卫:被指结构体**未具化**(opaque,elements is None)。 - # llvmlite 的 GEPInstr 走 `ptr.type.gep(i)` → `IdentifiedStructType.gep` - # → `self.elements[i.constant]`,对 opaque pointee 抛裸 TypeError - # ('NoneType' object is not subscriptable;现场:astaux.vp `Keyword.__init__` - # 的 `self.arg = ...`)。typed pointer 下 GEP 必须能在编译期算出元素类型, - # 故"指向 opaque 结构体的 GEP"**无任何合法场景** ⇒ 直接 fail-fast 报出 - # 结构体名与 GEP 名,暴露"同名 struct 两份实例(一份已具化、一份仍 opaque) - # / body 从未 set_body"的根因。 - _Pointee: Any = getattr(Value.type, 'pointee', None) - if isinstance(_Pointee, ir.IdentifiedStructType) and _Pointee.elements is None: - from lib.core.LLVMCG.FuncGen import FailFastError - _MsgP: str = ( - f"{OpName} 的基址指向**未具化**的具名结构体:基址类型={Value.type}," - f"被指结构体={getattr(_Pointee, 'name', '?')!r}(opaque,elements 未设置)" - + (f",{Detail}" if Detail else "") - + "\n llvmlite 会在此处内部对 opaque pointee 计算元素类型并抛裸 TypeError" - "('NoneType' object is not subscriptable);本守卫在 IR 边界 fail-fast," - "暴露\"该类存在另一份已具化实例(偏移与 GEP 未同源)\"或" - "\"该类 body 从未 set_body\"的根因(不做兜底)") - if Gen is not None and hasattr(Gen, '_get_node_info'): - _MsgP += "\n " + Gen._get_node_info() - # 同短名 struct 实例清单(key(IR 名)=槽数):**直接**区分两种根因—— - # 清单里存在已具化项 ⇒ "偏移与 GEP 未同源"(应 bitcast 到该项,见 - # HandlesAssign 的孪生体搜索);全是 opaque ⇒ "该类 body 从未 set_body" - # (往上查 StructGen 的 set_body 为何没跑)。不必再靠猜。 - if Gen is not None and isinstance(getattr(Gen, 'structs', None), dict): - _PShort: str = '' - try: - _PShort = Gen._extract_short_name(getattr(_Pointee, 'name', '') or '') - except Exception: - _PShort = '' - _Kin: list[str] = [] - for _k_n, _k_v in list(Gen.structs.items()): - if not isinstance(_k_v, ir.IdentifiedStructType): - continue - try: - _k_s: str = Gen._extract_short_name(getattr(_k_v, 'name', '') or '') - except Exception: - _k_s = '' - if _PShort and _k_s != _PShort: - continue - _k_cnt: Any = (len(_k_v.elements) if _k_v.elements is not None else 'opaque') - _Kin.append(f"{_k_n}({getattr(_k_v, 'name', '?')})={_k_cnt}") - if _Kin: - _MsgP += "\n 同短名 struct 实例(key(IR 名)=槽数): " + ", ".join(_Kin) - # TPC 发射点(由内向外,最内 3 帧):行号只能指到**语句**,无法区分 - # 同一语句里是哪个 GEP 点(现场 astaux.vp:165 `self.arg = _copy_str(...)` - # 在读写两侧共有多个候选发射点,逐个读代码无法判定)。取栈中位于本仓 - # lib/、且不在本文件(守卫自身)的最内 3 帧,报出 文件:行(函数)。 - try: - import traceback as _tb - _Sites: list[str] = [] - for _fr in reversed(_tb.extract_stack()): - _fn: str = (getattr(_fr, 'filename', '') or '').replace('\\', '/') - if '/lib/' not in _fn or _fn.endswith('/LLVMCG/BaseGen.py'): - continue - _Sites.append(f"{_fn.split('/lib/', 1)[-1]}:{getattr(_fr, 'lineno', 0)}" - f"({getattr(_fr, 'name', '?')})") - if len(_Sites) >= 3: - break - if _Sites: - _MsgP += "\n TPC 发射点(由内向外): " + " ← ".join(_Sites) - except Exception: - pass - raise FailFastError(_MsgP) - return - # 失败路径才导入:热路径(每次 gep/load/store)不承担导入开销,也避免循环导入。 - from lib.core.LLVMCG.FuncGen import FailFastError - _TypeStr: str = str(getattr(Value, 'type', None)) - _ValName: Any = getattr(Value, 'name', '') - _Msg: str = f"{OpName} 的指针操作数不是指针类型:操作数类型={_TypeStr}" - if _ValName: - _Msg += f",操作数名='{_ValName}'" - if Detail: - _Msg += f",{Detail}" - _Msg += ("\n llvmlite 只会在此处内部访问 `ptr.type.is_opaque`,抛裸 AttributeError" - "('IntType' object has no attribute 'is_opaque');" - "本守卫在 IR 边界 fail-fast,暴露上游把非指针值当指针使用的根因" - "(不做兜底转换)") - if Gen is not None and hasattr(Gen, '_get_node_info'): - _Msg += "\n " + Gen._get_node_info() - raise FailFastError(_Msg) - - +from __future__ import annotations +from typing import Any +import vast as ast +import os +import re +import llvmlite.ir as ir +from lib.includes import t as _t +from lib.includes.t import CTypeRegistry +from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, BuiltinTypeMap +from lib.core.SymbolUtils import EnumIntWidth, _ENUM_DEFAULT_INT_WIDTH +from lib.constants.config import DEFAULT_HOTSPOT_CALLEES + +# `_current_node_info` 的形态:"line N[, col C][ (额外)]"(由 _set_node_info 生成)。 +# _get_node_info 用它把标签行号还原成磁盘行号(见 _DiskLineno)。 +_LINE_INFO_RE: re.Pattern[str] = re.compile(r'line (\d+)(.*)\Z', re.S) + + +class VaArgInstruction(ir.Instruction): + def descr(self, buf: list[str]) -> None: + ptr: ir.Value = self.operands[0] + buf.append("va_arg {0} {1}, {2}\n".format( + ptr.type, ptr.get_reference(), self.type)) + + +def RequirePointerOperand(Value: Any, OpName: str, Gen: Any = None, Detail: str = '', + RequireConcretePointee: bool = False) -> None: + """IR 边界守卫:指针类指令(GEP/load/store)的**指针操作数**必须是指针类型。 + + 为什么需要本守卫:llvmlite 的 `IRBuilder.gep` **不做任何类型校验**, + `GEPInstr.__init__` 直接读 `ptr.type.is_opaque`;`LoadInstr.__init__` 同族 + 亦同(见 llvmlite/ir/instructions.py:440/558,builder.py:777)。因此上游一旦 + 把一个**非指针值**(典型:i32)当指针传进来,抛出的就是裸 + `AttributeError: 'IntType' object has no attribute 'is_opaque'` —— + 报文里既没有指令名、没有操作数、也没有当前语句,无法定位发射点 + (现场:Phase2 翻译 ViperC/App/lib/core/Gen/TranslatorGenMixin.vp 时报错 + 只带 713/787/788 三条语句位置,剩下的全被 llvmlite 内部吞掉)。 + + 本函数在 IR 边界先行判定,**不做任何兜底转换**:操作数不是指针立即抛 + FailFastError,报文给出指令名、操作数类型、操作数名、GEP 的 `name=`(即发射 + 点标识)与当前节点位置,让"上游把非指针值当指针使用"的根因一眼可见。 + + 合法路径下(操作数确实是指针)零副作用,只多一次 isinstance 判定。 + """ + if isinstance(getattr(Value, 'type', None), ir.PointerType): + if RequireConcretePointee: + # GEP 的第二层守卫:被指结构体**未具化**(opaque,elements is None)。 + # llvmlite 的 GEPInstr 走 `ptr.type.gep(i)` → `IdentifiedStructType.gep` + # → `self.elements[i.constant]`,对 opaque pointee 抛裸 TypeError + # ('NoneType' object is not subscriptable;现场:astaux.vp `Keyword.__init__` + # 的 `self.arg = ...`)。typed pointer 下 GEP 必须能在编译期算出元素类型, + # 故"指向 opaque 结构体的 GEP"**无任何合法场景** ⇒ 直接 fail-fast 报出 + # 结构体名与 GEP 名,暴露"同名 struct 两份实例(一份已具化、一份仍 opaque) + # / body 从未 set_body"的根因。 + _Pointee: Any = getattr(Value.type, 'pointee', None) + if isinstance(_Pointee, ir.IdentifiedStructType) and _Pointee.elements is None: + from lib.core.LLVMCG.FuncGen import FailFastError + _MsgP: str = ( + f"{OpName} 的基址指向**未具化**的具名结构体:基址类型={Value.type}," + f"被指结构体={getattr(_Pointee, 'name', '?')!r}(opaque,elements 未设置)" + + (f",{Detail}" if Detail else "") + + "\n llvmlite 会在此处内部对 opaque pointee 计算元素类型并抛裸 TypeError" + "('NoneType' object is not subscriptable);本守卫在 IR 边界 fail-fast," + "暴露\"该类存在另一份已具化实例(偏移与 GEP 未同源)\"或" + "\"该类 body 从未 set_body\"的根因(不做兜底)") + if Gen is not None and hasattr(Gen, '_get_node_info'): + _MsgP += "\n " + Gen._get_node_info() + # 同短名 struct 实例清单(key(IR 名)=槽数):**直接**区分两种根因—— + # 清单里存在已具化项 ⇒ "偏移与 GEP 未同源"(应 bitcast 到该项,见 + # HandlesAssign 的孪生体搜索);全是 opaque ⇒ "该类 body 从未 set_body" + # (往上查 StructGen 的 set_body 为何没跑)。不必再靠猜。 + if Gen is not None and isinstance(getattr(Gen, 'structs', None), dict): + _PShort: str = '' + try: + _PShort = Gen._extract_short_name(getattr(_Pointee, 'name', '') or '') + except Exception: + _PShort = '' + _Kin: list[str] = [] + for _k_n, _k_v in list(Gen.structs.items()): + if not isinstance(_k_v, ir.IdentifiedStructType): + continue + try: + _k_s: str = Gen._extract_short_name(getattr(_k_v, 'name', '') or '') + except Exception: + _k_s = '' + if _PShort and _k_s != _PShort: + continue + _k_cnt: Any = (len(_k_v.elements) if _k_v.elements is not None else 'opaque') + _Kin.append(f"{_k_n}({getattr(_k_v, 'name', '?')})={_k_cnt}") + if _Kin: + _MsgP += "\n 同短名 struct 实例(key(IR 名)=槽数): " + ", ".join(_Kin) + # TPC 发射点(由内向外,最内 3 帧):行号只能指到**语句**,无法区分 + # 同一语句里是哪个 GEP 点(现场 astaux.vp:165 `self.arg = _copy_str(...)` + # 在读写两侧共有多个候选发射点,逐个读代码无法判定)。取栈中位于本仓 + # lib/、且不在本文件(守卫自身)的最内 3 帧,报出 文件:行(函数)。 + try: + import traceback as _tb + _Sites: list[str] = [] + for _fr in reversed(_tb.extract_stack()): + _fn: str = (getattr(_fr, 'filename', '') or '').replace('\\', '/') + if '/lib/' not in _fn or _fn.endswith('/LLVMCG/BaseGen.py'): + continue + _Sites.append(f"{_fn.split('/lib/', 1)[-1]}:{getattr(_fr, 'lineno', 0)}" + f"({getattr(_fr, 'name', '?')})") + if len(_Sites) >= 3: + break + if _Sites: + _MsgP += "\n TPC 发射点(由内向外): " + " ← ".join(_Sites) + except Exception: + pass + raise FailFastError(_MsgP) + return + # 失败路径才导入:热路径(每次 gep/load/store)不承担导入开销,也避免循环导入。 + from lib.core.LLVMCG.FuncGen import FailFastError + _TypeStr: str = str(getattr(Value, 'type', None)) + _ValName: Any = getattr(Value, 'name', '') + _Msg: str = f"{OpName} 的指针操作数不是指针类型:操作数类型={_TypeStr}" + if _ValName: + _Msg += f",操作数名='{_ValName}'" + if Detail: + _Msg += f",{Detail}" + _Msg += ("\n llvmlite 只会在此处内部访问 `ptr.type.is_opaque`,抛裸 AttributeError" + "('IntType' object has no attribute 'is_opaque');" + "本守卫在 IR 边界 fail-fast,暴露上游把非指针值当指针使用的根因" + "(不做兜底转换)") + if Gen is not None and hasattr(Gen, '_get_node_info'): + _Msg += "\n " + Gen._get_node_info() + raise FailFastError(_Msg) + + _TPC_FuncInitGuardInstalled: bool = False @@ -199,167 +199,167 @@ def InstallFuncCreateGuard() -> None: InstallFuncCreateGuard() -class _GuardIrBuilder(ir.IRBuilder): - """默认 IR 构建器:在 GEP/load/store 处对**指针操作数**做类型守卫。 - - 与 `_HotspotSiteBuilder`(--hotspot-sites 专用)不同,本类**无条件启用** - (BaseGenMixin._NewIrBuilder 返回它):只做"操作数不是指针 → fail-fast" - 一件事,合法 IR 的生成结果与原生 ir.IRBuilder 完全一致。 - - 为什么必须无条件启用:`IRBuilder.gep` 无类型校验(见 RequirePointerOperand - 的说明),而 TPC 侧全部 GEP 都走 `Gen.builder.gep`(无一处传 source_etype, - 故必走 GEPInstr 的 `ptr.type.is_opaque` 分支);load/store 虽在 llvmlite 内 - 有 isinstance 守卫,但报错不带源码位置。此处在唯一入口补上判定与位置信息。 - """ - - def __init__(self, block: ir.Block | None = None, gen: Any = None) -> None: - super().__init__(block) - self._GuardGen: Any = gen - - def gep(self, ptr: Any, indices: Any, inbounds: bool = False, - name: str = '', source_etype: Any = None) -> Any: - RequirePointerOperand(ptr, 'getelementptr(GEP)', self._GuardGen, - f"GEP 名='{name}',索引 {len(indices)} 项", - RequireConcretePointee=True) - return super().gep(ptr, indices, inbounds=inbounds, name=name, - source_etype=source_etype) - - def load(self, ptr: Any, name: str = '', align: int | None = None, - typ: Any = None) -> Any: - RequirePointerOperand(ptr, 'load', self._GuardGen, f"load 名='{name}'") - return super().load(ptr, name=name, align=align, typ=typ) - - def store(self, value: Any, ptr: Any, align: int | None = None) -> Any: - RequirePointerOperand(ptr, 'store', self._GuardGen, - f"store 值名='{getattr(value, 'name', '')}'") - return super().store(value, ptr, align=align) - - -class _HotspotSiteBuilder(_GuardIrBuilder): - """--hotspot-sites 专用 IRBuilder:为每条新生成的 call 指令挂源码位置。 - - 仅当调用点热点统计开关打开时才被实例化(见 BaseGenMixin._NewIrBuilder); - 未启用时使用 _GuardIrBuilder(同为 IR 边界守卫,见其类注释)。 - - 重写 call() 只在返回的 ir.CallInstr 对象上追加两个普通 Python 属性 - (_src_file / _src_line),不改变指令语义、操作数,也不参与 IR 渲染 - (llvmlite 仅按指令自身类型输出文本,忽略这些自定义属性)。取不到位置时 - 留空(None / 0),不伪造。 - """ - - def __init__(self, block: ir.Block | None, gen: Any) -> None: - super().__init__(block, gen) - self._HotspotGen: Any = gen - - def call(self, fn: Any, args: Any, name: str = '', cconv: Any = None, - tail: Any = False, fastmath: Any = (), attrs: Any = (), - arg_attrs: Any = None) -> ir.CallInstr: - instr: ir.CallInstr = super().call( - fn, args, name=name, cconv=cconv, tail=tail, - fastmath=fastmath, attrs=attrs, arg_attrs=arg_attrs) - gen: Any = self._HotspotGen - # 记录生成期当前源码位置(语句级 lineno + 当前源文件名);取不到则留空 - instr._src_file = getattr(gen, '_current_source_file', None) - instr._src_line = getattr(gen, '_current_lineno', 0) or 0 - return instr - - -class BaseGenMixin: - - @staticmethod - def _parse_triple(triple: str | None) -> dict[str, bool | int]: - """解析目标三元组,返回平台信息字典""" - info: dict[str, bool | int] = { - 'is_windows': False, 'is_linux': False, 'is_macos': False, - 'is_x86_64': False, 'is_arm64': False, 'is_32bit': False, - 'ptr_size': 8, 'long_size': 8, 'wchar_t_size': 32, - } - if not triple: - return info - triple_lower: str = triple.lower() - # 检测操作系统 - if 'windows' in triple_lower or 'win32' in triple_lower or 'mingw' in triple_lower or 'msvc' in triple_lower or 'cygwin' in triple_lower: - info['is_windows'] = True - # Windows LLP64: long = 32 位, wchar_t = 16 位 - info['long_size'] = 32 - info['wchar_t_size'] = 16 - elif 'linux' in triple_lower: - info['is_linux'] = True - elif 'darwin' in triple_lower or 'macos' in triple_lower or 'apple' in triple_lower: - info['is_macos'] = True - # 检测架构 - if 'x86_64' in triple_lower or 'amd64' in triple_lower or 'x64' in triple_lower: - info['is_x86_64'] = True - elif 'aarch64' in triple_lower or 'arm64' in triple_lower: - info['is_arm64'] = True - elif 'i386' in triple_lower or 'i686' in triple_lower or 'arm' in triple_lower: - info['is_32bit'] = True - info['ptr_size'] = 4 - info['long_size'] = 32 - if info['is_windows']: - info['wchar_t_size'] = 16 - else: - info['wchar_t_size'] = 32 - return info - - def __init__(self, triple: str | None = None, datalayout: str | None = None) -> None: - self.module: ir.Module = ir.Module(name="transpyc_module") - if triple: - self.module.triple = triple - else: - self.module.triple = "x86_64-none-elf" - if datalayout: - self.module.data_layout = datalayout - else: - self.module.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" - - # 解析目标平台信息 - self._platform_info: dict[str, bool | int] = self._parse_triple(self.module.triple) - self.ptr_size: int = self._platform_info['ptr_size'] - # 根据目标平台配置 t 模块中的平台相关类型大小 - _t.configure_platform(self.module.triple) - self.builder: ir.IRBuilder | None = None - self.func: ir.Function | None = None - self.functions: dict[str, ir.Function] = {} - self.variables: dict[str, ir.Value | None] = {} - self._direct_values: dict[str, ir.Value] = {} - self._reg_values: dict[str, ir.Value] = {} - self.var_signedness: dict[str, bool] = {} - self._unsigned_results: set[int] = {} - self.string_const_counter: int = 0 - self.Vtables: dict[str, ir.GlobalVariable] = {} - self.class_methods: dict[str, list[str]] = {} - self.class_members: dict[str, list[tuple[str, ir.Type]]] = {} - self.class_member_defaults: dict[str, dict[str, Any]] = {} - self.class_member_signeds: dict[str, dict[str, bool]] = {} - self.class_member_bitfields: dict[str, dict[str, int]] = {} - self.class_member_byteorders: dict[str, dict[str, str]] = {} - self.local_var_byteorders: dict[str, str] = {} - self.class_member_element_class: dict[str, dict[str, str]] = {} - self.class_member_bitoffsets: dict[str, dict[str, int]] = {} - self.structs: dict[str, ir.IdentifiedStructType] = {} - self.typedef_int_widths: dict[str, int] = {} # typedef名称 -> 底层整数位宽 (如 'UINT' -> 32, 'BYTE' -> 8) - self._cross_module_vtable_classes: set[str] = set() - self._cross_module_novtable: set[str] = set() - self.class_vtable: set[str] = set() - self._class_graph: dict[str, Any] = {} - self.class_parent: dict[str, str] = {} - # 多继承:父类名数组(声明顺序)。class_parent 兼容保留为第一个父类。 - self.class_parents: dict[str, list[str]] = {} - # Store member annotations for deferred type resolution (cross-module class references) - self.class_member_annotations: dict[str, dict[str, Any]] = {} - self.loop_break_targets: list[ir.Block] = [] - self.loop_continue_targets: list[ir.Block] = [] - # 当前是否正处于 switch 的 case 语句体内(True=是)。供 `fallthrough` - # 标记语句(vast 脱糖为 c.NoBreak())判定合法性:只在 switch 的 case 体内 - # 合法;写进 match(完全禁止 fallthrough)或 switch 之外一律 fail-fast。 - self.in_switch_case: bool = False - self.eh_jmp_buf_stack: list[ir.Value] = [] - self.eh_except_block_stack: list[ir.Block] = [] - self.eh_finally_stack: list[Any] = [] - self.global_vars: set[str] = set() - self.nonlocal_params: dict[str, Any] = {} - self._variable_scope_stack: list[dict[str, Any]] = [] # Stack of outer scope variables for nested function nonlocal lookup +class _GuardIrBuilder(ir.IRBuilder): + """默认 IR 构建器:在 GEP/load/store 处对**指针操作数**做类型守卫。 + + 与 `_HotspotSiteBuilder`(--hotspot-sites 专用)不同,本类**无条件启用** + (BaseGenMixin._NewIrBuilder 返回它):只做"操作数不是指针 → fail-fast" + 一件事,合法 IR 的生成结果与原生 ir.IRBuilder 完全一致。 + + 为什么必须无条件启用:`IRBuilder.gep` 无类型校验(见 RequirePointerOperand + 的说明),而 TPC 侧全部 GEP 都走 `Gen.builder.gep`(无一处传 source_etype, + 故必走 GEPInstr 的 `ptr.type.is_opaque` 分支);load/store 虽在 llvmlite 内 + 有 isinstance 守卫,但报错不带源码位置。此处在唯一入口补上判定与位置信息。 + """ + + def __init__(self, block: ir.Block | None = None, gen: Any = None) -> None: + super().__init__(block) + self._GuardGen: Any = gen + + def gep(self, ptr: Any, indices: Any, inbounds: bool = False, + name: str = '', source_etype: Any = None) -> Any: + RequirePointerOperand(ptr, 'getelementptr(GEP)', self._GuardGen, + f"GEP 名='{name}',索引 {len(indices)} 项", + RequireConcretePointee=True) + return super().gep(ptr, indices, inbounds=inbounds, name=name, + source_etype=source_etype) + + def load(self, ptr: Any, name: str = '', align: int | None = None, + typ: Any = None) -> Any: + RequirePointerOperand(ptr, 'load', self._GuardGen, f"load 名='{name}'") + return super().load(ptr, name=name, align=align, typ=typ) + + def store(self, value: Any, ptr: Any, align: int | None = None) -> Any: + RequirePointerOperand(ptr, 'store', self._GuardGen, + f"store 值名='{getattr(value, 'name', '')}'") + return super().store(value, ptr, align=align) + + +class _HotspotSiteBuilder(_GuardIrBuilder): + """--hotspot-sites 专用 IRBuilder:为每条新生成的 call 指令挂源码位置。 + + 仅当调用点热点统计开关打开时才被实例化(见 BaseGenMixin._NewIrBuilder); + 未启用时使用 _GuardIrBuilder(同为 IR 边界守卫,见其类注释)。 + + 重写 call() 只在返回的 ir.CallInstr 对象上追加两个普通 Python 属性 + (_src_file / _src_line),不改变指令语义、操作数,也不参与 IR 渲染 + (llvmlite 仅按指令自身类型输出文本,忽略这些自定义属性)。取不到位置时 + 留空(None / 0),不伪造。 + """ + + def __init__(self, block: ir.Block | None, gen: Any) -> None: + super().__init__(block, gen) + self._HotspotGen: Any = gen + + def call(self, fn: Any, args: Any, name: str = '', cconv: Any = None, + tail: Any = False, fastmath: Any = (), attrs: Any = (), + arg_attrs: Any = None) -> ir.CallInstr: + instr: ir.CallInstr = super().call( + fn, args, name=name, cconv=cconv, tail=tail, + fastmath=fastmath, attrs=attrs, arg_attrs=arg_attrs) + gen: Any = self._HotspotGen + # 记录生成期当前源码位置(语句级 lineno + 当前源文件名);取不到则留空 + instr._src_file = getattr(gen, '_current_source_file', None) + instr._src_line = getattr(gen, '_current_lineno', 0) or 0 + return instr + + +class BaseGenMixin: + + @staticmethod + def _parse_triple(triple: str | None) -> dict[str, bool | int]: + """解析目标三元组,返回平台信息字典""" + info: dict[str, bool | int] = { + 'is_windows': False, 'is_linux': False, 'is_macos': False, + 'is_x86_64': False, 'is_arm64': False, 'is_32bit': False, + 'ptr_size': 8, 'long_size': 8, 'wchar_t_size': 32, + } + if not triple: + return info + triple_lower: str = triple.lower() + # 检测操作系统 + if 'windows' in triple_lower or 'win32' in triple_lower or 'mingw' in triple_lower or 'msvc' in triple_lower or 'cygwin' in triple_lower: + info['is_windows'] = True + # Windows LLP64: long = 32 位, wchar_t = 16 位 + info['long_size'] = 32 + info['wchar_t_size'] = 16 + elif 'linux' in triple_lower: + info['is_linux'] = True + elif 'darwin' in triple_lower or 'macos' in triple_lower or 'apple' in triple_lower: + info['is_macos'] = True + # 检测架构 + if 'x86_64' in triple_lower or 'amd64' in triple_lower or 'x64' in triple_lower: + info['is_x86_64'] = True + elif 'aarch64' in triple_lower or 'arm64' in triple_lower: + info['is_arm64'] = True + elif 'i386' in triple_lower or 'i686' in triple_lower or 'arm' in triple_lower: + info['is_32bit'] = True + info['ptr_size'] = 4 + info['long_size'] = 32 + if info['is_windows']: + info['wchar_t_size'] = 16 + else: + info['wchar_t_size'] = 32 + return info + + def __init__(self, triple: str | None = None, datalayout: str | None = None) -> None: + self.module: ir.Module = ir.Module(name="transpyc_module") + if triple: + self.module.triple = triple + else: + self.module.triple = "x86_64-none-elf" + if datalayout: + self.module.data_layout = datalayout + else: + self.module.data_layout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" + + # 解析目标平台信息 + self._platform_info: dict[str, bool | int] = self._parse_triple(self.module.triple) + self.ptr_size: int = self._platform_info['ptr_size'] + # 根据目标平台配置 t 模块中的平台相关类型大小 + _t.configure_platform(self.module.triple) + self.builder: ir.IRBuilder | None = None + self.func: ir.Function | None = None + self.functions: dict[str, ir.Function] = {} + self.variables: dict[str, ir.Value | None] = {} + self._direct_values: dict[str, ir.Value] = {} + self._reg_values: dict[str, ir.Value] = {} + self.var_signedness: dict[str, bool] = {} + self._unsigned_results: set[int] = {} + self.string_const_counter: int = 0 + self.Vtables: dict[str, ir.GlobalVariable] = {} + self.class_methods: dict[str, list[str]] = {} + self.class_members: dict[str, list[tuple[str, ir.Type]]] = {} + self.class_member_defaults: dict[str, dict[str, Any]] = {} + self.class_member_signeds: dict[str, dict[str, bool]] = {} + self.class_member_bitfields: dict[str, dict[str, int]] = {} + self.class_member_byteorders: dict[str, dict[str, str]] = {} + self.local_var_byteorders: dict[str, str] = {} + self.class_member_element_class: dict[str, dict[str, str]] = {} + self.class_member_bitoffsets: dict[str, dict[str, int]] = {} + self.structs: dict[str, ir.IdentifiedStructType] = {} + self.typedef_int_widths: dict[str, int] = {} # typedef名称 -> 底层整数位宽 (如 'UINT' -> 32, 'BYTE' -> 8) + self._cross_module_vtable_classes: set[str] = set() + self._cross_module_novtable: set[str] = set() + self.class_vtable: set[str] = set() + self._class_graph: dict[str, Any] = {} + self.class_parent: dict[str, str] = {} + # 多继承:父类名数组(声明顺序)。class_parent 兼容保留为第一个父类。 + self.class_parents: dict[str, list[str]] = {} + # Store member annotations for deferred type resolution (cross-module class references) + self.class_member_annotations: dict[str, dict[str, Any]] = {} + self.loop_break_targets: list[ir.Block] = [] + self.loop_continue_targets: list[ir.Block] = [] + # 当前是否正处于 switch 的 case 语句体内(True=是)。供 `fallthrough` + # 标记语句(vast 脱糖为 c.NoBreak())判定合法性:只在 switch 的 case 体内 + # 合法;写进 match(完全禁止 fallthrough)或 switch 之外一律 fail-fast。 + self.in_switch_case: bool = False + self.eh_jmp_buf_stack: list[ir.Value] = [] + self.eh_except_block_stack: list[ir.Block] = [] + self.eh_finally_stack: list[Any] = [] + self.global_vars: set[str] = set() + self.nonlocal_params: dict[str, Any] = {} + self._variable_scope_stack: list[dict[str, Any]] = [] # Stack of outer scope variables for nested function nonlocal lookup self.var_struct_class: dict[str, str] = {} # 【注解声明类台账(2026-09-30)】带类型注解声明过的变量 → 其**权威**类名。 # 由 HandlesAnnAssign 在声明完成时登记;HandlesAssign 的"右值推出类"登记点 @@ -367,236 +367,236 @@ class BaseGenMixin: # 转换进该类型。否则 `node: rawptr[AST] = Name(...)` 会把 node 记成 Name, # 后续 `node = ` 就被判成被禁止的向下转型 # (现场 includes/ast/parser.vp:1919:%AST* → %Name*)。 - self.var_annotated_class: dict[str, str] = {} - self.var_ptr_element: dict[str, bool] = {} - # 全局变量的指针元素标志(持久,不受函数重置影响) - # 用于全局变量 _argv: bytes | t.CPtr 等,使 _argv[index] 使用 8 字节指针步长 - self.global_var_ptr_element: dict[str, bool] = {} - self.class_provides: dict[str, list[str]] = {} - self.class_requires: dict[str, list[str]] = {} - self.class_require_must: dict[str, list[str]] = {} - # 编译期 with 上下文栈:记录当前嵌套 with 链中所有可用的 provides 字段名 - # 用于 __require_must__ 的纯编译期静态可达性检查 - self._with_provides_stack: list[list[str]] = [] - # 函数参数自动取地址模式: func_name -> [None, None, 'ref', 'temp', 'always', ...] - # 'ref': &T — 实参非指针左值取址,右值 fail-fast - # 'temp': &temp T — 实参非指针时在调用方建同型临时再取址 - # 'always': t.CAutoPtr — 无条件取地址 - self._auto_addr_params: dict[str, list[str | None]] = {} - self.global_struct_class: dict[str, str] = {} - self.var_type_info: dict[str, Any] = {} - self.var_const_flags: dict[str, bool] = {} - # ---- const / volatile 登记(与 TransPyV 侧 VarEntry/FieldEntry 逐字段对应, - # 对照表见 Debugger/type-descriptors.md「两侧语义对照表」)---- - # 分两层,因为 TPC 的函数内语句与模块级语句走不同生命周期: - # 函数作用域层(随函数出栈清空,清零点 HandlesFunctions 函数出口): - # var_const_flags 变量(局部/形参)自身 const - # var_volatile_flags 变量自身 volatile - # var_pointee_volatile_flags 变量所指物 volatile(rawptr[t.CVolatile|T]) - # 模块级层(模块存活期持久): - # global_* 模块级同名三类 - # 类字段层(模块存活期持久): - # field_const_flags '类名.字段名' -> True - # field_volatile_flags '类名.字段名' -> True - # field_*_names 裸字段名集合(IR 侧按 GEP 指令名回查, - # GEP 以字段名命名,见 HandlesExprAttr:36 等) - self.var_volatile_flags: dict[str, bool] = {} - self.var_pointee_volatile_flags: dict[str, bool] = {} - self.global_const_flags: dict[str, bool] = {} - self.global_volatile_flags: dict[str, bool] = {} - self.global_pointee_volatile_flags: dict[str, bool] = {} - self.field_const_flags: dict[str, bool] = {} - self.field_const_names: set[str] = set() - self.field_volatile_flags: dict[str, bool] = {} - self.field_volatile_names: set[str] = set() - self.field_pointee_volatile_flags: dict[str, bool] = {} - self.field_pointee_volatile_names: set[str] = set() - # const 变量名 -> 声明处类型文本(仅用于报文的「声明处类型(含 const)」) - self.const_decl_types: dict[str, str] = {} - self._current_source_file: str | None = None - self._current_source_lines: list[str] = [] - self._ns_cache: dict[str, str] = {} - self._type_cache: dict[tuple[str, bool], ir.Type] = {} - self._typedef_cache: dict[str, Any] = {} - self._func_sig_cache: dict[str, Any] = {} - self.var_type_assignments: dict[str, Any] = {} - self._temp_struct_ptrs: list[ir.Value] = [] - self._stop_iter_flag_param: ir.Value | None = None - self._local_heap_ptrs: list[tuple[ir.Value, str, str | None]] = [] - self._var_to_heap_ptr: dict[str, ir.Value] = {} - self.known_return_types: dict[str, ir.Type] = {} - self._current_lineno: int = 0 - self._current_node_info: str = "" - self.module_sha1: str | None = None - self.ModuleSha1Map: dict[str, str] = {} - self._temp_dir: str | None = None - self._export_funcs: set[str] = set() - self._export_extern_funcs: dict[str, str] = {} - self._current_module_func_names: set[str] = set() - # __doc__ 魔法字符串注册表:symbol_name -> docstring 文本(None 表示空/优化剥离) - self._doc_strings: dict[str, str | None] = {} - self.class_packed: set[str] = set() - self._define_constants: dict[str, int] = {} - # 以下属性原先在外部懒注入,此处显式初始化以避免 hasattr/getattr 动态反射 - self._struct_sha1_map: dict[str, str] = {} - self.SymbolTable: Any = None - self._Trans: Any = None - self.class_sha1_map: dict[str, str] = {} - # 以下属性原先懒注入,此处显式初始化以消除 hasattr/getattr 动态反射 - self._import_handler_ref: Any = None - self._classes_in_progress: set[str] = set() - # 字段收集期间添加当前类名,阻止 CollectAnnAssignMember 触发的嵌套 - # _generate_structs 用不完整 class_members 调用 set_body(set_body 只能调用一次) - # 用集合而非单值,因为嵌套特化类 _EmitClassLlvm 会清除单值,导致外层类被错误设置 body - self._collecting_members_set: set[str] = set() - # 已按父类展平的类成员表登记(HandlesImports._EmitExternalClassDeclLlvm / - # HandlesClassDef._EmitClassLlvm 在展平完成时登记)。两处用途: - # ① _TryLoadClassMembersFromPyi 的 guard 只在"已展平"时短路 —— - # 停在自有字段的冻结表允许后续补齐(展平与加载顺序无关); - # ② FuncGen._member_base 查询前据此判定是否需要重跑展平。 - self._members_flattened: set[str] = set() - # 展平补齐重入保护:避免 _member_base → _TryLoadClassMembersFromPyi → - # _EmitExternalClassDeclLlvm → _member_base 的同类递归。 - self._members_flatten_in_progress: set[str] = set() - # 展平补齐的"依赖状态签名"(类名 → (struct 元素数, 成员表长度)): - # 同一签名下补齐无效时不再重复尝试(HandlesImports.EnsureClassMembersFlattened)。 - self._members_flatten_repair_sig: dict[str, tuple[int, int]] = {} - self._last_var_name: str | None = None - self._variadic_info: dict | None = None - self._vtable_copy_counter: int = 0 - self._decorated_funcs: dict = {} - self._current_tree: Any = None - self._DefineConstants: dict = {} - self._all_define_constants: dict = {} - # 闭包追踪:记录返回闭包的函数和变量 - # _closure_return_types: func_name -> (fn_return_type, nonlocal_arg_types) - self._closure_return_types: dict[str, tuple[Any, list[Any]]] = {} - # _closure_var_types: var_name -> (fn_return_type, nonlocal_arg_types) - self._closure_var_types: dict[str, tuple[Any, list[Any]]] = {} - # 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数落盘文件路径 - self.hotspot_sites_file: str | None = None - # 是否对全部直接 call 点插桩(忽略白名单;体积/开销大) - self.hotspot_sites_all: bool = False - # 白名单被调函数基名列表;None=使用默认内存/字符串族白名单 - self.hotspot_callees: list[str] | None = None - # 注册策略:False=种子 CLI 单模块(在 main 入口注册);True=Projectrans - # 多模块(每模块用 @llvm.global_ctors 自注册,不依赖 main 所在模块) - self.hotspot_sites_module_local: bool = False - # 函数级 DWARF 调试元数据(--dwarf / --asan 自动开启): - # False=未启用(零影响,不构造任何 DI 元数据、不挂 !dbg); - # True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。 - self.dwarf_enabled: bool = False - self.DebugInfo: Any = None - # Ctrl+C 调用栈(--traceback):仅在翻译 includes/traceback.vp 这一个模块时 - # 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其 - # Install()(见 _RegisterTracebackInstallViaCtor)。默认 False(零影响)。 - self.traceback_ctor: bool = False - pi: dict[str, bool | int] = self._platform_info - # 根据目标平台设置宏(声明式,从三元组推导) - if pi['is_windows']: - self._define_constants['_WIN32'] = 1 - if not pi['is_32bit']: - self._define_constants['_WIN64'] = 1 - self._define_constants['WIN64'] = 1 - self._define_constants['WIN32'] = 1 - if pi['is_linux']: - self._define_constants['__linux__'] = 1 - if pi['is_macos']: - self._define_constants['__APPLE__'] = 1 - self._define_constants['__MACH__'] = 1 - if pi['is_x86_64']: - self._define_constants['__x86_64__'] = 1 - self._define_constants['__x86_64'] = 1 - self._define_constants['_M_X64'] = 1 - elif pi['is_arm64']: - self._define_constants['__aarch64__'] = 1 - self._define_constants['_M_ARM64'] = 1 - if not pi['is_32bit'] and pi['is_linux']: - self._define_constants['__LP64__'] = 1 - elif pi['is_32bit']: - self._define_constants['_ILP32'] = 1 - self._define_constants['SIZEOF_VOID_P'] = pi['ptr_size'] - self._define_constants['__SIZEOF_POINTER__'] = pi['ptr_size'] - - def find_struct_by_pointee(self, pointee: ir.Type) -> tuple[str, ir.IdentifiedStructType] | None: - """根据 pointee 类型查找匹配的结构体,返回 (class_name, struct_type) 或 None""" - for class_name, struct_type in self.structs.items(): - if pointee == struct_type: - return (class_name, struct_type) - return None - - @staticmethod - def _extract_short_name(name: str) -> str: - """从结构体类型名中提取短名(去掉 sha1 前缀)。 - 如 "6bc91356c652f8ca.Function" → "Function" - 如 "Function" → "Function"(无 sha1 前缀时原样返回) - """ - if '.' in name: - prefix: str = name.split('.', 1)[0] - if len(prefix) == 16 and all(c in '0123456789abcdef' for c in prefix): - return name.split('.', 1)[1] - return name - - @staticmethod - def _extract_struct_sha1(st: ir.IdentifiedStructType) -> str: - """从 IdentifiedStructType 的 name 中提取 sha1 前缀(16 位十六进制)。 - 返回空字符串表示无 sha1 前缀。用于同名类冲突检测。 - """ - if not isinstance(st, ir.IdentifiedStructType): - return '' - try: - name: str = st.name - except (AssertionError, AttributeError): - return '' - if '.' in name: - prefix: str = name.split('.', 1)[0] - if len(prefix) == 16 and all(c in '0123456789abcdef' for c in prefix): - return prefix - return '' - - def _FindStructByIrName(self, ns_name: str) -> ir.IdentifiedStructType | None: - """按 **IR 类型名**(`IdentifiedStructType.name`)查已注册结构体。 - - 必要性:`self.structs` 的键有"裸短名 / sha1 前缀全名 / import 别名"多种, - 同一个 IR 名字可被多个键指向。若只按"键"判存在性,对同一 IR 名字会再建 - 第二个 `IdentifiedStructType` 对象 ⇒ 单模块 .ll 里出现**两条同名 type 定义** - (Body 逐字相同)。`llc` 容忍这种重复,但 `llvm-link` 报 - "error: redefinition of type" ⇒ 跨模块 alwaysinline 合并整体回退 - (现场:includes/ast/parser.vp 的 8312ce86a2fd82c0.Keyword —— 裸名 - 'Keyword' 先被 .tokens 的导入占用,astaux 的同名类只剩 'KwNode' 键, - 随后按全名 '8312ce86a2fd82c0.Keyword' 请求时又建了一个同名新对象)。 - - IR 里类型的身份**就是名字**(不是 Python 对象),故存在性必须按名字判定: - 命中即复用同一对象(需要 body 时由既有 set_body 流程补,与"opaque 占位 → - set_body"同语义)。名字不同的同名类冲突由 sha1 前缀命名负责区分,不靠 - "同名两个对象"。 - """ - if not ns_name: - return None - for _st in self.structs.values(): - if isinstance(_st, ir.IdentifiedStructType) and _st.name == ns_name: - return _st - return None - - @staticmethod - def _CleanStructName(name: str) -> str: - """结构体名清洗:去掉指针标记/空格/修饰符(clean_name 既有行为)。 - - 但**保留**泛型特化名 '[...]' 段内的原样文本:特化实参里的 '*' 是类型 - 身份的一部分('list[.AST*]' 表示元素为指向 AST 的指针),若一并 - 移除会让声明端引用名('list[.AST*]')与定义端注册名 - ('list[.AST]')分裂成两个 struct。头部(首个 '[' 之前)行为不变; - '[' 开头(LLVM 数组串 '[4 x i8*]')走原清洗,保持既有语义。 - """ - lb: int = name.find('[') - if lb > 0: - head, tail = name[:lb], name[lb:] - else: - head, tail = name, '' - head = (head.replace(' *', '').replace('*', '').replace(' ', '_') - .replace('const', 'const_').replace('volatile', 'volatile_')) - return head + tail - + self.var_annotated_class: dict[str, str] = {} + self.var_ptr_element: dict[str, bool] = {} + # 全局变量的指针元素标志(持久,不受函数重置影响) + # 用于全局变量 _argv: bytes | t.CPtr 等,使 _argv[index] 使用 8 字节指针步长 + self.global_var_ptr_element: dict[str, bool] = {} + self.class_provides: dict[str, list[str]] = {} + self.class_requires: dict[str, list[str]] = {} + self.class_require_must: dict[str, list[str]] = {} + # 编译期 with 上下文栈:记录当前嵌套 with 链中所有可用的 provides 字段名 + # 用于 __require_must__ 的纯编译期静态可达性检查 + self._with_provides_stack: list[list[str]] = [] + # 函数参数自动取地址模式: func_name -> [None, None, 'ref', 'temp', 'always', ...] + # 'ref': &T — 实参非指针左值取址,右值 fail-fast + # 'temp': &temp T — 实参非指针时在调用方建同型临时再取址 + # 'always': t.CAutoPtr — 无条件取地址 + self._auto_addr_params: dict[str, list[str | None]] = {} + self.global_struct_class: dict[str, str] = {} + self.var_type_info: dict[str, Any] = {} + self.var_const_flags: dict[str, bool] = {} + # ---- const / volatile 登记(与 ViperC 侧 VarEntry/FieldEntry 逐字段对应, + # 对照表见 Debugger/type-descriptors.md「两侧语义对照表」)---- + # 分两层,因为 TPC 的函数内语句与模块级语句走不同生命周期: + # 函数作用域层(随函数出栈清空,清零点 HandlesFunctions 函数出口): + # var_const_flags 变量(局部/形参)自身 const + # var_volatile_flags 变量自身 volatile + # var_pointee_volatile_flags 变量所指物 volatile(rawptr[t.CVolatile|T]) + # 模块级层(模块存活期持久): + # global_* 模块级同名三类 + # 类字段层(模块存活期持久): + # field_const_flags '类名.字段名' -> True + # field_volatile_flags '类名.字段名' -> True + # field_*_names 裸字段名集合(IR 侧按 GEP 指令名回查, + # GEP 以字段名命名,见 HandlesExprAttr:36 等) + self.var_volatile_flags: dict[str, bool] = {} + self.var_pointee_volatile_flags: dict[str, bool] = {} + self.global_const_flags: dict[str, bool] = {} + self.global_volatile_flags: dict[str, bool] = {} + self.global_pointee_volatile_flags: dict[str, bool] = {} + self.field_const_flags: dict[str, bool] = {} + self.field_const_names: set[str] = set() + self.field_volatile_flags: dict[str, bool] = {} + self.field_volatile_names: set[str] = set() + self.field_pointee_volatile_flags: dict[str, bool] = {} + self.field_pointee_volatile_names: set[str] = set() + # const 变量名 -> 声明处类型文本(仅用于报文的「声明处类型(含 const)」) + self.const_decl_types: dict[str, str] = {} + self._current_source_file: str | None = None + self._current_source_lines: list[str] = [] + self._ns_cache: dict[str, str] = {} + self._type_cache: dict[tuple[str, bool], ir.Type] = {} + self._typedef_cache: dict[str, Any] = {} + self._func_sig_cache: dict[str, Any] = {} + self.var_type_assignments: dict[str, Any] = {} + self._temp_struct_ptrs: list[ir.Value] = [] + self._stop_iter_flag_param: ir.Value | None = None + self._local_heap_ptrs: list[tuple[ir.Value, str, str | None]] = [] + self._var_to_heap_ptr: dict[str, ir.Value] = {} + self.known_return_types: dict[str, ir.Type] = {} + self._current_lineno: int = 0 + self._current_node_info: str = "" + self.module_sha1: str | None = None + self.ModuleSha1Map: dict[str, str] = {} + self._temp_dir: str | None = None + self._export_funcs: set[str] = set() + self._export_extern_funcs: dict[str, str] = {} + self._current_module_func_names: set[str] = set() + # __doc__ 魔法字符串注册表:symbol_name -> docstring 文本(None 表示空/优化剥离) + self._doc_strings: dict[str, str | None] = {} + self.class_packed: set[str] = set() + self._define_constants: dict[str, int] = {} + # 以下属性原先在外部懒注入,此处显式初始化以避免 hasattr/getattr 动态反射 + self._struct_sha1_map: dict[str, str] = {} + self.SymbolTable: Any = None + self._Trans: Any = None + self.class_sha1_map: dict[str, str] = {} + # 以下属性原先懒注入,此处显式初始化以消除 hasattr/getattr 动态反射 + self._import_handler_ref: Any = None + self._classes_in_progress: set[str] = set() + # 字段收集期间添加当前类名,阻止 CollectAnnAssignMember 触发的嵌套 + # _generate_structs 用不完整 class_members 调用 set_body(set_body 只能调用一次) + # 用集合而非单值,因为嵌套特化类 _EmitClassLlvm 会清除单值,导致外层类被错误设置 body + self._collecting_members_set: set[str] = set() + # 已按父类展平的类成员表登记(HandlesImports._EmitExternalClassDeclLlvm / + # HandlesClassDef._EmitClassLlvm 在展平完成时登记)。两处用途: + # ① _TryLoadClassMembersFromPyi 的 guard 只在"已展平"时短路 —— + # 停在自有字段的冻结表允许后续补齐(展平与加载顺序无关); + # ② FuncGen._member_base 查询前据此判定是否需要重跑展平。 + self._members_flattened: set[str] = set() + # 展平补齐重入保护:避免 _member_base → _TryLoadClassMembersFromPyi → + # _EmitExternalClassDeclLlvm → _member_base 的同类递归。 + self._members_flatten_in_progress: set[str] = set() + # 展平补齐的"依赖状态签名"(类名 → (struct 元素数, 成员表长度)): + # 同一签名下补齐无效时不再重复尝试(HandlesImports.EnsureClassMembersFlattened)。 + self._members_flatten_repair_sig: dict[str, tuple[int, int]] = {} + self._last_var_name: str | None = None + self._variadic_info: dict | None = None + self._vtable_copy_counter: int = 0 + self._decorated_funcs: dict = {} + self._current_tree: Any = None + self._DefineConstants: dict = {} + self._all_define_constants: dict = {} + # 闭包追踪:记录返回闭包的函数和变量 + # _closure_return_types: func_name -> (fn_return_type, nonlocal_arg_types) + self._closure_return_types: dict[str, tuple[Any, list[Any]]] = {} + # _closure_var_types: var_name -> (fn_return_type, nonlocal_arg_types) + self._closure_var_types: dict[str, tuple[Any, list[Any]]] = {} + # 调用点热点统计(--hotspot-sites):None=未启用(零影响);str=调用点计数落盘文件路径 + self.hotspot_sites_file: str | None = None + # 是否对全部直接 call 点插桩(忽略白名单;体积/开销大) + self.hotspot_sites_all: bool = False + # 白名单被调函数基名列表;None=使用默认内存/字符串族白名单 + self.hotspot_callees: list[str] | None = None + # 注册策略:False=种子 CLI 单模块(在 main 入口注册);True=Projectrans + # 多模块(每模块用 @llvm.global_ctors 自注册,不依赖 main 所在模块) + self.hotspot_sites_module_local: bool = False + # 函数级 DWARF 调试元数据(--dwarf / --asan 自动开启): + # False=未启用(零影响,不构造任何 DI 元数据、不挂 !dbg); + # True=每个 define 发 DISubprogram + 入口首条指令发 DILocation。 + self.dwarf_enabled: bool = False + self.DebugInfo: Any = None + # Ctrl+C 调用栈(--traceback):仅在翻译 includes/traceback.vp 这一个模块时 + # 置 True —— 落成 .ll 文本前为本模块注入 @llvm.global_ctors 构造器调用其 + # Install()(见 _RegisterTracebackInstallViaCtor)。默认 False(零影响)。 + self.traceback_ctor: bool = False + pi: dict[str, bool | int] = self._platform_info + # 根据目标平台设置宏(声明式,从三元组推导) + if pi['is_windows']: + self._define_constants['_WIN32'] = 1 + if not pi['is_32bit']: + self._define_constants['_WIN64'] = 1 + self._define_constants['WIN64'] = 1 + self._define_constants['WIN32'] = 1 + if pi['is_linux']: + self._define_constants['__linux__'] = 1 + if pi['is_macos']: + self._define_constants['__APPLE__'] = 1 + self._define_constants['__MACH__'] = 1 + if pi['is_x86_64']: + self._define_constants['__x86_64__'] = 1 + self._define_constants['__x86_64'] = 1 + self._define_constants['_M_X64'] = 1 + elif pi['is_arm64']: + self._define_constants['__aarch64__'] = 1 + self._define_constants['_M_ARM64'] = 1 + if not pi['is_32bit'] and pi['is_linux']: + self._define_constants['__LP64__'] = 1 + elif pi['is_32bit']: + self._define_constants['_ILP32'] = 1 + self._define_constants['SIZEOF_VOID_P'] = pi['ptr_size'] + self._define_constants['__SIZEOF_POINTER__'] = pi['ptr_size'] + + def find_struct_by_pointee(self, pointee: ir.Type) -> tuple[str, ir.IdentifiedStructType] | None: + """根据 pointee 类型查找匹配的结构体,返回 (class_name, struct_type) 或 None""" + for class_name, struct_type in self.structs.items(): + if pointee == struct_type: + return (class_name, struct_type) + return None + + @staticmethod + def _extract_short_name(name: str) -> str: + """从结构体类型名中提取短名(去掉 sha1 前缀)。 + 如 "6bc91356c652f8ca.Function" → "Function" + 如 "Function" → "Function"(无 sha1 前缀时原样返回) + """ + if '.' in name: + prefix: str = name.split('.', 1)[0] + if len(prefix) == 16 and all(c in '0123456789abcdef' for c in prefix): + return name.split('.', 1)[1] + return name + + @staticmethod + def _extract_struct_sha1(st: ir.IdentifiedStructType) -> str: + """从 IdentifiedStructType 的 name 中提取 sha1 前缀(16 位十六进制)。 + 返回空字符串表示无 sha1 前缀。用于同名类冲突检测。 + """ + if not isinstance(st, ir.IdentifiedStructType): + return '' + try: + name: str = st.name + except (AssertionError, AttributeError): + return '' + if '.' in name: + prefix: str = name.split('.', 1)[0] + if len(prefix) == 16 and all(c in '0123456789abcdef' for c in prefix): + return prefix + return '' + + def _FindStructByIrName(self, ns_name: str) -> ir.IdentifiedStructType | None: + """按 **IR 类型名**(`IdentifiedStructType.name`)查已注册结构体。 + + 必要性:`self.structs` 的键有"裸短名 / sha1 前缀全名 / import 别名"多种, + 同一个 IR 名字可被多个键指向。若只按"键"判存在性,对同一 IR 名字会再建 + 第二个 `IdentifiedStructType` 对象 ⇒ 单模块 .ll 里出现**两条同名 type 定义** + (Body 逐字相同)。`llc` 容忍这种重复,但 `llvm-link` 报 + "error: redefinition of type" ⇒ 跨模块 alwaysinline 合并整体回退 + (现场:includes/ast/parser.vp 的 8312ce86a2fd82c0.Keyword —— 裸名 + 'Keyword' 先被 .tokens 的导入占用,astaux 的同名类只剩 'KwNode' 键, + 随后按全名 '8312ce86a2fd82c0.Keyword' 请求时又建了一个同名新对象)。 + + IR 里类型的身份**就是名字**(不是 Python 对象),故存在性必须按名字判定: + 命中即复用同一对象(需要 body 时由既有 set_body 流程补,与"opaque 占位 → + set_body"同语义)。名字不同的同名类冲突由 sha1 前缀命名负责区分,不靠 + "同名两个对象"。 + """ + if not ns_name: + return None + for _st in self.structs.values(): + if isinstance(_st, ir.IdentifiedStructType) and _st.name == ns_name: + return _st + return None + + @staticmethod + def _CleanStructName(name: str) -> str: + """结构体名清洗:去掉指针标记/空格/修饰符(clean_name 既有行为)。 + + 但**保留**泛型特化名 '[...]' 段内的原样文本:特化实参里的 '*' 是类型 + 身份的一部分('list[.AST*]' 表示元素为指向 AST 的指针),若一并 + 移除会让声明端引用名('list[.AST*]')与定义端注册名 + ('list[.AST]')分裂成两个 struct。头部(首个 '[' 之前)行为不变; + '[' 开头(LLVM 数组串 '[4 x i8*]')走原清洗,保持既有语义。 + """ + lb: int = name.find('[') + if lb > 0: + head, tail = name[:lb], name[lb:] + else: + head, tail = name, '' + head = (head.replace(' *', '').replace('*', '').replace(' ', '_') + .replace('const', 'const_').replace('volatile', 'volatile_')) + return head + tail + def _enum_int_type(self, EnumName: str | None, MemberInfo: Any = None) -> ir.IntType: """枚举运行时常量 / 枚举类型注解的整数位宽类型(两侧 ABI 同点)。 @@ -614,1979 +614,1979 @@ class BaseGenMixin: return ir.IntType(EnumIntWidth(Table.lookup(EnumName))) return ir.IntType(_ENUM_DEFAULT_INT_WIDTH) - def _get_or_create_struct(self, name: str, source_sha1: str | None = None, packed: bool = False) -> ir.IdentifiedStructType: - clean_name: str = self._CleanStructName(name) - # 如果名称包含模块前缀(如 "a9fa0f6200c09e65.zbit_writer"), - # 先尝试用短名称(如 "zbit_writer")查找已注册的 struct, - # 避免为同一 struct 创建多个不同的类型对象导致跨模块引用类型不一致 - if '.' in clean_name and clean_name not in self.structs: - parts: list[str] = clean_name.split('.', 1) - if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): - short_name: str = parts[1] - if short_name in self.structs: - existing_short: ir.IdentifiedStructType = self.structs[short_name] - # 治本修复:同名类 sha1 冲突检测。 - # 当请求的 sha1 前缀与现有 struct 的 sha1 前缀不一致时 - # (如 stmts.py:Module 与 __module.py:Module 同名冲突), - # 不能复用已有 struct(set_body 已锁定为错误布局), - # 需创建独立 struct 并以 sha1 前缀全名存储。 - _existing_sha1: str = self._extract_struct_sha1(existing_short) - if _existing_sha1 and _existing_sha1 != parts[0]: - if clean_name in self.structs: - return self.structs[clean_name] - # 按 IR 名去重:该 IR 名字的 struct 可能已由**别的键** - # (裸名/别名)注册 ⇒ 复用,绝不建第二个同名对象 - # (否则单模块 .ll 出现两条同名 type 定义 ⇒ llvm-link - # "redefinition of type" ⇒ 跨模块内联整体回退)。 - _st_same: ir.IdentifiedStructType | None = self._FindStructByIrName(clean_name) - if _st_same is not None: - self.structs[clean_name] = _st_same - if packed and not _st_same.packed: - _st_same.packed = True - return _st_same - st_new: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=clean_name, packed=packed) - self.structs[clean_name] = st_new - return st_new - return existing_short - # 优先解析 typedef 整数类型(如 INT/UINT/BYTE),避免将其注册为 opaque struct。 - # 必须在 `if clean_name in self.structs` 缓存检查之前执行, - # 否则首次调用(SymbolTable 未就绪时)会将 typedef 名缓存为 opaque struct, - # 后续调用即使 SymbolTable 就绪也只会返回缓存的错误类型。 - # 此修复同时解决两个问题: - # 1. alloca unsized type:变量声明 `argc: INT = 6` 不再生成 `alloca %"...INT"` - # 2. 下标访问:FindStructNameInAnnotation 不再匹配 typedef 名(因不在 Gen.structs) - td_width: int | None = self._resolve_typedef_int_width(clean_name) - if td_width is not None: - if td_width > 0: - self.typedef_int_widths[clean_name] = td_width - return ir.IntType(td_width) - # td_width == 0: 特殊 typedef 名称(str/bytes/list 等),返回 i8 - self.typedef_int_widths[clean_name] = 0 - return ir.IntType(8) - if clean_name in self.structs: - existing: ir.IdentifiedStructType = self.structs[clean_name] - # 治本修复:同名类 sha1 冲突检测(短名调用路径)。 - # 当 source_sha1 与现有 struct 的 sha1 前缀不一致时(跨模块同名类冲突), - # 不能复用已有 struct(set_body 已锁定为错误布局), - # 需创建独立 struct 并以 sha1 前缀全名存储,不覆盖短名条目。 - if source_sha1: - _existing_sha1: str = self._extract_struct_sha1(existing) - if _existing_sha1 and _existing_sha1 != source_sha1: - full_key: str = f"{source_sha1}.{clean_name}" - if full_key in self.structs: - st_full: ir.IdentifiedStructType = self.structs[full_key] - if packed and isinstance(st_full, ir.IdentifiedStructType) and not st_full.packed: - st_full.packed = True - return st_full - ns_name: str = f"{source_sha1}.{clean_name}" - # 按 IR 名去重(见 _FindStructByIrName):同一 IR 名字已有对象 - # ⇒ 复用同一对象,只补注册本键,绝不建第二个同名对象。 - _st_same2: ir.IdentifiedStructType | None = self._FindStructByIrName(ns_name) - if _st_same2 is not None: - self.structs[full_key] = _st_same2 - if packed and not _st_same2.packed: - _st_same2.packed = True - return _st_same2 - st_new: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed) - self.structs[full_key] = st_new - return st_new - if packed and isinstance(existing, ir.IdentifiedStructType) and not existing.packed: - existing.packed = True - return existing - if clean_name in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum', 'Void', 'void'}: - return ir.IntType(8) - # typing.Any 是"任意类型"标记,应解析为 i8*(通用指针), - # 而非创建为 opaque 结构体(否则包含 Any 字段的结构体会变成 unsized)。 - if clean_name == 'Any': - return ir.IntType(8).as_pointer() - # t.CType 的基本类型子类(CInt/CChar/CSizeT/CLong/CFloat/CDouble 等, - # position 含 BASE 且 Size>0)有具体 LLVM 类型,应直接返回对应的 - # ir.IntType/ir.FloatType/ir.DoubleType,而非创建结构体。 - # _is_ctype_marker_name 对这些类型返回 False(不跳过),需要在此处拦截。 - _basic_llvm_type: ir.Type | None = self._try_resolve_ctype_basic(clean_name) - if _basic_llvm_type is not None: - return _basic_llvm_type - # t.CType 及其子类是"类型强转"标记,不应被编译为真实结构体。 - # 动态检查 t 模块中所有 CType 子类名(排除 CStruct/CEnum/CUnion/REnum 子类, - # 因为用户定义的结构体类继承自它们,需要被创建为结构体)。 - if self._is_ctype_marker_name(clean_name): - return ir.IntType(8) - if self.SymbolTable: - Entry: _CTypeInfo | None = self.SymbolTable.lookup(clean_name) - # 「有类定义 ⇒ 是类」的唯一判据(与 TypeConvert._type_str_to_llvm_base - # 的 `type_str in self.structs or type_str in self._struct_sha1_map`、 - # HandlesTypeMerge 的 Name/Attribute 分支、DeclarationGenerator 的 - # `attr_name in struct_names` 同一条规则源): - # _struct_sha1_map / class_sha1_map 已登记该名为**类**时,即便平坦符号表 - # 条目是**同名枚举成员**(如 ast/base.vp 的 `Constant: t.State` 与 - # ast/exprs.vp 的 `class Constant` 同名;谁后插入谁占住裸名),也必须建成 - # 结构体。否则此处返回 i32 ⇒ 上游 PtrCount=1 叠加后变成 i32*, - # 与调用点实参 %".Constant"* 分裂(现场:constant_get_str_val 的 - # 形参 i32*)。类型注解语境下枚举**成员**从不是类型,判据只能是"是否有类定义"。 - # 必须排除**枚举类型本身**(如 `class ASTKind(t.CEnum)`):_struct_sha1_map - # 是按 .vpi 里全部 ClassDef 建的,枚举类也在其中 —— 若一并当结构体,枚举 - # 注解(kind() 返回值等)会被建为 opaque struct,引发 i32 ↔ 结构体错位 - # ('IntType' object has no attribute 'is_opaque' 现场)。 - # 判据必须按 **IsEnumMember** 区分(现场实测标志组合): - # 枚举**成员** = IsEnum=True + IsEnumMember=True + BaseType=CEnum - # 枚举**类型** = IsEnum=True + IsEnumMember=False - # 只判 IsEnum 会把成员误当枚举类型 ⇒ 返回 i32 ⇒ 上游 PtrCount=1 ⇒ i32*。 - _known_class_name: bool = ( - (clean_name in getattr(self, '_struct_sha1_map', {}) - or clean_name in getattr(self, 'class_sha1_map', {})) - and not (Entry and Entry.IsEnum - and not getattr(Entry, 'IsEnumMember', False))) - if Entry and Entry.IsTypedef: - resolved_str: str = self._resolve_typedef(clean_name) - if resolved_str != clean_name: - return self._type_str_to_llvm(resolved_str) - if Entry.BaseType: - if not isinstance(Entry.BaseType, (_t._CTypedef,)) or Entry.PtrCount > 0: - resolved: str = self._resolve_ctype_to_str(Entry) - if resolved: - return self._type_str_to_llvm(resolved) - if Entry and Entry.IsRenum: - if clean_name in self.structs: - return self.structs[clean_name] - # REnum 结构体尚未创建时不能 fall through 到 IsEnum(会返回 i32), - # 需要继续到下方正常结构体创建路径,创建 opaque 结构体。 - # 这对自引用 REnum(如 LLVMType 包含 LLVMType*)至关重要: - # _parse_llvm_type 先通过此路径创建 opaque 结构体,_TryLoadStructFromStub 再 set_body。 - elif (Entry and Entry.IsEnum and clean_name not in self.class_methods - and not _known_class_name): - # CEnum 存储宽度 = 最宽成员类型(登记在 Entry.enum_width;未登记 - # → 默认 i32)。此前此处恒 i32,与同文件 _enum_int_type 的常量/ - # 注解宽度分裂(宽成员枚举的类型与常量不同宽)。 - return self._enum_int_type(clean_name, Entry) - # 治本修复:当 clean_name 是已注册的类(在 class_methods 或 class_members 中)时, - # 不应走 IsExceptionClass/IsFunction/IsVariable 分支返回非结构体类型。 - # 否则 _get_or_create_struct 返回指针类型(如 _TypedPointerType)而非 IdentifiedStructType, - # 导致 _TryLoadStructFromStub 的 set_body 被跳过(isinstance 检查失败), - # struct 保持不完整字段数,引发跨模块 GEP 越界崩溃。 - _is_class_name: bool = clean_name in self.class_methods or clean_name in self.class_members - if Entry and Entry.IsExceptionClass and not _is_class_name: - return ir.IntType(32) - if Entry and Entry.IsFunction and not _is_class_name: - return ir.PointerType(ir.IntType(8)) - if Entry and Entry.IsVariable and not _is_class_name: - return ir.PointerType(ir.IntType(8)) - if Entry and Entry.BaseType and not _known_class_name: - if isinstance(Entry.BaseType, type) and issubclass(Entry.BaseType, _t.CEnum): - return self._enum_int_type(clean_name, Entry) - if Entry.BaseType is _t.CEnum: - return self._enum_int_type(clean_name, Entry) - ns_name: str - # 泛型特化名('list[str]' / 'dict[str][int]'):类→模块 sha1 表是按**基名** - # 登记的 ClassDef('list' 来自 includes/_list.vp),特化名本身不在表里 ⇒ - # 若不先按基名取 sha1,_mangle_name 会拼出 '<使用方模块sha1>.list[str]', - # 与 declare 端(DeclarationGenerator `gc_sha1 = struct_sha1_map.get(gc_name, - # module_sha1)` → '%"{模板sha1}.list[str]"*')及定义端 - # (HandlesClassDef 特化时 Gen.module_sha1 = 模板模块)**三处分裂**。 - _tmpl_name: str = clean_name.split('[', 1)[0] if '[' in clean_name else clean_name - _tmpl_sha1: str | None = self._struct_sha1_map.get(_tmpl_name, '') or None - if source_sha1: - ns_name = f"{source_sha1}.{clean_name}" - elif _tmpl_sha1: - ns_name = f"{_tmpl_sha1}.{clean_name}" - else: - ns_name = self._mangle_name(clean_name) - # 按 IR 名去重(见 _FindStructByIrName):该 IR 名字的对象可能已由**别的键** - # (裸名 / sha1 全名 / import 别名)注册 ⇒ 复用同一对象并补注册本键。 - # 否则单模块 .ll 会出现两条同名 type 定义 ⇒ llvm-link - # "error: redefinition of type" ⇒ 跨模块 alwaysinline 合并整体回退 - # (现场:includes/ast/parser.vp 的 8312ce86a2fd82c0.Keyword 定义两次)。 - _st_same3: ir.IdentifiedStructType | None = self._FindStructByIrName(ns_name) - if _st_same3 is not None: - self.structs[clean_name] = _st_same3 - if packed and not _st_same3.packed: - _st_same3.packed = True - return _st_same3 - st: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed) - self.structs[clean_name] = st - # 尝试解析 typedef 的底层整数位宽 - width: int | None = self._resolve_typedef_int_width(clean_name) - if width is not None: - self.typedef_int_widths[clean_name] = width - return st - - def _resolve_typedef_int_width(self, name: str) -> int | None: - """解析 typedef 名称的底层整数位宽 - - 通过 CTypeRegistry 和 BuiltinTypeMap 动态解析, - 替代硬编码的 TYPEDEF_NAMES 映射表。 - 返回 int 位宽或 None(无法解析)。 - """ - # 1. 先查 BuiltinTypeMap(覆盖 BYTEPTR 等指针别名) - bm: Any = BuiltinTypeMap.Get(name) - if bm: - ctype_cls: Any - ptr_count: int - ctype_cls, ptr_count = bm - if ptr_count > 0: - return None # 指针类型,不是整数 typedef - llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): - return int(llvm_str[1:]) - - # 2. 再查 SymbolTable(用户定义的 typedef) - if self.SymbolTable: - Entry: _CTypeInfo | None = self.SymbolTable.lookup(name) - if Entry and Entry.IsTypedef: - if Entry.BaseType: - if not isinstance(Entry.BaseType, (_t._CTypedef,)) and Entry.PtrCount == 0: - llvm_str: str = CTypeRegistry.CTypeToLLVM(type(Entry.BaseType) if isinstance(Entry.BaseType, _t.CType) else Entry.BaseType) - if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): - return int(llvm_str[1:]) - if Entry.OriginalType: - if isinstance(Entry.OriginalType, _CTypeInfo) and Entry.OriginalType.BaseType: - if not isinstance(Entry.OriginalType.BaseType, (_t._CTypedef,)) and Entry.OriginalType.PtrCount == 0: - llvm_str: str = CTypeRegistry.CTypeToLLVM(type(Entry.OriginalType.BaseType) if isinstance(Entry.OriginalType.BaseType, _t.CType) else Entry.OriginalType.BaseType) - if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): - return int(llvm_str[1:]) - elif isinstance(Entry.OriginalType, str): - # OriginalType 是类型名字符串(如 'CInt', 'CUnsignedInt'), - # 通过 CTypeRegistry 解析为整数位宽 - cls: Any = CTypeRegistry.GetClassByName(Entry.OriginalType) - if cls: - llvm_str: str = CTypeRegistry.CTypeToLLVM(cls) - if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): - return int(llvm_str[1:]) - - # 3. 尝试 CTypeRegistry 直接按名称查找 - ctype_cls: Any = CTypeRegistry.GetClassByName(name) - if ctype_cls: - llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) - if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): - return int(llvm_str[1:]) - - # 4. 特殊名称(非整数类型,但需要标记为 typedef 以跳过 opaque 定义) - # 注意:如果 name 已注册为 struct(用户定义了同名 class,如 includes/_dict.py 中的 dict), - # 则不应视为 typedef,应返回 None 让调用方走 struct 创建路径。 - # 解决时序问题:_parse_llvm_declare 从 stub.ll 解析时 dict 可能尚未注册到 Gen.structs, - # 但 SymbolTable 在 .pyi 解析阶段已就绪,可通过 is_struct 检查区分 class 和 typedef。 - _SPECIAL_TYPEDEFS: set[str] = { - 'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', - 'CStruct', 'enum', 'REnum', 'renum', - 'list', 'dict', 'tuple', 'set', 'str', 'bytes', 'array', 'type', 'CArray', - } - if name in _SPECIAL_TYPEDEFS: - if name in self.structs: - return None # 已注册为 struct,不视为 typedef - if self.SymbolTable is not None: - try: - if self.SymbolTable.is_struct(name): - return None # SymbolTable 中标记为 struct,不视为 typedef - except Exception: - pass - return 0 # 标记为 typedef,但位宽为 0(非整数) - - # t.CType 及其子类是"类型强转"标记,不应被创建为结构体。 - # 动态检查 t 模块中所有 CType 子类名(排除 CStruct/CEnum/CUnion/REnum 子类)。 - # 返回 0 标记为 typedef,使 _get_or_create_struct 走 typedef 路径返回 i8。 - if self._is_ctype_marker_name(name): - return 0 - - return None - - @staticmethod - def _is_ctype_marker_name(name: str) -> bool: - """检查名称是否是 t.CType 的"类型强转"标记子类名。 - - 只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/ - CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等) - 才应该跳过结构体创建。有 BASE position 的基本类型(CInt/CChar/CSizeT/ - CLong/CFloat/CDouble 等)有具体大小,应被正常解析为对应宽度的整数/浮点。 - - CStruct/CEnum/CUnion/REnum 的用户子类是合法结构体,需要被创建。 - - 注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault, - 所以这里直接用 getattr(t, name) 检查 t 模块属性。 - - Returns: - True 如果 name 是 CType 标记子类名(应跳过结构体创建) - """ - if not name: - return False - # 去除可能的 SHA1 前缀(如 "abc123.CPtr" -> "CPtr") - short_name: str = name.split('.')[-1] if '.' in name else name - # 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian) - t_cls: Any = getattr(_t, short_name, None) - if not isinstance(t_cls, type) or not issubclass(t_cls, _t.CType): - return False - # 排除 CStruct/CEnum/CUnion/REnum 子类(它们是命名类型的基类, - # 用户定义的结构体继承自它们,需要被创建为结构体) - if issubclass(t_cls, (_t.CStruct, _t.CEnum, _t.REnum, _t.CUnion)): - return False - # 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过 - # (CInt→i32, CChar→i8, CSizeT→i64, CLong→i64, CFloat→f32 等) - # CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过 - if _t.CType.BASE in t_cls.position: - # 检查实例的 Size(类属性可能未设置,用实例获取) - try: - inst = t_cls() - size_val = getattr(inst, 'Size', None) - if size_val is not None and size_val > 0: - return False - except Exception: - pass - return True - - def _try_resolve_ctype_basic(self, name: str) -> ir.Type | None: - """尝试将 t.CType 的基本类型子类名解析为对应的 LLVM 基本类型。 - - 仅对 t 模块内的 CType 基本类型子类(CInt/CChar/CSizeT/CLong/CFloat/ - CDouble 等,即 position 含 BASE 且 Size>0)生效,返回对应的 - ir.IntType/ir.FloatType/ir.DoubleType。 - - CStruct/CEnum/CUnion/REnum 子类不在此处理(用户自定义结构体需要被 - 创建为结构体)。CVoid/CType/CPtr 等无 Size 或 Size=0 的类型也不在此 - 处理(应通过 _is_ctype_marker_name 跳过结构体创建)。 - - Returns: - 对应的 ir.Type,或 None 表示不是基本类型,需要继续走结构体创建路径。 - """ - if not name: - return None - short_name: str = name.split('.')[-1] if '.' in name else name - cls: Any = getattr(_t, short_name, None) - if not isinstance(cls, type) or not issubclass(cls, _t.CType): - return None - if issubclass(cls, (_t.CStruct, _t.CEnum, _t.REnum, _t.CUnion)): - return None - if _t.CType.BASE not in getattr(cls, 'position', frozenset()): - return None - try: - inst = cls() - size_val = getattr(inst, 'Size', None) - if size_val is None or size_val <= 0: - return None - except Exception: - return None - llvm_str: str | None = CTypeRegistry.NameToLLVM(short_name) - if not llvm_str: - return None - return self._type_str_to_llvm(llvm_str) - - def _set_node_info(self, node: ast.AST, extra_info: str = "") -> None: - if hasattr(node, 'lineno'): - self._current_lineno = node.lineno - else: - self._current_lineno = 0 - if hasattr(node, 'col_offset'): - self._current_node_info = f"line {self._current_lineno}, col {node.col_offset}" - else: - self._current_node_info = f"line {self._current_lineno}" - if extra_info: - self._current_node_info += f" ({extra_info})" - # 语句/指令级 DWARF 位置(--dwarf / --asan):每进入一条语句就把当前 - # builder 的默认调试位置切到该 AST 节点的行/列(col_offset 是 0-based, - # +1 转 1-based;无位置时为 0 → 回退函数入口 DILocation)。此后该语句生成 - # 的所有指令(含 call)自动带本函数 scope 的 !dbg,llc 不再因缺 !dbg 丢弃 - # 整份 DI。未启用时 dwarf_enabled=False,一次都不触碰 builder.debug_metadata - # (零影响:_GetDebugInfo() 也不会被调用)。 - if self.dwarf_enabled and self.builder is not None: - _DbgInfo = self._GetDebugInfo() - if _DbgInfo is not None: - _DbgInfo.SetStatement( - self.builder, self._current_lineno, - getattr(node, 'col_offset', -1) + 1) - - def _set_source_info(self, filepath: str) -> None: - self._current_source_file = filepath - try: - with open(filepath, 'r', encoding='utf-8') as f: - self._current_source_lines = f.readlines() - except Exception: # 文件读取失败时回退为空行列表 - self._current_source_lines = [] - - def _GetDebugInfo(self) -> Any: - """返回本模块的 DebugInfoEmitter;未启用 --dwarf 或无源文件时返回 None。 - - 未启用(dwarf_enabled=False)时不构造任何对象、不产生任何 IR 差异, - 行为与改动前逐字节一致。源文件取 _current_source_file(由 GenerateCCode - 在生成 IR 前设定,模块内恒定),每个模块一份 DICompileUnit + DIFile。 - """ - if not self.dwarf_enabled: - return None - if not self._current_source_file: - return None - if self.DebugInfo is None: - from lib.core.LLVMCG.DebugInfo import DebugInfoEmitter - self.DebugInfo = DebugInfoEmitter(self.module, self._current_source_file) - return self.DebugInfo - - def _skip_mangle(self, name: str) -> bool: - skip_prefixes: tuple[str, ...] = ('llvm.', 'str_const_', 'printf', 'memset_pattern') - if any(name.startswith(p) for p in skip_prefixes): - return True - if '.' in name: - parts: list[str] = name.split('.', 1) - if len(parts[0]) >= 8 and all(c in '0123456789abcdef' for c in parts[0]): - return True - return False - - def _mangle_name(self, name: str) -> str: - if self._skip_mangle(name): - return name - if name in self._export_funcs: - return name - if self.module_sha1: - return f"{self.module_sha1}.{name}" - return name - - def _find_function(self, name: str) -> ir.Function | None: - if name in self.functions: - return self.functions[name] - g: Any = self.module.globals.get(name) - if g and isinstance(g, ir.Function): - self.functions[name] = g - return g - mangled: str = self._mangle_func_name(name) - if mangled != name: - if mangled in self.functions: - self.functions[name] = self.functions[mangled] - return self.functions[mangled] - g = self.module.globals.get(mangled) - if g and isinstance(g, ir.Function): - self.functions[mangled] = g - self.functions[name] = g - return g - for sha1 in self.ModuleSha1Map.values(): - prefixed: str = f"{sha1}.{name}" - if prefixed in self.functions: - self.functions[name] = self.functions[prefixed] - return self.functions[prefixed] - g = self.module.globals.get(prefixed) - if g and isinstance(g, ir.Function): - self.functions[prefixed] = g - self.functions[name] = g - return g - return None - - def _has_function(self, name: str) -> bool: - return self._find_function(name) is not None - - def _get_mro_chain(self, ClassName: str) -> list[str]: - """MRO 线性化类名列表(深度优先 + 声明顺序 + 去重,含自身)。 - - 顺序: 自身 → 父类1 及祖先 → 父类2 及祖先(先声明父类优先,菱形去重)。 - 与 TPV HandlesStruct.get_mro_chain 的语义一致。 - - 支持带 sha1 前缀的类名(如 "c9b44d17cd621a75.CallGenLib",跨模块 - structs 表键):查 class_parents 前剥离前缀,否则表键(裸类名)不匹配 - → MRO 链只剩自身 → 继承方法调用 fail-fast "Undefined method - 'find_func_in_module' in class '...CallGenLib'"(CallGenLib 根因)。 - """ - chain: list[str] = [] - visited: set[str] = set() - stack: list[str] = [ClassName] - parents_map: dict[str, list[str]] = getattr(self, 'class_parents', {}) - while stack: - cur: str = stack.pop() - if not cur or cur in visited: - continue - visited.add(cur) - chain.append(cur) - # 归一化:剥离 "16位hex." 前缀后用裸类名查父类表 - lookup: str = cur - if len(cur) > 17 and cur[16] == '.': - lookup = cur[17:] - parents: list[str] = parents_map.get(lookup, parents_map.get(cur, [])) - # 父类逆序压栈,保证声明顺序弹出(先访问父类1 的整条链) - for p in reversed(parents): - if p and p not in visited: - stack.append(p) - return chain - - def _get_parent_classes(self, ClassName: str) -> list[str]: - """获取类的所有父类名(声明顺序),兼容旧 class_parent 单链。""" - parents_map: dict[str, list[str]] = getattr(self, 'class_parents', {}) - parents: list[str] = parents_map.get(ClassName) - if parents: - return parents - p: str | None = self.class_parent.get(ClassName) - if p: - return [p] - return [] - - def _CreateOrReuseFuncDecl(self, name: str, func_type: ir.FunctionType) -> ir.Function: - """函数声明创建的唯一幂等入口(所有"新建 ir.Function"的路径都应走这里)。 - - llvmlite 的 NameScope 按名字去重(GlobalValue.deduplicate_name=False): - 同名 global 再建一次必抛 llvmlite.ir._utils.DuplicatedNameError(只给名字、 - 不给两侧签名,现场:5d84ea579d83ef73.CallGenFd._translate_fakeduck_call)。 - 故此处: - · module.globals 已有同名函数且函数类型(按字符串比较,FunctionType 无 - __eq__ ⇒ 只能比结构串)相同 ⇒ **复用**并补登记 Gen.functions; - · 已有但类型不同 ⇒ fail-fast 报出两侧签名 —— 这是 declare / define 分裂的 - 真因,绝不静默复用错签名(会让调用点按错签名传参)。 - """ - # ---- 创建前自检(两条,都是"进了 llvmlite 就不可救"的状态)---- - # 背景:llvmlite 的 `Function.__init__` 顺序是 - # NamedValue.__init__ → _set_name → module.scope.register(name) 【名字占用】 - # ... 逐形参 `Argument(...)`(内部 assert isinstance(typ, types.Type))... - # module.add_global(self) 【进入 globals】 - # 中间任一步抛错,Python 都不会回滚 scope ⇒ 留下"半注册"的名字:globals 查无、 - # scope 却已占用。此后同一名字再建必抛 DuplicatedNameError(报文只有名字、 - # 没有两侧信息)。现场:972cf2b7756765d5.ImportsGenMixin._index_imported_module - # —— 上游首建失败被 `except Exception` 静默吞掉,只剩这个裸报错。 - # ① 签名自检:返回类型/任一形参不是 llvmlite Type(典型 None=注解未解析出来) - # 会在 `Argument(...)` / `ReturnValue(...)` 处 assert 失败,而失败点在名字 - # 注册之后(见上)⇒ 在此先行判定,给出可定位报文。 - _bad_desc: str | None = None - _bad_ty: Any = None - _ret_ty: Any = getattr(func_type, 'return_type', None) - if not isinstance(_ret_ty, ir.Type): - _bad_desc = '返回类型' - _bad_ty = _ret_ty - else: - for _ai, _at in enumerate(getattr(func_type, 'args', ()) or ()): - if not isinstance(_at, ir.Type): - _bad_desc = f'第 {_ai} 个形参类型' - _bad_ty = _at - break - if _bad_desc is not None: - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"函数声明签名非法:'{name}' 的{_bad_desc}是 " - f"{type(_bad_ty).__name__}({str(_bad_ty)[:80]}),不是 llvmlite 类型" - f"\n · 本次签名: {func_type}" - f"\n 成因:该处注解未解析出 llvmlite Type(解析失败返回 None / 拿到的是" - f"符号表条目等非类型对象)。llvmlite 会在 Argument(...)/ReturnValue(...) " - f"处 assert 失败,而失败点落在「名字已进 module.scope、尚未 add_global」" - f"之间 ⇒ 留下半注册名字,后续同名创建只报 DuplicatedNameError。" - f"\n 修法:核对上面这个位置在声明侧(源码注解 / .vpi stub)为何解析不出类型。" - + self._get_node_info()) - # ② 悬空名字自检:scope 已占用而 globals 无对象 ⇒ 上一次同名创建中途失败 - # (异常被吞/未走完 add_global)。此处若继续新建只会得到裸 DuplicatedNameError, - # 故直接报出状态事实(不静默清理:真因在"上一处被吞的创建失败",清理会掩盖它)。 - _mod_scope: Any = getattr(self.module, 'scope', None) - if (name not in self.module.globals and _mod_scope is not None - and getattr(_mod_scope, 'is_used', None) is not None - and _mod_scope.is_used(name)): - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"函数声明名字悬空占用:'{name}' 已进 module.scope,但 module.globals " - f"中无对应对象——llvmlite 的 Function 在「名字注册」与 add_global 之间" - f"失败时不会回滚 scope。" - f"\n ⇒ 说明**此前对同名函数的创建中途失败且异常被吞**(真因在那一处," - f"不在本次调用)。本次继续新建只会得到裸 DuplicatedNameError。" - f"\n 修法:先修掉上一处被吞的创建失败(形参类型非法 / 签名解析失败)," - f"半注册名字随之消失。" - + self._get_node_info()) - existing: Any = self.module.globals.get(name) - if existing is not None and not isinstance(existing, ir.Function): - # 名字被非函数 global 占住(全局变量 / vtable / 字符串常量…):llvmlite 的 - # NameScope 按名字去重,再建同名 ir.Function 必抛 DuplicatedNameError - # (报文只有名字,不含冲突类型)⇒ 在此把冲突双方摊开,避免裸抛。 - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"同名全局符号冲突:'{name}' 已在 module.globals 中登记,但不是函数" - f"({type(existing).__name__}):{existing!r}" - f"\n 函数声明无法复用该符号(llvmlite NameScope 按名字去重);" - f"请核对该名字是否同时被用作全局变量 / vtable / 字符串常量。") - if isinstance(existing, ir.Function): - _old_type: Any = getattr(existing, 'ftype', None) - if _old_type is None or str(_old_type) == str(func_type): - self.functions[name] = existing - return existing - from lib.core.LLVMCG.FuncGen import FailFastError - raise FailFastError( - f"同名函数声明签名冲突(declare/define 分裂):'{name}'" - f"\n · 已存在声明: {_old_type}" - f"\n · 本次要创建: {func_type}" - f"\n 拒绝静默复用(调用点会按错签名传参);请核对同一函数在声明侧" - f"(stub / .vpi)与定义侧的注解解析结果是否一致。") - func: ir.Function = ir.Function(self.module, func_type, name=name) - self.functions[name] = func - return func - - def _get_or_declare_function(self, mangled_name: str, func_type: ir.FunctionType) -> ir.Function: - return self._CreateOrReuseFuncDecl(mangled_name, func_type) - - def _get_function(self, name: str) -> ir.Function | None: - func: ir.Function | None = self._find_function(name) - return func - - def _mangle_func_name(self, name: str, module_name: str | None = None) -> str: - if name in self._export_funcs and not module_name: - return name - if name in self._export_extern_funcs: - target_sha1: str = self._export_extern_funcs[name] - if module_name: - # 显式带 module_name 调用(如 stdlib.system):即使当前模块定义了同名函数, - # 也应按模块匹配 extern,返回裸名。snprintf 之所以工作而 system 不工作, - # 就是因为 os._win32 定义了自己的 system 包装,导致旧逻辑跳过了 extern 匹配。 - if module_name == target_sha1: - return name - if module_name in self.ModuleSha1Map and self.ModuleSha1Map[module_name] == target_sha1: - return name - init_name: str = f"{module_name}.__init__" - if init_name in self.ModuleSha1Map and self.ModuleSha1Map[init_name] == target_sha1: - return name - # 兜底:module_name 不在 ModuleSha1Map 中或 sha1 不匹配, - # 但函数在 _export_extern_funcs 中(C extern 不应混淆), - # 仅当当前模块未定义同名函数时才返回裸名。 - if name not in self._current_module_func_names: - return name - elif name not in self._current_module_func_names: - # 无 module_name 调用(如 from stdlib import system; system()): - # 仅当当前模块未定义同名函数时才返回 extern 裸名, - # 否则让本地定义走正常 mangle 路径(本地 def 遮蔽 import 语义)。 - return name - # main 函数作为程序入口点,永远不混淆 - if name == 'main': - return name - # Try class_sha1_map first for class methods (e.g., 'md5.__init__' -> class 'md5') - class_sha1_map: dict[str, str] = self.class_sha1_map - if class_sha1_map: - if '.' in name: - base_class: str = name.split('.')[0] - if base_class in class_sha1_map: - sha1: str = class_sha1_map[base_class] - return f"{sha1}.{name}" - elif name in class_sha1_map: - sha1: str = class_sha1_map[name] - return f"{sha1}.{name}" - # If module_name is already a SHA1 hash (16 hex chars), use it directly - if module_name and len(module_name) == 16 and all(c in '0123456789abcdef' for c in module_name): - return f"{module_name}.{name}" - if module_name and module_name in self.ModuleSha1Map: - sha1: str = self.ModuleSha1Map[module_name] - return f"{sha1}.{name}" - # Try parent package name (e.g., 'hashlib' -> 'hashlib.__init__') - if module_name: - init_name: str = f"{module_name}.__init__" - if init_name in self.ModuleSha1Map: - sha1: str = self.ModuleSha1Map[init_name] - return f"{sha1}.{name}" - if self._skip_mangle(name): - return name - if self.module_sha1: - result = f"{self.module_sha1}.{name}" - else: - result = name - return result - - def _get_source_line(self, lineno: int) -> str | None: - if 0 < lineno <= len(self._current_source_lines): - return self._current_source_lines[lineno - 1].rstrip('\r\n') - return None - - def _FindConcreteTwin(self, StructType: Any) -> Any: - """按**同短名**在 `structs` 里找一份**已具化**(elements 非 None)的 struct 实例。 - - 为什么需要:同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名 - '.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份" - 并不保证一致;一旦被 GEP 的那份是 opaque,llvmlite 在编译期算元素类型即抛裸 - TypeError(现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)` - 的基址 pointee 是 opaque 的 `%".Keyword"*`)。调用方把指针 bitcast 到 - 本函数返回的实例即可让"偏移与 GEP 同源"——与 HandlesAssign 里 - "同短名取字段最多版本"是同一判据。 - 返回 None 表示**没有**已具化孪生体(真因是"body 从未 set_body")⇒ 调用方不得 - 兜底,交由 RequirePointerOperand 的 opaque 守卫 fail-fast 并报出实例清单。 - """ - if not isinstance(StructType, ir.IdentifiedStructType): - return None - _Short: str = '' - try: - _Short = self._extract_short_name(getattr(StructType, 'name', '') or '') - except Exception: - _Short = '' - if not _Short: - return None - # 第一优先:**IR 全名相同**的实例(同类名两份实例的情形,必为同一类); - # 第二优先:同短名里元素最多的一份(跨模块同名类;与 HandlesAssign - # "同短名取字段最多版本"同判据)。全名相同优先可避免"短名相同但实为另一个 - # 模块的同名类"被误选而算出错的偏移。 - _Full: str = getattr(StructType, 'name', '') or '' - _Best: Any = None - _BestN: int = -1 - for _v in list(getattr(self, 'structs', {}).values()): - if not isinstance(_v, ir.IdentifiedStructType) or _v.elements is None: - continue - if _Full and getattr(_v, 'name', '') == _Full: - return _v - try: - _s: str = self._extract_short_name(getattr(_v, 'name', '') or '') - except Exception: - _s = '' - if _s != _Short: - continue - if len(_v.elements) > _BestN: - _BestN = len(_v.elements) - _Best = _v - return _Best - - def _DiskLineno(self, Lineno: int) -> int: - """AST 行号 → 磁盘真实行号(减掉文件头自动注入 import 的行数)。 - - 判据与其余各处**逐字一致**(HandlesBase._FailFastFixedArrayKeyword:687、 - HandlesAssign._RaiseGlobalAttrError:2051、LlvmGenerator/HandlesBody 的 - _ErrorStack 各站点):Phase2Translator 在解析前用 _inject_auto_imports - 往源码头注入 `import _list/_dict/json`,AST 全部节点行号因此整体下移 N - (translator._line_offset = N);而 `Gen._current_source_lines` 是**磁盘** - 原文 ⇒ 任何"AST 行号 + 源码行"的输出都必须先减 N,否则印出的源码是下一行。 - 本渲染器此前漏还原,于是同一次 fail-fast 里出现两套矛盾行号 - (现场:真实错误在 TypeGenResolve.vp:1141 `= "t.CPtr"`, - _get_node_info 印 1142 且源码取到 1142 行的 `return "t.CPtr"`, - 而 _ErrorStack 帧印的是正确的 1141/1140)。 - """ - if Lineno <= 0: - return Lineno - _offset: int = int(getattr(self._Trans, '_line_offset', 0) or 0) - if _offset > 0 and Lineno > _offset: - return Lineno - _offset - return Lineno - - def _get_node_info(self) -> str: - info: str = "" - if self._current_node_info: - # 标签里的行号同样是 AST 行号:必须与下面的源码行**一起**还原, - # 否则标签(1142)与源码(1141)互相矛盾。 - _m = _LINE_INFO_RE.match(self._current_node_info) - _info: str = (f"line {self._DiskLineno(int(_m.group(1)))}{_m.group(2)}" - if _m is not None else self._current_node_info) - info = f" [{_info}]" - if self._current_source_file: - info += f" in {self._current_source_file}" - lineno: int = self._DiskLineno(self._current_lineno) - if lineno > 0: - info += f", line {lineno}:" - source_line: str | None = self._get_source_line(lineno) - if source_line: - info += f"\n → {source_line}" - return info - - def _DescribeValueNode(self, Node: Any = None) -> str: - """把「真正被转换的值节点」渲染成一段诊断文本,供隐式转换禁令引用。 - - 报文风格照本仓既有「表达式文本 + 位置 + 源码片段」: - 表达式 ''(<节点形态>,位置 文件:行:列),源码: <该行> - 关键 ①:**列号按有符号打印**——未知(无属性 / <0)时打印 '-',绝不把 col_offset - 的 -1 当无符号打成 4294967295(此前 TPV 侧诊断错位的根因)。取不到节点时 - 回退当前语句上下文(_get_node_info 风格),不再固定打印外层 Assign。 - 关键 ②:**行号必须先还原为磁盘行号**(_DiskLineno)——节点行号含文件头 - 自动注入 import 的偏移,直接用来读源码会读到下一行(现场:真实错误行 - 1141,这里印成 1142 且源码显示出 1142 行的 `return "t.CPtr"`)。 - """ - if Node is None: - return self._get_node_info() - try: - Expr: str = ast.unparse(Node) - except Exception: - Expr = getattr(Node, 'attr', '') or getattr(Node, 'id', '') or '' - RawLine: int = getattr(Node, 'lineno', 0) or 0 - Col: Any = getattr(Node, 'col_offset', -1) - Line: int = self._DiskLineno(RawLine or self._current_lineno or 0) - Loc: str = self._current_source_file or '' - if Line > 0: - Loc += f":{Line}" - Loc += f":{Col}" if isinstance(Col, int) and Col >= 0 else ":-" - Code: str = '' - if Line > 0: - try: - Code = (self._get_source_line(Line) or '').strip() - except Exception: - Code = '' - Text: str = f";出错表达式: 表达式 '{Expr}'({type(Node).__name__} 形态,位置 {Loc})" - if Code: - Text += f",源码: {Code}" - return Text - - # ========== const / volatile:判定、登记与报文(两侧同口径) ========== - # 语义唯一来源见 Debugger/type-descriptors.md。TPC 与 TransPyV 必须**同判据、 - # 同报文**;任何未消除的差异都列在该文档的「已知差异」一节。 - - @staticmethod - def _IsConstObject(Info: Any) -> bool: - """注解解析结果是否为「变量自身 const」的对象(C 语义下的 const 对象)。 - - 判据(两侧统一):`(DataConst or VarConst)` 且 `PtrCount == 0`。 - `t.CConst` 在本仓库解析为 **DataConst**(t 模块分支:HandlesTypeMerge.py:192-196、 - TypeAnnotationResolver.py:382-389),其字段语义是「指向的数据 const」 - (HandlesBase.py:76-77)。因此: - - 非指针类型(PtrCount==0):该限定退化为「该对象自身 const」⇒ - `a: const int` / `a: t.CConst | int` 的 a 禁止赋值/增量赋值/元素赋值; - - 指针类型(PtrCount>=1):限定只作用于 pointee ⇒ `const T *p` 的 p - 仍可重新赋值(C 语义),**不**登记为 const 变量。 - 后者就是全仓既有两处 `t.CConst | T | t.CPtr` 局部变量(fat32_types.vp:1591 - `buf_ptr`、elf.vp:509 `cmp_name`)在其作用域内被重新赋值却合法的原因。 - """ - if Info is None: - return False - if not (getattr(Info, 'DataConst', False) or getattr(Info, 'VarConst', False)): - return False - return getattr(Info, 'PtrCount', 0) == 0 - - @staticmethod - def _AnnotationDeclText(Annotation: Any) -> str: - """注解节点的源码文本(仅用于报文;取不到时给占位,不伪造)。""" - if Annotation is None: - return '(无注解)' - try: - return ast.unparse(Annotation) - except Exception: - return '<无法还原注解文本>' - - @staticmethod - def _AnnotationHasQualifier(Annotation: Any, AttrName: str) -> bool: - """注解子树里是否出现 `t.`(与 TransPyV TypeGenErr.has_decorator_marker 同口径)。""" - if Annotation is None: - return False - for n in ast.walk(Annotation): - if isinstance(n, ast.Attribute) and n.attr == AttrName: - return True - return False - - @staticmethod - def _AnnotationHasPointeeVolatile(Annotation: Any) -> bool: - """注解是否为 `rawptr[t.CVolatile | T]`(volatile 指针指向物,等价 C 的 volatile T*)。 - - 与 TransPyV TypeGenErr.has_pointee_volatile_marker 同口径:只看 `rawptr[...]` - 的下标实参子树里有没有 CVolatile;多级指针(slice 仍是 Subscript)明确不支持。 - """ - if not isinstance(Annotation, ast.Subscript): - return False - base: Any = Annotation.value - if not isinstance(base, ast.Name) or base.id != 'rawptr': - return False - return BaseGenMixin._AnnotationHasQualifier(Annotation.slice, 'CVolatile') - - def RegisterVarQualifiers(self, Name: str, Info: Any, Annotation: Any, - IsGlobal: bool = False, IsParam: bool = False) -> None: - """登记一个变量(局部/形参/模块级全局)的 const / volatile 限定。 - - 统一的登记入口(TPC 的形参、局部变量、模块级全局三处声明点都调本函数), - 保证「登记点」只有一份判据。 - - IsParam=True 时 **const 判据沿用形参的历史条件** `DataConst or VarConst` - (不看指针层数):这是「形参保持现状 + 不得放宽任何既有 fail-fast」两条 - 硬约束下的唯一选择——把形参也改成指针感知判据虽然更合 C 语义,但会**放宽** - 既有形参检查(`fmt: t.CConst | str` 这类指针形参不再登记),从约束上不允许。 - 该历史条件对 `const T*` 形参过严,但全仓 462 个 .vp 普查(见 - Debugger/type-descriptors.md)表明**零个形参在其函数体内被重新赋值**, - 故这处差异在既有代码上不可观测。非形参的三种声明一律用指针感知判据 - `_IsConstObject`。 - """ - if not Name: - return - if IsParam: - IsConst: bool = bool(Info) and ( - getattr(Info, 'DataConst', False) or getattr(Info, 'VarConst', False)) - else: - IsConst = self._IsConstObject(Info) - if IsConst: - if IsGlobal: - self.global_const_flags[Name] = True - else: - self.var_const_flags[Name] = True - self.const_decl_types[Name] = self._AnnotationDeclText(Annotation) - if self._AnnotationHasQualifier(Annotation, 'CVolatile'): - if IsGlobal: - self.global_volatile_flags[Name] = True - else: - self.var_volatile_flags[Name] = True - if self._AnnotationHasPointeeVolatile(Annotation): - if IsGlobal: - self.global_pointee_volatile_flags[Name] = True - else: - self.var_pointee_volatile_flags[Name] = True - - def RegisterFieldQualifiers(self, ClassName: str | None, FieldName: str, - Info: Any, Annotation: Any) -> None: - """登记一个类字段的 const / volatile 限定(键 `类名.字段名` + 裸字段名)。""" - if not FieldName: - return - if self._IsConstObject(Info): - if ClassName: - self.field_const_flags[f'{ClassName}.{FieldName}'] = True - self.field_const_names.add(FieldName) - self.const_decl_types[FieldName] = self._AnnotationDeclText(Annotation) - if self._AnnotationHasQualifier(Annotation, 'CVolatile'): - if ClassName: - self.field_volatile_flags[f'{ClassName}.{FieldName}'] = True - self.field_volatile_names.add(FieldName) - if self._AnnotationHasPointeeVolatile(Annotation): - if ClassName: - self.field_pointee_volatile_flags[f'{ClassName}.{FieldName}'] = True - self.field_pointee_volatile_names.add(FieldName) - - def _IsVarConst(self, Name: str) -> bool: - """变量(局部/形参/模块级全局)自身是否 const。""" - return bool(Name) and (Name in self.var_const_flags or Name in self.global_const_flags) - - def _IsVarVolatile(self, Name: str) -> bool: - """变量自身是否 volatile。""" - return bool(Name) and (Name in self.var_volatile_flags or Name in self.global_volatile_flags) - - def _IsVarPointeeVolatile(self, Name: str) -> bool: - """变量所指物是否 volatile(等价 C 的 volatile T* 解引用)。""" - return bool(Name) and (Name in self.var_pointee_volatile_flags - or Name in self.global_pointee_volatile_flags) - - def _IsFieldConst(self, ClassName: str | None, FieldName: str) -> bool: - """类字段自身是否 const。 - - 类名可解析时按精确键 `类名.字段名`(**不**回退裸名,避免"另一个同名 - const 字段"造成跨类误伤);类名解析不到时退化为裸名集合(保守报错)。 - """ - if not FieldName: - return False - if ClassName: - return f'{ClassName}.{FieldName}' in self.field_const_flags - return FieldName in self.field_const_names - - def _IsFieldVolatile(self, FieldName: str) -> bool: - return bool(FieldName) and FieldName in self.field_volatile_names - - def _IsFieldPointeeVolatile(self, FieldName: str) -> bool: - return bool(FieldName) and FieldName in self.field_pointee_volatile_names - - @staticmethod - def _StripDedupSuffix(Name: str) -> list[str]: - """IR 名 -> 候选登记名(llvmlite 去重把重复名改成 `名.1` / `名.2`)。""" - Cands: list[str] = [Name] - Head, _, Tail = Name.rpartition('.') - if Head and Tail.isdigit(): - Cands.append(Head) - return Cands - - def _IsPtrVolatile(self, ptr: Any) -> bool: - """按 IR 指针对应的**登记名**判定该次访问是否带 volatile。 - - alloca / GlobalVariable 均以变量名命名,GEP 以字段名命名 - (见 HandlesExprAttr 的 `gep(..., name=AttrName)`),故变量自身 volatile、 - 变量所指物 volatile 与字段 volatile 都在此命中。名字取不到时返回 False - (不做任何猜测/兜底)。 - - 下标 GEP 额外带基址名(`subscript_<基址名>` / `subscript_ptr_<基址名>`, - 见 HandlesExprAttr/HandlesAssign 的下标改写):基址是 pointee-volatile - 变量/字段时,经该下标的 load/store 也要带 volatile(等价 C 的 - `volatile T* p` 的 `p[i]`)。**基址名后缀为空时名字与改造前逐字相同** - ⇒ 无 pointee-volatile 的代码 IR 逐字节不变。 - """ - nm: Any = getattr(ptr, 'name', None) - if not nm or not isinstance(nm, str): - return False - for C in self._StripDedupSuffix(nm): - if self._IsVarVolatile(C) or self._IsVarPointeeVolatile(C): - return True - if C in self.field_volatile_names or C in self.field_pointee_volatile_names: - return True - BaseNames: list[str] = list(self.var_pointee_volatile_flags) - BaseNames += list(self.global_pointee_volatile_flags) - BaseNames += list(self.field_pointee_volatile_names) - for B in BaseNames: - if (nm == f'subscript_{B}' or nm.startswith(f'subscript_{B}.') - or nm == f'subscript_ptr_{B}' or nm.startswith(f'subscript_ptr_{B}.')): - return True - return False - - @staticmethod - def _SubscriptSuffix(BaseNode: Any) -> str: - """下标基址名后缀(供 pointee-volatile 按 IR 名回查,见 `_IsPtrVolatile`)。 - - 基址是 `Name`(变量)→ `_变量名`;是 `obj.字段` → `_字段名`;其余形态返回 - `''`。**返回 `''` 时下标 GEP 名与改造前逐字相同**(既有代码 IR 零变更)。 - """ - if isinstance(BaseNode, ast.Name): - return f'_{BaseNode.id}' if BaseNode.id else '' - if isinstance(BaseNode, ast.Attribute): - return f'_{BaseNode.attr}' if BaseNode.attr else '' - return '' - - def _ConstAssignMessage(self, Kind: str, Name: str) -> str: - """const 变量被赋值时的 fail-fast 报文(两侧同口径)。 - - 含:文件/行/列 + 源码片段(`_get_node_info`)+ 变量名 + 声明处类型(含 const) - + 修法。 - """ - DeclType: str = self.const_decl_types.get(Name, '(声明处类型未登记)') - return ( - f"不能对 const 变量 '{Name}' {Kind}{self._get_node_info()}" - f"(声明处类型(含 const):{DeclType};" - f"修法:删掉该声明里的 `const` 描述符 / `t.CConst`,或改为声明时一次赋值、" - f"之后只用非 const 的副本/中间量;const 对象在其作用域内禁止赋值、" - f"增量赋值与元素赋值,且不会降级为警告)" - ) - - # ========== 统一工具方法 ========== - - def _create_string_global(self, s: str, name: str | None = None) -> ir.Constant: - """创建字符串全局变量并返回 i8* 指针。 - - 统一字符串全局变量创建模式:编码为 utf-8 + '\\x00' 终止符, - 创建 internal linkage 的 GlobalVariable,返回 i8* 指针常量。 - """ - str_val: str = s + '\x00' - str_bytes: bytes = str_val.encode('utf-8') - arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) - gv_name: str = name or f"str_const_{self.string_const_counter}" - self.string_const_counter += 1 - gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_type, name=gv_name) - gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) - gv.linkage = 'internal' - return ir.Constant(ir.PointerType(ir.IntType(8)), gv.get_reference()) - - def _create_doc_global(self, symbol_name: str, docstring: str | None) -> None: - """为符号创建 __doc__ 全局常量。 - - - docstring 为 None 或空字符串时,不创建常量(访问时内联空指针) - - slice_level != 0(优化开启)时,内容视为空 - - 非空时创建 internal linkage、global_constant=True 的只读字符串常量 - - 常量名格式:{module_sha1}.{symbol_name}.__doc__ - """ - slice_level: int = getattr(self._Trans, 'SliceLevel', 3) - if slice_level != 0: - docstring = None - if docstring is None or docstring == '': - self._doc_strings[symbol_name] = None - return - self._doc_strings[symbol_name] = docstring - mangled: str = self._mangle_name(f"{symbol_name}.__doc__") - self._create_string_global(docstring, name=mangled) - gv: ir.GlobalVariable | None = self.module.globals.get(mangled) - if gv is not None: - gv.global_constant = True - - def _get_doc_ptr(self, symbol_name: str) -> ir.Value | None: - """获取符号 __doc__ 的 i8* 指针。 - - - 若符号未注册 __doc__,返回 None(调用方回退到其他解析) - - 若 __doc__ 为空(无 docstring 或优化剥离),返回 i8* null 内联空指针 - - 若 __doc__ 非空,返回指向字符串全局常量的 i8* 指针 - - 跨模块 docstring(在 _doc_strings 中有文本但 module.globals 无对应常量)首次访问时创建本地 internal linkage 只读副本 - """ - if symbol_name not in self._doc_strings: - return None - docstring: str | None = self._doc_strings[symbol_name] - if docstring is None or docstring == '': - return ir.Constant(ir.PointerType(ir.IntType(8)), None) - mangled: str = self._mangle_name(f"{symbol_name}.__doc__") - gv: ir.GlobalVariable | None = self.module.globals.get(mangled) - if gv is None: - # 跨模块 docstring:创建本地 internal linkage 只读字符串常量副本 - self._create_string_global(docstring, name=mangled) - gv = self.module.globals.get(mangled) - if gv is None: - return ir.Constant(ir.PointerType(ir.IntType(8)), None) - gv.global_constant = True - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - return self.builder.gep(gv, [zero, zero], name=f"{symbol_name}_doc_ptr") - - def _apply_bswap_if_big(self, val: ir.Value, byte_order: str, name: str) -> ir.Value: - """大端字节序时对整数应用 bswap,否则原值返回。 - - 仅对 16/32/64 位整数生效,统一 bswap 逻辑。 - """ - if byte_order == 'big' and isinstance(val.type, ir.IntType) and val.type.width in (16, 32, 64): - return self.builder.bswap(val, name=name) - return val - - def _zero_value_for_type(self, llvm_type: ir.Type) -> ir.Constant | None: - """为指定 LLVM 类型生成零值常量。 - - 合并原 HandlesAssign._zero_value 与 StructGen._zero_value_for_type 的逻辑。 - 返回 ir.Constant 或 None(无法生成零值的类型,如 opaque struct)。 - """ - if isinstance(llvm_type, ir.IntType): - return ir.Constant(llvm_type, 0) - elif isinstance(llvm_type, (ir.FloatType, ir.DoubleType)): - return ir.Constant(llvm_type, 0.0) - elif isinstance(llvm_type, ir.PointerType): - return ir.Constant(llvm_type, None) - elif isinstance(llvm_type, ir.ArrayType): - elem_zv: ir.Constant | None = self._zero_value_for_type(llvm_type.element) - if elem_zv is None: - return None - return ir.Constant(llvm_type, [elem_zv] * llvm_type.count) - elif isinstance(llvm_type, ir.BaseStructType): - if llvm_type.elements is not None and len(llvm_type.elements) > 0: - elem_zvs: list[ir.Constant | None] = [self._zero_value_for_type(m) for m in llvm_type.elements] - if any(zv is None for zv in elem_zvs): - return None - return ir.Constant(llvm_type, elem_zvs) - return None - return None - - # ========== 调用点热点统计基础设施(--hotspot-sites) ========== - - def _UniqueGlobalName(self, base: str) -> str: - """返回本模块内未被占用的全局符号名(追加下划线直到不冲突)。 - - 仅用于 --hotspot-sites 自动生成的内部符号,避免与用户全局/函数重名。 - """ - name: str = base - while name in self.module.globals: - name += '_' - return name - - def _DeclareExternalFunc(self, name: str, ftype: ir.FunctionType) -> ir.Function: - """获取或声明外部 C 函数(用于 --hotspot-sites 所需的 Win32/CRT 接口)。 - - 已存在同名函数时直接复用;若已存在符号类型不一致则显式报错(不静默降级)。 - """ - existing: Any = self.module.globals.get(name) - if existing is not None: - if not isinstance(existing, ir.Function): - raise RuntimeError( - f'--hotspot-sites: 全局符号 {name} 已存在且不是函数,无法声明外部接口') - if existing.ftype != ftype: - raise RuntimeError( - f'--hotspot-sites: 外部函数 {name} 已存在但签名不一致' - f'(期望 {ftype},实际 {existing.ftype})') - return existing - return self._CreateOrReuseFuncDecl(name, ftype) - - def _InstallHotspotTruncate( - self, - defs: list[ir.Function], - path: str, - require_main: bool = False, - flag: str = '--hotspot-sites', - ) -> ir.Function | None: - """在含 main 的模块的 main 入口插入一次 "w" 打开→关闭,清空计数文件。 - - --hotspot-sites 的各处理器(调用点计数、每函数计时)以追加("a")方式写 - ,若不清空会累积历史运行的数据。清空必须满足两点: - - 只在含 main 的模块执行一次(其它模块无 main,返回 None 且不截断); - - 必须在 main 入口而非构造器:@llvm.global_ctors 的跨模块执行顺序无保证, - 放构造器里可能在其它模块已写入后清空 → 丢数据。 - main 入口在程序启动、任何 Ctrl+C 处理器写入之前执行,故每次运行都以空文件 - 开始,配合追加写保证"一次运行一份结果"。 - - flag 仅用于报错信息(调用点计数与每函数计时各自调用一次,传入对应开关名)。 - - 返回找到的 main 定义(供调用方继续注册处理器);未找到时返回 None。 - require_main=True(种子 CLI 单模块)时未找到 main 直接报错(不静默降级)。 - """ - main_func: ir.Function | None = None - for func in defs: - if func.name == 'main': - main_func = func - break - if main_func is None: - if require_main: - raise RuntimeError( - f'{flag}: 找不到 main 函数定义,无法清空计数文件') - return None - i32: ir.IntType = ir.IntType(32) - i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) - fopen: ir.Function = self._DeclareExternalFunc('fopen', ir.FunctionType(i8p, [i8p, i8p])) - fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p])) - path_ptr: ir.Constant = self._create_string_global( - path, name=self._UniqueGlobalName('__fhs_trunc_path')) - mode_ptr: ir.Constant = self._create_string_global( - 'w', name=self._UniqueGlobalName('__fhs_trunc_mode')) - b: ir.IRBuilder = ir.IRBuilder(main_func.entry_basic_block) - b.position_at_start(main_func.entry_basic_block) - fp: ir.Value = b.call(fopen, [path_ptr, mode_ptr], name='fhs_trunc_fp') - b.call(fclose, [fp]) - return main_func - - # ========== 调用点热点统计(--hotspot-sites) ========== - - def _NewIrBuilder(self, block: ir.Block) -> ir.IRBuilder: - """创建 IR 构建器:IR 边界守卫(无条件)+ --hotspot-sites 源码位置挂钩。 - - 未启用热点统计(hotspot_sites_file 为 None)时返回 _GuardIrBuilder: - 合法 IR 与原生 ir.IRBuilder 完全一致,只在 GEP/load/store 的指针操作数 - 不是指针时 fail-fast(否则 llvmlite 会抛无位置信息的裸 AttributeError - 'IntType' object has no attribute 'is_opaque',见 RequirePointerOperand)。 - 启用时返回 _HotspotSiteBuilder(同为 _GuardIrBuilder 子类,守卫不变), - 仅在每条新生成的 call 指令对象上追加 _src_file/_src_line 两个普通属性, - 供调用点热点插桩读取源码位置。 - """ - if self.hotspot_sites_file: - return _HotspotSiteBuilder(block, self) - return _GuardIrBuilder(block, self) - - @staticmethod - def _HotspotCalleeBase(name: str) -> str: - """返回被调函数名的基名:去掉 16 位 sha1 模块前缀。 - - 仅当形如 "<16位十六进制>.xxx" 时才剥离,避免误伤普通函数名; - 例如 "23499a69276862b2.strcmp" -> "strcmp","strlen" -> "strlen"。 - """ - if len(name) > 17 and name[16] == '.': - prefix: str = name[:16] - if all(ch in '0123456789abcdefABCDEF' for ch in prefix): - return name[17:] - return name - - def _EmitHotspotSiteCounters( - self, - ) -> tuple[list[ir.Function], list[tuple[ir.GlobalVariable, str, str, str, int]]]: - """为匹配白名单的每个直接 call 调用点安装 internal i64 计数器并在 call 之前递增。 - - 返回 (模块内函数定义列表, 记录列表);记录为 - (计数器全局, 调用方函数名, 被调方基名, 源码文件名, 源码行号), - 记录顺序即计数器编号 __fhs_site_ 的顺序。源码位置来自生成期挂在 - call 指令对象上的 _src_file/_src_line(见 _HotspotSiteBuilder);生成期 - 无法关联到 AST 的合成调用点取不到位置,则文件名留空、行号记 0。 - - 插桩规则: - - 白名单:hotspot_sites_all=True 时不过滤(全部直接调用点);否则仅对 - "被调名去 16 位 sha1 前缀后的基名" 命中白名单的调用点插桩。 - - 跳过间接调用(callee 不是具名 ir.Function):无法归属被调方,故不插桩。 - - 跳过 __fhs_ 前缀的辅助函数自身,避免在插桩生成的代码里计数 - (本功能稍后生成的处理器/构造器/计时辅助亦在此列)。 - - 只在 call 之前插入 load/add/store,不改变返回值/参数/控制流。 - - 计数器一律 internal,跨模块(独立翻译单元)链接不产生重复符号,初值 0。 - """ - i64: ir.IntType = ir.IntType(64) - if self.hotspot_sites_all: - whitelist: set[str] | None = None - else: - names: list[str] = ( - list(self.hotspot_callees) if self.hotspot_callees is not None - else list(DEFAULT_HOTSPOT_CALLEES) - ) - whitelist = {self._HotspotCalleeBase(n) for n in names} - defs: list[ir.Function] = [f for f in self.module.functions if getattr(f, 'blocks', None)] - records: list[tuple[ir.GlobalVariable, str, str, str, int]] = [] - for func in defs: - if func.name.startswith('__fhs_'): - continue - for block in list(func.blocks): - for instr in list(block.instructions): - if not isinstance(instr, ir.CallInstr): - continue - callee: Any = instr.callee - if not isinstance(callee, ir.Function) or not callee.name: - continue - base: str = self._HotspotCalleeBase(callee.name) - if whitelist is not None and base not in whitelist: - continue - idx: int = len(records) - counter: ir.GlobalVariable = ir.GlobalVariable( - self.module, i64, name=self._UniqueGlobalName(f'__fhs_site_{idx}')) - counter.linkage = 'internal' - counter.initializer = ir.Constant(i64, 0) - # 在 call 之前递增计数:load -> add 1 -> store(不改变 call 本身) - b: ir.IRBuilder = ir.IRBuilder(block) - b.position_before(instr) - cur: ir.Value = b.load(counter, name=f'fhs_site_load_{idx}') - inc: ir.Value = b.add(cur, ir.Constant(i64, 1), name=f'fhs_site_inc_{idx}') - b.store(inc, counter) - # 源码位置:生成期 _HotspotSiteBuilder 挂在 call 指令对象上;缺失则留空 - src_file: str = getattr(instr, '_src_file', None) or '' - src_line: int = getattr(instr, '_src_line', 0) or 0 - records.append((counter, func.name, base, src_file, src_line)) - return defs, records - - @staticmethod - def _HotspotTimePath(sites_path: str) -> str: - """由调用点计数文件路径推导每函数计时输出文件路径。 - - 取同目录下的固定名 hotspot_time.txt(不覆盖、不解析既有 hotspot_sites.txt - 格式),便于下游分别解析两类结果。 - """ - return os.path.join(os.path.dirname(sites_path), 'hotspot_time.txt') - - # 深度栈容量:512。超出该深度的调用不记账(self/inc 均不计),但仍在 - # __fhs_ftime_enter_ 里计入进入次数——溢出是"拿不到数值",明确跳过而非伪造。 - _HotspotDepth: int = 512 - - def _EmitReadTimestamp(self, b: ir.IRBuilder, name: str = '') -> ir.Value: - """发射一次**串行化**时间戳读取,返回 i64 tick(入口与每个 ret 前共用)。 - - 串行化方案:在 x86_64 目标上先取一次内存屏障 @llvm.x86.sse2.lfence 再读 - @llvm.readcyclecounter()(该 intrinsic 在 x86_64 后端降为 rdtsc)。理由: - - lfence 在 Intel/AMD 上对取指与访存有序列化效果,能阻止 rdtsc 与前后访存 - 乱序(裸 rdtsc 只保证自身 edx:eax 原子,不保证与其它指令的顺序),这是 - "lfence; rdtsc" 这一业界通行的序列化读法; - - 用 LLVM intrinsic 组合而非 ir.InlineAsm:无内联汇编约束串的转义/渲染风险, - 也不依赖 Clang 风格 asm 约束是否被 llvmlite 正确渲染,可移植性更好。 - 非 x86_64 目标(如 aarch64/arm/i386)降级为**裸** @llvm.readcyclecounter(): - lfence 是 x86(SSE2) 专有 intrinsic,在非 x86 目标上不存在、编译期即不可用, - 故此处按目标三元组在**编译期**决定是否发射 lfence(不在运行期静默退化)。 - """ - i64: ir.IntType = ir.IntType(64) - if bool(self._platform_info.get('is_x86_64')): - lfence: ir.Function = self._DeclareExternalFunc( - 'llvm.x86.sse2.lfence', ir.FunctionType(ir.VoidType(), [])) - b.call(lfence, []) - rcc: ir.Function = self._DeclareExternalFunc( - 'llvm.readcyclecounter', ir.FunctionType(i64, [])) - return b.call(rcc, [], name=name) - - def _EmitHotspotTimeCounters( - self, - ) -> list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]]: - """为模块内每个函数定义安装"自身耗时(self) + 包含式耗时(inc) + 进入次数"计时器。 - - 计时原语(串行化读):见 _EmitReadTimestamp(x86_64 上 lfence; rdtsc, - 其它目标降级为裸 rdtsc)。无 libc/Win32 依赖、无需额外链接;精度为 1 个 - tick(x86_64 上即一个 CPU 周期,~0.3ns 量级),故耗时单位记为 tick。 - - exclusive(自身耗时)算法——深度栈: - - 全局 common i64 __fhs_tdepth:当前插桩深度,初值 0;common linkage 让每个 - 翻译单元各自发射同名 tentative 定义、由链接器合并为**全程序唯一一份**, - 故跨模块调用也共用同一张深度栈(独立翻译单元链接不产生重复符号)。 - - 全局 common [DEPTH x i64] __fhs_tstart / __fhs_tchild(零初始化, - DEPTH = _HotspotDepth = 512):按深度索引的"本帧起始 tick"与"本帧内被 - 插桩子调用 inclusive 之和";另有一个 internal i64 抖动槽 __fhs_tscratch, - 用于把"不应记账"的写入导向一个永不读取的位置(等价于不写,但不分支)。 - - 入口(每个被插桩函数 F,索引 N): - t0 = 串行化读时间戳() - d = load __fhs_tdepth - ok = d < DEPTH - __fhs_tstart[ok ? d : 0] = t0 (溢出时写抖动槽) - __fhs_tchild[ok ? d : 0] = 0 (溢出时写抖动槽) - __fhs_tdepth = ok ? d + 1 : d (溢出时不推进深度) - slot = ok ? d : -1 存进该次调用的 alloca(每调用一个独立栈槽) - __fhs_ftime_enter_N += 1 (溢出也计进入次数) - 说明:slot 是"本帧独占的深度索引",递归/重入时各帧各占一层,天然正确; - 因为本帧在其返回前独享 slot,故 ret 时用 __fhs_tstart[slot] 即本帧 t0 - (无需再单独保存 t0 到 alloca,避免出现无人读取的死存储)。 - - 每个 ret 之前(slot = load): - inok = slot >= 0;idx = inok ? slot : 0 - t1 = 串行化读时间戳() - inc = t1 - __fhs_tstart[idx] (本次 inclusive) - self = inc - __fhs_tchild[idx] (减去本次内被插桩子调用的 inc 之和) - __fhs_fself_N += inok ? self : 0 (新增:自身耗时) - __fhs_finc_N += inok ? inc : 0 (保留:包含式耗时) - __fhs_tdepth = inok ? slot : load __fhs_tdepth (回到本帧深度) - if slot > 0(含用抖动槽等价实现): __fhs_tchild[slot-1] += inc - (把本次 inc 记到父帧的 child 累加器) - 溢出帧(slot=-1)不记账、也不写入父帧 child;其墙钟时间仍落在父帧的 - inc 里,因而会计入父帧的 **self**(见下方说明)。 - - 返回记录列表 (自身耗时全局, 包含式耗时全局, 进入次数全局, 函数名),顺序即 - N 的编号顺序。落盘由 _CreateHotspotTimeDump 完成(按 self 降序)。 - - 固有性质(务必据此解读数值):**未插桩的被调方**(间接调用、无定义体的外部 - 函数如 strcmp/memcpy、名字以 __fhs_ 开头的辅助函数、深度溢出而跳过的调用) - 其耗时不会被本方法从调用者 self 中扣除,一律计入**调用者自身 self**。故 self - 是"该函数自身指令 + 未插桩子调用"的墙钟时间,只是比 inc 更贴近自身成本。 - 深度栈为 common(全程序一份)后,跨模块被调方只要被插桩即能正确扣除;修复前 - 各模块各持一份 internal 深度栈,跨模块子调用的 inc 记不进调用方 tchild, - 表现为调用方 self ≈ inc 的假象(见 common linkage 说明)。 - - 插桩规则与跳过条件: - - 只处理有基本块(即已定义)且名字不以 __fhs_ 开头的函数;每函数计数器一律 - internal(同名索引跨模块并存,不可合并);深度栈用 common 由链接器合并为 - 全程序唯一一份,初值 0。 - - 入口代码置于入口块最前(无终结指令,不改变 CFG);条件写入用 select 实现, - 不新增基本块、不修改任何既有分支/终结指令。 - - 只在该函数的 ret 终结指令之前插桩;以 unreachable 等其它终结指令结尾的 - 块(如 noreturn 调用后的代码)没有"返回点",明确跳过、不伪造数值(数量见 - 报告,如需可另行统计)。 - - 累加只做 load/sub/add/store/select 与读取计时原语,不改变返回值/参数/控制流。 - """ - depth: int = self._HotspotDepth - i32: ir.IntType = ir.IntType(32) - i64: ir.IntType = ir.IntType(64) - c0_32: ir.Constant = ir.Constant(i32, 0) - c0_64: ir.Constant = ir.Constant(i64, 0) - c1_64: ir.Constant = ir.Constant(i64, 1) - cneg1_64: ir.Constant = ir.Constant(i64, -1) - # 深度栈三件套用 common linkage:每模块各自发射一份同名 tentative 定义, - # 由链接器合并成"全程序唯一一份"(与 HandlesAssign 中多模块同名全局同法, - # 避免 ld.lld duplicate symbol)。跨模块调用链必须共用同一张深度栈,否则 - # 被调方在自模块栈里 slot=0(或 slot 落在错误父帧)→ 其 inc 永远记不到 - # 调用方 __fhs_tchild[slot-1] → 调用方 self ≈ inc(子时间一分不扣)。 - # 抖动槽 __fhs_tscratch 只写不读,留 internal 即可(各模块独立无影响)。 - depth_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, i64, name=self._UniqueGlobalName('__fhs_tdepth')) - depth_gv.linkage = 'common' - depth_gv.initializer = c0_64 - arr_ty: ir.ArrayType = ir.ArrayType(i64, depth) - tstart_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, arr_ty, name=self._UniqueGlobalName('__fhs_tstart')) - tstart_gv.linkage = 'common' - tstart_gv.initializer = ir.Constant(arr_ty, None) - tchild_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, arr_ty, name=self._UniqueGlobalName('__fhs_tchild')) - tchild_gv.linkage = 'common' - tchild_gv.initializer = ir.Constant(arr_ty, None) - scratch_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, i64, name=self._UniqueGlobalName('__fhs_tscratch')) - scratch_gv.linkage = 'internal' - scratch_gv.initializer = c0_64 - records: list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]] = [] - for func in [f for f in self.module.functions if getattr(f, 'blocks', None)]: - if func.name.startswith('__fhs_'): - continue - idx: int = len(records) - self_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, i64, name=self._UniqueGlobalName(f'__fhs_fself_{idx}')) - self_gv.linkage = 'internal' - self_gv.initializer = c0_64 - inc_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, i64, name=self._UniqueGlobalName(f'__fhs_finc_{idx}')) - inc_gv.linkage = 'internal' - inc_gv.initializer = c0_64 - enter_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, i64, name=self._UniqueGlobalName(f'__fhs_ftime_enter_{idx}')) - enter_gv.linkage = 'internal' - enter_gv.initializer = c0_64 - # 入口:取 t0 → 压深度栈 → 记录本帧 slot → 进入次数 +1(置于入口块最前) - entry: ir.Block = func.entry_basic_block - eb: ir.IRBuilder = ir.IRBuilder(entry) - eb.position_at_start(entry) - slot_ptr: ir.Value = eb.alloca(i64, name=f'fhs_t_slot_{idx}') - t0: ir.Value = self._EmitReadTimestamp(eb, name=f'fhs_t_t0_{idx}') - d: ir.Value = eb.load(depth_gv, name=f'fhs_t_depth_{idx}') - ok: ir.Value = eb.icmp_unsigned('<', d, ir.Constant(i64, depth), name=f'fhs_t_ok_{idx}') - safe_idx: ir.Value = eb.select(ok, d, c0_64, name=f'fhs_t_idx_{idx}') - eb.store(t0, eb.select( - ok, eb.gep(tstart_gv, [c0_32, safe_idx]), scratch_gv)) - eb.store(c0_64, eb.select( - ok, eb.gep(tchild_gv, [c0_32, safe_idx]), scratch_gv)) - eb.store(eb.select(ok, eb.add(d, c1_64), d), depth_gv) - eb.store(eb.select(ok, d, cneg1_64), slot_ptr) - eb.store(eb.add(eb.load(enter_gv), c1_64), enter_gv) - # 每个 ret 之前:算 self/inc,回退深度,累加父帧 child - for block in func.blocks: - term: Any = block.terminator - if not isinstance(term, ir.Ret): - continue - rb: ir.IRBuilder = ir.IRBuilder(block) - rb.position_before(term) - slot: ir.Value = rb.load(slot_ptr, name=f'fhs_t_slotv_{idx}') - inok: ir.Value = rb.icmp_signed('>=', slot, c0_64, name=f'fhs_t_inok_{idx}') - t1: ir.Value = self._EmitReadTimestamp(rb, name=f'fhs_t_t1_{idx}') - safe_slot: ir.Value = rb.select(inok, slot, c0_64, name=f'fhs_t_slotidx_{idx}') - inc_v: ir.Value = rb.sub( - t1, rb.load(rb.gep(tstart_gv, [c0_32, safe_slot])), - name=f'fhs_t_inc_{idx}') - child_v: ir.Value = rb.load( - rb.gep(tchild_gv, [c0_32, safe_slot]), name=f'fhs_t_child_{idx}') - self_v: ir.Value = rb.sub(inc_v, child_v, name=f'fhs_t_self_{idx}') - rb.store(rb.add(rb.load(self_gv), rb.select(inok, self_v, c0_64)), self_gv) - rb.store(rb.add(rb.load(inc_gv), rb.select(inok, inc_v, c0_64)), inc_gv) - rb.store(rb.select(inok, slot, rb.load(depth_gv)), depth_gv) - pok: ir.Value = rb.icmp_signed('>', slot, c0_64, name=f'fhs_t_pok_{idx}') - p_idx: ir.Value = rb.select(pok, rb.sub(slot, c1_64), c0_64) - p_ptr: ir.Value = rb.gep(tchild_gv, [c0_32, p_idx]) - p_target: ir.Value = rb.select(pok, p_ptr, scratch_gv) - rb.store(rb.add(rb.load(p_target), inc_v), p_target) - records.append((self_gv, inc_gv, enter_gv, func.name)) - return records - - def _CreateHotspotSitesDump( - self, - path: str, - records: list[tuple[ir.GlobalVariable, str, str, str, int]], - module_id: str | None = None, - ) -> ir.Function: - """生成 void() 落盘函数:按调用次数降序把各调用点追加写入 。 - - 落盘格式(追加): - # sites \\n - -> :\\n - module_id 为空时省略行首 sha1 与头部 sha1(留空,不伪造)。 - - - 排序:落盘前用选择排序(两层运行期循环,IR 规模 O(N)、运行期 O(N^2) 仅 - 在落盘时触发一次)按计数降序输出,只输出 count>0 的调用点;计数相同时 - 按计数器编号 K 升序(稳定、可复现)。 - - 源码位置:作为可选尾字段,格式 ":"(含前置单个空格);该位置 - 来自生成期挂在 call 指令对象上的 _src_file/_src_line(见 _HotspotSiteBuilder)。 - 生成期无法关联到 AST 的合成调用点取不到位置,则此字段整体省略(不伪造、不填默认值)。 - - 函数签名为 void(),由 __fhs_dump_all(见 _CreateHotspotDumpAll)统一带守卫 - 调用,本函数自身不检查守卫。用 CRT fopen("a")/fprintf/fclose 落盘(沿用既有做法)。 - """ - n: int = len(records) - i8: ir.IntType = ir.IntType(8) - i32: ir.IntType = ir.IntType(32) - i64: ir.IntType = ir.IntType(64) - i8p: ir.PointerType = ir.PointerType(i8) - i64p: ir.PointerType = ir.PointerType(i64) - c0_8: ir.Constant = ir.Constant(i8, 0) - c1_8: ir.Constant = ir.Constant(i8, 1) - c0_32: ir.Constant = ir.Constant(i32, 0) - c0_64: ir.Constant = ir.Constant(i64, 0) - c1_64: ir.Constant = ir.Constant(i64, 1) - fopen: ir.Function = self._DeclareExternalFunc('fopen', ir.FunctionType(i8p, [i8p, i8p])) - fprintf: ir.Function = self._DeclareExternalFunc( - 'fprintf', ir.FunctionType(i32, [i8p, i8p], var_arg=True)) - fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p])) - dump_fn: ir.Function = self._CreateOrReuseFuncDecl( - self._UniqueGlobalName('__fhs_dump_sites'), - ir.FunctionType(ir.VoidType(), [])) - dump_fn.linkage = 'internal' - b: ir.IRBuilder = ir.IRBuilder(dump_fn.append_basic_block(name='entry')) - b.position_at_start(dump_fn.entry_basic_block) - # 无匹配调用点:生成空落盘函数(不落盘、不建数组),保持调用链一致 - if n == 0: - b.ret_void() - return dump_fn - path_ptr: ir.Constant = self._create_string_global( - path, name=self._UniqueGlobalName('__fhs_sites_path')) - mode_ptr: ir.Constant = self._create_string_global( - 'a', name=self._UniqueGlobalName('__fhs_sites_mode')) - mid_ptr: ir.Constant | None = None - hdr_fmt_ptr: ir.Constant | None = None - if module_id: - fmt_ptr: ir.Constant = self._create_string_global( - '%s %s -> %s %llu%s\n', name=self._UniqueGlobalName('__fhs_sites_fmt')) - hdr_fmt_ptr = self._create_string_global( - '# sites %s\n', name=self._UniqueGlobalName('__fhs_sites_hdr_fmt')) - mid_ptr = self._create_string_global( - module_id, name=self._UniqueGlobalName('__fhs_sites_module_id')) - else: - fmt_ptr = self._create_string_global( - '%s -> %s %llu%s\n', name=self._UniqueGlobalName('__fhs_sites_fmt')) - fp: ir.Value = b.call(fopen, [path_ptr, mode_ptr], name='fhs_sites_fp') - if module_id and hdr_fmt_ptr is not None: - b.call(fprintf, [fp, hdr_fmt_ptr, mid_ptr], name='fhs_sites_write_hdr') - # 归属元数据全局数组:运行期按索引取 "调用方名 / 被调方基名 / 计数器地址" - caller_ty: ir.ArrayType = ir.ArrayType(i8p, n) - callee_ty: ir.ArrayType = ir.ArrayType(i8p, n) - cntptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) - caller_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, caller_ty, name=self._UniqueGlobalName('__fhs_sites_caller')) - caller_gv.linkage = 'internal' - caller_gv.global_constant = True - caller_gv.initializer = ir.Constant(caller_ty, [ - self._create_string_global(rec[1], name=self._UniqueGlobalName(f'__fhs_sites_caller_{k}')) - for k, rec in enumerate(records) - ]) - callee_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, callee_ty, name=self._UniqueGlobalName('__fhs_sites_callee')) - callee_gv.linkage = 'internal' - callee_gv.global_constant = True - callee_gv.initializer = ir.Constant(callee_ty, [ - self._create_string_global(rec[2], name=self._UniqueGlobalName(f'__fhs_sites_callee_{k}')) - for k, rec in enumerate(records) - ]) - # 源码位置全局数组:每项形如 " :"(含前置空格);取不到位置则为空串 - loc_ty: ir.ArrayType = ir.ArrayType(i8p, n) - loc_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, loc_ty, name=self._UniqueGlobalName('__fhs_sites_loc')) - loc_gv.linkage = 'internal' - loc_gv.global_constant = True - loc_gv.initializer = ir.Constant(loc_ty, [ - self._create_string_global( - f' {rec[3]}:{rec[4]}' if (rec[3] and rec[4] > 0) else '', - name=self._UniqueGlobalName(f'__fhs_sites_loc_{k}')) - for k, rec in enumerate(records) - ]) - cnt_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, cntptr_ty, name=self._UniqueGlobalName('__fhs_sites_cnt_ptrs')) - cnt_gv.linkage = 'internal' - cnt_gv.global_constant = True - cnt_gv.initializer = ir.Constant(cntptr_ty, [ - ir.Constant(i64p, rec[0].get_reference()) for rec in records - ]) - # 全部 alloca 置于 entry 块:避免在循环体内 alloca 导致每次迭代爆栈 - used: ir.Value = b.alloca(ir.ArrayType(i8, n), name='fhs_sites_used') - for i in range(n): - b.store(c0_8, b.gep(used, [c0_32, ir.Constant(i32, i)])) - k_ptr: ir.Value = b.alloca(i64, name='fhs_sites_k') - best_ptr: ir.Value = b.alloca(i64, name='fhs_sites_best') - bestv_ptr: ir.Value = b.alloca(i64, name='fhs_sites_bestv') - i_ptr: ir.Value = b.alloca(i64, name='fhs_sites_i') - b.store(c0_64, k_ptr) - # 选择排序:外层 k 走 0..n-1,每轮在未输出项里选计数最大者(计数相同取 K 小者) - loop_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_k') - body_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_k_body') - loop_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i') - body_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_body') - scan_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_scan') - upd_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_upd') - next_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_next') - end_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_end') - print_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_print_k') - after_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_end') - b.branch(loop_k) - # loop_k: if k < n -> body_k else after_k - b.position_at_end(loop_k) - k_val: ir.Value = b.load(k_ptr) - b.cbranch(b.icmp_signed('<', k_val, ir.Constant(i64, n)), body_k, after_k) - # body_k: best=-1, bestv=0, i=0; -> loop_i - b.position_at_end(body_k) - b.store(ir.Constant(i64, -1), best_ptr) - b.store(c0_64, bestv_ptr) - b.store(c0_64, i_ptr) - b.branch(loop_i) - # loop_i: if i < n -> body_i else end_i - b.position_at_end(loop_i) - i_val: ir.Value = b.load(i_ptr) - b.cbranch(b.icmp_signed('<', i_val, ir.Constant(i64, n)), body_i, end_i) - # body_i: if used[i]==0 -> scan_i else next_i - b.position_at_end(body_i) - used_i: ir.Value = b.load(b.gep(used, [c0_32, i_val])) - b.cbranch(b.icmp_signed('==', used_i, c0_8), scan_i, next_i) - # scan_i: v = *cnt_ptrs[i]; if v > bestv -> upd_i else next_i - b.position_at_end(scan_i) - cnt_p: ir.Value = b.load(b.gep(cnt_gv, [c0_32, i_val])) - v_val: ir.Value = b.load(cnt_p) - b.cbranch(b.icmp_signed('>', v_val, b.load(bestv_ptr)), upd_i, next_i) - # upd_i: bestv=v, best=i; -> next_i - b.position_at_end(upd_i) - b.store(v_val, bestv_ptr) - b.store(i_val, best_ptr) - b.branch(next_i) - # next_i: i++; -> loop_i - b.position_at_end(next_i) - b.store(b.add(b.load(i_ptr), c1_64), i_ptr) - b.branch(loop_i) - # end_i: best<0(剩余全为 0 计数)-> after_k;否则 print_k - b.position_at_end(end_i) - best_val: ir.Value = b.load(best_ptr) - b.cbranch(b.icmp_signed('<', best_val, c0_64), after_k, print_k) - # print_k: used[best]=1; fprintf(...); k++; -> loop_k - b.position_at_end(print_k) - idx: ir.Value = b.load(best_ptr) - b.store(c1_8, b.gep(used, [c0_32, idx])) - caller_ptr: ir.Value = b.load(b.gep(caller_gv, [c0_32, idx])) - callee_ptr: ir.Value = b.load(b.gep(callee_gv, [c0_32, idx])) - loc_ptr: ir.Value = b.load(b.gep(loc_gv, [c0_32, idx])) - if module_id and mid_ptr is not None: - b.call(fprintf, [fp, fmt_ptr, mid_ptr, caller_ptr, callee_ptr, - b.load(bestv_ptr), loc_ptr], - name='fhs_sites_write') - else: - b.call(fprintf, [fp, fmt_ptr, caller_ptr, callee_ptr, - b.load(bestv_ptr), loc_ptr], - name='fhs_sites_write') - b.store(b.add(b.load(k_ptr), c1_64), k_ptr) - b.branch(loop_k) - # after_k: fclose; ret_void - b.position_at_end(after_k) - b.call(fclose, [fp]) - b.ret_void() - return dump_fn - - def _CreateHotspotTimeDump( - self, - path: str, - records: list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]], - module_id: str | None = None, - ) -> ir.Function: - """生成 void() 落盘函数:按**自身耗时**降序写入每函数计时文件。 - - 落盘格式(追加): - # time \\n - \\n - module_id 为空时省略头部 sha1 行(留空,不伪造)。列含义(单位均为 tick): - - self_tick:自身耗时(exclusive,见 _EmitHotspotTimeCounters),已扣除本 - 函数内**被插桩**子调用的 inclusive 之和;但**未插桩**的被调方(间接调用、 - 无定义体的外部函数如 strcmp/memcpy、深度溢出跳过的调用)仍计入其中。 - - inc_tick:包含式耗时(inclusive,含全部被调方)。 - - enter_count:被进入次数(含递归/重入),用于交叉校验。 - - - 排序:与调用点计数同法,落盘前用选择排序按 self_tick 降序输出,只输出 - 进入次数 >0 的函数;self_tick 相同时按编号 N 升序(稳定、可复现)。 - - 函数签名为 void(),由 __fhs_dump_all(见 _CreateHotspotDumpAll)统一带守卫 - 调用,本函数自身不检查守卫。用 CRT fopen("a")/fprintf/fclose 落盘(沿用既有做法)。 - """ - n: int = len(records) - i8: ir.IntType = ir.IntType(8) - i32: ir.IntType = ir.IntType(32) - i64: ir.IntType = ir.IntType(64) - i8p: ir.PointerType = ir.PointerType(i8) - i64p: ir.PointerType = ir.PointerType(i64) - c0_8: ir.Constant = ir.Constant(i8, 0) - c1_8: ir.Constant = ir.Constant(i8, 1) - c0_32: ir.Constant = ir.Constant(i32, 0) - c0_64: ir.Constant = ir.Constant(i64, 0) - c1_64: ir.Constant = ir.Constant(i64, 1) - fopen: ir.Function = self._DeclareExternalFunc('fopen', ir.FunctionType(i8p, [i8p, i8p])) - fprintf: ir.Function = self._DeclareExternalFunc( - 'fprintf', ir.FunctionType(i32, [i8p, i8p], var_arg=True)) - fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p])) - dump_fn: ir.Function = self._CreateOrReuseFuncDecl( - self._UniqueGlobalName('__fhs_dump_time'), - ir.FunctionType(ir.VoidType(), [])) - dump_fn.linkage = 'internal' - b: ir.IRBuilder = ir.IRBuilder(dump_fn.append_basic_block(name='entry')) - b.position_at_start(dump_fn.entry_basic_block) - # 无计时对象(模块内无用户函数定义):生成空落盘函数占位,保持调用链一致 - if n == 0: - b.ret_void() - return dump_fn - path_ptr: ir.Constant = self._create_string_global( - path, name=self._UniqueGlobalName('__fhs_ftime_path')) - mode_ptr: ir.Constant = self._create_string_global( - 'a', name=self._UniqueGlobalName('__fhs_ftime_mode')) - fmt_ptr: ir.Constant = self._create_string_global( - '%s %llu %llu %llu\n', name=self._UniqueGlobalName('__fhs_ftime_fmt')) - fp: ir.Value = b.call(fopen, [path_ptr, mode_ptr], name='fhs_ftime_fp') - if module_id: - hdr_fmt_ptr: ir.Constant = self._create_string_global( - '# time %s name self inc enter (tick; self excludes instrumented callees)\n', - name=self._UniqueGlobalName('__fhs_ftime_hdr_fmt')) - mid_ptr: ir.Constant = self._create_string_global( - module_id, name=self._UniqueGlobalName('__fhs_ftime_module_id')) - b.call(fprintf, [fp, hdr_fmt_ptr, mid_ptr], name='fhs_ftime_write_hdr') - # 归属元数据全局数组:运行期按索引取 "函数名 / 自身耗时地址 / 包含式地址 / 进入次数地址" - name_ty: ir.ArrayType = ir.ArrayType(i8p, n) - selfptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) - incptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) - enterptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) - name_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, name_ty, name=self._UniqueGlobalName('__fhs_ftime_name')) - name_gv.linkage = 'internal' - name_gv.global_constant = True - name_gv.initializer = ir.Constant(name_ty, [ - self._create_string_global(rec[3], name=self._UniqueGlobalName(f'__fhs_ftime_name_{k}')) - for k, rec in enumerate(records) - ]) - self_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, selfptr_ty, name=self._UniqueGlobalName('__fhs_ftime_self_ptrs')) - self_gv.linkage = 'internal' - self_gv.global_constant = True - self_gv.initializer = ir.Constant(selfptr_ty, [ - ir.Constant(i64p, rec[0].get_reference()) for rec in records - ]) - inc_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, incptr_ty, name=self._UniqueGlobalName('__fhs_ftime_inc_ptrs')) - inc_gv.linkage = 'internal' - inc_gv.global_constant = True - inc_gv.initializer = ir.Constant(incptr_ty, [ - ir.Constant(i64p, rec[1].get_reference()) for rec in records - ]) - enter_gv: ir.GlobalVariable = ir.GlobalVariable( - self.module, enterptr_ty, name=self._UniqueGlobalName('__fhs_ftime_enter_ptrs')) - enter_gv.linkage = 'internal' - enter_gv.global_constant = True - enter_gv.initializer = ir.Constant(enterptr_ty, [ - ir.Constant(i64p, rec[2].get_reference()) for rec in records - ]) - # 全部 alloca 置于 entry 块:避免在循环体内 alloca 导致每次迭代爆栈 - used: ir.Value = b.alloca(ir.ArrayType(i8, n), name='fhs_ftime_used') - for i in range(n): - b.store(c0_8, b.gep(used, [c0_32, ir.Constant(i32, i)])) - k_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_k') - best_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_best') - bestv_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_bestv') - i_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_i') - b.store(c0_64, k_ptr) - # 选择排序:外层 k 走 0..n-1,每轮在未输出项里选自身耗时(self)最大者(相同取 N 小者) - loop_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_k') - body_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_k_body') - loop_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i') - body_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_body') - scan_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_scan') - upd_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_upd') - next_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_next') - end_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_end') - print_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_print_k') - after_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_end') - b.branch(loop_k) - b.position_at_end(loop_k) - k_val: ir.Value = b.load(k_ptr) - b.cbranch(b.icmp_signed('<', k_val, ir.Constant(i64, n)), body_k, after_k) - b.position_at_end(body_k) - b.store(ir.Constant(i64, -1), best_ptr) - b.store(c0_64, bestv_ptr) - b.store(c0_64, i_ptr) - b.branch(loop_i) - b.position_at_end(loop_i) - i_val: ir.Value = b.load(i_ptr) - b.cbranch(b.icmp_signed('<', i_val, ir.Constant(i64, n)), body_i, end_i) - b.position_at_end(body_i) - used_i: ir.Value = b.load(b.gep(used, [c0_32, i_val])) - b.cbranch(b.icmp_signed('==', used_i, c0_8), scan_i, next_i) - # scan_i: v = *self_ptrs[i](自身耗时);同时把进入次数非 0 作为"有样本"判据 - b.position_at_end(scan_i) - self_p: ir.Value = b.load(b.gep(self_gv, [c0_32, i_val])) - v_val: ir.Value = b.load(self_p) - enter_p: ir.Value = b.load(b.gep(enter_gv, [c0_32, i_val])) - entered: ir.Value = b.load(enter_p) - has: ir.Value = b.icmp_signed('>', entered, c0_64) - better: ir.Value = b.icmp_signed('>', v_val, b.load(bestv_ptr)) - b.cbranch(b.and_(has, better), upd_i, next_i) - b.position_at_end(upd_i) - b.store(v_val, bestv_ptr) - b.store(i_val, best_ptr) - b.branch(next_i) - b.position_at_end(next_i) - b.store(b.add(b.load(i_ptr), c1_64), i_ptr) - b.branch(loop_i) - # end_i: best<0(剩余均为 0 或未被进入)-> after_k;否则 print_k - b.position_at_end(end_i) - best_val: ir.Value = b.load(best_ptr) - b.cbranch(b.icmp_signed('<', best_val, c0_64), after_k, print_k) - b.position_at_end(print_k) - idx: ir.Value = b.load(best_ptr) - b.store(c1_8, b.gep(used, [c0_32, idx])) - name_ptr: ir.Value = b.load(b.gep(name_gv, [c0_32, idx])) - sp: ir.Value = b.load(b.gep(self_gv, [c0_32, idx])) - ip: ir.Value = b.load(b.gep(inc_gv, [c0_32, idx])) - ep: ir.Value = b.load(b.gep(enter_gv, [c0_32, idx])) - b.call(fprintf, [fp, fmt_ptr, name_ptr, b.load(sp), b.load(ip), b.load(ep)], - name='fhs_ftime_write') - b.store(b.add(b.load(k_ptr), c1_64), k_ptr) - b.branch(loop_k) - # after_k: fclose; ret_void - b.position_at_end(after_k) - b.call(fclose, [fp]) - b.ret_void() - return dump_fn - - def _CreateHotspotDumpAll( - self, - sites_dump: ir.Function, - time_dump: ir.Function, - ) -> tuple[ir.Function, ir.Function]: - """生成模块级"总落盘"链路:幂等守卫 + void() 总落盘函数 + Ctrl+C 处理器。 - - 同一模块**只落盘一次**:Ctrl+C 与 atexit 可能先后都触发(例如处理器返回后 - 进程继续退出),故用每模块一份的 internal i32 @__fhs_dumped(初值 0)作幂等 - 守卫: - - void @__fhs_dump_all():先读守卫,非 0 直接返回;否则置 1 后依次调用 - sites_dump(调用点计数)与 time_dump(每函数计时)——两个落盘函数本身 - 不检查守卫; - - i32 @__fhs_ctl_handler(i32):直接调用 __fhs_dump_all() 后返回 FALSE(0), - 供 SetConsoleCtrlHandler 注册(保持"仍让进程终止"的既有行为)。 - - atexit 与 Ctrl+C 共用同一守卫,先到者落盘、后到者直接返回,避免重复段/行。 - 返回 (Ctrl+C 处理器, 总落盘函数)。 - """ - i32: ir.IntType = ir.IntType(32) - guard: ir.GlobalVariable = ir.GlobalVariable( - self.module, i32, name=self._UniqueGlobalName('__fhs_dumped')) - guard.linkage = 'internal' - guard.initializer = ir.Constant(i32, 0) - dump_all: ir.Function = self._CreateOrReuseFuncDecl( - self._UniqueGlobalName('__fhs_dump_all'), - ir.FunctionType(ir.VoidType(), [])) - dump_all.linkage = 'internal' - b: ir.IRBuilder = ir.IRBuilder(dump_all.append_basic_block(name='entry')) - do_bb: ir.Block = dump_all.append_basic_block(name='fhs_dump_do') - done_bb: ir.Block = dump_all.append_basic_block(name='fhs_dump_done') - b.position_at_start(dump_all.entry_basic_block) - cur: ir.Value = b.load(guard, name='fhs_dumped_load') - b.cbranch(b.icmp_signed('!=', cur, ir.Constant(i32, 0)), done_bb, do_bb) - b.position_at_end(do_bb) - b.store(ir.Constant(i32, 1), guard) - b.call(sites_dump, []) - b.call(time_dump, []) - b.branch(done_bb) - b.position_at_end(done_bb) - b.ret_void() - ctl_handler: ir.Function = self._CreateOrReuseFuncDecl( - self._UniqueGlobalName('__fhs_ctl_handler'), - ir.FunctionType(i32, [i32])) - ctl_handler.linkage = 'internal' - cb: ir.IRBuilder = ir.IRBuilder(ctl_handler.append_basic_block(name='entry')) - cb.call(dump_all, []) - cb.ret(ir.Constant(i32, 0)) - return ctl_handler, dump_all - - def _EmitHotspotDumpRegistration( - self, - builder: ir.IRBuilder, - ctl_handler: ir.Function, - dump_all: ir.Function, - ) -> None: - """向给定构建器发射两处落盘触发注册: - - - SetConsoleCtrlHandler(bitcast @__fhs_ctl_handler, i8*), TRUE):Ctrl+C 路径; - - atexit(bitcast @__fhs_dump_all, i8*)):正常返回 / exit() 退出路径。 - - 二者共用 __fhs_dumped 幂等守卫(见 _CreateHotspotDumpAll),任一先触发即落盘, - 后触发者直接返回,不重复写段/行。atexit 声明沿用本文件既有外部函数写法(见 - fopen/SetConsoleCtrlHandler:ir.FunctionType + ir.PointerType(ir.IntType(8))); - 本仓库 IR 为 typed pointer(函数指针统一以 i8* 传递),故以 bitcast 取函数地址。 - 返回值为 int,此处有意忽略(拿不到 atexit 会在链接期报错,不静默降级)。 - - 覆盖 libc exit()(含正常 return 与 sys.exit 的 fail-fast);不覆盖 _exit()/ - abort()/TerminateProcess/崩溃信号等不经过 exit() 的终止路径。 - """ - i32: ir.IntType = ir.IntType(32) - i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) - set_handler: ir.Function = self._DeclareExternalFunc( - 'SetConsoleCtrlHandler', ir.FunctionType(i32, [i8p, i32])) - atexit_fn: ir.Function = self._DeclareExternalFunc( - 'atexit', ir.FunctionType(i32, [i8p])) - builder.call(set_handler, [builder.bitcast(ctl_handler, i8p), ir.Constant(i32, 1)]) - builder.call(atexit_fn, [builder.bitcast(dump_all, i8p)]) - - def _RegisterHotspotSitesViaCtor(self, ctl_handler: ir.Function, dump_all: ir.Function) -> None: - """用 @llvm.global_ctors 在进程启动期为本模块注册热点落盘触发。 - - 生成 internal void @__fhs_sites_ctor()(内部调用 SetConsoleCtrlHandler 注册 - Ctrl+C 处理器 __fhs_ctl_handler,并调用 atexit 注册退出处理器 __fhs_dump_all), - 并写入 appending linkage 的 @llvm.global_ctors —— 每个模块一份,链接器按 - appending 语义把各模块的构造器拼接进 .init_array,进程启动时各执行一次。相比 - "在 main 入口注册",本方案不依赖 main 所在模块,适配 Projectrans 的"每源文件 - 独立翻译单元"模型。 - - 模块内若已存在 @llvm.global_ctors 视为冲突,显式报错(不静默降级)。 - """ - i32: ir.IntType = ir.IntType(32) - i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) - void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), [])) - ctor: ir.Function = self._CreateOrReuseFuncDecl( - self._UniqueGlobalName('__fhs_sites_ctor'), - ir.FunctionType(ir.VoidType(), [])) - ctor.linkage = 'internal' - cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry')) - self._EmitHotspotDumpRegistration(cb, ctl_handler, dump_all) - cb.ret_void() - if 'llvm.global_ctors' in self.module.globals: - raise RuntimeError( - '--hotspot-sites: 模块内已存在 @llvm.global_ctors,无法注册热点处理器(不静默降级)') - elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p]) - arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1) - entry_val: ir.Constant = ir.Constant(elem_ty, [ - ir.Constant(i32, 65535), - # 传函数引用字符串(非 Function 对象):与 _create_string_global 同法, - # 否则 llvmlite 会以 str(Function) 渲染整个 define 体 - ir.Constant(void_fnptr, ctor.get_reference()), - ir.Constant(i8p, None), - ]) - gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_ty, name='llvm.global_ctors') - gv.linkage = 'appending' - gv.initializer = ir.Constant(arr_ty, [entry_val]) - - def _RegisterTracebackInstallViaCtor(self) -> None: - """用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--traceback)。 - - 仅在"本翻译单元就是 includes/traceback.vp"时由 StructGen.finalize() 调用 - (traceback_ctor 由 Phase2Translator._translate_file 按源文件名置位)。 - 生成 internal void @__traceback_install_ctor()(entry 块直接 call 本模块的 - Install() 并忽略其 int 返回值),再写入 appending linkage 的 - @llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array, - 进程启动时执行一次。 - - 为什么用构造器而不是"在 main 入口调用":适配 Projectrans 的"每源文件独立 - 翻译单元"模型(不依赖哪个模块含 main),且 CRT 初始化期即执行,能覆盖 - "main 之前就卡死"的场景 —— 这正是本功能的用途。 - - 同模块内取 Install 符号(不走跨模块声明),避免跨模块符号名/mangling 耦合。 - fail-fast:找不到 Install 符号、或模块内已存在 @llvm.global_ctors 时显式 - 报错(不静默降级 —— 否则产物缺注册、功能静默失效)。 - """ - install_fn: ir.Function | None = self._find_function('Install') - if install_fn is None: - raise RuntimeError( - '--traceback: 在 traceback 模块内找不到 Install() 函数,' - '无法注入启动期构造器(不静默降级)') - i32: ir.IntType = ir.IntType(32) - i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) - void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), [])) - ctor: ir.Function = self._CreateOrReuseFuncDecl( - self._UniqueGlobalName('__traceback_install_ctor'), - ir.FunctionType(ir.VoidType(), [])) - ctor.linkage = 'internal' - cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry')) - cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略 - cb.ret_void() - if 'llvm.global_ctors' in self.module.globals: - raise RuntimeError( - '--traceback: 模块内已存在 @llvm.global_ctors,' - '无法注入 traceback 启动期构造器(不静默降级)') - elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p]) - arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1) - entry_val: ir.Constant = ir.Constant(elem_ty, [ - ir.Constant(i32, 65535), - # 传函数引用字符串(非 Function 对象):与 _RegisterHotspotSitesViaCtor 同法, - # 否则 llvmlite 会以 str(Function) 渲染整个 define 体 - ir.Constant(void_fnptr, ctor.get_reference()), - ir.Constant(i8p, None), - ]) - gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_ty, name='llvm.global_ctors') - gv.linkage = 'appending' - gv.initializer = ir.Constant(arr_ty, [entry_val]) - + def _get_or_create_struct(self, name: str, source_sha1: str | None = None, packed: bool = False) -> ir.IdentifiedStructType: + clean_name: str = self._CleanStructName(name) + # 如果名称包含模块前缀(如 "a9fa0f6200c09e65.zbit_writer"), + # 先尝试用短名称(如 "zbit_writer")查找已注册的 struct, + # 避免为同一 struct 创建多个不同的类型对象导致跨模块引用类型不一致 + if '.' in clean_name and clean_name not in self.structs: + parts: list[str] = clean_name.split('.', 1) + if len(parts[0]) == 16 and all(c in '0123456789abcdef' for c in parts[0]): + short_name: str = parts[1] + if short_name in self.structs: + existing_short: ir.IdentifiedStructType = self.structs[short_name] + # 治本修复:同名类 sha1 冲突检测。 + # 当请求的 sha1 前缀与现有 struct 的 sha1 前缀不一致时 + # (如 stmts.py:Module 与 __module.py:Module 同名冲突), + # 不能复用已有 struct(set_body 已锁定为错误布局), + # 需创建独立 struct 并以 sha1 前缀全名存储。 + _existing_sha1: str = self._extract_struct_sha1(existing_short) + if _existing_sha1 and _existing_sha1 != parts[0]: + if clean_name in self.structs: + return self.structs[clean_name] + # 按 IR 名去重:该 IR 名字的 struct 可能已由**别的键** + # (裸名/别名)注册 ⇒ 复用,绝不建第二个同名对象 + # (否则单模块 .ll 出现两条同名 type 定义 ⇒ llvm-link + # "redefinition of type" ⇒ 跨模块内联整体回退)。 + _st_same: ir.IdentifiedStructType | None = self._FindStructByIrName(clean_name) + if _st_same is not None: + self.structs[clean_name] = _st_same + if packed and not _st_same.packed: + _st_same.packed = True + return _st_same + st_new: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=clean_name, packed=packed) + self.structs[clean_name] = st_new + return st_new + return existing_short + # 优先解析 typedef 整数类型(如 INT/UINT/BYTE),避免将其注册为 opaque struct。 + # 必须在 `if clean_name in self.structs` 缓存检查之前执行, + # 否则首次调用(SymbolTable 未就绪时)会将 typedef 名缓存为 opaque struct, + # 后续调用即使 SymbolTable 就绪也只会返回缓存的错误类型。 + # 此修复同时解决两个问题: + # 1. alloca unsized type:变量声明 `argc: INT = 6` 不再生成 `alloca %"...INT"` + # 2. 下标访问:FindStructNameInAnnotation 不再匹配 typedef 名(因不在 Gen.structs) + td_width: int | None = self._resolve_typedef_int_width(clean_name) + if td_width is not None: + if td_width > 0: + self.typedef_int_widths[clean_name] = td_width + return ir.IntType(td_width) + # td_width == 0: 特殊 typedef 名称(str/bytes/list 等),返回 i8 + self.typedef_int_widths[clean_name] = 0 + return ir.IntType(8) + if clean_name in self.structs: + existing: ir.IdentifiedStructType = self.structs[clean_name] + # 治本修复:同名类 sha1 冲突检测(短名调用路径)。 + # 当 source_sha1 与现有 struct 的 sha1 前缀不一致时(跨模块同名类冲突), + # 不能复用已有 struct(set_body 已锁定为错误布局), + # 需创建独立 struct 并以 sha1 前缀全名存储,不覆盖短名条目。 + if source_sha1: + _existing_sha1: str = self._extract_struct_sha1(existing) + if _existing_sha1 and _existing_sha1 != source_sha1: + full_key: str = f"{source_sha1}.{clean_name}" + if full_key in self.structs: + st_full: ir.IdentifiedStructType = self.structs[full_key] + if packed and isinstance(st_full, ir.IdentifiedStructType) and not st_full.packed: + st_full.packed = True + return st_full + ns_name: str = f"{source_sha1}.{clean_name}" + # 按 IR 名去重(见 _FindStructByIrName):同一 IR 名字已有对象 + # ⇒ 复用同一对象,只补注册本键,绝不建第二个同名对象。 + _st_same2: ir.IdentifiedStructType | None = self._FindStructByIrName(ns_name) + if _st_same2 is not None: + self.structs[full_key] = _st_same2 + if packed and not _st_same2.packed: + _st_same2.packed = True + return _st_same2 + st_new: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed) + self.structs[full_key] = st_new + return st_new + if packed and isinstance(existing, ir.IdentifiedStructType) and not existing.packed: + existing.packed = True + return existing + if clean_name in {'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', 'CStruct', 'enum', 'REnum', 'renum', 'Void', 'void'}: + return ir.IntType(8) + # typing.Any 是"任意类型"标记,应解析为 i8*(通用指针), + # 而非创建为 opaque 结构体(否则包含 Any 字段的结构体会变成 unsized)。 + if clean_name == 'Any': + return ir.IntType(8).as_pointer() + # t.CType 的基本类型子类(CInt/CChar/CSizeT/CLong/CFloat/CDouble 等, + # position 含 BASE 且 Size>0)有具体 LLVM 类型,应直接返回对应的 + # ir.IntType/ir.FloatType/ir.DoubleType,而非创建结构体。 + # _is_ctype_marker_name 对这些类型返回 False(不跳过),需要在此处拦截。 + _basic_llvm_type: ir.Type | None = self._try_resolve_ctype_basic(clean_name) + if _basic_llvm_type is not None: + return _basic_llvm_type + # t.CType 及其子类是"类型强转"标记,不应被编译为真实结构体。 + # 动态检查 t 模块中所有 CType 子类名(排除 CStruct/CEnum/CUnion/REnum 子类, + # 因为用户定义的结构体类继承自它们,需要被创建为结构体)。 + if self._is_ctype_marker_name(clean_name): + return ir.IntType(8) + if self.SymbolTable: + Entry: _CTypeInfo | None = self.SymbolTable.lookup(clean_name) + # 「有类定义 ⇒ 是类」的唯一判据(与 TypeConvert._type_str_to_llvm_base + # 的 `type_str in self.structs or type_str in self._struct_sha1_map`、 + # HandlesTypeMerge 的 Name/Attribute 分支、DeclarationGenerator 的 + # `attr_name in struct_names` 同一条规则源): + # _struct_sha1_map / class_sha1_map 已登记该名为**类**时,即便平坦符号表 + # 条目是**同名枚举成员**(如 ast/base.vp 的 `Constant: t.State` 与 + # ast/exprs.vp 的 `class Constant` 同名;谁后插入谁占住裸名),也必须建成 + # 结构体。否则此处返回 i32 ⇒ 上游 PtrCount=1 叠加后变成 i32*, + # 与调用点实参 %".Constant"* 分裂(现场:constant_get_str_val 的 + # 形参 i32*)。类型注解语境下枚举**成员**从不是类型,判据只能是"是否有类定义"。 + # 必须排除**枚举类型本身**(如 `class ASTKind(t.CEnum)`):_struct_sha1_map + # 是按 .vpi 里全部 ClassDef 建的,枚举类也在其中 —— 若一并当结构体,枚举 + # 注解(kind() 返回值等)会被建为 opaque struct,引发 i32 ↔ 结构体错位 + # ('IntType' object has no attribute 'is_opaque' 现场)。 + # 判据必须按 **IsEnumMember** 区分(现场实测标志组合): + # 枚举**成员** = IsEnum=True + IsEnumMember=True + BaseType=CEnum + # 枚举**类型** = IsEnum=True + IsEnumMember=False + # 只判 IsEnum 会把成员误当枚举类型 ⇒ 返回 i32 ⇒ 上游 PtrCount=1 ⇒ i32*。 + _known_class_name: bool = ( + (clean_name in getattr(self, '_struct_sha1_map', {}) + or clean_name in getattr(self, 'class_sha1_map', {})) + and not (Entry and Entry.IsEnum + and not getattr(Entry, 'IsEnumMember', False))) + if Entry and Entry.IsTypedef: + resolved_str: str = self._resolve_typedef(clean_name) + if resolved_str != clean_name: + return self._type_str_to_llvm(resolved_str) + if Entry.BaseType: + if not isinstance(Entry.BaseType, (_t._CTypedef,)) or Entry.PtrCount > 0: + resolved: str = self._resolve_ctype_to_str(Entry) + if resolved: + return self._type_str_to_llvm(resolved) + if Entry and Entry.IsRenum: + if clean_name in self.structs: + return self.structs[clean_name] + # REnum 结构体尚未创建时不能 fall through 到 IsEnum(会返回 i32), + # 需要继续到下方正常结构体创建路径,创建 opaque 结构体。 + # 这对自引用 REnum(如 LLVMType 包含 LLVMType*)至关重要: + # _parse_llvm_type 先通过此路径创建 opaque 结构体,_TryLoadStructFromStub 再 set_body。 + elif (Entry and Entry.IsEnum and clean_name not in self.class_methods + and not _known_class_name): + # CEnum 存储宽度 = 最宽成员类型(登记在 Entry.enum_width;未登记 + # → 默认 i32)。此前此处恒 i32,与同文件 _enum_int_type 的常量/ + # 注解宽度分裂(宽成员枚举的类型与常量不同宽)。 + return self._enum_int_type(clean_name, Entry) + # 治本修复:当 clean_name 是已注册的类(在 class_methods 或 class_members 中)时, + # 不应走 IsExceptionClass/IsFunction/IsVariable 分支返回非结构体类型。 + # 否则 _get_or_create_struct 返回指针类型(如 _TypedPointerType)而非 IdentifiedStructType, + # 导致 _TryLoadStructFromStub 的 set_body 被跳过(isinstance 检查失败), + # struct 保持不完整字段数,引发跨模块 GEP 越界崩溃。 + _is_class_name: bool = clean_name in self.class_methods or clean_name in self.class_members + if Entry and Entry.IsExceptionClass and not _is_class_name: + return ir.IntType(32) + if Entry and Entry.IsFunction and not _is_class_name: + return ir.PointerType(ir.IntType(8)) + if Entry and Entry.IsVariable and not _is_class_name: + return ir.PointerType(ir.IntType(8)) + if Entry and Entry.BaseType and not _known_class_name: + if isinstance(Entry.BaseType, type) and issubclass(Entry.BaseType, _t.CEnum): + return self._enum_int_type(clean_name, Entry) + if Entry.BaseType is _t.CEnum: + return self._enum_int_type(clean_name, Entry) + ns_name: str + # 泛型特化名('list[str]' / 'dict[str][int]'):类→模块 sha1 表是按**基名** + # 登记的 ClassDef('list' 来自 includes/_list.vp),特化名本身不在表里 ⇒ + # 若不先按基名取 sha1,_mangle_name 会拼出 '<使用方模块sha1>.list[str]', + # 与 declare 端(DeclarationGenerator `gc_sha1 = struct_sha1_map.get(gc_name, + # module_sha1)` → '%"{模板sha1}.list[str]"*')及定义端 + # (HandlesClassDef 特化时 Gen.module_sha1 = 模板模块)**三处分裂**。 + _tmpl_name: str = clean_name.split('[', 1)[0] if '[' in clean_name else clean_name + _tmpl_sha1: str | None = self._struct_sha1_map.get(_tmpl_name, '') or None + if source_sha1: + ns_name = f"{source_sha1}.{clean_name}" + elif _tmpl_sha1: + ns_name = f"{_tmpl_sha1}.{clean_name}" + else: + ns_name = self._mangle_name(clean_name) + # 按 IR 名去重(见 _FindStructByIrName):该 IR 名字的对象可能已由**别的键** + # (裸名 / sha1 全名 / import 别名)注册 ⇒ 复用同一对象并补注册本键。 + # 否则单模块 .ll 会出现两条同名 type 定义 ⇒ llvm-link + # "error: redefinition of type" ⇒ 跨模块 alwaysinline 合并整体回退 + # (现场:includes/ast/parser.vp 的 8312ce86a2fd82c0.Keyword 定义两次)。 + _st_same3: ir.IdentifiedStructType | None = self._FindStructByIrName(ns_name) + if _st_same3 is not None: + self.structs[clean_name] = _st_same3 + if packed and not _st_same3.packed: + _st_same3.packed = True + return _st_same3 + st: ir.IdentifiedStructType = ir.IdentifiedStructType(self.module, name=ns_name, packed=packed) + self.structs[clean_name] = st + # 尝试解析 typedef 的底层整数位宽 + width: int | None = self._resolve_typedef_int_width(clean_name) + if width is not None: + self.typedef_int_widths[clean_name] = width + return st + + def _resolve_typedef_int_width(self, name: str) -> int | None: + """解析 typedef 名称的底层整数位宽 + + 通过 CTypeRegistry 和 BuiltinTypeMap 动态解析, + 替代硬编码的 TYPEDEF_NAMES 映射表。 + 返回 int 位宽或 None(无法解析)。 + """ + # 1. 先查 BuiltinTypeMap(覆盖 BYTEPTR 等指针别名) + bm: Any = BuiltinTypeMap.Get(name) + if bm: + ctype_cls: Any + ptr_count: int + ctype_cls, ptr_count = bm + if ptr_count > 0: + return None # 指针类型,不是整数 typedef + llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + + # 2. 再查 SymbolTable(用户定义的 typedef) + if self.SymbolTable: + Entry: _CTypeInfo | None = self.SymbolTable.lookup(name) + if Entry and Entry.IsTypedef: + if Entry.BaseType: + if not isinstance(Entry.BaseType, (_t._CTypedef,)) and Entry.PtrCount == 0: + llvm_str: str = CTypeRegistry.CTypeToLLVM(type(Entry.BaseType) if isinstance(Entry.BaseType, _t.CType) else Entry.BaseType) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + if Entry.OriginalType: + if isinstance(Entry.OriginalType, _CTypeInfo) and Entry.OriginalType.BaseType: + if not isinstance(Entry.OriginalType.BaseType, (_t._CTypedef,)) and Entry.OriginalType.PtrCount == 0: + llvm_str: str = CTypeRegistry.CTypeToLLVM(type(Entry.OriginalType.BaseType) if isinstance(Entry.OriginalType.BaseType, _t.CType) else Entry.OriginalType.BaseType) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + elif isinstance(Entry.OriginalType, str): + # OriginalType 是类型名字符串(如 'CInt', 'CUnsignedInt'), + # 通过 CTypeRegistry 解析为整数位宽 + cls: Any = CTypeRegistry.GetClassByName(Entry.OriginalType) + if cls: + llvm_str: str = CTypeRegistry.CTypeToLLVM(cls) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + + # 3. 尝试 CTypeRegistry 直接按名称查找 + ctype_cls: Any = CTypeRegistry.GetClassByName(name) + if ctype_cls: + llvm_str: str = CTypeRegistry.CTypeToLLVM(ctype_cls) + if llvm_str and llvm_str.startswith('i') and llvm_str[1:].isdigit(): + return int(llvm_str[1:]) + + # 4. 特殊名称(非整数类型,但需要标记为 typedef 以跳过 opaque 定义) + # 注意:如果 name 已注册为 struct(用户定义了同名 class,如 includes/_dict.py 中的 dict), + # 则不应视为 typedef,应返回 None 让调用方走 struct 创建路径。 + # 解决时序问题:_parse_llvm_declare 从 stub.ll 解析时 dict 可能尚未注册到 Gen.structs, + # 但 SymbolTable 在 .pyi 解析阶段已就绪,可通过 is_struct 检查区分 class 和 typedef。 + _SPECIAL_TYPEDEFS: set[str] = { + 'typedef', 'CType', 'CVolatile', 'CEnum', 'CUnion', + 'CStruct', 'enum', 'REnum', 'renum', + 'list', 'dict', 'tuple', 'set', 'str', 'bytes', 'array', 'type', 'CArray', + } + if name in _SPECIAL_TYPEDEFS: + if name in self.structs: + return None # 已注册为 struct,不视为 typedef + if self.SymbolTable is not None: + try: + if self.SymbolTable.is_struct(name): + return None # SymbolTable 中标记为 struct,不视为 typedef + except Exception: + pass + return 0 # 标记为 typedef,但位宽为 0(非整数) + + # t.CType 及其子类是"类型强转"标记,不应被创建为结构体。 + # 动态检查 t 模块中所有 CType 子类名(排除 CStruct/CEnum/CUnion/REnum 子类)。 + # 返回 0 标记为 typedef,使 _get_or_create_struct 走 typedef 路径返回 i8。 + if self._is_ctype_marker_name(name): + return 0 + + return None + + @staticmethod + def _is_ctype_marker_name(name: str) -> bool: + """检查名称是否是 t.CType 的"类型强转"标记子类名。 + + 只有 position 不含 BASE 的纯修饰符/标记类型(CPtr/CVolatile/CConst/ + CInline/CStatic/CExtern/CExport/CRegister/CAuto/_CTypedef/CTypeDefault 等) + 才应该跳过结构体创建。有 BASE position 的基本类型(CInt/CChar/CSizeT/ + CLong/CFloat/CDouble 等)有具体大小,应被正常解析为对应宽度的整数/浮点。 + + CStruct/CEnum/CUnion/REnum 的用户子类是合法结构体,需要被创建。 + + 注意:CTypeRegistry.GetClassByName 会跳过 CType 本身和 CTypeDefault, + 所以这里直接用 getattr(t, name) 检查 t 模块属性。 + + Returns: + True 如果 name 是 CType 标记子类名(应跳过结构体创建) + """ + if not name: + return False + # 去除可能的 SHA1 前缀(如 "abc123.CPtr" -> "CPtr") + short_name: str = name.split('.')[-1] if '.' in name else name + # 直接查 t 模块属性(CTypeRegistry._build 会跳过 CType/CTypeDefault/BigEndian/LittleEndian) + t_cls: Any = getattr(_t, short_name, None) + if not isinstance(t_cls, type) or not issubclass(t_cls, _t.CType): + return False + # 排除 CStruct/CEnum/CUnion/REnum 子类(它们是命名类型的基类, + # 用户定义的结构体继承自它们,需要被创建为结构体) + if issubclass(t_cls, (_t.CStruct, _t.CEnum, _t.REnum, _t.CUnion)): + return False + # 基本类型(position 含 BASE 且 Size > 0)有具体大小,不应跳过 + # (CInt→i32, CChar→i8, CSizeT→i64, CLong→i64, CFloat→f32 等) + # CVoid 虽含 BASE 但 Size=0,CType Size=None,仍需跳过 + if _t.CType.BASE in t_cls.position: + # 检查实例的 Size(类属性可能未设置,用实例获取) + try: + inst = t_cls() + size_val = getattr(inst, 'Size', None) + if size_val is not None and size_val > 0: + return False + except Exception: + pass + return True + + def _try_resolve_ctype_basic(self, name: str) -> ir.Type | None: + """尝试将 t.CType 的基本类型子类名解析为对应的 LLVM 基本类型。 + + 仅对 t 模块内的 CType 基本类型子类(CInt/CChar/CSizeT/CLong/CFloat/ + CDouble 等,即 position 含 BASE 且 Size>0)生效,返回对应的 + ir.IntType/ir.FloatType/ir.DoubleType。 + + CStruct/CEnum/CUnion/REnum 子类不在此处理(用户自定义结构体需要被 + 创建为结构体)。CVoid/CType/CPtr 等无 Size 或 Size=0 的类型也不在此 + 处理(应通过 _is_ctype_marker_name 跳过结构体创建)。 + + Returns: + 对应的 ir.Type,或 None 表示不是基本类型,需要继续走结构体创建路径。 + """ + if not name: + return None + short_name: str = name.split('.')[-1] if '.' in name else name + cls: Any = getattr(_t, short_name, None) + if not isinstance(cls, type) or not issubclass(cls, _t.CType): + return None + if issubclass(cls, (_t.CStruct, _t.CEnum, _t.REnum, _t.CUnion)): + return None + if _t.CType.BASE not in getattr(cls, 'position', frozenset()): + return None + try: + inst = cls() + size_val = getattr(inst, 'Size', None) + if size_val is None or size_val <= 0: + return None + except Exception: + return None + llvm_str: str | None = CTypeRegistry.NameToLLVM(short_name) + if not llvm_str: + return None + return self._type_str_to_llvm(llvm_str) + + def _set_node_info(self, node: ast.AST, extra_info: str = "") -> None: + if hasattr(node, 'lineno'): + self._current_lineno = node.lineno + else: + self._current_lineno = 0 + if hasattr(node, 'col_offset'): + self._current_node_info = f"line {self._current_lineno}, col {node.col_offset}" + else: + self._current_node_info = f"line {self._current_lineno}" + if extra_info: + self._current_node_info += f" ({extra_info})" + # 语句/指令级 DWARF 位置(--dwarf / --asan):每进入一条语句就把当前 + # builder 的默认调试位置切到该 AST 节点的行/列(col_offset 是 0-based, + # +1 转 1-based;无位置时为 0 → 回退函数入口 DILocation)。此后该语句生成 + # 的所有指令(含 call)自动带本函数 scope 的 !dbg,llc 不再因缺 !dbg 丢弃 + # 整份 DI。未启用时 dwarf_enabled=False,一次都不触碰 builder.debug_metadata + # (零影响:_GetDebugInfo() 也不会被调用)。 + if self.dwarf_enabled and self.builder is not None: + _DbgInfo = self._GetDebugInfo() + if _DbgInfo is not None: + _DbgInfo.SetStatement( + self.builder, self._current_lineno, + getattr(node, 'col_offset', -1) + 1) + + def _set_source_info(self, filepath: str) -> None: + self._current_source_file = filepath + try: + with open(filepath, 'r', encoding='utf-8') as f: + self._current_source_lines = f.readlines() + except Exception: # 文件读取失败时回退为空行列表 + self._current_source_lines = [] + + def _GetDebugInfo(self) -> Any: + """返回本模块的 DebugInfoEmitter;未启用 --dwarf 或无源文件时返回 None。 + + 未启用(dwarf_enabled=False)时不构造任何对象、不产生任何 IR 差异, + 行为与改动前逐字节一致。源文件取 _current_source_file(由 GenerateCCode + 在生成 IR 前设定,模块内恒定),每个模块一份 DICompileUnit + DIFile。 + """ + if not self.dwarf_enabled: + return None + if not self._current_source_file: + return None + if self.DebugInfo is None: + from lib.core.LLVMCG.DebugInfo import DebugInfoEmitter + self.DebugInfo = DebugInfoEmitter(self.module, self._current_source_file) + return self.DebugInfo + + def _skip_mangle(self, name: str) -> bool: + skip_prefixes: tuple[str, ...] = ('llvm.', 'str_const_', 'printf', 'memset_pattern') + if any(name.startswith(p) for p in skip_prefixes): + return True + if '.' in name: + parts: list[str] = name.split('.', 1) + if len(parts[0]) >= 8 and all(c in '0123456789abcdef' for c in parts[0]): + return True + return False + + def _mangle_name(self, name: str) -> str: + if self._skip_mangle(name): + return name + if name in self._export_funcs: + return name + if self.module_sha1: + return f"{self.module_sha1}.{name}" + return name + + def _find_function(self, name: str) -> ir.Function | None: + if name in self.functions: + return self.functions[name] + g: Any = self.module.globals.get(name) + if g and isinstance(g, ir.Function): + self.functions[name] = g + return g + mangled: str = self._mangle_func_name(name) + if mangled != name: + if mangled in self.functions: + self.functions[name] = self.functions[mangled] + return self.functions[mangled] + g = self.module.globals.get(mangled) + if g and isinstance(g, ir.Function): + self.functions[mangled] = g + self.functions[name] = g + return g + for sha1 in self.ModuleSha1Map.values(): + prefixed: str = f"{sha1}.{name}" + if prefixed in self.functions: + self.functions[name] = self.functions[prefixed] + return self.functions[prefixed] + g = self.module.globals.get(prefixed) + if g and isinstance(g, ir.Function): + self.functions[prefixed] = g + self.functions[name] = g + return g + return None + + def _has_function(self, name: str) -> bool: + return self._find_function(name) is not None + + def _get_mro_chain(self, ClassName: str) -> list[str]: + """MRO 线性化类名列表(深度优先 + 声明顺序 + 去重,含自身)。 + + 顺序: 自身 → 父类1 及祖先 → 父类2 及祖先(先声明父类优先,菱形去重)。 + 与 VPC HandlesStruct.get_mro_chain 的语义一致。 + + 支持带 sha1 前缀的类名(如 "c9b44d17cd621a75.CallGenLib",跨模块 + structs 表键):查 class_parents 前剥离前缀,否则表键(裸类名)不匹配 + → MRO 链只剩自身 → 继承方法调用 fail-fast "Undefined method + 'find_func_in_module' in class '...CallGenLib'"(CallGenLib 根因)。 + """ + chain: list[str] = [] + visited: set[str] = set() + stack: list[str] = [ClassName] + parents_map: dict[str, list[str]] = getattr(self, 'class_parents', {}) + while stack: + cur: str = stack.pop() + if not cur or cur in visited: + continue + visited.add(cur) + chain.append(cur) + # 归一化:剥离 "16位hex." 前缀后用裸类名查父类表 + lookup: str = cur + if len(cur) > 17 and cur[16] == '.': + lookup = cur[17:] + parents: list[str] = parents_map.get(lookup, parents_map.get(cur, [])) + # 父类逆序压栈,保证声明顺序弹出(先访问父类1 的整条链) + for p in reversed(parents): + if p and p not in visited: + stack.append(p) + return chain + + def _get_parent_classes(self, ClassName: str) -> list[str]: + """获取类的所有父类名(声明顺序),兼容旧 class_parent 单链。""" + parents_map: dict[str, list[str]] = getattr(self, 'class_parents', {}) + parents: list[str] = parents_map.get(ClassName) + if parents: + return parents + p: str | None = self.class_parent.get(ClassName) + if p: + return [p] + return [] + + def _CreateOrReuseFuncDecl(self, name: str, func_type: ir.FunctionType) -> ir.Function: + """函数声明创建的唯一幂等入口(所有"新建 ir.Function"的路径都应走这里)。 + + llvmlite 的 NameScope 按名字去重(GlobalValue.deduplicate_name=False): + 同名 global 再建一次必抛 llvmlite.ir._utils.DuplicatedNameError(只给名字、 + 不给两侧签名,现场:5d84ea579d83ef73.CallGenFd._translate_fakeduck_call)。 + 故此处: + · module.globals 已有同名函数且函数类型(按字符串比较,FunctionType 无 + __eq__ ⇒ 只能比结构串)相同 ⇒ **复用**并补登记 Gen.functions; + · 已有但类型不同 ⇒ fail-fast 报出两侧签名 —— 这是 declare / define 分裂的 + 真因,绝不静默复用错签名(会让调用点按错签名传参)。 + """ + # ---- 创建前自检(两条,都是"进了 llvmlite 就不可救"的状态)---- + # 背景:llvmlite 的 `Function.__init__` 顺序是 + # NamedValue.__init__ → _set_name → module.scope.register(name) 【名字占用】 + # ... 逐形参 `Argument(...)`(内部 assert isinstance(typ, types.Type))... + # module.add_global(self) 【进入 globals】 + # 中间任一步抛错,Python 都不会回滚 scope ⇒ 留下"半注册"的名字:globals 查无、 + # scope 却已占用。此后同一名字再建必抛 DuplicatedNameError(报文只有名字、 + # 没有两侧信息)。现场:972cf2b7756765d5.ImportsGenMixin._index_imported_module + # —— 上游首建失败被 `except Exception` 静默吞掉,只剩这个裸报错。 + # ① 签名自检:返回类型/任一形参不是 llvmlite Type(典型 None=注解未解析出来) + # 会在 `Argument(...)` / `ReturnValue(...)` 处 assert 失败,而失败点在名字 + # 注册之后(见上)⇒ 在此先行判定,给出可定位报文。 + _bad_desc: str | None = None + _bad_ty: Any = None + _ret_ty: Any = getattr(func_type, 'return_type', None) + if not isinstance(_ret_ty, ir.Type): + _bad_desc = '返回类型' + _bad_ty = _ret_ty + else: + for _ai, _at in enumerate(getattr(func_type, 'args', ()) or ()): + if not isinstance(_at, ir.Type): + _bad_desc = f'第 {_ai} 个形参类型' + _bad_ty = _at + break + if _bad_desc is not None: + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"函数声明签名非法:'{name}' 的{_bad_desc}是 " + f"{type(_bad_ty).__name__}({str(_bad_ty)[:80]}),不是 llvmlite 类型" + f"\n · 本次签名: {func_type}" + f"\n 成因:该处注解未解析出 llvmlite Type(解析失败返回 None / 拿到的是" + f"符号表条目等非类型对象)。llvmlite 会在 Argument(...)/ReturnValue(...) " + f"处 assert 失败,而失败点落在「名字已进 module.scope、尚未 add_global」" + f"之间 ⇒ 留下半注册名字,后续同名创建只报 DuplicatedNameError。" + f"\n 修法:核对上面这个位置在声明侧(源码注解 / .vpi stub)为何解析不出类型。" + + self._get_node_info()) + # ② 悬空名字自检:scope 已占用而 globals 无对象 ⇒ 上一次同名创建中途失败 + # (异常被吞/未走完 add_global)。此处若继续新建只会得到裸 DuplicatedNameError, + # 故直接报出状态事实(不静默清理:真因在"上一处被吞的创建失败",清理会掩盖它)。 + _mod_scope: Any = getattr(self.module, 'scope', None) + if (name not in self.module.globals and _mod_scope is not None + and getattr(_mod_scope, 'is_used', None) is not None + and _mod_scope.is_used(name)): + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"函数声明名字悬空占用:'{name}' 已进 module.scope,但 module.globals " + f"中无对应对象——llvmlite 的 Function 在「名字注册」与 add_global 之间" + f"失败时不会回滚 scope。" + f"\n ⇒ 说明**此前对同名函数的创建中途失败且异常被吞**(真因在那一处," + f"不在本次调用)。本次继续新建只会得到裸 DuplicatedNameError。" + f"\n 修法:先修掉上一处被吞的创建失败(形参类型非法 / 签名解析失败)," + f"半注册名字随之消失。" + + self._get_node_info()) + existing: Any = self.module.globals.get(name) + if existing is not None and not isinstance(existing, ir.Function): + # 名字被非函数 global 占住(全局变量 / vtable / 字符串常量…):llvmlite 的 + # NameScope 按名字去重,再建同名 ir.Function 必抛 DuplicatedNameError + # (报文只有名字,不含冲突类型)⇒ 在此把冲突双方摊开,避免裸抛。 + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"同名全局符号冲突:'{name}' 已在 module.globals 中登记,但不是函数" + f"({type(existing).__name__}):{existing!r}" + f"\n 函数声明无法复用该符号(llvmlite NameScope 按名字去重);" + f"请核对该名字是否同时被用作全局变量 / vtable / 字符串常量。") + if isinstance(existing, ir.Function): + _old_type: Any = getattr(existing, 'ftype', None) + if _old_type is None or str(_old_type) == str(func_type): + self.functions[name] = existing + return existing + from lib.core.LLVMCG.FuncGen import FailFastError + raise FailFastError( + f"同名函数声明签名冲突(declare/define 分裂):'{name}'" + f"\n · 已存在声明: {_old_type}" + f"\n · 本次要创建: {func_type}" + f"\n 拒绝静默复用(调用点会按错签名传参);请核对同一函数在声明侧" + f"(stub / .vpi)与定义侧的注解解析结果是否一致。") + func: ir.Function = ir.Function(self.module, func_type, name=name) + self.functions[name] = func + return func + + def _get_or_declare_function(self, mangled_name: str, func_type: ir.FunctionType) -> ir.Function: + return self._CreateOrReuseFuncDecl(mangled_name, func_type) + + def _get_function(self, name: str) -> ir.Function | None: + func: ir.Function | None = self._find_function(name) + return func + + def _mangle_func_name(self, name: str, module_name: str | None = None) -> str: + if name in self._export_funcs and not module_name: + return name + if name in self._export_extern_funcs: + target_sha1: str = self._export_extern_funcs[name] + if module_name: + # 显式带 module_name 调用(如 stdlib.system):即使当前模块定义了同名函数, + # 也应按模块匹配 extern,返回裸名。snprintf 之所以工作而 system 不工作, + # 就是因为 os._win32 定义了自己的 system 包装,导致旧逻辑跳过了 extern 匹配。 + if module_name == target_sha1: + return name + if module_name in self.ModuleSha1Map and self.ModuleSha1Map[module_name] == target_sha1: + return name + init_name: str = f"{module_name}.__init__" + if init_name in self.ModuleSha1Map and self.ModuleSha1Map[init_name] == target_sha1: + return name + # 兜底:module_name 不在 ModuleSha1Map 中或 sha1 不匹配, + # 但函数在 _export_extern_funcs 中(C extern 不应混淆), + # 仅当当前模块未定义同名函数时才返回裸名。 + if name not in self._current_module_func_names: + return name + elif name not in self._current_module_func_names: + # 无 module_name 调用(如 from stdlib import system; system()): + # 仅当当前模块未定义同名函数时才返回 extern 裸名, + # 否则让本地定义走正常 mangle 路径(本地 def 遮蔽 import 语义)。 + return name + # main 函数作为程序入口点,永远不混淆 + if name == 'main': + return name + # Try class_sha1_map first for class methods (e.g., 'md5.__init__' -> class 'md5') + class_sha1_map: dict[str, str] = self.class_sha1_map + if class_sha1_map: + if '.' in name: + base_class: str = name.split('.')[0] + if base_class in class_sha1_map: + sha1: str = class_sha1_map[base_class] + return f"{sha1}.{name}" + elif name in class_sha1_map: + sha1: str = class_sha1_map[name] + return f"{sha1}.{name}" + # If module_name is already a SHA1 hash (16 hex chars), use it directly + if module_name and len(module_name) == 16 and all(c in '0123456789abcdef' for c in module_name): + return f"{module_name}.{name}" + if module_name and module_name in self.ModuleSha1Map: + sha1: str = self.ModuleSha1Map[module_name] + return f"{sha1}.{name}" + # Try parent package name (e.g., 'hashlib' -> 'hashlib.__init__') + if module_name: + init_name: str = f"{module_name}.__init__" + if init_name in self.ModuleSha1Map: + sha1: str = self.ModuleSha1Map[init_name] + return f"{sha1}.{name}" + if self._skip_mangle(name): + return name + if self.module_sha1: + result = f"{self.module_sha1}.{name}" + else: + result = name + return result + + def _get_source_line(self, lineno: int) -> str | None: + if 0 < lineno <= len(self._current_source_lines): + return self._current_source_lines[lineno - 1].rstrip('\r\n') + return None + + def _FindConcreteTwin(self, StructType: Any) -> Any: + """按**同短名**在 `structs` 里找一份**已具化**(elements 非 None)的 struct 实例。 + + 为什么需要:同一类名常有两份 struct 实例(短名 'Keyword' / sha1 全名 + '.Keyword'),"函数签名里的 self 用哪一份"与"StructGen 具化的是哪一份" + 并不保证一致;一旦被 GEP 的那份是 opaque,llvmlite 在编译期算元素类型即抛裸 + TypeError(现场:includes/ast/astaux.vp:165 `self.arg = _copy_str(pool, arg)` + 的基址 pointee 是 opaque 的 `%".Keyword"*`)。调用方把指针 bitcast 到 + 本函数返回的实例即可让"偏移与 GEP 同源"——与 HandlesAssign 里 + "同短名取字段最多版本"是同一判据。 + 返回 None 表示**没有**已具化孪生体(真因是"body 从未 set_body")⇒ 调用方不得 + 兜底,交由 RequirePointerOperand 的 opaque 守卫 fail-fast 并报出实例清单。 + """ + if not isinstance(StructType, ir.IdentifiedStructType): + return None + _Short: str = '' + try: + _Short = self._extract_short_name(getattr(StructType, 'name', '') or '') + except Exception: + _Short = '' + if not _Short: + return None + # 第一优先:**IR 全名相同**的实例(同类名两份实例的情形,必为同一类); + # 第二优先:同短名里元素最多的一份(跨模块同名类;与 HandlesAssign + # "同短名取字段最多版本"同判据)。全名相同优先可避免"短名相同但实为另一个 + # 模块的同名类"被误选而算出错的偏移。 + _Full: str = getattr(StructType, 'name', '') or '' + _Best: Any = None + _BestN: int = -1 + for _v in list(getattr(self, 'structs', {}).values()): + if not isinstance(_v, ir.IdentifiedStructType) or _v.elements is None: + continue + if _Full and getattr(_v, 'name', '') == _Full: + return _v + try: + _s: str = self._extract_short_name(getattr(_v, 'name', '') or '') + except Exception: + _s = '' + if _s != _Short: + continue + if len(_v.elements) > _BestN: + _BestN = len(_v.elements) + _Best = _v + return _Best + + def _DiskLineno(self, Lineno: int) -> int: + """AST 行号 → 磁盘真实行号(减掉文件头自动注入 import 的行数)。 + + 判据与其余各处**逐字一致**(HandlesBase._FailFastFixedArrayKeyword:687、 + HandlesAssign._RaiseGlobalAttrError:2051、LlvmGenerator/HandlesBody 的 + _ErrorStack 各站点):Phase2Translator 在解析前用 _inject_auto_imports + 往源码头注入 `import _list/_dict/json`,AST 全部节点行号因此整体下移 N + (translator._line_offset = N);而 `Gen._current_source_lines` 是**磁盘** + 原文 ⇒ 任何"AST 行号 + 源码行"的输出都必须先减 N,否则印出的源码是下一行。 + 本渲染器此前漏还原,于是同一次 fail-fast 里出现两套矛盾行号 + (现场:真实错误在 TypeGenResolve.vp:1141 `= "t.CPtr"`, + _get_node_info 印 1142 且源码取到 1142 行的 `return "t.CPtr"`, + 而 _ErrorStack 帧印的是正确的 1141/1140)。 + """ + if Lineno <= 0: + return Lineno + _offset: int = int(getattr(self._Trans, '_line_offset', 0) or 0) + if _offset > 0 and Lineno > _offset: + return Lineno - _offset + return Lineno + + def _get_node_info(self) -> str: + info: str = "" + if self._current_node_info: + # 标签里的行号同样是 AST 行号:必须与下面的源码行**一起**还原, + # 否则标签(1142)与源码(1141)互相矛盾。 + _m = _LINE_INFO_RE.match(self._current_node_info) + _info: str = (f"line {self._DiskLineno(int(_m.group(1)))}{_m.group(2)}" + if _m is not None else self._current_node_info) + info = f" [{_info}]" + if self._current_source_file: + info += f" in {self._current_source_file}" + lineno: int = self._DiskLineno(self._current_lineno) + if lineno > 0: + info += f", line {lineno}:" + source_line: str | None = self._get_source_line(lineno) + if source_line: + info += f"\n → {source_line}" + return info + + def _DescribeValueNode(self, Node: Any = None) -> str: + """把「真正被转换的值节点」渲染成一段诊断文本,供隐式转换禁令引用。 + + 报文风格照本仓既有「表达式文本 + 位置 + 源码片段」: + 表达式 ''(<节点形态>,位置 文件:行:列),源码: <该行> + 关键 ①:**列号按有符号打印**——未知(无属性 / <0)时打印 '-',绝不把 col_offset + 的 -1 当无符号打成 4294967295(此前 VPC 侧诊断错位的根因)。取不到节点时 + 回退当前语句上下文(_get_node_info 风格),不再固定打印外层 Assign。 + 关键 ②:**行号必须先还原为磁盘行号**(_DiskLineno)——节点行号含文件头 + 自动注入 import 的偏移,直接用来读源码会读到下一行(现场:真实错误行 + 1141,这里印成 1142 且源码显示出 1142 行的 `return "t.CPtr"`)。 + """ + if Node is None: + return self._get_node_info() + try: + Expr: str = ast.unparse(Node) + except Exception: + Expr = getattr(Node, 'attr', '') or getattr(Node, 'id', '') or '' + RawLine: int = getattr(Node, 'lineno', 0) or 0 + Col: Any = getattr(Node, 'col_offset', -1) + Line: int = self._DiskLineno(RawLine or self._current_lineno or 0) + Loc: str = self._current_source_file or '' + if Line > 0: + Loc += f":{Line}" + Loc += f":{Col}" if isinstance(Col, int) and Col >= 0 else ":-" + Code: str = '' + if Line > 0: + try: + Code = (self._get_source_line(Line) or '').strip() + except Exception: + Code = '' + Text: str = f";出错表达式: 表达式 '{Expr}'({type(Node).__name__} 形态,位置 {Loc})" + if Code: + Text += f",源码: {Code}" + return Text + + # ========== const / volatile:判定、登记与报文(两侧同口径) ========== + # 语义唯一来源见 Debugger/type-descriptors.md。TPC 与 ViperC 必须**同判据、 + # 同报文**;任何未消除的差异都列在该文档的「已知差异」一节。 + + @staticmethod + def _IsConstObject(Info: Any) -> bool: + """注解解析结果是否为「变量自身 const」的对象(C 语义下的 const 对象)。 + + 判据(两侧统一):`(DataConst or VarConst)` 且 `PtrCount == 0`。 + `t.CConst` 在本仓库解析为 **DataConst**(t 模块分支:HandlesTypeMerge.py:192-196、 + TypeAnnotationResolver.py:382-389),其字段语义是「指向的数据 const」 + (HandlesBase.py:76-77)。因此: + - 非指针类型(PtrCount==0):该限定退化为「该对象自身 const」⇒ + `a: const int` / `a: t.CConst | int` 的 a 禁止赋值/增量赋值/元素赋值; + - 指针类型(PtrCount>=1):限定只作用于 pointee ⇒ `const T *p` 的 p + 仍可重新赋值(C 语义),**不**登记为 const 变量。 + 后者就是全仓既有两处 `t.CConst | T | t.CPtr` 局部变量(fat32_types.vp:1591 + `buf_ptr`、elf.vp:509 `cmp_name`)在其作用域内被重新赋值却合法的原因。 + """ + if Info is None: + return False + if not (getattr(Info, 'DataConst', False) or getattr(Info, 'VarConst', False)): + return False + return getattr(Info, 'PtrCount', 0) == 0 + + @staticmethod + def _AnnotationDeclText(Annotation: Any) -> str: + """注解节点的源码文本(仅用于报文;取不到时给占位,不伪造)。""" + if Annotation is None: + return '(无注解)' + try: + return ast.unparse(Annotation) + except Exception: + return '<无法还原注解文本>' + + @staticmethod + def _AnnotationHasQualifier(Annotation: Any, AttrName: str) -> bool: + """注解子树里是否出现 `t.`(与 ViperC TypeGenErr.has_decorator_marker 同口径)。""" + if Annotation is None: + return False + for n in ast.walk(Annotation): + if isinstance(n, ast.Attribute) and n.attr == AttrName: + return True + return False + + @staticmethod + def _AnnotationHasPointeeVolatile(Annotation: Any) -> bool: + """注解是否为 `rawptr[t.CVolatile | T]`(volatile 指针指向物,等价 C 的 volatile T*)。 + + 与 ViperC TypeGenErr.has_pointee_volatile_marker 同口径:只看 `rawptr[...]` + 的下标实参子树里有没有 CVolatile;多级指针(slice 仍是 Subscript)明确不支持。 + """ + if not isinstance(Annotation, ast.Subscript): + return False + base: Any = Annotation.value + if not isinstance(base, ast.Name) or base.id != 'rawptr': + return False + return BaseGenMixin._AnnotationHasQualifier(Annotation.slice, 'CVolatile') + + def RegisterVarQualifiers(self, Name: str, Info: Any, Annotation: Any, + IsGlobal: bool = False, IsParam: bool = False) -> None: + """登记一个变量(局部/形参/模块级全局)的 const / volatile 限定。 + + 统一的登记入口(TPC 的形参、局部变量、模块级全局三处声明点都调本函数), + 保证「登记点」只有一份判据。 + + IsParam=True 时 **const 判据沿用形参的历史条件** `DataConst or VarConst` + (不看指针层数):这是「形参保持现状 + 不得放宽任何既有 fail-fast」两条 + 硬约束下的唯一选择——把形参也改成指针感知判据虽然更合 C 语义,但会**放宽** + 既有形参检查(`fmt: t.CConst | str` 这类指针形参不再登记),从约束上不允许。 + 该历史条件对 `const T*` 形参过严,但全仓 462 个 .vp 普查(见 + Debugger/type-descriptors.md)表明**零个形参在其函数体内被重新赋值**, + 故这处差异在既有代码上不可观测。非形参的三种声明一律用指针感知判据 + `_IsConstObject`。 + """ + if not Name: + return + if IsParam: + IsConst: bool = bool(Info) and ( + getattr(Info, 'DataConst', False) or getattr(Info, 'VarConst', False)) + else: + IsConst = self._IsConstObject(Info) + if IsConst: + if IsGlobal: + self.global_const_flags[Name] = True + else: + self.var_const_flags[Name] = True + self.const_decl_types[Name] = self._AnnotationDeclText(Annotation) + if self._AnnotationHasQualifier(Annotation, 'CVolatile'): + if IsGlobal: + self.global_volatile_flags[Name] = True + else: + self.var_volatile_flags[Name] = True + if self._AnnotationHasPointeeVolatile(Annotation): + if IsGlobal: + self.global_pointee_volatile_flags[Name] = True + else: + self.var_pointee_volatile_flags[Name] = True + + def RegisterFieldQualifiers(self, ClassName: str | None, FieldName: str, + Info: Any, Annotation: Any) -> None: + """登记一个类字段的 const / volatile 限定(键 `类名.字段名` + 裸字段名)。""" + if not FieldName: + return + if self._IsConstObject(Info): + if ClassName: + self.field_const_flags[f'{ClassName}.{FieldName}'] = True + self.field_const_names.add(FieldName) + self.const_decl_types[FieldName] = self._AnnotationDeclText(Annotation) + if self._AnnotationHasQualifier(Annotation, 'CVolatile'): + if ClassName: + self.field_volatile_flags[f'{ClassName}.{FieldName}'] = True + self.field_volatile_names.add(FieldName) + if self._AnnotationHasPointeeVolatile(Annotation): + if ClassName: + self.field_pointee_volatile_flags[f'{ClassName}.{FieldName}'] = True + self.field_pointee_volatile_names.add(FieldName) + + def _IsVarConst(self, Name: str) -> bool: + """变量(局部/形参/模块级全局)自身是否 const。""" + return bool(Name) and (Name in self.var_const_flags or Name in self.global_const_flags) + + def _IsVarVolatile(self, Name: str) -> bool: + """变量自身是否 volatile。""" + return bool(Name) and (Name in self.var_volatile_flags or Name in self.global_volatile_flags) + + def _IsVarPointeeVolatile(self, Name: str) -> bool: + """变量所指物是否 volatile(等价 C 的 volatile T* 解引用)。""" + return bool(Name) and (Name in self.var_pointee_volatile_flags + or Name in self.global_pointee_volatile_flags) + + def _IsFieldConst(self, ClassName: str | None, FieldName: str) -> bool: + """类字段自身是否 const。 + + 类名可解析时按精确键 `类名.字段名`(**不**回退裸名,避免"另一个同名 + const 字段"造成跨类误伤);类名解析不到时退化为裸名集合(保守报错)。 + """ + if not FieldName: + return False + if ClassName: + return f'{ClassName}.{FieldName}' in self.field_const_flags + return FieldName in self.field_const_names + + def _IsFieldVolatile(self, FieldName: str) -> bool: + return bool(FieldName) and FieldName in self.field_volatile_names + + def _IsFieldPointeeVolatile(self, FieldName: str) -> bool: + return bool(FieldName) and FieldName in self.field_pointee_volatile_names + + @staticmethod + def _StripDedupSuffix(Name: str) -> list[str]: + """IR 名 -> 候选登记名(llvmlite 去重把重复名改成 `名.1` / `名.2`)。""" + Cands: list[str] = [Name] + Head, _, Tail = Name.rpartition('.') + if Head and Tail.isdigit(): + Cands.append(Head) + return Cands + + def _IsPtrVolatile(self, ptr: Any) -> bool: + """按 IR 指针对应的**登记名**判定该次访问是否带 volatile。 + + alloca / GlobalVariable 均以变量名命名,GEP 以字段名命名 + (见 HandlesExprAttr 的 `gep(..., name=AttrName)`),故变量自身 volatile、 + 变量所指物 volatile 与字段 volatile 都在此命中。名字取不到时返回 False + (不做任何猜测/兜底)。 + + 下标 GEP 额外带基址名(`subscript_<基址名>` / `subscript_ptr_<基址名>`, + 见 HandlesExprAttr/HandlesAssign 的下标改写):基址是 pointee-volatile + 变量/字段时,经该下标的 load/store 也要带 volatile(等价 C 的 + `volatile T* p` 的 `p[i]`)。**基址名后缀为空时名字与改造前逐字相同** + ⇒ 无 pointee-volatile 的代码 IR 逐字节不变。 + """ + nm: Any = getattr(ptr, 'name', None) + if not nm or not isinstance(nm, str): + return False + for C in self._StripDedupSuffix(nm): + if self._IsVarVolatile(C) or self._IsVarPointeeVolatile(C): + return True + if C in self.field_volatile_names or C in self.field_pointee_volatile_names: + return True + BaseNames: list[str] = list(self.var_pointee_volatile_flags) + BaseNames += list(self.global_pointee_volatile_flags) + BaseNames += list(self.field_pointee_volatile_names) + for B in BaseNames: + if (nm == f'subscript_{B}' or nm.startswith(f'subscript_{B}.') + or nm == f'subscript_ptr_{B}' or nm.startswith(f'subscript_ptr_{B}.')): + return True + return False + + @staticmethod + def _SubscriptSuffix(BaseNode: Any) -> str: + """下标基址名后缀(供 pointee-volatile 按 IR 名回查,见 `_IsPtrVolatile`)。 + + 基址是 `Name`(变量)→ `_变量名`;是 `obj.字段` → `_字段名`;其余形态返回 + `''`。**返回 `''` 时下标 GEP 名与改造前逐字相同**(既有代码 IR 零变更)。 + """ + if isinstance(BaseNode, ast.Name): + return f'_{BaseNode.id}' if BaseNode.id else '' + if isinstance(BaseNode, ast.Attribute): + return f'_{BaseNode.attr}' if BaseNode.attr else '' + return '' + + def _ConstAssignMessage(self, Kind: str, Name: str) -> str: + """const 变量被赋值时的 fail-fast 报文(两侧同口径)。 + + 含:文件/行/列 + 源码片段(`_get_node_info`)+ 变量名 + 声明处类型(含 const) + + 修法。 + """ + DeclType: str = self.const_decl_types.get(Name, '(声明处类型未登记)') + return ( + f"不能对 const 变量 '{Name}' {Kind}{self._get_node_info()}" + f"(声明处类型(含 const):{DeclType};" + f"修法:删掉该声明里的 `const` 描述符 / `t.CConst`,或改为声明时一次赋值、" + f"之后只用非 const 的副本/中间量;const 对象在其作用域内禁止赋值、" + f"增量赋值与元素赋值,且不会降级为警告)" + ) + + # ========== 统一工具方法 ========== + + def _create_string_global(self, s: str, name: str | None = None) -> ir.Constant: + """创建字符串全局变量并返回 i8* 指针。 + + 统一字符串全局变量创建模式:编码为 utf-8 + '\\x00' 终止符, + 创建 internal linkage 的 GlobalVariable,返回 i8* 指针常量。 + """ + str_val: str = s + '\x00' + str_bytes: bytes = str_val.encode('utf-8') + arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) + gv_name: str = name or f"str_const_{self.string_const_counter}" + self.string_const_counter += 1 + gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_type, name=gv_name) + gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + gv.linkage = 'internal' + return ir.Constant(ir.PointerType(ir.IntType(8)), gv.get_reference()) + + def _create_doc_global(self, symbol_name: str, docstring: str | None) -> None: + """为符号创建 __doc__ 全局常量。 + + - docstring 为 None 或空字符串时,不创建常量(访问时内联空指针) + - slice_level != 0(优化开启)时,内容视为空 + - 非空时创建 internal linkage、global_constant=True 的只读字符串常量 + - 常量名格式:{module_sha1}.{symbol_name}.__doc__ + """ + slice_level: int = getattr(self._Trans, 'SliceLevel', 3) + if slice_level != 0: + docstring = None + if docstring is None or docstring == '': + self._doc_strings[symbol_name] = None + return + self._doc_strings[symbol_name] = docstring + mangled: str = self._mangle_name(f"{symbol_name}.__doc__") + self._create_string_global(docstring, name=mangled) + gv: ir.GlobalVariable | None = self.module.globals.get(mangled) + if gv is not None: + gv.global_constant = True + + def _get_doc_ptr(self, symbol_name: str) -> ir.Value | None: + """获取符号 __doc__ 的 i8* 指针。 + + - 若符号未注册 __doc__,返回 None(调用方回退到其他解析) + - 若 __doc__ 为空(无 docstring 或优化剥离),返回 i8* null 内联空指针 + - 若 __doc__ 非空,返回指向字符串全局常量的 i8* 指针 + - 跨模块 docstring(在 _doc_strings 中有文本但 module.globals 无对应常量)首次访问时创建本地 internal linkage 只读副本 + """ + if symbol_name not in self._doc_strings: + return None + docstring: str | None = self._doc_strings[symbol_name] + if docstring is None or docstring == '': + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + mangled: str = self._mangle_name(f"{symbol_name}.__doc__") + gv: ir.GlobalVariable | None = self.module.globals.get(mangled) + if gv is None: + # 跨模块 docstring:创建本地 internal linkage 只读字符串常量副本 + self._create_string_global(docstring, name=mangled) + gv = self.module.globals.get(mangled) + if gv is None: + return ir.Constant(ir.PointerType(ir.IntType(8)), None) + gv.global_constant = True + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + return self.builder.gep(gv, [zero, zero], name=f"{symbol_name}_doc_ptr") + + def _apply_bswap_if_big(self, val: ir.Value, byte_order: str, name: str) -> ir.Value: + """大端字节序时对整数应用 bswap,否则原值返回。 + + 仅对 16/32/64 位整数生效,统一 bswap 逻辑。 + """ + if byte_order == 'big' and isinstance(val.type, ir.IntType) and val.type.width in (16, 32, 64): + return self.builder.bswap(val, name=name) + return val + + def _zero_value_for_type(self, llvm_type: ir.Type) -> ir.Constant | None: + """为指定 LLVM 类型生成零值常量。 + + 合并原 HandlesAssign._zero_value 与 StructGen._zero_value_for_type 的逻辑。 + 返回 ir.Constant 或 None(无法生成零值的类型,如 opaque struct)。 + """ + if isinstance(llvm_type, ir.IntType): + return ir.Constant(llvm_type, 0) + elif isinstance(llvm_type, (ir.FloatType, ir.DoubleType)): + return ir.Constant(llvm_type, 0.0) + elif isinstance(llvm_type, ir.PointerType): + return ir.Constant(llvm_type, None) + elif isinstance(llvm_type, ir.ArrayType): + elem_zv: ir.Constant | None = self._zero_value_for_type(llvm_type.element) + if elem_zv is None: + return None + return ir.Constant(llvm_type, [elem_zv] * llvm_type.count) + elif isinstance(llvm_type, ir.BaseStructType): + if llvm_type.elements is not None and len(llvm_type.elements) > 0: + elem_zvs: list[ir.Constant | None] = [self._zero_value_for_type(m) for m in llvm_type.elements] + if any(zv is None for zv in elem_zvs): + return None + return ir.Constant(llvm_type, elem_zvs) + return None + return None + + # ========== 调用点热点统计基础设施(--hotspot-sites) ========== + + def _UniqueGlobalName(self, base: str) -> str: + """返回本模块内未被占用的全局符号名(追加下划线直到不冲突)。 + + 仅用于 --hotspot-sites 自动生成的内部符号,避免与用户全局/函数重名。 + """ + name: str = base + while name in self.module.globals: + name += '_' + return name + + def _DeclareExternalFunc(self, name: str, ftype: ir.FunctionType) -> ir.Function: + """获取或声明外部 C 函数(用于 --hotspot-sites 所需的 Win32/CRT 接口)。 + + 已存在同名函数时直接复用;若已存在符号类型不一致则显式报错(不静默降级)。 + """ + existing: Any = self.module.globals.get(name) + if existing is not None: + if not isinstance(existing, ir.Function): + raise RuntimeError( + f'--hotspot-sites: 全局符号 {name} 已存在且不是函数,无法声明外部接口') + if existing.ftype != ftype: + raise RuntimeError( + f'--hotspot-sites: 外部函数 {name} 已存在但签名不一致' + f'(期望 {ftype},实际 {existing.ftype})') + return existing + return self._CreateOrReuseFuncDecl(name, ftype) + + def _InstallHotspotTruncate( + self, + defs: list[ir.Function], + path: str, + require_main: bool = False, + flag: str = '--hotspot-sites', + ) -> ir.Function | None: + """在含 main 的模块的 main 入口插入一次 "w" 打开→关闭,清空计数文件。 + + --hotspot-sites 的各处理器(调用点计数、每函数计时)以追加("a")方式写 + ,若不清空会累积历史运行的数据。清空必须满足两点: + - 只在含 main 的模块执行一次(其它模块无 main,返回 None 且不截断); + - 必须在 main 入口而非构造器:@llvm.global_ctors 的跨模块执行顺序无保证, + 放构造器里可能在其它模块已写入后清空 → 丢数据。 + main 入口在程序启动、任何 Ctrl+C 处理器写入之前执行,故每次运行都以空文件 + 开始,配合追加写保证"一次运行一份结果"。 + + flag 仅用于报错信息(调用点计数与每函数计时各自调用一次,传入对应开关名)。 + + 返回找到的 main 定义(供调用方继续注册处理器);未找到时返回 None。 + require_main=True(种子 CLI 单模块)时未找到 main 直接报错(不静默降级)。 + """ + main_func: ir.Function | None = None + for func in defs: + if func.name == 'main': + main_func = func + break + if main_func is None: + if require_main: + raise RuntimeError( + f'{flag}: 找不到 main 函数定义,无法清空计数文件') + return None + i32: ir.IntType = ir.IntType(32) + i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) + fopen: ir.Function = self._DeclareExternalFunc('fopen', ir.FunctionType(i8p, [i8p, i8p])) + fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p])) + path_ptr: ir.Constant = self._create_string_global( + path, name=self._UniqueGlobalName('__fhs_trunc_path')) + mode_ptr: ir.Constant = self._create_string_global( + 'w', name=self._UniqueGlobalName('__fhs_trunc_mode')) + b: ir.IRBuilder = ir.IRBuilder(main_func.entry_basic_block) + b.position_at_start(main_func.entry_basic_block) + fp: ir.Value = b.call(fopen, [path_ptr, mode_ptr], name='fhs_trunc_fp') + b.call(fclose, [fp]) + return main_func + + # ========== 调用点热点统计(--hotspot-sites) ========== + + def _NewIrBuilder(self, block: ir.Block) -> ir.IRBuilder: + """创建 IR 构建器:IR 边界守卫(无条件)+ --hotspot-sites 源码位置挂钩。 + + 未启用热点统计(hotspot_sites_file 为 None)时返回 _GuardIrBuilder: + 合法 IR 与原生 ir.IRBuilder 完全一致,只在 GEP/load/store 的指针操作数 + 不是指针时 fail-fast(否则 llvmlite 会抛无位置信息的裸 AttributeError + 'IntType' object has no attribute 'is_opaque',见 RequirePointerOperand)。 + 启用时返回 _HotspotSiteBuilder(同为 _GuardIrBuilder 子类,守卫不变), + 仅在每条新生成的 call 指令对象上追加 _src_file/_src_line 两个普通属性, + 供调用点热点插桩读取源码位置。 + """ + if self.hotspot_sites_file: + return _HotspotSiteBuilder(block, self) + return _GuardIrBuilder(block, self) + + @staticmethod + def _HotspotCalleeBase(name: str) -> str: + """返回被调函数名的基名:去掉 16 位 sha1 模块前缀。 + + 仅当形如 "<16位十六进制>.xxx" 时才剥离,避免误伤普通函数名; + 例如 "23499a69276862b2.strcmp" -> "strcmp","strlen" -> "strlen"。 + """ + if len(name) > 17 and name[16] == '.': + prefix: str = name[:16] + if all(ch in '0123456789abcdefABCDEF' for ch in prefix): + return name[17:] + return name + + def _EmitHotspotSiteCounters( + self, + ) -> tuple[list[ir.Function], list[tuple[ir.GlobalVariable, str, str, str, int]]]: + """为匹配白名单的每个直接 call 调用点安装 internal i64 计数器并在 call 之前递增。 + + 返回 (模块内函数定义列表, 记录列表);记录为 + (计数器全局, 调用方函数名, 被调方基名, 源码文件名, 源码行号), + 记录顺序即计数器编号 __fhs_site_ 的顺序。源码位置来自生成期挂在 + call 指令对象上的 _src_file/_src_line(见 _HotspotSiteBuilder);生成期 + 无法关联到 AST 的合成调用点取不到位置,则文件名留空、行号记 0。 + + 插桩规则: + - 白名单:hotspot_sites_all=True 时不过滤(全部直接调用点);否则仅对 + "被调名去 16 位 sha1 前缀后的基名" 命中白名单的调用点插桩。 + - 跳过间接调用(callee 不是具名 ir.Function):无法归属被调方,故不插桩。 + - 跳过 __fhs_ 前缀的辅助函数自身,避免在插桩生成的代码里计数 + (本功能稍后生成的处理器/构造器/计时辅助亦在此列)。 + - 只在 call 之前插入 load/add/store,不改变返回值/参数/控制流。 + - 计数器一律 internal,跨模块(独立翻译单元)链接不产生重复符号,初值 0。 + """ + i64: ir.IntType = ir.IntType(64) + if self.hotspot_sites_all: + whitelist: set[str] | None = None + else: + names: list[str] = ( + list(self.hotspot_callees) if self.hotspot_callees is not None + else list(DEFAULT_HOTSPOT_CALLEES) + ) + whitelist = {self._HotspotCalleeBase(n) for n in names} + defs: list[ir.Function] = [f for f in self.module.functions if getattr(f, 'blocks', None)] + records: list[tuple[ir.GlobalVariable, str, str, str, int]] = [] + for func in defs: + if func.name.startswith('__fhs_'): + continue + for block in list(func.blocks): + for instr in list(block.instructions): + if not isinstance(instr, ir.CallInstr): + continue + callee: Any = instr.callee + if not isinstance(callee, ir.Function) or not callee.name: + continue + base: str = self._HotspotCalleeBase(callee.name) + if whitelist is not None and base not in whitelist: + continue + idx: int = len(records) + counter: ir.GlobalVariable = ir.GlobalVariable( + self.module, i64, name=self._UniqueGlobalName(f'__fhs_site_{idx}')) + counter.linkage = 'internal' + counter.initializer = ir.Constant(i64, 0) + # 在 call 之前递增计数:load -> add 1 -> store(不改变 call 本身) + b: ir.IRBuilder = ir.IRBuilder(block) + b.position_before(instr) + cur: ir.Value = b.load(counter, name=f'fhs_site_load_{idx}') + inc: ir.Value = b.add(cur, ir.Constant(i64, 1), name=f'fhs_site_inc_{idx}') + b.store(inc, counter) + # 源码位置:生成期 _HotspotSiteBuilder 挂在 call 指令对象上;缺失则留空 + src_file: str = getattr(instr, '_src_file', None) or '' + src_line: int = getattr(instr, '_src_line', 0) or 0 + records.append((counter, func.name, base, src_file, src_line)) + return defs, records + + @staticmethod + def _HotspotTimePath(sites_path: str) -> str: + """由调用点计数文件路径推导每函数计时输出文件路径。 + + 取同目录下的固定名 hotspot_time.txt(不覆盖、不解析既有 hotspot_sites.txt + 格式),便于下游分别解析两类结果。 + """ + return os.path.join(os.path.dirname(sites_path), 'hotspot_time.txt') + + # 深度栈容量:512。超出该深度的调用不记账(self/inc 均不计),但仍在 + # __fhs_ftime_enter_ 里计入进入次数——溢出是"拿不到数值",明确跳过而非伪造。 + _HotspotDepth: int = 512 + + def _EmitReadTimestamp(self, b: ir.IRBuilder, name: str = '') -> ir.Value: + """发射一次**串行化**时间戳读取,返回 i64 tick(入口与每个 ret 前共用)。 + + 串行化方案:在 x86_64 目标上先取一次内存屏障 @llvm.x86.sse2.lfence 再读 + @llvm.readcyclecounter()(该 intrinsic 在 x86_64 后端降为 rdtsc)。理由: + - lfence 在 Intel/AMD 上对取指与访存有序列化效果,能阻止 rdtsc 与前后访存 + 乱序(裸 rdtsc 只保证自身 edx:eax 原子,不保证与其它指令的顺序),这是 + "lfence; rdtsc" 这一业界通行的序列化读法; + - 用 LLVM intrinsic 组合而非 ir.InlineAsm:无内联汇编约束串的转义/渲染风险, + 也不依赖 Clang 风格 asm 约束是否被 llvmlite 正确渲染,可移植性更好。 + 非 x86_64 目标(如 aarch64/arm/i386)降级为**裸** @llvm.readcyclecounter(): + lfence 是 x86(SSE2) 专有 intrinsic,在非 x86 目标上不存在、编译期即不可用, + 故此处按目标三元组在**编译期**决定是否发射 lfence(不在运行期静默退化)。 + """ + i64: ir.IntType = ir.IntType(64) + if bool(self._platform_info.get('is_x86_64')): + lfence: ir.Function = self._DeclareExternalFunc( + 'llvm.x86.sse2.lfence', ir.FunctionType(ir.VoidType(), [])) + b.call(lfence, []) + rcc: ir.Function = self._DeclareExternalFunc( + 'llvm.readcyclecounter', ir.FunctionType(i64, [])) + return b.call(rcc, [], name=name) + + def _EmitHotspotTimeCounters( + self, + ) -> list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]]: + """为模块内每个函数定义安装"自身耗时(self) + 包含式耗时(inc) + 进入次数"计时器。 + + 计时原语(串行化读):见 _EmitReadTimestamp(x86_64 上 lfence; rdtsc, + 其它目标降级为裸 rdtsc)。无 libc/Win32 依赖、无需额外链接;精度为 1 个 + tick(x86_64 上即一个 CPU 周期,~0.3ns 量级),故耗时单位记为 tick。 + + exclusive(自身耗时)算法——深度栈: + - 全局 common i64 __fhs_tdepth:当前插桩深度,初值 0;common linkage 让每个 + 翻译单元各自发射同名 tentative 定义、由链接器合并为**全程序唯一一份**, + 故跨模块调用也共用同一张深度栈(独立翻译单元链接不产生重复符号)。 + - 全局 common [DEPTH x i64] __fhs_tstart / __fhs_tchild(零初始化, + DEPTH = _HotspotDepth = 512):按深度索引的"本帧起始 tick"与"本帧内被 + 插桩子调用 inclusive 之和";另有一个 internal i64 抖动槽 __fhs_tscratch, + 用于把"不应记账"的写入导向一个永不读取的位置(等价于不写,但不分支)。 + - 入口(每个被插桩函数 F,索引 N): + t0 = 串行化读时间戳() + d = load __fhs_tdepth + ok = d < DEPTH + __fhs_tstart[ok ? d : 0] = t0 (溢出时写抖动槽) + __fhs_tchild[ok ? d : 0] = 0 (溢出时写抖动槽) + __fhs_tdepth = ok ? d + 1 : d (溢出时不推进深度) + slot = ok ? d : -1 存进该次调用的 alloca(每调用一个独立栈槽) + __fhs_ftime_enter_N += 1 (溢出也计进入次数) + 说明:slot 是"本帧独占的深度索引",递归/重入时各帧各占一层,天然正确; + 因为本帧在其返回前独享 slot,故 ret 时用 __fhs_tstart[slot] 即本帧 t0 + (无需再单独保存 t0 到 alloca,避免出现无人读取的死存储)。 + - 每个 ret 之前(slot = load): + inok = slot >= 0;idx = inok ? slot : 0 + t1 = 串行化读时间戳() + inc = t1 - __fhs_tstart[idx] (本次 inclusive) + self = inc - __fhs_tchild[idx] (减去本次内被插桩子调用的 inc 之和) + __fhs_fself_N += inok ? self : 0 (新增:自身耗时) + __fhs_finc_N += inok ? inc : 0 (保留:包含式耗时) + __fhs_tdepth = inok ? slot : load __fhs_tdepth (回到本帧深度) + if slot > 0(含用抖动槽等价实现): __fhs_tchild[slot-1] += inc + (把本次 inc 记到父帧的 child 累加器) + 溢出帧(slot=-1)不记账、也不写入父帧 child;其墙钟时间仍落在父帧的 + inc 里,因而会计入父帧的 **self**(见下方说明)。 + + 返回记录列表 (自身耗时全局, 包含式耗时全局, 进入次数全局, 函数名),顺序即 + N 的编号顺序。落盘由 _CreateHotspotTimeDump 完成(按 self 降序)。 + + 固有性质(务必据此解读数值):**未插桩的被调方**(间接调用、无定义体的外部 + 函数如 strcmp/memcpy、名字以 __fhs_ 开头的辅助函数、深度溢出而跳过的调用) + 其耗时不会被本方法从调用者 self 中扣除,一律计入**调用者自身 self**。故 self + 是"该函数自身指令 + 未插桩子调用"的墙钟时间,只是比 inc 更贴近自身成本。 + 深度栈为 common(全程序一份)后,跨模块被调方只要被插桩即能正确扣除;修复前 + 各模块各持一份 internal 深度栈,跨模块子调用的 inc 记不进调用方 tchild, + 表现为调用方 self ≈ inc 的假象(见 common linkage 说明)。 + + 插桩规则与跳过条件: + - 只处理有基本块(即已定义)且名字不以 __fhs_ 开头的函数;每函数计数器一律 + internal(同名索引跨模块并存,不可合并);深度栈用 common 由链接器合并为 + 全程序唯一一份,初值 0。 + - 入口代码置于入口块最前(无终结指令,不改变 CFG);条件写入用 select 实现, + 不新增基本块、不修改任何既有分支/终结指令。 + - 只在该函数的 ret 终结指令之前插桩;以 unreachable 等其它终结指令结尾的 + 块(如 noreturn 调用后的代码)没有"返回点",明确跳过、不伪造数值(数量见 + 报告,如需可另行统计)。 + - 累加只做 load/sub/add/store/select 与读取计时原语,不改变返回值/参数/控制流。 + """ + depth: int = self._HotspotDepth + i32: ir.IntType = ir.IntType(32) + i64: ir.IntType = ir.IntType(64) + c0_32: ir.Constant = ir.Constant(i32, 0) + c0_64: ir.Constant = ir.Constant(i64, 0) + c1_64: ir.Constant = ir.Constant(i64, 1) + cneg1_64: ir.Constant = ir.Constant(i64, -1) + # 深度栈三件套用 common linkage:每模块各自发射一份同名 tentative 定义, + # 由链接器合并成"全程序唯一一份"(与 HandlesAssign 中多模块同名全局同法, + # 避免 ld.lld duplicate symbol)。跨模块调用链必须共用同一张深度栈,否则 + # 被调方在自模块栈里 slot=0(或 slot 落在错误父帧)→ 其 inc 永远记不到 + # 调用方 __fhs_tchild[slot-1] → 调用方 self ≈ inc(子时间一分不扣)。 + # 抖动槽 __fhs_tscratch 只写不读,留 internal 即可(各模块独立无影响)。 + depth_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, i64, name=self._UniqueGlobalName('__fhs_tdepth')) + depth_gv.linkage = 'common' + depth_gv.initializer = c0_64 + arr_ty: ir.ArrayType = ir.ArrayType(i64, depth) + tstart_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, arr_ty, name=self._UniqueGlobalName('__fhs_tstart')) + tstart_gv.linkage = 'common' + tstart_gv.initializer = ir.Constant(arr_ty, None) + tchild_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, arr_ty, name=self._UniqueGlobalName('__fhs_tchild')) + tchild_gv.linkage = 'common' + tchild_gv.initializer = ir.Constant(arr_ty, None) + scratch_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, i64, name=self._UniqueGlobalName('__fhs_tscratch')) + scratch_gv.linkage = 'internal' + scratch_gv.initializer = c0_64 + records: list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]] = [] + for func in [f for f in self.module.functions if getattr(f, 'blocks', None)]: + if func.name.startswith('__fhs_'): + continue + idx: int = len(records) + self_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, i64, name=self._UniqueGlobalName(f'__fhs_fself_{idx}')) + self_gv.linkage = 'internal' + self_gv.initializer = c0_64 + inc_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, i64, name=self._UniqueGlobalName(f'__fhs_finc_{idx}')) + inc_gv.linkage = 'internal' + inc_gv.initializer = c0_64 + enter_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, i64, name=self._UniqueGlobalName(f'__fhs_ftime_enter_{idx}')) + enter_gv.linkage = 'internal' + enter_gv.initializer = c0_64 + # 入口:取 t0 → 压深度栈 → 记录本帧 slot → 进入次数 +1(置于入口块最前) + entry: ir.Block = func.entry_basic_block + eb: ir.IRBuilder = ir.IRBuilder(entry) + eb.position_at_start(entry) + slot_ptr: ir.Value = eb.alloca(i64, name=f'fhs_t_slot_{idx}') + t0: ir.Value = self._EmitReadTimestamp(eb, name=f'fhs_t_t0_{idx}') + d: ir.Value = eb.load(depth_gv, name=f'fhs_t_depth_{idx}') + ok: ir.Value = eb.icmp_unsigned('<', d, ir.Constant(i64, depth), name=f'fhs_t_ok_{idx}') + safe_idx: ir.Value = eb.select(ok, d, c0_64, name=f'fhs_t_idx_{idx}') + eb.store(t0, eb.select( + ok, eb.gep(tstart_gv, [c0_32, safe_idx]), scratch_gv)) + eb.store(c0_64, eb.select( + ok, eb.gep(tchild_gv, [c0_32, safe_idx]), scratch_gv)) + eb.store(eb.select(ok, eb.add(d, c1_64), d), depth_gv) + eb.store(eb.select(ok, d, cneg1_64), slot_ptr) + eb.store(eb.add(eb.load(enter_gv), c1_64), enter_gv) + # 每个 ret 之前:算 self/inc,回退深度,累加父帧 child + for block in func.blocks: + term: Any = block.terminator + if not isinstance(term, ir.Ret): + continue + rb: ir.IRBuilder = ir.IRBuilder(block) + rb.position_before(term) + slot: ir.Value = rb.load(slot_ptr, name=f'fhs_t_slotv_{idx}') + inok: ir.Value = rb.icmp_signed('>=', slot, c0_64, name=f'fhs_t_inok_{idx}') + t1: ir.Value = self._EmitReadTimestamp(rb, name=f'fhs_t_t1_{idx}') + safe_slot: ir.Value = rb.select(inok, slot, c0_64, name=f'fhs_t_slotidx_{idx}') + inc_v: ir.Value = rb.sub( + t1, rb.load(rb.gep(tstart_gv, [c0_32, safe_slot])), + name=f'fhs_t_inc_{idx}') + child_v: ir.Value = rb.load( + rb.gep(tchild_gv, [c0_32, safe_slot]), name=f'fhs_t_child_{idx}') + self_v: ir.Value = rb.sub(inc_v, child_v, name=f'fhs_t_self_{idx}') + rb.store(rb.add(rb.load(self_gv), rb.select(inok, self_v, c0_64)), self_gv) + rb.store(rb.add(rb.load(inc_gv), rb.select(inok, inc_v, c0_64)), inc_gv) + rb.store(rb.select(inok, slot, rb.load(depth_gv)), depth_gv) + pok: ir.Value = rb.icmp_signed('>', slot, c0_64, name=f'fhs_t_pok_{idx}') + p_idx: ir.Value = rb.select(pok, rb.sub(slot, c1_64), c0_64) + p_ptr: ir.Value = rb.gep(tchild_gv, [c0_32, p_idx]) + p_target: ir.Value = rb.select(pok, p_ptr, scratch_gv) + rb.store(rb.add(rb.load(p_target), inc_v), p_target) + records.append((self_gv, inc_gv, enter_gv, func.name)) + return records + + def _CreateHotspotSitesDump( + self, + path: str, + records: list[tuple[ir.GlobalVariable, str, str, str, int]], + module_id: str | None = None, + ) -> ir.Function: + """生成 void() 落盘函数:按调用次数降序把各调用点追加写入 。 + + 落盘格式(追加): + # sites \\n + -> :\\n + module_id 为空时省略行首 sha1 与头部 sha1(留空,不伪造)。 + + - 排序:落盘前用选择排序(两层运行期循环,IR 规模 O(N)、运行期 O(N^2) 仅 + 在落盘时触发一次)按计数降序输出,只输出 count>0 的调用点;计数相同时 + 按计数器编号 K 升序(稳定、可复现)。 + - 源码位置:作为可选尾字段,格式 ":"(含前置单个空格);该位置 + 来自生成期挂在 call 指令对象上的 _src_file/_src_line(见 _HotspotSiteBuilder)。 + 生成期无法关联到 AST 的合成调用点取不到位置,则此字段整体省略(不伪造、不填默认值)。 + - 函数签名为 void(),由 __fhs_dump_all(见 _CreateHotspotDumpAll)统一带守卫 + 调用,本函数自身不检查守卫。用 CRT fopen("a")/fprintf/fclose 落盘(沿用既有做法)。 + """ + n: int = len(records) + i8: ir.IntType = ir.IntType(8) + i32: ir.IntType = ir.IntType(32) + i64: ir.IntType = ir.IntType(64) + i8p: ir.PointerType = ir.PointerType(i8) + i64p: ir.PointerType = ir.PointerType(i64) + c0_8: ir.Constant = ir.Constant(i8, 0) + c1_8: ir.Constant = ir.Constant(i8, 1) + c0_32: ir.Constant = ir.Constant(i32, 0) + c0_64: ir.Constant = ir.Constant(i64, 0) + c1_64: ir.Constant = ir.Constant(i64, 1) + fopen: ir.Function = self._DeclareExternalFunc('fopen', ir.FunctionType(i8p, [i8p, i8p])) + fprintf: ir.Function = self._DeclareExternalFunc( + 'fprintf', ir.FunctionType(i32, [i8p, i8p], var_arg=True)) + fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p])) + dump_fn: ir.Function = self._CreateOrReuseFuncDecl( + self._UniqueGlobalName('__fhs_dump_sites'), + ir.FunctionType(ir.VoidType(), [])) + dump_fn.linkage = 'internal' + b: ir.IRBuilder = ir.IRBuilder(dump_fn.append_basic_block(name='entry')) + b.position_at_start(dump_fn.entry_basic_block) + # 无匹配调用点:生成空落盘函数(不落盘、不建数组),保持调用链一致 + if n == 0: + b.ret_void() + return dump_fn + path_ptr: ir.Constant = self._create_string_global( + path, name=self._UniqueGlobalName('__fhs_sites_path')) + mode_ptr: ir.Constant = self._create_string_global( + 'a', name=self._UniqueGlobalName('__fhs_sites_mode')) + mid_ptr: ir.Constant | None = None + hdr_fmt_ptr: ir.Constant | None = None + if module_id: + fmt_ptr: ir.Constant = self._create_string_global( + '%s %s -> %s %llu%s\n', name=self._UniqueGlobalName('__fhs_sites_fmt')) + hdr_fmt_ptr = self._create_string_global( + '# sites %s\n', name=self._UniqueGlobalName('__fhs_sites_hdr_fmt')) + mid_ptr = self._create_string_global( + module_id, name=self._UniqueGlobalName('__fhs_sites_module_id')) + else: + fmt_ptr = self._create_string_global( + '%s -> %s %llu%s\n', name=self._UniqueGlobalName('__fhs_sites_fmt')) + fp: ir.Value = b.call(fopen, [path_ptr, mode_ptr], name='fhs_sites_fp') + if module_id and hdr_fmt_ptr is not None: + b.call(fprintf, [fp, hdr_fmt_ptr, mid_ptr], name='fhs_sites_write_hdr') + # 归属元数据全局数组:运行期按索引取 "调用方名 / 被调方基名 / 计数器地址" + caller_ty: ir.ArrayType = ir.ArrayType(i8p, n) + callee_ty: ir.ArrayType = ir.ArrayType(i8p, n) + cntptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) + caller_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, caller_ty, name=self._UniqueGlobalName('__fhs_sites_caller')) + caller_gv.linkage = 'internal' + caller_gv.global_constant = True + caller_gv.initializer = ir.Constant(caller_ty, [ + self._create_string_global(rec[1], name=self._UniqueGlobalName(f'__fhs_sites_caller_{k}')) + for k, rec in enumerate(records) + ]) + callee_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, callee_ty, name=self._UniqueGlobalName('__fhs_sites_callee')) + callee_gv.linkage = 'internal' + callee_gv.global_constant = True + callee_gv.initializer = ir.Constant(callee_ty, [ + self._create_string_global(rec[2], name=self._UniqueGlobalName(f'__fhs_sites_callee_{k}')) + for k, rec in enumerate(records) + ]) + # 源码位置全局数组:每项形如 " :"(含前置空格);取不到位置则为空串 + loc_ty: ir.ArrayType = ir.ArrayType(i8p, n) + loc_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, loc_ty, name=self._UniqueGlobalName('__fhs_sites_loc')) + loc_gv.linkage = 'internal' + loc_gv.global_constant = True + loc_gv.initializer = ir.Constant(loc_ty, [ + self._create_string_global( + f' {rec[3]}:{rec[4]}' if (rec[3] and rec[4] > 0) else '', + name=self._UniqueGlobalName(f'__fhs_sites_loc_{k}')) + for k, rec in enumerate(records) + ]) + cnt_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, cntptr_ty, name=self._UniqueGlobalName('__fhs_sites_cnt_ptrs')) + cnt_gv.linkage = 'internal' + cnt_gv.global_constant = True + cnt_gv.initializer = ir.Constant(cntptr_ty, [ + ir.Constant(i64p, rec[0].get_reference()) for rec in records + ]) + # 全部 alloca 置于 entry 块:避免在循环体内 alloca 导致每次迭代爆栈 + used: ir.Value = b.alloca(ir.ArrayType(i8, n), name='fhs_sites_used') + for i in range(n): + b.store(c0_8, b.gep(used, [c0_32, ir.Constant(i32, i)])) + k_ptr: ir.Value = b.alloca(i64, name='fhs_sites_k') + best_ptr: ir.Value = b.alloca(i64, name='fhs_sites_best') + bestv_ptr: ir.Value = b.alloca(i64, name='fhs_sites_bestv') + i_ptr: ir.Value = b.alloca(i64, name='fhs_sites_i') + b.store(c0_64, k_ptr) + # 选择排序:外层 k 走 0..n-1,每轮在未输出项里选计数最大者(计数相同取 K 小者) + loop_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_k') + body_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_k_body') + loop_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i') + body_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_body') + scan_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_scan') + upd_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_upd') + next_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_next') + end_i: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_i_end') + print_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_print_k') + after_k: ir.Block = dump_fn.append_basic_block(name='fhs_sites_sort_end') + b.branch(loop_k) + # loop_k: if k < n -> body_k else after_k + b.position_at_end(loop_k) + k_val: ir.Value = b.load(k_ptr) + b.cbranch(b.icmp_signed('<', k_val, ir.Constant(i64, n)), body_k, after_k) + # body_k: best=-1, bestv=0, i=0; -> loop_i + b.position_at_end(body_k) + b.store(ir.Constant(i64, -1), best_ptr) + b.store(c0_64, bestv_ptr) + b.store(c0_64, i_ptr) + b.branch(loop_i) + # loop_i: if i < n -> body_i else end_i + b.position_at_end(loop_i) + i_val: ir.Value = b.load(i_ptr) + b.cbranch(b.icmp_signed('<', i_val, ir.Constant(i64, n)), body_i, end_i) + # body_i: if used[i]==0 -> scan_i else next_i + b.position_at_end(body_i) + used_i: ir.Value = b.load(b.gep(used, [c0_32, i_val])) + b.cbranch(b.icmp_signed('==', used_i, c0_8), scan_i, next_i) + # scan_i: v = *cnt_ptrs[i]; if v > bestv -> upd_i else next_i + b.position_at_end(scan_i) + cnt_p: ir.Value = b.load(b.gep(cnt_gv, [c0_32, i_val])) + v_val: ir.Value = b.load(cnt_p) + b.cbranch(b.icmp_signed('>', v_val, b.load(bestv_ptr)), upd_i, next_i) + # upd_i: bestv=v, best=i; -> next_i + b.position_at_end(upd_i) + b.store(v_val, bestv_ptr) + b.store(i_val, best_ptr) + b.branch(next_i) + # next_i: i++; -> loop_i + b.position_at_end(next_i) + b.store(b.add(b.load(i_ptr), c1_64), i_ptr) + b.branch(loop_i) + # end_i: best<0(剩余全为 0 计数)-> after_k;否则 print_k + b.position_at_end(end_i) + best_val: ir.Value = b.load(best_ptr) + b.cbranch(b.icmp_signed('<', best_val, c0_64), after_k, print_k) + # print_k: used[best]=1; fprintf(...); k++; -> loop_k + b.position_at_end(print_k) + idx: ir.Value = b.load(best_ptr) + b.store(c1_8, b.gep(used, [c0_32, idx])) + caller_ptr: ir.Value = b.load(b.gep(caller_gv, [c0_32, idx])) + callee_ptr: ir.Value = b.load(b.gep(callee_gv, [c0_32, idx])) + loc_ptr: ir.Value = b.load(b.gep(loc_gv, [c0_32, idx])) + if module_id and mid_ptr is not None: + b.call(fprintf, [fp, fmt_ptr, mid_ptr, caller_ptr, callee_ptr, + b.load(bestv_ptr), loc_ptr], + name='fhs_sites_write') + else: + b.call(fprintf, [fp, fmt_ptr, caller_ptr, callee_ptr, + b.load(bestv_ptr), loc_ptr], + name='fhs_sites_write') + b.store(b.add(b.load(k_ptr), c1_64), k_ptr) + b.branch(loop_k) + # after_k: fclose; ret_void + b.position_at_end(after_k) + b.call(fclose, [fp]) + b.ret_void() + return dump_fn + + def _CreateHotspotTimeDump( + self, + path: str, + records: list[tuple[ir.GlobalVariable, ir.GlobalVariable, ir.GlobalVariable, str]], + module_id: str | None = None, + ) -> ir.Function: + """生成 void() 落盘函数:按**自身耗时**降序写入每函数计时文件。 + + 落盘格式(追加): + # time \\n + \\n + module_id 为空时省略头部 sha1 行(留空,不伪造)。列含义(单位均为 tick): + - self_tick:自身耗时(exclusive,见 _EmitHotspotTimeCounters),已扣除本 + 函数内**被插桩**子调用的 inclusive 之和;但**未插桩**的被调方(间接调用、 + 无定义体的外部函数如 strcmp/memcpy、深度溢出跳过的调用)仍计入其中。 + - inc_tick:包含式耗时(inclusive,含全部被调方)。 + - enter_count:被进入次数(含递归/重入),用于交叉校验。 + + - 排序:与调用点计数同法,落盘前用选择排序按 self_tick 降序输出,只输出 + 进入次数 >0 的函数;self_tick 相同时按编号 N 升序(稳定、可复现)。 + - 函数签名为 void(),由 __fhs_dump_all(见 _CreateHotspotDumpAll)统一带守卫 + 调用,本函数自身不检查守卫。用 CRT fopen("a")/fprintf/fclose 落盘(沿用既有做法)。 + """ + n: int = len(records) + i8: ir.IntType = ir.IntType(8) + i32: ir.IntType = ir.IntType(32) + i64: ir.IntType = ir.IntType(64) + i8p: ir.PointerType = ir.PointerType(i8) + i64p: ir.PointerType = ir.PointerType(i64) + c0_8: ir.Constant = ir.Constant(i8, 0) + c1_8: ir.Constant = ir.Constant(i8, 1) + c0_32: ir.Constant = ir.Constant(i32, 0) + c0_64: ir.Constant = ir.Constant(i64, 0) + c1_64: ir.Constant = ir.Constant(i64, 1) + fopen: ir.Function = self._DeclareExternalFunc('fopen', ir.FunctionType(i8p, [i8p, i8p])) + fprintf: ir.Function = self._DeclareExternalFunc( + 'fprintf', ir.FunctionType(i32, [i8p, i8p], var_arg=True)) + fclose: ir.Function = self._DeclareExternalFunc('fclose', ir.FunctionType(i32, [i8p])) + dump_fn: ir.Function = self._CreateOrReuseFuncDecl( + self._UniqueGlobalName('__fhs_dump_time'), + ir.FunctionType(ir.VoidType(), [])) + dump_fn.linkage = 'internal' + b: ir.IRBuilder = ir.IRBuilder(dump_fn.append_basic_block(name='entry')) + b.position_at_start(dump_fn.entry_basic_block) + # 无计时对象(模块内无用户函数定义):生成空落盘函数占位,保持调用链一致 + if n == 0: + b.ret_void() + return dump_fn + path_ptr: ir.Constant = self._create_string_global( + path, name=self._UniqueGlobalName('__fhs_ftime_path')) + mode_ptr: ir.Constant = self._create_string_global( + 'a', name=self._UniqueGlobalName('__fhs_ftime_mode')) + fmt_ptr: ir.Constant = self._create_string_global( + '%s %llu %llu %llu\n', name=self._UniqueGlobalName('__fhs_ftime_fmt')) + fp: ir.Value = b.call(fopen, [path_ptr, mode_ptr], name='fhs_ftime_fp') + if module_id: + hdr_fmt_ptr: ir.Constant = self._create_string_global( + '# time %s name self inc enter (tick; self excludes instrumented callees)\n', + name=self._UniqueGlobalName('__fhs_ftime_hdr_fmt')) + mid_ptr: ir.Constant = self._create_string_global( + module_id, name=self._UniqueGlobalName('__fhs_ftime_module_id')) + b.call(fprintf, [fp, hdr_fmt_ptr, mid_ptr], name='fhs_ftime_write_hdr') + # 归属元数据全局数组:运行期按索引取 "函数名 / 自身耗时地址 / 包含式地址 / 进入次数地址" + name_ty: ir.ArrayType = ir.ArrayType(i8p, n) + selfptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) + incptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) + enterptr_ty: ir.ArrayType = ir.ArrayType(i64p, n) + name_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, name_ty, name=self._UniqueGlobalName('__fhs_ftime_name')) + name_gv.linkage = 'internal' + name_gv.global_constant = True + name_gv.initializer = ir.Constant(name_ty, [ + self._create_string_global(rec[3], name=self._UniqueGlobalName(f'__fhs_ftime_name_{k}')) + for k, rec in enumerate(records) + ]) + self_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, selfptr_ty, name=self._UniqueGlobalName('__fhs_ftime_self_ptrs')) + self_gv.linkage = 'internal' + self_gv.global_constant = True + self_gv.initializer = ir.Constant(selfptr_ty, [ + ir.Constant(i64p, rec[0].get_reference()) for rec in records + ]) + inc_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, incptr_ty, name=self._UniqueGlobalName('__fhs_ftime_inc_ptrs')) + inc_gv.linkage = 'internal' + inc_gv.global_constant = True + inc_gv.initializer = ir.Constant(incptr_ty, [ + ir.Constant(i64p, rec[1].get_reference()) for rec in records + ]) + enter_gv: ir.GlobalVariable = ir.GlobalVariable( + self.module, enterptr_ty, name=self._UniqueGlobalName('__fhs_ftime_enter_ptrs')) + enter_gv.linkage = 'internal' + enter_gv.global_constant = True + enter_gv.initializer = ir.Constant(enterptr_ty, [ + ir.Constant(i64p, rec[2].get_reference()) for rec in records + ]) + # 全部 alloca 置于 entry 块:避免在循环体内 alloca 导致每次迭代爆栈 + used: ir.Value = b.alloca(ir.ArrayType(i8, n), name='fhs_ftime_used') + for i in range(n): + b.store(c0_8, b.gep(used, [c0_32, ir.Constant(i32, i)])) + k_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_k') + best_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_best') + bestv_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_bestv') + i_ptr: ir.Value = b.alloca(i64, name='fhs_ftime_i') + b.store(c0_64, k_ptr) + # 选择排序:外层 k 走 0..n-1,每轮在未输出项里选自身耗时(self)最大者(相同取 N 小者) + loop_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_k') + body_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_k_body') + loop_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i') + body_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_body') + scan_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_scan') + upd_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_upd') + next_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_next') + end_i: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_i_end') + print_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_print_k') + after_k: ir.Block = dump_fn.append_basic_block(name='fhs_ftime_sort_end') + b.branch(loop_k) + b.position_at_end(loop_k) + k_val: ir.Value = b.load(k_ptr) + b.cbranch(b.icmp_signed('<', k_val, ir.Constant(i64, n)), body_k, after_k) + b.position_at_end(body_k) + b.store(ir.Constant(i64, -1), best_ptr) + b.store(c0_64, bestv_ptr) + b.store(c0_64, i_ptr) + b.branch(loop_i) + b.position_at_end(loop_i) + i_val: ir.Value = b.load(i_ptr) + b.cbranch(b.icmp_signed('<', i_val, ir.Constant(i64, n)), body_i, end_i) + b.position_at_end(body_i) + used_i: ir.Value = b.load(b.gep(used, [c0_32, i_val])) + b.cbranch(b.icmp_signed('==', used_i, c0_8), scan_i, next_i) + # scan_i: v = *self_ptrs[i](自身耗时);同时把进入次数非 0 作为"有样本"判据 + b.position_at_end(scan_i) + self_p: ir.Value = b.load(b.gep(self_gv, [c0_32, i_val])) + v_val: ir.Value = b.load(self_p) + enter_p: ir.Value = b.load(b.gep(enter_gv, [c0_32, i_val])) + entered: ir.Value = b.load(enter_p) + has: ir.Value = b.icmp_signed('>', entered, c0_64) + better: ir.Value = b.icmp_signed('>', v_val, b.load(bestv_ptr)) + b.cbranch(b.and_(has, better), upd_i, next_i) + b.position_at_end(upd_i) + b.store(v_val, bestv_ptr) + b.store(i_val, best_ptr) + b.branch(next_i) + b.position_at_end(next_i) + b.store(b.add(b.load(i_ptr), c1_64), i_ptr) + b.branch(loop_i) + # end_i: best<0(剩余均为 0 或未被进入)-> after_k;否则 print_k + b.position_at_end(end_i) + best_val: ir.Value = b.load(best_ptr) + b.cbranch(b.icmp_signed('<', best_val, c0_64), after_k, print_k) + b.position_at_end(print_k) + idx: ir.Value = b.load(best_ptr) + b.store(c1_8, b.gep(used, [c0_32, idx])) + name_ptr: ir.Value = b.load(b.gep(name_gv, [c0_32, idx])) + sp: ir.Value = b.load(b.gep(self_gv, [c0_32, idx])) + ip: ir.Value = b.load(b.gep(inc_gv, [c0_32, idx])) + ep: ir.Value = b.load(b.gep(enter_gv, [c0_32, idx])) + b.call(fprintf, [fp, fmt_ptr, name_ptr, b.load(sp), b.load(ip), b.load(ep)], + name='fhs_ftime_write') + b.store(b.add(b.load(k_ptr), c1_64), k_ptr) + b.branch(loop_k) + # after_k: fclose; ret_void + b.position_at_end(after_k) + b.call(fclose, [fp]) + b.ret_void() + return dump_fn + + def _CreateHotspotDumpAll( + self, + sites_dump: ir.Function, + time_dump: ir.Function, + ) -> tuple[ir.Function, ir.Function]: + """生成模块级"总落盘"链路:幂等守卫 + void() 总落盘函数 + Ctrl+C 处理器。 + + 同一模块**只落盘一次**:Ctrl+C 与 atexit 可能先后都触发(例如处理器返回后 + 进程继续退出),故用每模块一份的 internal i32 @__fhs_dumped(初值 0)作幂等 + 守卫: + - void @__fhs_dump_all():先读守卫,非 0 直接返回;否则置 1 后依次调用 + sites_dump(调用点计数)与 time_dump(每函数计时)——两个落盘函数本身 + 不检查守卫; + - i32 @__fhs_ctl_handler(i32):直接调用 __fhs_dump_all() 后返回 FALSE(0), + 供 SetConsoleCtrlHandler 注册(保持"仍让进程终止"的既有行为)。 + + atexit 与 Ctrl+C 共用同一守卫,先到者落盘、后到者直接返回,避免重复段/行。 + 返回 (Ctrl+C 处理器, 总落盘函数)。 + """ + i32: ir.IntType = ir.IntType(32) + guard: ir.GlobalVariable = ir.GlobalVariable( + self.module, i32, name=self._UniqueGlobalName('__fhs_dumped')) + guard.linkage = 'internal' + guard.initializer = ir.Constant(i32, 0) + dump_all: ir.Function = self._CreateOrReuseFuncDecl( + self._UniqueGlobalName('__fhs_dump_all'), + ir.FunctionType(ir.VoidType(), [])) + dump_all.linkage = 'internal' + b: ir.IRBuilder = ir.IRBuilder(dump_all.append_basic_block(name='entry')) + do_bb: ir.Block = dump_all.append_basic_block(name='fhs_dump_do') + done_bb: ir.Block = dump_all.append_basic_block(name='fhs_dump_done') + b.position_at_start(dump_all.entry_basic_block) + cur: ir.Value = b.load(guard, name='fhs_dumped_load') + b.cbranch(b.icmp_signed('!=', cur, ir.Constant(i32, 0)), done_bb, do_bb) + b.position_at_end(do_bb) + b.store(ir.Constant(i32, 1), guard) + b.call(sites_dump, []) + b.call(time_dump, []) + b.branch(done_bb) + b.position_at_end(done_bb) + b.ret_void() + ctl_handler: ir.Function = self._CreateOrReuseFuncDecl( + self._UniqueGlobalName('__fhs_ctl_handler'), + ir.FunctionType(i32, [i32])) + ctl_handler.linkage = 'internal' + cb: ir.IRBuilder = ir.IRBuilder(ctl_handler.append_basic_block(name='entry')) + cb.call(dump_all, []) + cb.ret(ir.Constant(i32, 0)) + return ctl_handler, dump_all + + def _EmitHotspotDumpRegistration( + self, + builder: ir.IRBuilder, + ctl_handler: ir.Function, + dump_all: ir.Function, + ) -> None: + """向给定构建器发射两处落盘触发注册: + + - SetConsoleCtrlHandler(bitcast @__fhs_ctl_handler, i8*), TRUE):Ctrl+C 路径; + - atexit(bitcast @__fhs_dump_all, i8*)):正常返回 / exit() 退出路径。 + + 二者共用 __fhs_dumped 幂等守卫(见 _CreateHotspotDumpAll),任一先触发即落盘, + 后触发者直接返回,不重复写段/行。atexit 声明沿用本文件既有外部函数写法(见 + fopen/SetConsoleCtrlHandler:ir.FunctionType + ir.PointerType(ir.IntType(8))); + 本仓库 IR 为 typed pointer(函数指针统一以 i8* 传递),故以 bitcast 取函数地址。 + 返回值为 int,此处有意忽略(拿不到 atexit 会在链接期报错,不静默降级)。 + + 覆盖 libc exit()(含正常 return 与 sys.exit 的 fail-fast);不覆盖 _exit()/ + abort()/TerminateProcess/崩溃信号等不经过 exit() 的终止路径。 + """ + i32: ir.IntType = ir.IntType(32) + i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) + set_handler: ir.Function = self._DeclareExternalFunc( + 'SetConsoleCtrlHandler', ir.FunctionType(i32, [i8p, i32])) + atexit_fn: ir.Function = self._DeclareExternalFunc( + 'atexit', ir.FunctionType(i32, [i8p])) + builder.call(set_handler, [builder.bitcast(ctl_handler, i8p), ir.Constant(i32, 1)]) + builder.call(atexit_fn, [builder.bitcast(dump_all, i8p)]) + + def _RegisterHotspotSitesViaCtor(self, ctl_handler: ir.Function, dump_all: ir.Function) -> None: + """用 @llvm.global_ctors 在进程启动期为本模块注册热点落盘触发。 + + 生成 internal void @__fhs_sites_ctor()(内部调用 SetConsoleCtrlHandler 注册 + Ctrl+C 处理器 __fhs_ctl_handler,并调用 atexit 注册退出处理器 __fhs_dump_all), + 并写入 appending linkage 的 @llvm.global_ctors —— 每个模块一份,链接器按 + appending 语义把各模块的构造器拼接进 .init_array,进程启动时各执行一次。相比 + "在 main 入口注册",本方案不依赖 main 所在模块,适配 Projectrans 的"每源文件 + 独立翻译单元"模型。 + + 模块内若已存在 @llvm.global_ctors 视为冲突,显式报错(不静默降级)。 + """ + i32: ir.IntType = ir.IntType(32) + i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) + void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), [])) + ctor: ir.Function = self._CreateOrReuseFuncDecl( + self._UniqueGlobalName('__fhs_sites_ctor'), + ir.FunctionType(ir.VoidType(), [])) + ctor.linkage = 'internal' + cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry')) + self._EmitHotspotDumpRegistration(cb, ctl_handler, dump_all) + cb.ret_void() + if 'llvm.global_ctors' in self.module.globals: + raise RuntimeError( + '--hotspot-sites: 模块内已存在 @llvm.global_ctors,无法注册热点处理器(不静默降级)') + elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p]) + arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1) + entry_val: ir.Constant = ir.Constant(elem_ty, [ + ir.Constant(i32, 65535), + # 传函数引用字符串(非 Function 对象):与 _create_string_global 同法, + # 否则 llvmlite 会以 str(Function) 渲染整个 define 体 + ir.Constant(void_fnptr, ctor.get_reference()), + ir.Constant(i8p, None), + ]) + gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_ty, name='llvm.global_ctors') + gv.linkage = 'appending' + gv.initializer = ir.Constant(arr_ty, [entry_val]) + + def _RegisterTracebackInstallViaCtor(self) -> None: + """用 @llvm.global_ctors 在进程启动期调用本模块的 Install()(--traceback)。 + + 仅在"本翻译单元就是 includes/traceback.vp"时由 StructGen.finalize() 调用 + (traceback_ctor 由 Phase2Translator._translate_file 按源文件名置位)。 + 生成 internal void @__traceback_install_ctor()(entry 块直接 call 本模块的 + Install() 并忽略其 int 返回值),再写入 appending linkage 的 + @llvm.global_ctors —— 每模块一份,链接器按 appending 语义拼进 .init_array, + 进程启动时执行一次。 + + 为什么用构造器而不是"在 main 入口调用":适配 Projectrans 的"每源文件独立 + 翻译单元"模型(不依赖哪个模块含 main),且 CRT 初始化期即执行,能覆盖 + "main 之前就卡死"的场景 —— 这正是本功能的用途。 + + 同模块内取 Install 符号(不走跨模块声明),避免跨模块符号名/mangling 耦合。 + fail-fast:找不到 Install 符号、或模块内已存在 @llvm.global_ctors 时显式 + 报错(不静默降级 —— 否则产物缺注册、功能静默失效)。 + """ + install_fn: ir.Function | None = self._find_function('Install') + if install_fn is None: + raise RuntimeError( + '--traceback: 在 traceback 模块内找不到 Install() 函数,' + '无法注入启动期构造器(不静默降级)') + i32: ir.IntType = ir.IntType(32) + i8p: ir.PointerType = ir.PointerType(ir.IntType(8)) + void_fnptr: ir.PointerType = ir.PointerType(ir.FunctionType(ir.VoidType(), [])) + ctor: ir.Function = self._CreateOrReuseFuncDecl( + self._UniqueGlobalName('__traceback_install_ctor'), + ir.FunctionType(ir.VoidType(), [])) + ctor.linkage = 'internal' + cb: ir.IRBuilder = ir.IRBuilder(ctor.append_basic_block(name='entry')) + cb.call(install_fn, []) # Install() 无参;返回值(int)有意忽略 + cb.ret_void() + if 'llvm.global_ctors' in self.module.globals: + raise RuntimeError( + '--traceback: 模块内已存在 @llvm.global_ctors,' + '无法注入 traceback 启动期构造器(不静默降级)') + elem_ty: ir.LiteralStructType = ir.LiteralStructType([i32, void_fnptr, i8p]) + arr_ty: ir.ArrayType = ir.ArrayType(elem_ty, 1) + entry_val: ir.Constant = ir.Constant(elem_ty, [ + ir.Constant(i32, 65535), + # 传函数引用字符串(非 Function 对象):与 _RegisterHotspotSitesViaCtor 同法, + # 否则 llvmlite 会以 str(Function) 渲染整个 define 体 + ir.Constant(void_fnptr, ctor.get_reference()), + ir.Constant(i8p, None), + ]) + gv: ir.GlobalVariable = ir.GlobalVariable(self.module, arr_ty, name='llvm.global_ctors') + gv.linkage = 'appending' + gv.initializer = ir.Constant(arr_ty, [entry_val]) + def _InstallHotspotSites(self) -> None: """安装调用点热点统计(--hotspot-sites):种子 CLI 单模块路径。 diff --git a/lib/core/LLVMCG/ExprGen.py b/lib/core/LLVMCG/ExprGen.py index 5b36048..0adf7c7 100644 --- a/lib/core/LLVMCG/ExprGen.py +++ b/lib/core/LLVMCG/ExprGen.py @@ -76,7 +76,7 @@ class ExprGenMixin: # (GEP 只接受「一指针一整数」,已在上方两个 ptr/int 分支处理并返回)。 # 落到下方 ops/shift_ops/bit_ops 会直出非法 IR(`add i8*, i8*` ⇒ opt 报 # invalid operand type for instruction),故在 IR 生成前 fail-fast。 - # 判据与报文逐句对齐 TPV ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。 + # 判据与报文逐句对齐 VPC ExprGenOps.translate_binop / AssignGenMixin._apply_aug_op。 # ============================================================ if op in ('+', 'Add', '-', 'Sub', '*', 'Mult', '/', 'Div', '//', 'FloorDiv', '%', 'Mod', '**', 'Pow', '&', 'BitAnd', '|', 'BitOr', '^', 'BitXor', @@ -346,7 +346,7 @@ class ExprGenMixin: value = self.builder.bitcast(value, ret_type, name="ret_cast") elif isinstance(value.type, ir.PointerType) and not isinstance(ret_type, ir.PointerType): if isinstance(ret_type, ir.IntType): - # 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。 + # 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。 # 原先"同宽 load 所指物 / 其余 ptrtoint"都是静默转换。 from lib.core.LLVMCG.FuncGen import FailFastError raise FailFastError( diff --git a/lib/core/LLVMCG/FuncGen.py b/lib/core/LLVMCG/FuncGen.py index 808fd42..6bc8e43 100644 --- a/lib/core/LLVMCG/FuncGen.py +++ b/lib/core/LLVMCG/FuncGen.py @@ -1,1263 +1,1263 @@ -from __future__ import annotations -from typing import Any -import vast as ast -import os -import re -import sys -import llvmlite.ir as ir -from lib.core.Handles.HandlesBase import CTypeInfo -from lib.core.VLogger import get_logger as _vlog -from lib.constants.config import mode as _config_mode - - -class FailFastError(Exception): - """专用 fail-fast 异常:编译器主动抛出的编译错误(不可隐式转换/不可恢复)。 - - 刻意与 llvmlite 自身抛出的 TypeError/ValueError 区分开 —— 后者属于"此处可以尝试 - 简化回退"的类型调整失败,本异常则是必须上抛、禁止被兜底 `except` 吞掉的信号 - (例如 `_adjust_args` 的 L120/L181 两条 fail-fast)。 - """ - - -class LifetimeError(FailFastError): - """生命周期/逃逸类编译错误(如栈地址逃逸:把本帧局部/形参的地址写到帧外)。 - - 继承 FailFastError:既有 `isinstance(..., FailFastError)` 的捕获/上抛语义全部保持不变, - 仅让错误类型名独立出来(与 TransPyV 的 err_type "LifetimeError" 同口径)。 - """ - - -class MemberLookupFailFast(KeyError): - """成员偏移查找失败的 fail-fast 异常(**继承 KeyError** 以保持既有兜底点可用)。 - - 为什么继承 KeyError:本仓有两处 `except KeyError` 把 `_get_member_offset` 的 - 失败当作"该字段类型未知"继续回查(HandlesExprAttr 的字段类型三级回退),改成 - 其它异常类型会把这些兜底点击穿。 - - 为什么重写 `__str__`:KeyError 的默认 `__str__` 是 `repr(消息)` —— 会在报文两端 - 加引号、并把换行/制表转义成字面 `\\n`,多行 fail-fast 报文(含"源代码 (文件, 行号): - 行号 | 源码")因此彻底不可读。这里原样返回裸消息,与 FailFastError 的观感一致。 - """ - - def __str__(self) -> str: - if self.args and isinstance(self.args[0], str): - return self.args[0] - return super().__str__() - - -class FuncGenMixin: - - def setup_from_symbol_table(self, SymbolTable: Any) -> None: - self.SymbolTable: Any = SymbolTable - for name, info in SymbolTable.items(): - if isinstance(info, CTypeInfo): - # CTypeInfo 对象:struct/union 由 StructGen 处理,此处只处理 dict 格式 - continue - if not isinstance(info, dict): - if hasattr(info, 'get'): - info = dict(info) if hasattr(info, '__iter__') else {} - else: - continue - TypeKind: str = info.get('type', '') - if TypeKind == 'struct': - ClassName: str = name - self.class_methods[ClassName] = [] - self.class_members[ClassName] = [] - members: dict[str, Any] = info.get('members', {}) - if members: - for MemberName, MemberInfo in members.items(): - if isinstance(MemberInfo, dict): - MemberType: ir.Type = self._type_str_to_llvm(MemberInfo.get('type', 'int'), MemberInfo.get('IsPtr', False)) - self.class_members[ClassName].append((MemberName, MemberType)) - elif isinstance(MemberInfo, CTypeInfo): - MemberType: ir.Type = self._ctype_to_llvm(MemberInfo) - if MemberType is None: - MemberType = self._type_str_to_llvm(MemberInfo.ToString(), MemberInfo.IsPtr) - self.class_members[ClassName].append((MemberName, MemberType)) - self._generate_structs() - self._create_Vtable_globals() - - def register_method(self, ClassName: str, MethodName: str, IsVtableSlot: bool = True) -> None: - """登记类方法。 - - IsVtableSlot:该方法是否占用虚表槽(class_methods 即 vtable 槽序唯一真源)。 - 默认 True 与既有语义逐字一致;False 用于方法级 `novirtual def`(⇒ 方法装饰器 - @t.NoVTable)——此时只保证类的方法容器存在,**不**把它排进槽序,使 TPC 与 - TPV(ClassGenBase._should_method_be_virtual 排除该项)的虚表布局逐槽相同。 - """ - if ClassName not in self.class_methods: - self.class_methods[ClassName] = [] - if ClassName not in self.class_members: - self.class_members[ClassName] = [] - self._generate_structs() - self._create_Vtable_globals() - # 构造函数(__new__/__init__/__before_init__)不放入 vtable, - # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移 - _short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName - if _short_name in ('__new__', '__init__', '__before_init__'): - return - if not IsVtableSlot: - return - if MethodName not in self.class_methods[ClassName]: - self.class_methods[ClassName].append(MethodName) - - def _apply_auto_addr(self, func_name: str, args: list[ir.Value]) -> list[ir.Value]: - """对函数参数应用自动取址逻辑(ref / temp / always 三种模式)。 - - 在调用前对标记为 auto-addr 的实参做地址适配(模式由 HandlesFunctions 从形参注解 - 读出并写入 Gen._auto_addr_params[func_name]): - - 'ref'(&T):实参已是指针 → 原样传;非指针左值 → 传该左值地址; - 非指针且非左值(字面量/表达式结果)→ fail-fast(禁止静默降级)。 - - 'temp'(&temp T):实参已是指针 → 原样传;否则在 entry 块建同型临时 + store + 传地址。 - - 'always'(t.CAutoPtr):无条件取址(保持既有语义)。 - - 非指针左值的地址取自实参 load 指令的指针操作数(`load(p)` ⇒ p 即左值地址), - 因为调用点实参已按 rvalue 求值(标量变量的 load 结果)。 - - 唯一入口:本方法**只在** `_adjust_args` 开头被调用(调用链唯一汇聚点), - 故同一次调用绝不会取址两次——'always' 不幂等,多调一次即多包一层指针。 - """ - modes: list[str | None] | None = self._auto_addr_params.get(func_name) - if not modes: - return args - new_args: list[ir.Value] = list(args) - for i, mode in enumerate(modes): - if i >= len(new_args) or mode is None: - continue - arg: ir.Value = new_args[i] - is_ptr: bool = isinstance(arg.type, ir.PointerType) - if mode == 'ref': - if is_ptr: - continue - if isinstance(arg, ir.LoadInstr) and len(arg.operands) == 1: - # load(p) 的 p 即该左值的地址(该实参是变量/字段/下标的取值) - new_args[i] = arg.operands[0] - continue - raise FailFastError( - f"&T 形参只接受左值,右值请用具名临时变量或改用 &temp T" - f"(调用 {func_name} 第 {i} 个实参类型 {arg.type})") - if mode == 'temp': - if is_ptr: - continue - slot: ir.Value = self._allocaEntry(arg.type, name=f"autoaddr_tmp_{i}") - self.builder.store(arg, slot) - new_args[i] = slot - elif mode == 'always': - slot: ir.Value = self.builder.alloca(arg.type) - self.builder.store(arg, slot) - new_args[i] = slot - return new_args - - def _adjust_args(self, args: list[ir.Value], func: ir.Function) -> list[ir.Value]: - # 自动取址(&T=ref / &temp T=temp / t.CAutoPtr=always)的**唯一汇聚点**: - # 任何类型强转之前先做一次取址适配,于是 direct / vtable 虚分派 / 构造 / - # 静态方法 / __call__ / 下标赋值 / c.Addr 构造 / 闭包 / 泛型 / 方法兜底等 - # **所有**调用路径都自动获得取址能力,无需在每个调用点手写。 - # 为什么只能在这里做:'always'(t.CAutoPtr)不幂等——它无条件 - # alloca+store+传地址,重复应用会把指针再包一层;原先散落在各调用点的 - # 显式 Gen._apply_auto_addr(...) 与本汇聚点重复 ⇒ 已全部删除,由本处唯一负责。 - # 查表键统一为**真实发射出的函数名** func.name(登记侧逐字写入同名键)。 - if func is not None: - _auto_addr_name: str | None = getattr(func, 'name', None) - if _auto_addr_name: - args = self._apply_auto_addr(_auto_addr_name, args) - adjusted: list[ir.Value] = [] - for i, (arg, param) in enumerate(zip(args, func.args)): - if arg.type != param.type: - if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): - # C 语言中数组参数退化为指针,不做 load 而是退化为元素指针 - if isinstance(param.type, ir.ArrayType): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") - adjusted.append(elem_ptr) - else: - adjusted.append(self._load(arg, name=f"Load_arg_{i}")) - continue - try: - if isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType): - if isinstance(param.type.pointee, ir.IntType) and param.type.pointee.width == 8 and arg.type.width == 8: - # 当参数是 i8(字符值)而函数期望 i8*(字符串指针)时 - # 为字符值分配内存,创建 null-terminated 字符串 - tmp: ir.AllocaInstr = self._alloca(ir.ArrayType(ir.IntType(8), 2), name=f"char2str_arg_{i}") - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - char_ptr: ir.Value = self.builder.gep(tmp, [zero, zero], name=f"char2str_ptr_{i}") - self._store(arg, char_ptr) - null_ptr: ir.Value = self.builder.gep(tmp, [zero, ir.Constant(ir.IntType(32), 1)], name=f"char2str_null_{i}") - self._store(ir.Constant(ir.IntType(8), 0), null_ptr) - adjusted.append(char_ptr) - else: - if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): - # 当参数是指针而函数期望整数时,使用 ptrtoint - adjusted.append(self.builder.ptrtoint(arg, param.type, name=f"ptrtoint_arg_{i}")) - else: - if arg.type.width < 32: - arg = self.builder.zext(arg, ir.IntType(32), name=f"zext_arg_{i}") - adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}")) - elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): - # fail-fast:指针→整数隐式转换(跨类型层级,有损)禁止, - # 须显式转换(用户隐式转换规则:同层指针可转,指针→整数不允许)。 - # 诊断增强(只在即将终止时拼串,零行为影响):把 callee 名、 - # 实参/形参个数与**逐项类型**一并报出——只给单个类型对无法判断 - # 「形参表整体错位一位」(如 self/pool 约定不一致)这类根因。 - _diag: str = "; ".join( - f"[{k}] arg={a.type} param={p.type}" - for k, (a, p) in enumerate(zip(args, func.args))) - raise FailFastError( - f"调用参数类型不匹配: 指针类型 {arg.type} 不能隐式转换为" - f"整数参数 {param.type};如需转换请显式 cast" - f"(callee={func.name} 实参{len(args)}个/形参{len(func.args)}个;" - f"逐项: {_diag})") - elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): - if isinstance(arg.type.pointee, ir.PointerType) and isinstance(param.type.pointee, ir.IntType): - Loaded: ir.Value = self._load(arg, name=f"Load_arg_{i}") - adjusted.append(Loaded) - elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, (ir.IntType, ir.FloatType, ir.DoubleType)): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") - if elem_ptr.type != param.type: - adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}")) - else: - adjusted.append(elem_ptr) - elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, ir.PointerType): - zero: ir.Constant = ir.Constant(ir.IntType(32), 0) - elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") - if elem_ptr.type != param.type: - adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}")) - else: - adjusted.append(elem_ptr) - else: - # 新增指针隐式转换规则①/②:通用指针通配放行;非通用 - # T*→U* 仅在无损时放行,非基本/有损/层数不同 fail-fast。 - Allowed: bool - Reason: str - Allowed, Reason = self._ClassifyPtrImplicitConvert(arg.type, param.type) - if not Allowed: - raise FailFastError( - f"指针隐式转换已禁止({Reason}):调用 {func.name} 第 {i} 个实参" - f" {arg.type} 不能隐式转为形参 {param.type};如需转换请显式 cast" - f"(如 t.CPtr(...)){self._DescribeValueNode()}") - adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) - else: - if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): - adjusted.append(self._load(arg, name=f"Load_arg_{i}")) - elif isinstance(arg.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}") - Loaded: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}") - Loaded_val: ir.Value = self._load(Loaded, name=f"Load_arg_{i}") - self._store(Loaded_val, tmp) - adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}")) - else: - tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}") - cast_ptr: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}") - Loaded_val: ir.Value = self._load(cast_ptr, name=f"Load_arg_{i}") - self._store(Loaded_val, tmp) - adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}")) - elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type: - adjusted.append(self._load(arg, name=f"Load_arg_{i}")) - elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type: - tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}") - self._store(arg, tmp) - adjusted.append(tmp) - elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.IntType): - if arg.type.width < param.type.width: - # 整数扩展:使用 sext(符号扩展)以正确处理负数 - adjusted.append(self.builder.sext(arg, param.type, name=f"sext_arg_{i}")) - else: - adjusted.append(self.builder.trunc(arg, param.type, name=f"trunc_arg_{i}")) - elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, (ir.FloatType, ir.DoubleType)): - if isinstance(arg.type, ir.DoubleType) and isinstance(param.type, ir.FloatType): - adjusted.append(self.builder.fptrunc(arg, param.type, name=f"fptrunc_arg_{i}")) - elif isinstance(arg.type, ir.FloatType) and isinstance(param.type, ir.DoubleType): - adjusted.append(self.builder.fpext(arg, param.type, name=f"fpext_arg_{i}")) - else: - adjusted.append(arg) - elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, ir.IntType): - # fail-fast:浮点→整数隐式转换(有损)禁止,须显式转换 - # (用户隐式转换规则:浮点→整数不允许)。 - raise FailFastError( - f"调用参数类型不匹配: 浮点类型 {arg.type} 不能隐式转换为" - f"整数参数 {param.type};如需转换请显式 cast") - elif isinstance(arg.type, ir.IntType) and isinstance(param.type, (ir.FloatType, ir.DoubleType)): - adjusted.append(self.builder.sitofp(arg, param.type, name=f"sitofp_arg_{i}")) - elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType): - # 整数转指针:inttoptr - if arg.type.width < 64: - i64_val: ir.Value = self.builder.sext(arg, ir.IntType(64), name=f"sext_arg_{i}") - adjusted.append(self.builder.inttoptr(i64_val, param.type, name=f"inttoptr_arg_{i}")) - else: - adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}")) - elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): - # 指针转整数:ptrtoint - i64_val: ir.Value = self.builder.ptrtoint(arg, ir.IntType(64), name=f"ptrtoint_arg_{i}") - if i64_val.type.width > param.type.width: - adjusted.append(self.builder.trunc(i64_val, param.type, name=f"trunc_arg_{i}")) - elif i64_val.type.width < param.type.width: - adjusted.append(self.builder.sext(i64_val, param.type, name=f"sext_arg_{i}")) - else: - adjusted.append(i64_val) - else: - adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) +from __future__ import annotations +from typing import Any +import vast as ast +import os +import re +import sys +import llvmlite.ir as ir +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.VLogger import get_logger as _vlog +from lib.constants.config import mode as _config_mode + + +class FailFastError(Exception): + """专用 fail-fast 异常:编译器主动抛出的编译错误(不可隐式转换/不可恢复)。 + + 刻意与 llvmlite 自身抛出的 TypeError/ValueError 区分开 —— 后者属于"此处可以尝试 + 简化回退"的类型调整失败,本异常则是必须上抛、禁止被兜底 `except` 吞掉的信号 + (例如 `_adjust_args` 的 L120/L181 两条 fail-fast)。 + """ + + +class LifetimeError(FailFastError): + """生命周期/逃逸类编译错误(如栈地址逃逸:把本帧局部/形参的地址写到帧外)。 + + 继承 FailFastError:既有 `isinstance(..., FailFastError)` 的捕获/上抛语义全部保持不变, + 仅让错误类型名独立出来(与 ViperC 的 err_type "LifetimeError" 同口径)。 + """ + + +class MemberLookupFailFast(KeyError): + """成员偏移查找失败的 fail-fast 异常(**继承 KeyError** 以保持既有兜底点可用)。 + + 为什么继承 KeyError:本仓有两处 `except KeyError` 把 `_get_member_offset` 的 + 失败当作"该字段类型未知"继续回查(HandlesExprAttr 的字段类型三级回退),改成 + 其它异常类型会把这些兜底点击穿。 + + 为什么重写 `__str__`:KeyError 的默认 `__str__` 是 `repr(消息)` —— 会在报文两端 + 加引号、并把换行/制表转义成字面 `\\n`,多行 fail-fast 报文(含"源代码 (文件, 行号): + 行号 | 源码")因此彻底不可读。这里原样返回裸消息,与 FailFastError 的观感一致。 + """ + + def __str__(self) -> str: + if self.args and isinstance(self.args[0], str): + return self.args[0] + return super().__str__() + + +class FuncGenMixin: + + def setup_from_symbol_table(self, SymbolTable: Any) -> None: + self.SymbolTable: Any = SymbolTable + for name, info in SymbolTable.items(): + if isinstance(info, CTypeInfo): + # CTypeInfo 对象:struct/union 由 StructGen 处理,此处只处理 dict 格式 + continue + if not isinstance(info, dict): + if hasattr(info, 'get'): + info = dict(info) if hasattr(info, '__iter__') else {} + else: + continue + TypeKind: str = info.get('type', '') + if TypeKind == 'struct': + ClassName: str = name + self.class_methods[ClassName] = [] + self.class_members[ClassName] = [] + members: dict[str, Any] = info.get('members', {}) + if members: + for MemberName, MemberInfo in members.items(): + if isinstance(MemberInfo, dict): + MemberType: ir.Type = self._type_str_to_llvm(MemberInfo.get('type', 'int'), MemberInfo.get('IsPtr', False)) + self.class_members[ClassName].append((MemberName, MemberType)) + elif isinstance(MemberInfo, CTypeInfo): + MemberType: ir.Type = self._ctype_to_llvm(MemberInfo) + if MemberType is None: + MemberType = self._type_str_to_llvm(MemberInfo.ToString(), MemberInfo.IsPtr) + self.class_members[ClassName].append((MemberName, MemberType)) + self._generate_structs() + self._create_Vtable_globals() + + def register_method(self, ClassName: str, MethodName: str, IsVtableSlot: bool = True) -> None: + """登记类方法。 + + IsVtableSlot:该方法是否占用虚表槽(class_methods 即 vtable 槽序唯一真源)。 + 默认 True 与既有语义逐字一致;False 用于方法级 `novirtual def`(⇒ 方法装饰器 + @t.NoVTable)——此时只保证类的方法容器存在,**不**把它排进槽序,使 TPC 与 + VPC(ClassGenBase._should_method_be_virtual 排除该项)的虚表布局逐槽相同。 + """ + if ClassName not in self.class_methods: + self.class_methods[ClassName] = [] + if ClassName not in self.class_members: + self.class_members[ClassName] = [] + self._generate_structs() + self._create_Vtable_globals() + # 构造函数(__new__/__init__/__before_init__)不放入 vtable, + # 避免子类与基类 vtable 大小不一致导致虚分派索引偏移 + _short_name: str = MethodName.split('.')[-1] if '.' in MethodName else MethodName + if _short_name in ('__new__', '__init__', '__before_init__'): + return + if not IsVtableSlot: + return + if MethodName not in self.class_methods[ClassName]: + self.class_methods[ClassName].append(MethodName) + + def _apply_auto_addr(self, func_name: str, args: list[ir.Value]) -> list[ir.Value]: + """对函数参数应用自动取址逻辑(ref / temp / always 三种模式)。 + + 在调用前对标记为 auto-addr 的实参做地址适配(模式由 HandlesFunctions 从形参注解 + 读出并写入 Gen._auto_addr_params[func_name]): + - 'ref'(&T):实参已是指针 → 原样传;非指针左值 → 传该左值地址; + 非指针且非左值(字面量/表达式结果)→ fail-fast(禁止静默降级)。 + - 'temp'(&temp T):实参已是指针 → 原样传;否则在 entry 块建同型临时 + store + 传地址。 + - 'always'(t.CAutoPtr):无条件取址(保持既有语义)。 + + 非指针左值的地址取自实参 load 指令的指针操作数(`load(p)` ⇒ p 即左值地址), + 因为调用点实参已按 rvalue 求值(标量变量的 load 结果)。 + + 唯一入口:本方法**只在** `_adjust_args` 开头被调用(调用链唯一汇聚点), + 故同一次调用绝不会取址两次——'always' 不幂等,多调一次即多包一层指针。 + """ + modes: list[str | None] | None = self._auto_addr_params.get(func_name) + if not modes: + return args + new_args: list[ir.Value] = list(args) + for i, mode in enumerate(modes): + if i >= len(new_args) or mode is None: + continue + arg: ir.Value = new_args[i] + is_ptr: bool = isinstance(arg.type, ir.PointerType) + if mode == 'ref': + if is_ptr: + continue + if isinstance(arg, ir.LoadInstr) and len(arg.operands) == 1: + # load(p) 的 p 即该左值的地址(该实参是变量/字段/下标的取值) + new_args[i] = arg.operands[0] + continue + raise FailFastError( + f"&T 形参只接受左值,右值请用具名临时变量或改用 &temp T" + f"(调用 {func_name} 第 {i} 个实参类型 {arg.type})") + if mode == 'temp': + if is_ptr: + continue + slot: ir.Value = self._allocaEntry(arg.type, name=f"autoaddr_tmp_{i}") + self.builder.store(arg, slot) + new_args[i] = slot + elif mode == 'always': + slot: ir.Value = self.builder.alloca(arg.type) + self.builder.store(arg, slot) + new_args[i] = slot + return new_args + + def _adjust_args(self, args: list[ir.Value], func: ir.Function) -> list[ir.Value]: + # 自动取址(&T=ref / &temp T=temp / t.CAutoPtr=always)的**唯一汇聚点**: + # 任何类型强转之前先做一次取址适配,于是 direct / vtable 虚分派 / 构造 / + # 静态方法 / __call__ / 下标赋值 / c.Addr 构造 / 闭包 / 泛型 / 方法兜底等 + # **所有**调用路径都自动获得取址能力,无需在每个调用点手写。 + # 为什么只能在这里做:'always'(t.CAutoPtr)不幂等——它无条件 + # alloca+store+传地址,重复应用会把指针再包一层;原先散落在各调用点的 + # 显式 Gen._apply_auto_addr(...) 与本汇聚点重复 ⇒ 已全部删除,由本处唯一负责。 + # 查表键统一为**真实发射出的函数名** func.name(登记侧逐字写入同名键)。 + if func is not None: + _auto_addr_name: str | None = getattr(func, 'name', None) + if _auto_addr_name: + args = self._apply_auto_addr(_auto_addr_name, args) + adjusted: list[ir.Value] = [] + for i, (arg, param) in enumerate(zip(args, func.args)): + if arg.type != param.type: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): + # C 语言中数组参数退化为指针,不做 load 而是退化为元素指针 + if isinstance(param.type, ir.ArrayType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") + adjusted.append(elem_ptr) + else: + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + continue + try: + if isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType): + if isinstance(param.type.pointee, ir.IntType) and param.type.pointee.width == 8 and arg.type.width == 8: + # 当参数是 i8(字符值)而函数期望 i8*(字符串指针)时 + # 为字符值分配内存,创建 null-terminated 字符串 + tmp: ir.AllocaInstr = self._alloca(ir.ArrayType(ir.IntType(8), 2), name=f"char2str_arg_{i}") + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + char_ptr: ir.Value = self.builder.gep(tmp, [zero, zero], name=f"char2str_ptr_{i}") + self._store(arg, char_ptr) + null_ptr: ir.Value = self.builder.gep(tmp, [zero, ir.Constant(ir.IntType(32), 1)], name=f"char2str_null_{i}") + self._store(ir.Constant(ir.IntType(8), 0), null_ptr) + adjusted.append(char_ptr) + else: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): + # 当参数是指针而函数期望整数时,使用 ptrtoint + adjusted.append(self.builder.ptrtoint(arg, param.type, name=f"ptrtoint_arg_{i}")) + else: + if arg.type.width < 32: + arg = self.builder.zext(arg, ir.IntType(32), name=f"zext_arg_{i}") + adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): + # fail-fast:指针→整数隐式转换(跨类型层级,有损)禁止, + # 须显式转换(用户隐式转换规则:同层指针可转,指针→整数不允许)。 + # 诊断增强(只在即将终止时拼串,零行为影响):把 callee 名、 + # 实参/形参个数与**逐项类型**一并报出——只给单个类型对无法判断 + # 「形参表整体错位一位」(如 self/pool 约定不一致)这类根因。 + _diag: str = "; ".join( + f"[{k}] arg={a.type} param={p.type}" + for k, (a, p) in enumerate(zip(args, func.args))) + raise FailFastError( + f"调用参数类型不匹配: 指针类型 {arg.type} 不能隐式转换为" + f"整数参数 {param.type};如需转换请显式 cast" + f"(callee={func.name} 实参{len(args)}个/形参{len(func.args)}个;" + f"逐项: {_diag})") + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): + if isinstance(arg.type.pointee, ir.PointerType) and isinstance(param.type.pointee, ir.IntType): + Loaded: ir.Value = self._load(arg, name=f"Load_arg_{i}") + adjusted.append(Loaded) + elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, (ir.IntType, ir.FloatType, ir.DoubleType)): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") + if elem_ptr.type != param.type: + adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}")) + else: + adjusted.append(elem_ptr) + elif isinstance(arg.type.pointee, ir.ArrayType) and isinstance(param.type.pointee, ir.PointerType): + zero: ir.Constant = ir.Constant(ir.IntType(32), 0) + elem_ptr: ir.Value = self.builder.gep(arg, [zero, zero], name=f"array_decay_arg_{i}") + if elem_ptr.type != param.type: + adjusted.append(self.builder.bitcast(elem_ptr, param.type, name=f"cast_arg_{i}")) + else: + adjusted.append(elem_ptr) + else: + # 新增指针隐式转换规则①/②:通用指针通配放行;非通用 + # T*→U* 仅在无损时放行,非基本/有损/层数不同 fail-fast。 + Allowed: bool + Reason: str + Allowed, Reason = self._ClassifyPtrImplicitConvert(arg.type, param.type) + if not Allowed: + raise FailFastError( + f"指针隐式转换已禁止({Reason}):调用 {func.name} 第 {i} 个实参" + f" {arg.type} 不能隐式转为形参 {param.type};如需转换请显式 cast" + f"(如 t.CPtr(...)){self._DescribeValueNode()}") + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + else: + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + elif isinstance(arg.type.pointee, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}") + Loaded: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}") + Loaded_val: ir.Value = self._load(Loaded, name=f"Load_arg_{i}") + self._store(Loaded_val, tmp) + adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}")) + else: + tmp: ir.AllocaInstr = self._alloca(param.type, name=f"temp_arg_{i}") + cast_ptr: ir.Value = self.builder.bitcast(arg, ir.PointerType(param.type), name=f"cast_arg_{i}") + Loaded_val: ir.Value = self._load(cast_ptr, name=f"Load_arg_{i}") + self._store(Loaded_val, tmp) + adjusted.append(self._load(tmp, name=f"reLoad_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type: + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type: + tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}") + self._store(arg, tmp) + adjusted.append(tmp) + elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.IntType): + if arg.type.width < param.type.width: + # 整数扩展:使用 sext(符号扩展)以正确处理负数 + adjusted.append(self.builder.sext(arg, param.type, name=f"sext_arg_{i}")) + else: + adjusted.append(self.builder.trunc(arg, param.type, name=f"trunc_arg_{i}")) + elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, (ir.FloatType, ir.DoubleType)): + if isinstance(arg.type, ir.DoubleType) and isinstance(param.type, ir.FloatType): + adjusted.append(self.builder.fptrunc(arg, param.type, name=f"fptrunc_arg_{i}")) + elif isinstance(arg.type, ir.FloatType) and isinstance(param.type, ir.DoubleType): + adjusted.append(self.builder.fpext(arg, param.type, name=f"fpext_arg_{i}")) + else: + adjusted.append(arg) + elif isinstance(arg.type, (ir.FloatType, ir.DoubleType)) and isinstance(param.type, ir.IntType): + # fail-fast:浮点→整数隐式转换(有损)禁止,须显式转换 + # (用户隐式转换规则:浮点→整数不允许)。 + raise FailFastError( + f"调用参数类型不匹配: 浮点类型 {arg.type} 不能隐式转换为" + f"整数参数 {param.type};如需转换请显式 cast") + elif isinstance(arg.type, ir.IntType) and isinstance(param.type, (ir.FloatType, ir.DoubleType)): + adjusted.append(self.builder.sitofp(arg, param.type, name=f"sitofp_arg_{i}")) + elif isinstance(arg.type, ir.IntType) and isinstance(param.type, ir.PointerType): + # 整数转指针:inttoptr + if arg.type.width < 64: + i64_val: ir.Value = self.builder.sext(arg, ir.IntType(64), name=f"sext_arg_{i}") + adjusted.append(self.builder.inttoptr(i64_val, param.type, name=f"inttoptr_arg_{i}")) + else: + adjusted.append(self.builder.inttoptr(arg, param.type, name=f"inttoptr_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.IntType): + # 指针转整数:ptrtoint + i64_val: ir.Value = self.builder.ptrtoint(arg, ir.IntType(64), name=f"ptrtoint_arg_{i}") + if i64_val.type.width > param.type.width: + adjusted.append(self.builder.trunc(i64_val, param.type, name=f"trunc_arg_{i}")) + elif i64_val.type.width < param.type.width: + adjusted.append(self.builder.sext(i64_val, param.type, name=f"sext_arg_{i}")) + else: + adjusted.append(i64_val) + else: + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) # 只收口 llvmlite 自身"可回退"的类型调整失败(builder/IR 构造抛出的 # TypeError/ValueError);FailFastError 与其余异常一律上抛, # 避免编译器主动抛出的真因被伪装成 llvmlite 的下游报文。 - except (TypeError, ValueError): # 参数类型调整失败时使用简化回退逻辑 - if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): - if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): - adjusted.append(self._load(arg, name=f"Load_arg_{i}")) - else: - adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) - elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type: - adjusted.append(self._load(arg, name=f"Load_arg_{i}")) - elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type: - tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}") - self._store(arg, tmp) - adjusted.append(tmp) - elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): - # 任意指针到指针的类型转换(如 struct* -> i8*) - adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) - else: - adjusted.append(arg) - else: - adjusted.append(arg) - while len(adjusted) < len(func.args): - missing_idx: int = len(adjusted) - param: ir.Argument = func.args[missing_idx] - param_type: ir.Type = param.type if hasattr(param, 'type') else param - if isinstance(param_type, ir.PointerType): - adjusted.append(ir.Constant(param_type, None)) - elif isinstance(param_type, ir.IntType): - adjusted.append(ir.Constant(param_type, 0)) - elif isinstance(param_type, (ir.FloatType, ir.DoubleType)): - adjusted.append(ir.Constant(param_type, 0.0)) - else: - adjusted.append(ir.Constant(ir.IntType(32), 0)) - if func.type.pointee.var_arg: - for i in range(len(func.args), len(args)): - arg: ir.Value = args[i] - adjusted.append(arg) - return adjusted - - def _class_has_vtable(self, ClassName: str | None) -> bool: - """类(或其任一祖先)是否带 vtable 槽。 - - 与 StructGen.py:442-450 的父链传播同源——vtable 沿继承链携带,子类 struct 布局为 - [vtable?] + [基类字段…] + [自身字段…]。只认 class_vtable / - _cross_module_vtable_classes(装饰器集合)会漏掉源码未写 @t.CVTable 的跨模块 - AST 节点类(ast.Try / ast.Match / ast.ExceptHandler…):它们有方法、继承自 - @t.CVTable 的 AST,struct 首元素确实是 vtable 指针,但 StructGen.py:415 在 - "struct body 已从 stub 完整加载"时直接 continue,永不把它们登记进集合 - (实测 TransPyV/temp/_shared_sym.json 的 class_vtable/cross_module_vtable 仅有 - {AST, MemManager, Module})。此时 base 静默取 0 → 该类字段偏移整体前移 1 - (ast.ExceptHandler.children 落到 end_col_offset,i32)。 - """ - if not ClassName: - return False - _Seen: set[str] = set() - _Cur: str | None = ClassName - while _Cur and _Cur not in _Seen: - _Seen.add(_Cur) - if _Cur in self.class_vtable or _Cur in self._cross_module_vtable_classes: - return True - _Cur = self.class_parent.get(_Cur) - return False - - def _NormalizeMemberClass(self, ClassName: str | None) -> str | None: - """成员查询用的类键归一(短名 + 指针包装展开)。 - - - 统一短名解析(治本修复 2026-08-10):struct/class_members/class_vtable/ - class_parent 均以短名注册(跨模块加载 HandlesImports L1604),而调用方 - 可能传入带 sha1 前缀的全名(如 "f47f88ae6545965a.Import")。 - - rawptr[X] / t.CPtr[X] 指针包装归一:rawptr[X] 是内建裸指针类型 - (X*),非真实类——接收者注解/类型名为 'rawptr[AST]' 时字段访问 - (child.parent)应作用于内层 struct X,否则查 class_members 全 miss - → fast-fail "属性 'parent' 在类 'rawptr[AST]' 中找不到(struct=opaque)" - (includes/ast/base.vp _set_parent_list bootstrap 根因)。 - """ - if not ClassName: - return ClassName - _Cls: str = self._extract_short_name(ClassName) - if _Cls.startswith('rawptr[') or _Cls.startswith('t.CPtr['): - _lb: int = _Cls.find('[') - if _Cls.endswith(']') and _lb > 0: - _inner: str = _Cls[_lb + 1:-1].strip() - _short_inner: str = _inner.rsplit('.', 1)[-1] if '.' in _inner else _inner - _hit: str | None = None - if _short_inner in self.structs: - _hit = _short_inner - else: - for _k in self.structs: - if self._extract_short_name(_k) == _short_inner: - _hit = _k - break - if _hit: - _Cls = _hit - return _Cls - - def _member_base(self, ClassName: str | None, StructHint: Any = None) -> int: - """类 struct 的成员偏移基址(vtable 占位槽数)——**全仓唯一真源**。 - - `StructHint`(可选):调用方手里**实际要用于 GEP 的那个 struct 实例** - (通常就是 `ObjVal.type.pointee`)。同一类键上可能同时存在两个实例 - (同名类 sha1 冲突的双注册,或一个已从 stub 加载完整、另一个仍 opaque), - 而偏移基址必须与"实际 GEP 用的那份布局"一致 —— 用键上那份算、用另一份读, - 会让整类字段偏移平移(现场:`Constant.lineno` 偏移 2 落到 `pool`(MemManager*), - 实参第 6 项被报"指针不能隐式转整数 i32")。传 hint 时以 hint 为准并**报出** - 两侧差异(每类一次),不传时行为与改前逐字一致。 - - `_get_member_offset` 与所有"自算 base 再 GEP 字段"的消费点(构造/初始化、 - 字段默认值、继承方法 this 调整等)都必须走本方法,禁止各自直查 - `class_vtable`/`_cross_module_vtable_classes`——旧实现只认这两个装饰器集合, - 跨模块 AST 节点类(ast.Try/ast.Match/ast.ExceptHandler…)源码未写 - @t.CVTable 且 StructGen 在"struct body 已从 stub 加载"时提前 continue, - 永不把它们登记进集合,于是 base 只能靠"成员表长度 == struct 元素数 - 1" - 这个长度启发式救回;成员表未展平/尚未加载时启发式失效 ⇒ base 静默取 0 - ⇒ 该类所有字段偏移整体前移 1(ast.ExceptHandler.children 落到 - end_col_offset(i32),len() 分派拿到 i32 ⇒ Type of #1 arg mismatch)。 - - 判定规则(与 `StructGen._generate_structs` 生成 struct 的规则同源): - - vtable 归属沿 class_parent 链判定(见 `_class_has_vtable`); - - 再用 struct 实际元素数校准: - delta(=struct 元素数 - 成员表长度) == 0 ⇒ 该 struct 无 vtable 槽 → base=0; - delta == 1 ⇒ 恰多一个槽:首元素为指针 / 无父类 / 继承链归属成立 → base=1; - delta >= 2 ⇒ 长度启发式失效,保留"继承链 vtable 归属"结果(不再静默取 0)。 - - 查询前会调用 `HandlesImports.EnsureClassMembersFlattened`(幂等)把"停在自有 - 字段"的未展平表补齐——未展平表会让下列 delta 校准失真,也会让按表内下标 - 取字段的消费点漏掉继承前缀。 - """ - _Cls: str | None = self._NormalizeMemberClass(ClassName) - if not _Cls: - return 0 - # 查询前确保成员表已按父类展平(幂等、顺序无关)。 - # 未展平的表(停在自有字段)会让本方法的 delta 校准失真,也会让所有 - # "按成员表下标 base+i 取字段"的消费点漏掉继承前缀 —— 见 - # HandlesImports.EnsureClassMembersFlattened(内部有纯内存前置判断与 - # 重入/重复尝试保护,对已展平类零额外开销)。 - _EnsureIH: Any = getattr(self, '_import_handler_ref', None) - if _EnsureIH is not None and hasattr(_EnsureIH, 'EnsureClassMembersFlattened'): - try: - _EnsureIH.EnsureClassMembersFlattened(_Cls, self) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"补齐类成员展平失败: {_e}", "Exception") - _ByInheritVtable: bool = self._class_has_vtable(_Cls) - base: int = 1 if _ByInheritVtable else 0 - # 布局来源:优先调用方**实际 GEP 用的那个实例**(StructHint)——偏移必须与 - # 真正被索引的那份 body 同源。只在 hint 不可用时才回落到类键上的实例。 - _RegBase: ir.IdentifiedStructType | None = self.structs.get(_Cls) - _StructBase: ir.IdentifiedStructType | None = _RegBase - if isinstance(StructHint, ir.IdentifiedStructType): - # 仅接受**同类**的 hint:接收者静态类型是基类、实际指向派生类实例时, - # hint 的布局属于派生类,拿它配 `class_members[_Cls]` 会算错 delta。 - _HintShort: str = self._extract_short_name(getattr(StructHint, 'name', '') or '') - if _HintShort == _Cls: - _StructBase = StructHint - if _RegBase is not None and _RegBase is not StructHint: - # 同一类键上两个 struct 实例:报出事实(每类一次),避免"用一份算、 - # 用另一份读"的静默偏移平移(本函数此处即该平移的判定源)。 - _Rep: set = getattr(self, '_MemberBaseInstMismatch', None) - if _Rep is None: - _Rep = set() - self._MemberBaseInstMismatch = _Rep - if _Cls not in _Rep: - _Rep.add(_Cls) - _RegN: str = (f"{len(_RegBase.elements)} 元素" - if _RegBase.elements is not None else 'opaque') - _HintN: str = (f"{len(StructHint.elements)} 元素" - if StructHint.elements is not None else 'opaque') - _vlog().warning( - f"[成员偏移] 类 '{_Cls}' 存在两个 struct 实例:类键上 " - f"{_RegBase.name}({_RegN}) vs 取值处 {StructHint.name}({_HintN})" - f";偏移基址已按取值处实例计算(两者布局不同会让字段偏移平移)", - "MemberBase") - if (isinstance(_StructBase, ir.IdentifiedStructType) - and _StructBase.elements is not None and len(_StructBase.elements) > 0): - _NumElems: int = len(_StructBase.elements) - _MembersRaw: list = self.class_members.get(_Cls, []) - _NumMembers: int = len(_MembersRaw) - # 【重名条目不是布局槽位 ⇒ 长度校准按**去重名数**算(2026-09-30)】 - # 与同文件 `_get_member_offset` 的配对段(重名条目跳过、不占槽位)以及 - # StructGen 的"重名条目"分支同口径。依据:源码层面的字段名遮蔽已由 - # HandlesClassDef 的"字段名遮蔽 fail-fast"(:1083)拦下 ⇒ 成员表里的重名 - # 只可能是**产物**(展平残留 / 同一字段被两条收集路径各登记一次), - # 不是合法槽位。 - # 不这样算的后果(现场):`ast.MatchCase` 表 10 项(9 个字段 + 1 条重复) - # 而 body 恰 10 槽 ⇒ `_Delta == 0` ⇒ base 静默取 0 ⇒ 该类所有字段偏移整体 - # 前移 1:`MatchCase.children` 表内下标 6 被当作槽 6(`end_col_offset`,i32) - # ⇒ `len(mc_c.children)` 分派 `list[AST*].__len__` 时收到 i32: - # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 - _UniqMembers: set = {n for n, _ in _MembersRaw} - if len(_UniqMembers) < _NumMembers: - _NumMembers = len(_UniqMembers) - _Delta: int = _NumElems - _NumMembers - _FirstElem: ir.Type = _StructBase.elements[0] - _IsVtablePtr: bool = (isinstance(_FirstElem, ir.PointerType) - and isinstance(_FirstElem.pointee, ir.IntType) - and _FirstElem.pointee.width == 8) - if _Delta == 0: - base = 0 - elif _Delta == 1: - _HasParent: bool = self.class_parent.get(_Cls) is not None - base = 1 if (_IsVtablePtr or not _HasParent or _ByInheritVtable) else 0 - # ---- 类型一致性校准(治本)---- - # 上面的 delta 长度启发式只在"表与布局恰好差 vtable 一项"时成立:一旦成员表 - # 比布局多/少**非 vtable** 项(跨模块类展平不全、同名类复用等),base 会被判错, - # 结果是**整个类所有字段偏移整体平移**。实测:`ast.Constant` 的 - # struct = {i8*(vtable), AST*(parent), MemManager*(pool), i32(lineno), …} - # 而查询 `cnst.lineno` 得到的是 pool 那槽(`%MemManager*`)⇒ 调用点报 - # "形参 lineno: i32 收到指针",掩盖了真因。 - # 校准办法:对 b∈{0,1,2} 逐项比较"成员声明类型 vs elements[b+i] 类型", - # 取一致度最高者(完全相同 +2;同为指针但类型不同 +0)。正确对齐时 - # 指针槽几乎全中,错位对齐只能靠 i32 撞运气,分数明显更低;平手保留启发式。 - # - # 【只在长度不齐(_Delta != 0)时校准】_Delta == 0 表示"成员表项数 == body - # 槽数":定义端 body 由成员表生成、跨模块端 body(.stub.ll)与 .vpi 同源 - # (同一声明顺序),长度相等即**逐位对齐**,base 必为 0。此时再用严格类型 - # 比较挑 b 反而会误判——声明侧的联合类型(如 `list[str] | t.CPtr`)可能被记成 - # 折叠后的 i8*,而 body 槽仍是具体指针(`%list[str]*`):b=0 丢掉这些匹配, - # 某个平移 b 却能靠指针槽互相撞分拿到更高分。 - # 现场(2026-09-30):TypeGenMixin(41 成员 / 41 槽 / @t.NoVTable,_Delta=0) - # base 被抬到 2 ⇒ `SubTypeGen.Gen = self`(Gen 声明 Generator*、正确槽 0) - # 落到槽 40(TypeSpecArena*)⇒ "指针隐式转换已禁止(非基本类型(结构体/类/联合))"。 - # _Delta != 0 的既有现场(ast.Constant 的 `cnst.lineno` 落到 pool 槽等)不受影响, - # 仍走校准。 - _MembersCal: list = self.class_members.get(_Cls, []) - if _MembersCal and _NumElems > 0 and _Delta != 0: - _BestB: int = base - _BestScore: int = -1 - _HeurScore: int = -1 - _b: int - for _b in range(0, 3): - _Score: int = 0 - _i2: int = 0 - for _nm2, _mt2 in _MembersCal: - _j2: int = _b + _i2 - if _j2 >= _NumElems: - break - _et2: ir.Type = _StructBase.elements[_j2] - if _mt2 is _et2 or str(_mt2) == str(_et2): - _Score += 2 - _i2 += 1 - if _b == base: - _HeurScore = _Score - if _Score > _BestScore: - _BestScore = _Score - _BestB = _b - # 仅当"明显更优"(至少多对齐 1 个成员)才覆盖启发式结果,避免抖动。 - if _HeurScore >= 0 and _BestScore >= _HeurScore + 2: - base = _BestB - return base - - def _RenumVariantFieldOwner(self, ClassName: str | None, field_name: str) -> str | None: - """REnum **变体字段**的归属类键:主 REnum 不声明该字段时,返回**唯一**声明它的变体键。 - - 判据(逐条可查,不是猜): + except (TypeError, ValueError): # 参数类型调整失败时使用简化回退逻辑 + if isinstance(arg.type, ir.PointerType) and isinstance(param.type, (ir.IdentifiedStructType, ir.LiteralStructType, ir.ArrayType)): + if arg.type.pointee == param.type or (hasattr(arg.type.pointee, 'name') and hasattr(param.type, 'name') and arg.type.pointee.name == param.type.name): + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + else: + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + elif isinstance(arg.type, ir.PointerType) and arg.type.pointee == param.type: + adjusted.append(self._load(arg, name=f"Load_arg_{i}")) + elif isinstance(param.type, ir.PointerType) and param.type.pointee == arg.type: + tmp: ir.AllocaInstr = self._alloca(arg.type, name=f"temp_arg_{i}") + self._store(arg, tmp) + adjusted.append(tmp) + elif isinstance(arg.type, ir.PointerType) and isinstance(param.type, ir.PointerType): + # 任意指针到指针的类型转换(如 struct* -> i8*) + adjusted.append(self.builder.bitcast(arg, param.type, name=f"cast_arg_{i}")) + else: + adjusted.append(arg) + else: + adjusted.append(arg) + while len(adjusted) < len(func.args): + missing_idx: int = len(adjusted) + param: ir.Argument = func.args[missing_idx] + param_type: ir.Type = param.type if hasattr(param, 'type') else param + if isinstance(param_type, ir.PointerType): + adjusted.append(ir.Constant(param_type, None)) + elif isinstance(param_type, ir.IntType): + adjusted.append(ir.Constant(param_type, 0)) + elif isinstance(param_type, (ir.FloatType, ir.DoubleType)): + adjusted.append(ir.Constant(param_type, 0.0)) + else: + adjusted.append(ir.Constant(ir.IntType(32), 0)) + if func.type.pointee.var_arg: + for i in range(len(func.args), len(args)): + arg: ir.Value = args[i] + adjusted.append(arg) + return adjusted + + def _class_has_vtable(self, ClassName: str | None) -> bool: + """类(或其任一祖先)是否带 vtable 槽。 + + 与 StructGen.py:442-450 的父链传播同源——vtable 沿继承链携带,子类 struct 布局为 + [vtable?] + [基类字段…] + [自身字段…]。只认 class_vtable / + _cross_module_vtable_classes(装饰器集合)会漏掉源码未写 @t.CVTable 的跨模块 + AST 节点类(ast.Try / ast.Match / ast.ExceptHandler…):它们有方法、继承自 + @t.CVTable 的 AST,struct 首元素确实是 vtable 指针,但 StructGen.py:415 在 + "struct body 已从 stub 完整加载"时直接 continue,永不把它们登记进集合 + (实测 ViperC/temp/_shared_sym.json 的 class_vtable/cross_module_vtable 仅有 + {AST, MemManager, Module})。此时 base 静默取 0 → 该类字段偏移整体前移 1 + (ast.ExceptHandler.children 落到 end_col_offset,i32)。 + """ + if not ClassName: + return False + _Seen: set[str] = set() + _Cur: str | None = ClassName + while _Cur and _Cur not in _Seen: + _Seen.add(_Cur) + if _Cur in self.class_vtable or _Cur in self._cross_module_vtable_classes: + return True + _Cur = self.class_parent.get(_Cur) + return False + + def _NormalizeMemberClass(self, ClassName: str | None) -> str | None: + """成员查询用的类键归一(短名 + 指针包装展开)。 + + - 统一短名解析(治本修复 2026-08-10):struct/class_members/class_vtable/ + class_parent 均以短名注册(跨模块加载 HandlesImports L1604),而调用方 + 可能传入带 sha1 前缀的全名(如 "f47f88ae6545965a.Import")。 + - rawptr[X] / t.CPtr[X] 指针包装归一:rawptr[X] 是内建裸指针类型 + (X*),非真实类——接收者注解/类型名为 'rawptr[AST]' 时字段访问 + (child.parent)应作用于内层 struct X,否则查 class_members 全 miss + → fast-fail "属性 'parent' 在类 'rawptr[AST]' 中找不到(struct=opaque)" + (includes/ast/base.vp _set_parent_list bootstrap 根因)。 + """ + if not ClassName: + return ClassName + _Cls: str = self._extract_short_name(ClassName) + if _Cls.startswith('rawptr[') or _Cls.startswith('t.CPtr['): + _lb: int = _Cls.find('[') + if _Cls.endswith(']') and _lb > 0: + _inner: str = _Cls[_lb + 1:-1].strip() + _short_inner: str = _inner.rsplit('.', 1)[-1] if '.' in _inner else _inner + _hit: str | None = None + if _short_inner in self.structs: + _hit = _short_inner + else: + for _k in self.structs: + if self._extract_short_name(_k) == _short_inner: + _hit = _k + break + if _hit: + _Cls = _hit + return _Cls + + def _member_base(self, ClassName: str | None, StructHint: Any = None) -> int: + """类 struct 的成员偏移基址(vtable 占位槽数)——**全仓唯一真源**。 + + `StructHint`(可选):调用方手里**实际要用于 GEP 的那个 struct 实例** + (通常就是 `ObjVal.type.pointee`)。同一类键上可能同时存在两个实例 + (同名类 sha1 冲突的双注册,或一个已从 stub 加载完整、另一个仍 opaque), + 而偏移基址必须与"实际 GEP 用的那份布局"一致 —— 用键上那份算、用另一份读, + 会让整类字段偏移平移(现场:`Constant.lineno` 偏移 2 落到 `pool`(MemManager*), + 实参第 6 项被报"指针不能隐式转整数 i32")。传 hint 时以 hint 为准并**报出** + 两侧差异(每类一次),不传时行为与改前逐字一致。 + + `_get_member_offset` 与所有"自算 base 再 GEP 字段"的消费点(构造/初始化、 + 字段默认值、继承方法 this 调整等)都必须走本方法,禁止各自直查 + `class_vtable`/`_cross_module_vtable_classes`——旧实现只认这两个装饰器集合, + 跨模块 AST 节点类(ast.Try/ast.Match/ast.ExceptHandler…)源码未写 + @t.CVTable 且 StructGen 在"struct body 已从 stub 加载"时提前 continue, + 永不把它们登记进集合,于是 base 只能靠"成员表长度 == struct 元素数 - 1" + 这个长度启发式救回;成员表未展平/尚未加载时启发式失效 ⇒ base 静默取 0 + ⇒ 该类所有字段偏移整体前移 1(ast.ExceptHandler.children 落到 + end_col_offset(i32),len() 分派拿到 i32 ⇒ Type of #1 arg mismatch)。 + + 判定规则(与 `StructGen._generate_structs` 生成 struct 的规则同源): + - vtable 归属沿 class_parent 链判定(见 `_class_has_vtable`); + - 再用 struct 实际元素数校准: + delta(=struct 元素数 - 成员表长度) == 0 ⇒ 该 struct 无 vtable 槽 → base=0; + delta == 1 ⇒ 恰多一个槽:首元素为指针 / 无父类 / 继承链归属成立 → base=1; + delta >= 2 ⇒ 长度启发式失效,保留"继承链 vtable 归属"结果(不再静默取 0)。 + + 查询前会调用 `HandlesImports.EnsureClassMembersFlattened`(幂等)把"停在自有 + 字段"的未展平表补齐——未展平表会让下列 delta 校准失真,也会让按表内下标 + 取字段的消费点漏掉继承前缀。 + """ + _Cls: str | None = self._NormalizeMemberClass(ClassName) + if not _Cls: + return 0 + # 查询前确保成员表已按父类展平(幂等、顺序无关)。 + # 未展平的表(停在自有字段)会让本方法的 delta 校准失真,也会让所有 + # "按成员表下标 base+i 取字段"的消费点漏掉继承前缀 —— 见 + # HandlesImports.EnsureClassMembersFlattened(内部有纯内存前置判断与 + # 重入/重复尝试保护,对已展平类零额外开销)。 + _EnsureIH: Any = getattr(self, '_import_handler_ref', None) + if _EnsureIH is not None and hasattr(_EnsureIH, 'EnsureClassMembersFlattened'): + try: + _EnsureIH.EnsureClassMembersFlattened(_Cls, self) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"补齐类成员展平失败: {_e}", "Exception") + _ByInheritVtable: bool = self._class_has_vtable(_Cls) + base: int = 1 if _ByInheritVtable else 0 + # 布局来源:优先调用方**实际 GEP 用的那个实例**(StructHint)——偏移必须与 + # 真正被索引的那份 body 同源。只在 hint 不可用时才回落到类键上的实例。 + _RegBase: ir.IdentifiedStructType | None = self.structs.get(_Cls) + _StructBase: ir.IdentifiedStructType | None = _RegBase + if isinstance(StructHint, ir.IdentifiedStructType): + # 仅接受**同类**的 hint:接收者静态类型是基类、实际指向派生类实例时, + # hint 的布局属于派生类,拿它配 `class_members[_Cls]` 会算错 delta。 + _HintShort: str = self._extract_short_name(getattr(StructHint, 'name', '') or '') + if _HintShort == _Cls: + _StructBase = StructHint + if _RegBase is not None and _RegBase is not StructHint: + # 同一类键上两个 struct 实例:报出事实(每类一次),避免"用一份算、 + # 用另一份读"的静默偏移平移(本函数此处即该平移的判定源)。 + _Rep: set = getattr(self, '_MemberBaseInstMismatch', None) + if _Rep is None: + _Rep = set() + self._MemberBaseInstMismatch = _Rep + if _Cls not in _Rep: + _Rep.add(_Cls) + _RegN: str = (f"{len(_RegBase.elements)} 元素" + if _RegBase.elements is not None else 'opaque') + _HintN: str = (f"{len(StructHint.elements)} 元素" + if StructHint.elements is not None else 'opaque') + _vlog().warning( + f"[成员偏移] 类 '{_Cls}' 存在两个 struct 实例:类键上 " + f"{_RegBase.name}({_RegN}) vs 取值处 {StructHint.name}({_HintN})" + f";偏移基址已按取值处实例计算(两者布局不同会让字段偏移平移)", + "MemberBase") + if (isinstance(_StructBase, ir.IdentifiedStructType) + and _StructBase.elements is not None and len(_StructBase.elements) > 0): + _NumElems: int = len(_StructBase.elements) + _MembersRaw: list = self.class_members.get(_Cls, []) + _NumMembers: int = len(_MembersRaw) + # 【重名条目不是布局槽位 ⇒ 长度校准按**去重名数**算(2026-09-30)】 + # 与同文件 `_get_member_offset` 的配对段(重名条目跳过、不占槽位)以及 + # StructGen 的"重名条目"分支同口径。依据:源码层面的字段名遮蔽已由 + # HandlesClassDef 的"字段名遮蔽 fail-fast"(:1083)拦下 ⇒ 成员表里的重名 + # 只可能是**产物**(展平残留 / 同一字段被两条收集路径各登记一次), + # 不是合法槽位。 + # 不这样算的后果(现场):`ast.MatchCase` 表 10 项(9 个字段 + 1 条重复) + # 而 body 恰 10 槽 ⇒ `_Delta == 0` ⇒ base 静默取 0 ⇒ 该类所有字段偏移整体 + # 前移 1:`MatchCase.children` 表内下标 6 被当作槽 6(`end_col_offset`,i32) + # ⇒ `len(mc_c.children)` 分派 `list[AST*].__len__` 时收到 i32: + # "Type of #1 arg mismatch: %"…list[…]"* != i32"。 + _UniqMembers: set = {n for n, _ in _MembersRaw} + if len(_UniqMembers) < _NumMembers: + _NumMembers = len(_UniqMembers) + _Delta: int = _NumElems - _NumMembers + _FirstElem: ir.Type = _StructBase.elements[0] + _IsVtablePtr: bool = (isinstance(_FirstElem, ir.PointerType) + and isinstance(_FirstElem.pointee, ir.IntType) + and _FirstElem.pointee.width == 8) + if _Delta == 0: + base = 0 + elif _Delta == 1: + _HasParent: bool = self.class_parent.get(_Cls) is not None + base = 1 if (_IsVtablePtr or not _HasParent or _ByInheritVtable) else 0 + # ---- 类型一致性校准(治本)---- + # 上面的 delta 长度启发式只在"表与布局恰好差 vtable 一项"时成立:一旦成员表 + # 比布局多/少**非 vtable** 项(跨模块类展平不全、同名类复用等),base 会被判错, + # 结果是**整个类所有字段偏移整体平移**。实测:`ast.Constant` 的 + # struct = {i8*(vtable), AST*(parent), MemManager*(pool), i32(lineno), …} + # 而查询 `cnst.lineno` 得到的是 pool 那槽(`%MemManager*`)⇒ 调用点报 + # "形参 lineno: i32 收到指针",掩盖了真因。 + # 校准办法:对 b∈{0,1,2} 逐项比较"成员声明类型 vs elements[b+i] 类型", + # 取一致度最高者(完全相同 +2;同为指针但类型不同 +0)。正确对齐时 + # 指针槽几乎全中,错位对齐只能靠 i32 撞运气,分数明显更低;平手保留启发式。 + # + # 【只在长度不齐(_Delta != 0)时校准】_Delta == 0 表示"成员表项数 == body + # 槽数":定义端 body 由成员表生成、跨模块端 body(.stub.ll)与 .vpi 同源 + # (同一声明顺序),长度相等即**逐位对齐**,base 必为 0。此时再用严格类型 + # 比较挑 b 反而会误判——声明侧的联合类型(如 `list[str] | t.CPtr`)可能被记成 + # 折叠后的 i8*,而 body 槽仍是具体指针(`%list[str]*`):b=0 丢掉这些匹配, + # 某个平移 b 却能靠指针槽互相撞分拿到更高分。 + # 现场(2026-09-30):TypeGenMixin(41 成员 / 41 槽 / @t.NoVTable,_Delta=0) + # base 被抬到 2 ⇒ `SubTypeGen.Gen = self`(Gen 声明 Generator*、正确槽 0) + # 落到槽 40(TypeSpecArena*)⇒ "指针隐式转换已禁止(非基本类型(结构体/类/联合))"。 + # _Delta != 0 的既有现场(ast.Constant 的 `cnst.lineno` 落到 pool 槽等)不受影响, + # 仍走校准。 + _MembersCal: list = self.class_members.get(_Cls, []) + if _MembersCal and _NumElems > 0 and _Delta != 0: + _BestB: int = base + _BestScore: int = -1 + _HeurScore: int = -1 + _b: int + for _b in range(0, 3): + _Score: int = 0 + _i2: int = 0 + for _nm2, _mt2 in _MembersCal: + _j2: int = _b + _i2 + if _j2 >= _NumElems: + break + _et2: ir.Type = _StructBase.elements[_j2] + if _mt2 is _et2 or str(_mt2) == str(_et2): + _Score += 2 + _i2 += 1 + if _b == base: + _HeurScore = _Score + if _Score > _BestScore: + _BestScore = _Score + _BestB = _b + # 仅当"明显更优"(至少多对齐 1 个成员)才覆盖启发式结果,避免抖动。 + if _HeurScore >= 0 and _BestScore >= _HeurScore + 2: + base = _BestB + return base + + def _RenumVariantFieldOwner(self, ClassName: str | None, field_name: str) -> str | None: + """REnum **变体字段**的归属类键:主 REnum 不声明该字段时,返回**唯一**声明它的变体键。 + + 判据(逐条可查,不是猜): ① 类短名按 `_IsRenumLayoutClass`(全仓唯一判据源)判为 REnum —— 只有 REnum - 才有变体层; - ② 变体类键固定为 `<主类短名>_<变体名>`,且成员表**首项恒为 `__tag`** - (唯一产源:HandlesClassDef._EmitREnumLlvm:1659-1673 与 HandlesImports - 的 REnum 跨模块注册 :1975-2015); - ③ 恰有**一个**变体声明该字段(多个变体同名字段 ⇒ 无法判定 ⇒ None,由调用方 - 照常 fail-fast); - ④ 该变体键的 struct 已注册(布局可用)。 - - 用变体键算偏移为什么正确:变体 struct 的 body 是**主 REnum 镜像** - `{i32 __tag, }`,而变体成员表 `[__tag] + 载荷` 与主 REnum - 槽位一一对应 ⇒ `base + 表内下标` 即真实槽位。 - 现场(2026-09-30):CallGenDispatch.vp:787 `len_val.Ty.Pointee` —— 'Pointee' 只属 - 变体键 'LLVMType_Ptr',主类 'LLVMType' 的成员表为空(HandlesClassDef:716)。 - """ - if not ClassName or not field_name: - return None - _Short: str | None = self._extract_short_name(ClassName) - if not _Short: - return None - if not self._IsRenumLayoutClass(_Short): - return None - _Owner: str | None = None - _Prefix: str = f"{_Short}_" - for _Key, _Members in self.class_members.items(): - if not _Key.startswith(_Prefix): - continue - _List: list = _Members or [] - if not _List or _List[0][0] != '__tag': - continue - if not any(_n == field_name for _n, _ in _List): - continue - if _Key not in self.structs: - continue - if _Owner is not None: - return None - _Owner = _Key - return _Owner - - def _MemberSlotIndex(self, Members: list, FieldName: str) -> int: - """字段在成员表里的**槽位下标**(重名条目**不占槽位**;找不到返回 -1)。 - - 与 `_get_member_offset` 的配对路径同口径:重名条目是产物,不是布局槽位 - —— 源码层面的字段名遮蔽已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, - 其余重名只可能来自"展平残留 / 同一字段被两条收集路径各登记一次"。 - 所有"按表内下标 + base 求偏移"的站点都必须走本方法,否则含重名条目的表会让 - 偏移整体偏斜: - · `ast.ExceptHandler` 表 10 项、'children' 出现两次 ⇒ 'name' 原始下标 9 ⇒ 偏移 10(越界); - · `ast.MatchCase` 表 10 项 / 去重 9(body 10 槽)⇒ `_Delta == 0` ⇒ base 变 0 ⇒ - `children` 偏移 6 落到 `end_col_offset`(i32)。 - 重名条目在**首个同名(正本)之后**才出现,故按"首个同名的去重下标"取值。 - """ - _Slot: int = 0 - _Seen: set = set() - for _n, _ in (Members or []): - if _n in _Seen: - continue - _Seen.add(_n) - if _n == FieldName: - return _Slot - _Slot += 1 - return -1 - - def _get_member_offset(self, field_name: str, ClassName: str | None = None, - StructHint: Any = None) -> int | None: - # base 判定用未归一的原始类名:_member_base 内部自行做同一套归一 - # (_NormalizeMemberClass),二者结果逐字一致(避免二次归一在 - # "rawptr[X] 展开命中 sha1 前缀 struct 键"这一角落改变取值)。 - _MemberBaseKey: str | None = ClassName - # 治本修复:同名类 sha1 冲突检测。 - # 当 class_sha1_map[ClassName] 的 sha1 与 Gen.structs[ClassName] 的 sha1 不一致时, - # class_members[ClassName] 可能存储了错误模块的同名类成员。 - # 此处检测冲突,加载正确模块的 class_members 并以 sha1 前缀全名缓存。 - _ResolvedClass: str = ClassName if ClassName else '' - if ClassName: - # 类键归一(短名 + rawptr[X]/t.CPtr[X] 展开)统一走 _NormalizeMemberClass, - # 与 _member_base 的 base 判定共用同一归一,禁止各写一份。 - ClassName = self._NormalizeMemberClass(ClassName) - _ResolvedClass = ClassName - _ClassSha1Map: dict[str, str] = getattr(self, 'class_sha1_map', {}) - _ExpectedSha1: str | None = _ClassSha1Map.get(ClassName) - if _ExpectedSha1: - _ExistingStruct = self.structs.get(ClassName) - if isinstance(_ExistingStruct, ir.IdentifiedStructType): - _ExistingSha1: str = self._extract_struct_sha1(_ExistingStruct) - if _ExistingSha1 and _ExistingSha1 != _ExpectedSha1: - # sha1 冲突:使用全名查找 class_members - _FullKey: str = f"{_ExpectedSha1}.{ClassName}" - if _FullKey in self.class_members and self.class_members[_FullKey]: - _ResolvedClass = _FullKey - else: - # 从正确 stub 加载 class_members - if self._Trans and hasattr(self._Trans, 'ImportHandler'): - _StubName: str = f"{_ExpectedSha1}.stub.ll" - # 临时保存旧值,清除短名条目以绕过 guard,加载后恢复 - _OldMembers = self.class_members.get(ClassName) - self.class_members[ClassName] = [] - try: - self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, _StubName, self) - except Exception: - pass - _NewMembers = self.class_members.get(ClassName) - if _NewMembers: - self.class_members[_FullKey] = _NewMembers - _ResolvedClass = _FullKey - # 恢复旧值(避免影响其他代码对短名的依赖) - if _OldMembers: - self.class_members[ClassName] = _OldMembers - else: - self.class_members.pop(ClassName, None) - # base(vtable 占位槽数):唯一真源 = _member_base(规则与文档见该方法)。 - # 本函数在同一次调用内还会在下方多处重载成员表(懒加载 425-436、模块重载 - # 482-508、字段预注册 558-578、force 重建 587-643),每次都可能改写 - # _member_base 的三个输入(class_parent 父链、structs 布局、class_members - # 长度)。因此 base 与"即将返回的下标 i"必须取自**同一份最新已加载状态**: - # 所有 return 一律用 _BaseNow() 现算,禁止缓存早先算出的 base——缓存会让 - # 偏移整体差 1(ast.ExceptHandler.children 落到 end_col_offset(i32), - # len 分派拿到 i32)。 - def _BaseNow() -> int: - return self._member_base(_MemberBaseKey, StructHint) - # 预热(与原行为逐字一致):先触发一次 EnsureClassMembersFlattened, - # 再决定下面的懒加载。 - _BaseNow() - if ClassName and ClassName not in self.class_members and self._Trans and hasattr(self._Trans, 'ImportHandler'): - # 跨模块 class_members 懒加载:struct 已从 stub 加载但 class_members - # 可能尚未从 .vpi 填充(vpi/stub 就绪时序不一致),主动按类 sha1 加载。 - _Sha1sForLoad: dict[str, str] = getattr(self, 'class_sha1_map', {}) - for _mod_name_lz, _mod_sha1_lz in _Sha1sForLoad.items(): - try: - self._Trans.ImportHandler._TryLoadClassMembersFromPyi( - ClassName, f"{_mod_sha1_lz}.stub.ll", self) - except Exception: - continue - if ClassName in self.class_members and len(self.class_members[ClassName]) > 0: - break - # 依赖补齐(上面的懒加载)后再确认一次展平(幂等);偏移基址由 _BaseNow() - # 在真正 return 处现算,不在此缓存。 - _BaseNow() - # ---- REnum 变体字段:主 REnum 不声明该字段时,改用它**唯一**的变体键继续 ---- - # 现场(2026-09-30):CallGenDispatch.vp:787 `len_val.Ty.Pointee` —— 'Pointee' 属 - # LLVMType 的 REnum 变体键 'LLVMType_Ptr',主类 'LLVMType' 成员表为空 ⇒ 旧路径 - # 直接 fast-fail "属性 'Pointee' 在类 'LLVMType' 中找不到" 终止整个编译。 - # 判据与槽位正确性见 _RenumVariantFieldOwner(变体 struct body = 主 REnum 镜像)。 - if field_name and ClassName: - _MainMembers: list = (self.class_members.get(_ResolvedClass) - or self.class_members.get(ClassName) or []) - if not any(_n == field_name for _n, _ in _MainMembers): - _VariantOwner: str | None = None - try: - _VariantOwner = self._RenumVariantFieldOwner(ClassName, field_name) - except Exception: - _VariantOwner = None - if _VariantOwner: - _RepRK: set = getattr(self, '_RenumVariantFieldReported', None) - if _RepRK is None: - _RepRK = set() - self._RenumVariantFieldReported = _RepRK - _RKey: tuple = (_VariantOwner, field_name) - if _RKey not in _RepRK: - _RepRK.add(_RKey) - _vlog().warning( - f"[REnum 变体字段] '{field_name}' 不在主 REnum 类 " - f"{_ResolvedClass!r} 中,按唯一变体类 '{_VariantOwner}' 取偏移" - f"(变体 struct body = 主 REnum 镜像,槽位一一对应)", - "MemberBase") - ClassName = _VariantOwner - _ResolvedClass = _VariantOwner - _MemberBaseKey = _VariantOwner - _BaseNow() - # ---- 布局真源优先(配对式偏移):偏移只认"运行时真正被 GEP 的那份 struct body" ---- - # class_members 是**可变**表(继承展平会在父类就绪后前置前缀、跨模块可按需重载), - # 而 struct body 一旦 set_body 即**永久冻结** ⇒ "base + 成员表下标"会随表漂移 - # 整体平移(现场:Converter 模块读 `Alias.name` 取到槽 9(= asname), - # `import t` 无 as 子句 ⇒ asname=NULL ⇒ vpi 生成误报"import 别名 name 为空"; - # 同族旧现场:ast.Constant.lineno 取到 pool 槽)。改为两条"只看布局"的路径: - # ① 建 body 时冻结下来的"成员名 → 槽位"映射(StructGen._generate_structs 挂载); - # ② 用**类型**把成员表逐项配对到 elements:类型不匹配的条目视为方法/占位/漂移项, - # 不占槽位;配对中类型相等的那个槽位即该字段的真实槽位。 - # 表与布局自洽时两条路径给出与旧下标法**逐字相同**的结果(body 本按该表建); - # 都覆盖不到时落回旧下标法(对既有正确行为零变化)。 - if field_name: - _OffStruct: Any = self.structs.get(ClassName) - if isinstance(StructHint, ir.IdentifiedStructType) and ClassName: - # 与 _member_base 同口径:只接受**同类**的 hint(接收者静态类型可能是基类, - # 实际指向派生类实例,其布局不属于本类)。 - if self._extract_short_name(getattr(StructHint, 'name', '') or '') == ClassName: - _OffStruct = StructHint - if isinstance(_OffStruct, ir.IdentifiedStructType): - _OffEls: Any = _OffStruct.elements - _Frozen: Any = getattr(_OffStruct, '_tpc_member_slots', None) - # 冻结表**有效性**判定:表挂在 struct 实例上,而 body 会被"不完整即清除" - # (StructGen 预检 `existing_st.elements = None`)与 stub 加载 - # (HandlesImports._TryLoadStructFromStub 的 set_body)**清空后重设**, - # 重设路径不更新本表 ⇒ 陈旧表把字段指到别的槽。现场:AnnAssign 在 - # "自有 4 字段 + vtable"(5 槽:i8*,AST*,AST*,AST*,i32)时冻结 - # {'value': 3},随后 body 被重设为 12 槽正确布局(value 在 10), - # 查询仍读槽 3(lineno, i32)⇒ `.kind()` 虚分派 GEP 的操作数是 i32。 - # 判据 = 建表时记下的 **body 对象身份**(set_body 每次赋新 tuple): - # 与当前 elements 非同一对象即视为失效,落到按布局求槽的路径。 - _FrozenFresh: bool = (isinstance(_Frozen, dict) - and getattr(_OffStruct, '_tpc_member_body', None) is _OffEls) - if _FrozenFresh and field_name in _Frozen: - _FrozenSlot: Any = _Frozen[field_name] - if isinstance(_FrozenSlot, int) and (not _OffEls or 0 <= _FrozenSlot < len(_OffEls)): - return _FrozenSlot - _OffMembers: list = self.class_members.get(_ResolvedClass) or [] - _BitFields: Any = getattr(self, 'class_member_bitfields', None) - if _OffEls and _OffMembers and not (_BitFields and _BitFields.get(ClassName)): - # ---- 布局真源之二:两种对齐候选按"逐项类型可写入"**打分**取高者 ---- - # 成员表可能是①展平表(含父类前缀)②仅自有字段(父类前缀未补齐 ⇒ 真身是 - # body **尾部**)③残缺前缀(如仅 ['Gen'] 一项 ⇒ 真身在 body **头部**)。 - # 只用"长度关系/父类槽数"来断言①③是猜,已被两轮现场打脸 - # (Generator.vp:136 TypeGenMixin 被锚到槽 40;:164 TypeGenErr 因恰好 - # 只有 1 个自有字段、`body-len(表) == 父类槽数` 巧合成立而被锚到槽 41)。 - # 改为**比较**两个候选锚(前缀锚 `_BaseNow()` vs 尾部锚 `len(body)-len(表)`) - # 的逐项得分(判据与建 body/存储侧同一套 `_MemberSlotAccepts`): - # 只有尾部锚**严格更高分**才采用它,同分/更低一律保持原行为 - # (配对 → 下标法),因此对既有正确布局零风险。 - _ElemN: int = len(_OffEls) - _AnchorA: int = _BaseNow() - _AnchorB: int = _ElemN - len(_OffMembers) - _ScoreA: int = 0 - _aA: int = 0 - while _aA < len(_OffMembers): - if _AnchorA + _aA >= _ElemN: - break - _nA, _tA = _OffMembers[_aA] - if self._MemberSlotAccepts(_tA, _OffEls[_AnchorA + _aA]): - _ScoreA += 1 - _aA += 1 - _ScoreB: int = 0 - if _AnchorB >= 0 and _AnchorB != _AnchorA and not self._IsRenumLayoutClass(_ResolvedClass): - _aB: int = 0 - while _aB < len(_OffMembers): - if _AnchorB + _aB >= _ElemN: - break - _nB, _tB = _OffMembers[_aB] - if self._MemberSlotAccepts(_tB, _OffEls[_AnchorB + _aB]): - _ScoreB += 1 - _aB += 1 - if _ScoreB > _ScoreA and _ScoreB > 0: - _TIdx: int = -1 - for _i5, (_n5, _) in enumerate(_OffMembers): - if _n5 == field_name: - _TIdx = _i5 - break - if _TIdx >= 0: - # 每类只留一次痕(大类的每个字段都会命中本路径) - _TailKey: str = _ResolvedClass - _TailRep: set = getattr(self, '_MemberTailAlignedReported', None) - if _TailRep is None: - _TailRep = set() - self._MemberTailAlignedReported = _TailRep - if _TailKey not in _TailRep: - _TailRep.add(_TailKey) - _vlog().warning( - f"[成员偏移] 类 '{_ResolvedClass}' 的成员表({len(_OffMembers)} 项)" - f"在 body({_ElemN} 槽)上按**尾部对齐**得分更高" - f"(尾部锚 {_AnchorB} 得 {_ScoreB} vs 前缀锚 {_AnchorA} 得 {_ScoreA})" - f"⇒ 判定该表缺的是**父类前缀**:字段 '{field_name}' = " - f"{_AnchorB + _TIdx}", - "MemberBase") - return _AnchorB + _TIdx - _Cursor: int = _BaseNow() - _PairedNames: set = set() - for _nmP, _mtP in _OffMembers: - if _Cursor >= _ElemN: - break - # 重名条目不是布局槽位(表构造/重载产生的重复项)⇒ 不占槽位 - if _nmP in _PairedNames: - continue - # None 成员在 body 里被物化为 i32(见 StructGen 建 body 分支), - # 配对类型必须同口径,否则该类字段会被整条跳过。 - _mtEff: Any = _mtP if _mtP is not None else ir.IntType(32) - _etP: Any = _OffEls[_Cursor] - _PairOk: bool = (_mtEff is _etP or str(_mtEff) == str(_etP)) - if (not _PairOk and isinstance(_mtEff, ir.PointerType) - and isinstance(_etP, ir.PointerType)): - # 联合类型字段在成员表里存成通用指针(如 AST.children = - # `list[rawptr[AST]] | t.CPtr` ⇒ i8*),而 body 槽是具体指针 - # (`%"...list[...]"*`):严格串比较会在这条**断链**,其后的 - # 所有条目都不再消费槽位(cursor 不动)⇒ 目标字段全部退化成 - # "base + 表内下标"(现场:ExceptHandler.name 表内下标 9 ⇒ - # 偏移 10,越界 fail-fast,而正确槽是 9)。判据与存储/建 body - # 侧同一套(_MemberSlotAccepts:两侧都是指针时按指针转换规则, - # i8* 通用指针放行);标量仍走严格串比较,不引入新的过配对。 - _PairOk = self._MemberSlotAccepts(_mtEff, _etP) - if _PairOk: - _PairedNames.add(_nmP) - if _nmP == field_name: - # 与旧下标法不一致时留痕:这正是"成员表与 struct 布局 - # 不自洽"的现场事实(每类每字段一次,便于定位漂移来源)。 - _OldIdx: int = -1 - for _i3, (_n3, _) in enumerate(_OffMembers): - if _n3 == field_name: - _OldIdx = _i3 - break - if _OldIdx >= 0 and _BaseNow() + _OldIdx != _Cursor: - _RepKey: tuple = (_ResolvedClass, field_name) - _RepSet: set = getattr(self, '_MemberSlotDriftReported', None) - if _RepSet is None: - _RepSet = set() - self._MemberSlotDriftReported = _RepSet - if _RepKey not in _RepSet: - _RepSet.add(_RepKey) - _vlog().warning( - f"[成员偏移] 类 '{_ResolvedClass}' 的成员表与 struct " - f"布局不自洽:字段 '{field_name}' 下标法={_BaseNow() + _OldIdx}" - f"(成员表 {len(_OffMembers)} 项) vs 布局配对比={_Cursor}" - f"(struct {_ElemN} 槽)——已采用布局配对结果", - "MemberBase") - return _Cursor - _Cursor += 1 - if _ResolvedClass and _ResolvedClass in self.class_members: - # 只有走到这里(布局配对未命中)才做一次重名检查:重名条目**不占布局槽**, - # 却会让 `base + 表内下标` 整体后移(现场:ExceptHandler 表 10 项、其中 - # 'children' 出现两次 ⇒ 'name' 下标 9 ⇒ 偏移 10 越界)。此处把事实报出来 - # (每类一次),并标注该返回值取自回退路径、可能失准。 - _MemList: list = self.class_members[_ResolvedClass] - _SeenNm: set = set() - _DupNm: list[str] = [] - for _nmC, _ in _MemList: - if _nmC in _SeenNm: - if _nmC not in _DupNm: - _DupNm.append(_nmC) - else: - _SeenNm.add(_nmC) - if _DupNm: - _DupRep: set = getattr(self, '_MemberDupNamesReported', None) - if _DupRep is None: - _DupRep = set() - self._MemberDupNamesReported = _DupRep - if _ResolvedClass not in _DupRep: - _DupRep.add(_ResolvedClass) - _vlog().warning( - f"[成员偏移] 类 '{_ResolvedClass}' 的成员表含**重名条目** " - f"{_DupNm!r}(表 {len(_MemList)} 项 / 去重 {len(_SeenNm)} 项)——" - f"重名项不是布局槽位,`base + 表内下标` 会因此整体后移;" - f"本次字段 '{field_name}' 走的是回退路径(未被 body 配对命中)," - f"返回值可能失准。重名来源通常是「同一字段被两条收集路径各登记一次" - f"(类内 AnnAssign 声明 + 方法体 self.x 赋值)」或继承前缀重复前置。", - "MemberBase") - # 回退取值也按"重名条目**不占槽位**"算(与上方配对路径同口径):直接取 - # `base + 原始下标` 会把重名条目也算进槽位,整体后移(现场:ExceptHandler - # 表 10 项、'children' 出现两次 ⇒ 'name' 原始下标 9 ⇒ 偏移 10 越界)。 - _SlotIdx: int = 0 - _Counted: set = set() - for name, _ in _MemList: - if name in _Counted: - continue - _Counted.add(name) - if name == field_name: - return _BaseNow() + _SlotIdx - _SlotIdx += 1 - # 继承链回退:当字段不在当前类的 class_members 中时(常见于跨模块导入 - # 父类未加载,字段被填充为 __inherited_N 占位名),沿 class_parent 链向上查找。 - # 子类 struct 布局为 [vtable?] + [基类字段...] + [子类字段...], - # 基类字段在子类 struct 中的起始偏移即为 base(子类), - # 因此祖先 class_members 中的索引 i 对应子类 struct 中的偏移 base + i。 - if ClassName and field_name: - _parent: str | None = self.class_parent.get(ClassName) - _visited: set = {ClassName} if ClassName else set() - _cls_short: str = ClassName.split('.')[-1] if ClassName else '' - while _parent and _parent not in _visited: - _visited.add(_parent) - # 父类候选键:class_parent 只登记**短名**(含泛型基类时是模板名, - # 如 'GSListNode'),而 class_members 里真实键可能是**实例化名** - # ('GSListNode[GlobalVariable]',见 includes/llvmlite/__module.vp - # `class GlobalVariable(GSListNode[GlobalVariable])`)或模板名。 - # 少这一步 ⇒ 泛型基类字段(Next/Prev 等)沿链永远查不到 → fast-fail - # ("属性 'Next' 在类 'GlobalVariable' 中找不到")。 - _p_tmpl: str = _parent.split('[')[0] - _cand_keys: list = [] - for _k in (_parent, _p_tmpl, f"{_p_tmpl}[{_cls_short}]" if _cls_short else _p_tmpl): - if _k and _k not in _cand_keys: - _cand_keys.append(_k) - # 跳过 __inherited_N 占位条目,只在真实字段名中查找 - _hit_idx: int = -1 - for _k in _cand_keys: - if _k in self.class_members: - _members: list = self.class_members[_k] - _has_placeholder: bool = any(n.startswith('__inherited_') for n, _ in _members) - if not _has_placeholder: - _hit_idx = self._MemberSlotIndex(_members, field_name) - if _hit_idx >= 0: - break - if _hit_idx >= 0: - # 基类字段位于子类 struct 的 base 起始处,索引 i 即基类内偏移 - return _BaseNow() + _hit_idx - # 父类 class_members 未加载或含占位条目,尝试从 stub 加载真实字段 - if self._Trans and hasattr(self._Trans, 'ImportHandler'): - sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) - for _k in _cand_keys: - for mod_name, mod_sha1 in sha1_map.items(): - stub_name: str = f"{mod_sha1}.stub.ll" - self._Trans.ImportHandler._TryLoadClassMembersFromPyi(_k, stub_name, self) - if _k in self.class_members and not any(n.startswith('__inherited_') for n, _ in self.class_members[_k]): - _pIdx: int = self._MemberSlotIndex(self.class_members[_k], field_name) - if _pIdx >= 0: - return _BaseNow() + _pIdx - break - if _k in self.class_members and any(n == field_name for n, _ in self.class_members.get(_k, [])): - break - _parent = self.class_parent.get(_parent) - short_name: str | None = ClassName.split('.')[-1] if ClassName and '.' in ClassName else None - if short_name and short_name in self.class_members: - _sIdx: int = self._MemberSlotIndex(self.class_members[short_name], field_name) - if _sIdx >= 0: - return _BaseNow() + _sIdx - if ClassName and field_name: - if ClassName not in self.class_members or field_name not in [n for n, _ in self.class_members.get(ClassName, [])]: - if self._Trans and hasattr(self._Trans, 'ImportHandler'): - sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) - for mod_name, mod_sha1 in sha1_map.items(): - stub_name: str = f"{mod_sha1}.stub.ll" - self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, stub_name, self) - if ClassName in self.class_members and len(self.class_members[ClassName]) > 0: - _rIdx: int = self._MemberSlotIndex(self.class_members[ClassName], field_name) - if _rIdx >= 0: - return _BaseNow() + _rIdx - break - if short_name: - for mod_name, mod_sha1 in sha1_map.items(): - stub_name: str = f"{mod_sha1}.stub.ll" - self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_name, stub_name, self) - if short_name in self.class_members and len(self.class_members[short_name]) > 0: - _rIdx2: int = self._MemberSlotIndex(self.class_members[short_name], field_name) - if _rIdx2 >= 0: - return _BaseNow() + _rIdx2 - break - if ClassName and field_name: - for cn_key in self.class_members: - if cn_key == ClassName or cn_key.endswith(f'.{ClassName}') or (short_name and (cn_key == short_name or cn_key.endswith(f'.{short_name}'))): - _cIdx: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) - if _cIdx >= 0: - return _BaseNow() + _cIdx - # REnum 变体搜索: ClassName 是 REnum(如 MetaType),成员注册在变体结构 - # (MetaType_Var, MetaType_Concrete, MetaType_App)中。变体与 REnum 主结构 - # 共享 max_variant_struct 布局,偏移一致(__tag 在偏移 0,字段从偏移 1 开始) - if ClassName and field_name: - variant_prefix: str = f"{ClassName}_" - for cn_key in self.class_members: - if cn_key.startswith(variant_prefix): - _vIdx: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) - if _vIdx >= 0: - return _vIdx - if short_name: - variant_prefix = f"{short_name}_" - for cn_key in self.class_members: - if cn_key.startswith(variant_prefix): - _vIdx2: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) - if _vIdx2 >= 0: - return _vIdx2 - if ClassName and ClassName in self.structs: - struct_type: ir.IdentifiedStructType = self.structs[ClassName] - if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None and len(struct_type.elements) > 0: - for cn_key, members in self.class_members.items(): - if cn_key == short_name or (short_name and cn_key.endswith(f'.{short_name}')): - _kIdx: int = self._MemberSlotIndex(members, field_name) - if 0 <= _kIdx < len(struct_type.elements): - return _kIdx - break - else: - if short_name: - for cn_key, members in self.class_members.items(): - sn: str = cn_key.split('.')[-1] if '.' in cn_key else cn_key - if sn == short_name: - _kIdx2: int = self._MemberSlotIndex(members, field_name) - if 0 <= _kIdx2 < len(struct_type.elements): - return _kIdx2 - break - if ClassName and field_name and ClassName not in self.class_members: - if self._Trans and hasattr(self._Trans, 'ImportHandler'): - for temp_dir_candidate in [getattr(self, '_temp_dir', None)]: - if temp_dir_candidate and os.path.isdir(temp_dir_candidate): - for pyi_file in os.listdir(temp_dir_candidate): - if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'): - stub_name: str = pyi_file.replace('.vpi', '.stub.ll').replace('.pyi', '.stub.ll') - short_cn: str = short_name if short_name else ClassName - self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_cn, stub_name, self) - if short_cn in self.class_members and len(self.class_members[short_cn]) > 0: - _scIdx: int = self._MemberSlotIndex(self.class_members[short_cn], field_name) - if _scIdx >= 0: - return _BaseNow() + _scIdx - break - if ClassName and ClassName in self.structs and (ClassName not in self.class_members or len(self.class_members.get(ClassName, [])) == 0): - struct_type: ir.IdentifiedStructType = self.structs[ClassName] - if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None: - if self._Trans and hasattr(self._Trans, 'ClassHandler'): - class_handler: Any = self._Trans.ClassHandler - if self._current_tree: - for node in ast.iter_child_nodes(self._current_tree): - if isinstance(node, ast.ClassDef) and (node.name == ClassName or node.name == short_name): - if node.name not in self.class_members: - self.class_members[node.name] = [] - if len(self.class_members[node.name]) == 0: - class_handler._PreRegisterClassMembers(node, self) - if ClassName in self.class_members: - _prIdx: int = self._MemberSlotIndex(self.class_members[ClassName], field_name) - if _prIdx >= 0: - return _BaseNow() + _prIdx - if short_name and short_name in self.class_members: - _prIdx2: int = self._MemberSlotIndex(self.class_members[short_name], field_name) - if _prIdx2 >= 0: - return _BaseNow() + _prIdx2 - break - # 最后回退:当继承链回退失败但 struct body 已加载时, - # 从 struct elements 数和 class_members 数推断继承字段偏移。 - # 继承字段在 struct 开头(base 之后),此 fallback 仅适用于单继承字段的情况 - # (如 GSListNode[T] 的 Next 字段)。子类 struct 布局: - # [vtable?] + [基类字段...] + [子类字段...] - # 当 class_members 只含子类自身字段、基类 class_members 未加载时, - # 继承字段数 = struct_elements - class_members_count, - # 若该值 == 1,则所查字段(如 Next)的偏移 = base + 0。 - if ClassName and field_name: - _st_final = self.structs.get(ClassName) - if isinstance(_st_final, ir.IdentifiedStructType) and _st_final.elements is not None: - _num_elements_final: int = len(_st_final.elements) - _members_final: list = self.class_members.get(ClassName, []) - # 与 _member_base 的长度校准同口径:重名条目**不是布局槽位** ⇒ 按去重名数算 - _num_members_final: int = len({_Rn for _Rn, _ in _members_final}) - if _num_elements_final > _num_members_final: - # base 是 vtable 占位(struct[0] = i8*),不是继承字段,必须扣除: - # 不扣 base 时,凡"struct 元素数恰好多 1"的类,差值其实只是 vtable - # 本身(class_members 不含 vtable),却会对任意字段名返回 base+0。 - # 实例:ast.ImportFrom 的 level/module → 静默返回偏移 1(=parent, - # %"…AST"*)→ 调用点把 AST* 当 i32 传入 _resolve_relative_module - # → llvmlite "Type of #4 arg mismatch: i32 != %"…AST"*"。 - # 成员表与 struct 不一致(少一个非 vtable 槽)时,先强制按定义模块 - # 的 .vpi 重载一次成员表再按名查:_TryLoadClassMembersFromPyi 的 guard - # 会永久跳过"已存在但不完整"的表,此处即该冻结的解除点。重载后仍缺 - # 才走下方推断/fast-fail。每类只重载一次(避免递归与重复 IO)。 - _ReloadKey: tuple = (id(self), ClassName) - _ReloadTried: set = getattr(self, '_MemberReloadTried', None) - if _ReloadTried is None: - _ReloadTried = set() - self._MemberReloadTried = _ReloadTried - if _ReloadKey not in _ReloadTried: - _ReloadTried.add(_ReloadKey) - _Sha1Final: str | None = getattr(self, 'class_sha1_map', {}).get(ClassName) - if _Sha1Final and self._Trans and hasattr(self._Trans, 'ImportHandler'): - _OldMembers = _members_final - try: - self._Trans.ImportHandler._TryLoadClassMembersFromPyi( - ClassName, f"{_Sha1Final}.stub.ll", self, force=True) - except Exception: - pass - _ReloadedMembers: list = self.class_members.get(ClassName, []) - if len(_ReloadedMembers) < len(_OldMembers): - # 重载反而变少(.vpi 缺该类):回滚,避免丢字段 - self.class_members[ClassName] = _OldMembers - _ReloadedMembers = _OldMembers - _members_final = _ReloadedMembers - _num_members_final = len({_Rn for _Rn, _ in _members_final}) - _rfIdx: int = self._MemberSlotIndex(_members_final, field_name) - if _rfIdx >= 0: - return _BaseNow() + _rfIdx - _num_inherited_final: int = _num_elements_final - _num_members_final - _BaseNow() - if _num_inherited_final == 1: - # 推断(缺失槽位 = 位于 base+0 的未展平继承字段)仅在成员表 - # 尚未含展平继承字段时成立:若成员表首项本身就是任一已加载 - # 祖先的字段名(如 ImportFrom 'parent' ← Stmt),说明继承字段 - # 已展平、缺的是类自身字段,推断 base+0 必然读错槽位。 - _ParentNamesFinal: set = set() - for _ParentF in (getattr(self, 'class_parents', {}).get(ClassName) or []): - for _Np, _Tp in self.class_members.get(_ParentF, []) or []: - _ParentNamesFinal.add(_Np) - if _members_final and _members_final[0][0] in _ParentNamesFinal: - pass # 已含展平继承字段 → 落入下方 fast-fail 暴露真实缺失 - else: - # 单继承字段,偏移 = base(vtable 之后第一个位置) - return _BaseNow() + 0 - # fast-fail(治本修复 2026-08-10):字段未找到不再静默返回 None。 - # 静默 None 会让调用方(_gep_and_Load_member L26-27)直接 return None, - # AnnAssign InitValue 变 None → 变量 alloca 未初始化 → 运行期读垃圾指针 - # (如 HandlesImports.HandleImport 的 imp.names → 0xffffffffffffffff 崩溃)。 - # 抛出编译错误,暴露 class_members/struct 的真实状态便于定位根因。 - _members_diag: list = self.class_members.get(ClassName, []) if ClassName else [] - _struct_diag: str = 'not-registered' - if ClassName and ClassName in self.structs: - _st_diag = self.structs[ClassName] - if isinstance(_st_diag, ir.IdentifiedStructType): - _struct_diag = f"{len(_st_diag.elements)} elems" if _st_diag.elements is not None else 'opaque' - _member_names_diag: list = [n for n, _ in _members_diag][:16] - # ---- 取证(只读,不改变行为,2026-09-30)---- - # 现场:CallGenDispatch.vp:787 `len_val.Ty.Pointee` 里 `.Pointee` 被判成在类 - # 'ParamNode'(2 字段 Next/Ty)里找 —— 而 'Pointee' 本属 LLVMType 的 REnum - # 变体镜像字段。要一次定位"是谁把接收者记成了 ParamNode",报文需给出: - # ① 本类键 → struct 实例 IR 名(键/实例名不一致即"同名两份实例"); - # ② **真正声明了该字段**的类键(唯一者即"本该用的类"); - # ③ var_struct_class 里被登记为**本类**的变量(封顶 6 个)及其实际 pointee - # IR 名 —— 两者不一致即"记录类名与实际 IR 类型不符",直接圈定记录点。 - _extra_diag: str = '' - try: - _st_key = self.structs.get(ClassName) if ClassName else None - if isinstance(_st_key, ir.IdentifiedStructType): - _extra_diag += (f"\n 本类键 '{ClassName}' → struct 实例 IR 名 " - f"'{getattr(_st_key, 'name', '?')}'") - _owners: list = [] - for _ck2, _cm2 in self.class_members.items(): - if _ck2 == ClassName: - continue - if any(_n2 == field_name for _n2, _ in (_cm2 or [])): - _owners.append(_ck2) - _extra_diag += (f"\n 声明了 '{field_name}' 的其它类键({len(_owners)} 个): " - f"{_owners[:8]}{' …' if len(_owners) > 8 else ''}") - _vsc: dict = getattr(self, 'var_struct_class', {}) or {} - _vars_here: list = [v for v, cn in _vsc.items() if cn == ClassName] - if _vars_here: - _vdesc: list = [] - for _v2 in _vars_here[:6]: - _pointee_desc: str = '?' - for _src3 in (getattr(self, 'variables', {}) or {}, - getattr(self, '_reg_values', {}) or {}, - getattr(self, '_direct_values', {}) or {}): - _val3 = _src3.get(_v2) - if _val3 is not None and isinstance(getattr(_val3, 'type', None), ir.PointerType): - _pointee_desc = str(_val3.type.pointee) - break - _vdesc.append(f"{_v2}(pointee={_pointee_desc})") - _extra_diag += (f"\n var_struct_class 登记为 '{ClassName}' 的变量(" - f"{len(_vars_here)} 个,示前 6): {_vdesc}") - except Exception as _e_diag: - _extra_diag += f"\n (取证附加信息生成失败: {type(_e_diag).__name__}: {_e_diag})" - # 异常类型:MemberLookupFailFast(KeyError 子类 + 裸消息 __str__)—— - # 既保留 `except KeyError` 兜底点,又让报文不再被 repr 成"带引号 + \n 转义" - # 的不可读形式(原有 traceback.print_stack 调试转储一并移除,保持输出干净)。 - raise MemberLookupFailFast( - f"[fast-fail] 属性 '{field_name}' 在类 '{ClassName}' 中找不到" - f"(class_members={len(_members_diag)} 个: {_member_names_diag}, struct={_struct_diag})" - f"{_extra_diag}" - ) - - def _resolve_class_for_var(self, var_name: str) -> str | None: - for ClassName in self.class_methods: - if var_name == ClassName or var_name.startswith(f"__with_{ClassName}_"): - return ClassName - if var_name in self.variables: - var: ir.Value = self.variables[var_name] - if isinstance(var.type, ir.PointerType): - for ClassName, struct_type in self.structs.items(): - if var.type.pointee == struct_type: - return ClassName - if isinstance(var.type.pointee, ir.PointerType) and var.type.pointee.pointee == struct_type: - return ClassName - return None - - def _infer_return_type(self, FuncName: str) -> ir.Type: - if FuncName in self.functions: - return self.functions[FuncName].type.pointee.return_type - # 也尝试混淆后的名称 - mangled: str = self._mangle_func_name(FuncName) - if mangled != FuncName and mangled in self.functions: - return self.functions[mangled].type.pointee.return_type - if FuncName in self.known_return_types: - return self.known_return_types[FuncName] - if mangled != FuncName and mangled in self.known_return_types: - return self.known_return_types[mangled] - for ClassName, methods in self.class_methods.items(): - for method in methods: - if FuncName == method: - return ir.VoidType() - if self._import_handler_ref: - try: - # 先用原始名称查找,再用混淆名称查找 - stub_func_type: Any = self._import_handler_ref._LookupStubFuncType(FuncName, self) - if stub_func_type is None and mangled != FuncName: - stub_func_type = self._import_handler_ref._LookupStubFuncType(mangled, self) - if stub_func_type and hasattr(stub_func_type, 'return_type'): - return stub_func_type.return_type - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"查找桩函数返回类型失败: {_e}", "Exception") - return ir.IntType(32) - - def _get_or_declare_func(self, name: str, func_type: ir.FunctionType) -> ir.Function: - for fname, fobj in self.functions.items(): - if fname == name: - return fobj - if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): - return fobj - # module.globals 里可能已有同名函数而 Gen.functions 未登记(如 - # BaseGen._get_or_declare_function 的历史路径):直接新建必抛 llvmlite - # DuplicatedNameError(NameScope 按名去重)⇒ 统一走幂等入口。 - return self._CreateOrReuseFuncDecl(name, func_type) - - def get_or_declare_c_func(self, name: str, fallback_type: ir.FunctionType | None = None) -> ir.Function | None: - if name in self.functions: - return self.functions[name] - for fname, fobj in self.functions.items(): - if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): - return fobj - stub_func_type: ir.FunctionType | None = None - if self._import_handler_ref: - try: - stub_func_type = self._import_handler_ref._LookupStubFuncType(name, self) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"查找桩函数类型失败: {_e}", "Exception") - if stub_func_type: - return self._CreateOrReuseFuncDecl(name, stub_func_type) - if fallback_type is not None: - return self._CreateOrReuseFuncDecl(name, fallback_type) + 才有变体层; + ② 变体类键固定为 `<主类短名>_<变体名>`,且成员表**首项恒为 `__tag`** + (唯一产源:HandlesClassDef._EmitREnumLlvm:1659-1673 与 HandlesImports + 的 REnum 跨模块注册 :1975-2015); + ③ 恰有**一个**变体声明该字段(多个变体同名字段 ⇒ 无法判定 ⇒ None,由调用方 + 照常 fail-fast); + ④ 该变体键的 struct 已注册(布局可用)。 + + 用变体键算偏移为什么正确:变体 struct 的 body 是**主 REnum 镜像** + `{i32 __tag, }`,而变体成员表 `[__tag] + 载荷` 与主 REnum + 槽位一一对应 ⇒ `base + 表内下标` 即真实槽位。 + 现场(2026-09-30):CallGenDispatch.vp:787 `len_val.Ty.Pointee` —— 'Pointee' 只属 + 变体键 'LLVMType_Ptr',主类 'LLVMType' 的成员表为空(HandlesClassDef:716)。 + """ + if not ClassName or not field_name: + return None + _Short: str | None = self._extract_short_name(ClassName) + if not _Short: + return None + if not self._IsRenumLayoutClass(_Short): + return None + _Owner: str | None = None + _Prefix: str = f"{_Short}_" + for _Key, _Members in self.class_members.items(): + if not _Key.startswith(_Prefix): + continue + _List: list = _Members or [] + if not _List or _List[0][0] != '__tag': + continue + if not any(_n == field_name for _n, _ in _List): + continue + if _Key not in self.structs: + continue + if _Owner is not None: + return None + _Owner = _Key + return _Owner + + def _MemberSlotIndex(self, Members: list, FieldName: str) -> int: + """字段在成员表里的**槽位下标**(重名条目**不占槽位**;找不到返回 -1)。 + + 与 `_get_member_offset` 的配对路径同口径:重名条目是产物,不是布局槽位 + —— 源码层面的字段名遮蔽已由 HandlesClassDef 的"字段名遮蔽 fail-fast"拦下, + 其余重名只可能来自"展平残留 / 同一字段被两条收集路径各登记一次"。 + 所有"按表内下标 + base 求偏移"的站点都必须走本方法,否则含重名条目的表会让 + 偏移整体偏斜: + · `ast.ExceptHandler` 表 10 项、'children' 出现两次 ⇒ 'name' 原始下标 9 ⇒ 偏移 10(越界); + · `ast.MatchCase` 表 10 项 / 去重 9(body 10 槽)⇒ `_Delta == 0` ⇒ base 变 0 ⇒ + `children` 偏移 6 落到 `end_col_offset`(i32)。 + 重名条目在**首个同名(正本)之后**才出现,故按"首个同名的去重下标"取值。 + """ + _Slot: int = 0 + _Seen: set = set() + for _n, _ in (Members or []): + if _n in _Seen: + continue + _Seen.add(_n) + if _n == FieldName: + return _Slot + _Slot += 1 + return -1 + + def _get_member_offset(self, field_name: str, ClassName: str | None = None, + StructHint: Any = None) -> int | None: + # base 判定用未归一的原始类名:_member_base 内部自行做同一套归一 + # (_NormalizeMemberClass),二者结果逐字一致(避免二次归一在 + # "rawptr[X] 展开命中 sha1 前缀 struct 键"这一角落改变取值)。 + _MemberBaseKey: str | None = ClassName + # 治本修复:同名类 sha1 冲突检测。 + # 当 class_sha1_map[ClassName] 的 sha1 与 Gen.structs[ClassName] 的 sha1 不一致时, + # class_members[ClassName] 可能存储了错误模块的同名类成员。 + # 此处检测冲突,加载正确模块的 class_members 并以 sha1 前缀全名缓存。 + _ResolvedClass: str = ClassName if ClassName else '' + if ClassName: + # 类键归一(短名 + rawptr[X]/t.CPtr[X] 展开)统一走 _NormalizeMemberClass, + # 与 _member_base 的 base 判定共用同一归一,禁止各写一份。 + ClassName = self._NormalizeMemberClass(ClassName) + _ResolvedClass = ClassName + _ClassSha1Map: dict[str, str] = getattr(self, 'class_sha1_map', {}) + _ExpectedSha1: str | None = _ClassSha1Map.get(ClassName) + if _ExpectedSha1: + _ExistingStruct = self.structs.get(ClassName) + if isinstance(_ExistingStruct, ir.IdentifiedStructType): + _ExistingSha1: str = self._extract_struct_sha1(_ExistingStruct) + if _ExistingSha1 and _ExistingSha1 != _ExpectedSha1: + # sha1 冲突:使用全名查找 class_members + _FullKey: str = f"{_ExpectedSha1}.{ClassName}" + if _FullKey in self.class_members and self.class_members[_FullKey]: + _ResolvedClass = _FullKey + else: + # 从正确 stub 加载 class_members + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + _StubName: str = f"{_ExpectedSha1}.stub.ll" + # 临时保存旧值,清除短名条目以绕过 guard,加载后恢复 + _OldMembers = self.class_members.get(ClassName) + self.class_members[ClassName] = [] + try: + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, _StubName, self) + except Exception: + pass + _NewMembers = self.class_members.get(ClassName) + if _NewMembers: + self.class_members[_FullKey] = _NewMembers + _ResolvedClass = _FullKey + # 恢复旧值(避免影响其他代码对短名的依赖) + if _OldMembers: + self.class_members[ClassName] = _OldMembers + else: + self.class_members.pop(ClassName, None) + # base(vtable 占位槽数):唯一真源 = _member_base(规则与文档见该方法)。 + # 本函数在同一次调用内还会在下方多处重载成员表(懒加载 425-436、模块重载 + # 482-508、字段预注册 558-578、force 重建 587-643),每次都可能改写 + # _member_base 的三个输入(class_parent 父链、structs 布局、class_members + # 长度)。因此 base 与"即将返回的下标 i"必须取自**同一份最新已加载状态**: + # 所有 return 一律用 _BaseNow() 现算,禁止缓存早先算出的 base——缓存会让 + # 偏移整体差 1(ast.ExceptHandler.children 落到 end_col_offset(i32), + # len 分派拿到 i32)。 + def _BaseNow() -> int: + return self._member_base(_MemberBaseKey, StructHint) + # 预热(与原行为逐字一致):先触发一次 EnsureClassMembersFlattened, + # 再决定下面的懒加载。 + _BaseNow() + if ClassName and ClassName not in self.class_members and self._Trans and hasattr(self._Trans, 'ImportHandler'): + # 跨模块 class_members 懒加载:struct 已从 stub 加载但 class_members + # 可能尚未从 .vpi 填充(vpi/stub 就绪时序不一致),主动按类 sha1 加载。 + _Sha1sForLoad: dict[str, str] = getattr(self, 'class_sha1_map', {}) + for _mod_name_lz, _mod_sha1_lz in _Sha1sForLoad.items(): + try: + self._Trans.ImportHandler._TryLoadClassMembersFromPyi( + ClassName, f"{_mod_sha1_lz}.stub.ll", self) + except Exception: + continue + if ClassName in self.class_members and len(self.class_members[ClassName]) > 0: + break + # 依赖补齐(上面的懒加载)后再确认一次展平(幂等);偏移基址由 _BaseNow() + # 在真正 return 处现算,不在此缓存。 + _BaseNow() + # ---- REnum 变体字段:主 REnum 不声明该字段时,改用它**唯一**的变体键继续 ---- + # 现场(2026-09-30):CallGenDispatch.vp:787 `len_val.Ty.Pointee` —— 'Pointee' 属 + # LLVMType 的 REnum 变体键 'LLVMType_Ptr',主类 'LLVMType' 成员表为空 ⇒ 旧路径 + # 直接 fast-fail "属性 'Pointee' 在类 'LLVMType' 中找不到" 终止整个编译。 + # 判据与槽位正确性见 _RenumVariantFieldOwner(变体 struct body = 主 REnum 镜像)。 + if field_name and ClassName: + _MainMembers: list = (self.class_members.get(_ResolvedClass) + or self.class_members.get(ClassName) or []) + if not any(_n == field_name for _n, _ in _MainMembers): + _VariantOwner: str | None = None + try: + _VariantOwner = self._RenumVariantFieldOwner(ClassName, field_name) + except Exception: + _VariantOwner = None + if _VariantOwner: + _RepRK: set = getattr(self, '_RenumVariantFieldReported', None) + if _RepRK is None: + _RepRK = set() + self._RenumVariantFieldReported = _RepRK + _RKey: tuple = (_VariantOwner, field_name) + if _RKey not in _RepRK: + _RepRK.add(_RKey) + _vlog().warning( + f"[REnum 变体字段] '{field_name}' 不在主 REnum 类 " + f"{_ResolvedClass!r} 中,按唯一变体类 '{_VariantOwner}' 取偏移" + f"(变体 struct body = 主 REnum 镜像,槽位一一对应)", + "MemberBase") + ClassName = _VariantOwner + _ResolvedClass = _VariantOwner + _MemberBaseKey = _VariantOwner + _BaseNow() + # ---- 布局真源优先(配对式偏移):偏移只认"运行时真正被 GEP 的那份 struct body" ---- + # class_members 是**可变**表(继承展平会在父类就绪后前置前缀、跨模块可按需重载), + # 而 struct body 一旦 set_body 即**永久冻结** ⇒ "base + 成员表下标"会随表漂移 + # 整体平移(现场:Converter 模块读 `Alias.name` 取到槽 9(= asname), + # `import t` 无 as 子句 ⇒ asname=NULL ⇒ vpi 生成误报"import 别名 name 为空"; + # 同族旧现场:ast.Constant.lineno 取到 pool 槽)。改为两条"只看布局"的路径: + # ① 建 body 时冻结下来的"成员名 → 槽位"映射(StructGen._generate_structs 挂载); + # ② 用**类型**把成员表逐项配对到 elements:类型不匹配的条目视为方法/占位/漂移项, + # 不占槽位;配对中类型相等的那个槽位即该字段的真实槽位。 + # 表与布局自洽时两条路径给出与旧下标法**逐字相同**的结果(body 本按该表建); + # 都覆盖不到时落回旧下标法(对既有正确行为零变化)。 + if field_name: + _OffStruct: Any = self.structs.get(ClassName) + if isinstance(StructHint, ir.IdentifiedStructType) and ClassName: + # 与 _member_base 同口径:只接受**同类**的 hint(接收者静态类型可能是基类, + # 实际指向派生类实例,其布局不属于本类)。 + if self._extract_short_name(getattr(StructHint, 'name', '') or '') == ClassName: + _OffStruct = StructHint + if isinstance(_OffStruct, ir.IdentifiedStructType): + _OffEls: Any = _OffStruct.elements + _Frozen: Any = getattr(_OffStruct, '_tpc_member_slots', None) + # 冻结表**有效性**判定:表挂在 struct 实例上,而 body 会被"不完整即清除" + # (StructGen 预检 `existing_st.elements = None`)与 stub 加载 + # (HandlesImports._TryLoadStructFromStub 的 set_body)**清空后重设**, + # 重设路径不更新本表 ⇒ 陈旧表把字段指到别的槽。现场:AnnAssign 在 + # "自有 4 字段 + vtable"(5 槽:i8*,AST*,AST*,AST*,i32)时冻结 + # {'value': 3},随后 body 被重设为 12 槽正确布局(value 在 10), + # 查询仍读槽 3(lineno, i32)⇒ `.kind()` 虚分派 GEP 的操作数是 i32。 + # 判据 = 建表时记下的 **body 对象身份**(set_body 每次赋新 tuple): + # 与当前 elements 非同一对象即视为失效,落到按布局求槽的路径。 + _FrozenFresh: bool = (isinstance(_Frozen, dict) + and getattr(_OffStruct, '_tpc_member_body', None) is _OffEls) + if _FrozenFresh and field_name in _Frozen: + _FrozenSlot: Any = _Frozen[field_name] + if isinstance(_FrozenSlot, int) and (not _OffEls or 0 <= _FrozenSlot < len(_OffEls)): + return _FrozenSlot + _OffMembers: list = self.class_members.get(_ResolvedClass) or [] + _BitFields: Any = getattr(self, 'class_member_bitfields', None) + if _OffEls and _OffMembers and not (_BitFields and _BitFields.get(ClassName)): + # ---- 布局真源之二:两种对齐候选按"逐项类型可写入"**打分**取高者 ---- + # 成员表可能是①展平表(含父类前缀)②仅自有字段(父类前缀未补齐 ⇒ 真身是 + # body **尾部**)③残缺前缀(如仅 ['Gen'] 一项 ⇒ 真身在 body **头部**)。 + # 只用"长度关系/父类槽数"来断言①③是猜,已被两轮现场打脸 + # (Generator.vp:136 TypeGenMixin 被锚到槽 40;:164 TypeGenErr 因恰好 + # 只有 1 个自有字段、`body-len(表) == 父类槽数` 巧合成立而被锚到槽 41)。 + # 改为**比较**两个候选锚(前缀锚 `_BaseNow()` vs 尾部锚 `len(body)-len(表)`) + # 的逐项得分(判据与建 body/存储侧同一套 `_MemberSlotAccepts`): + # 只有尾部锚**严格更高分**才采用它,同分/更低一律保持原行为 + # (配对 → 下标法),因此对既有正确布局零风险。 + _ElemN: int = len(_OffEls) + _AnchorA: int = _BaseNow() + _AnchorB: int = _ElemN - len(_OffMembers) + _ScoreA: int = 0 + _aA: int = 0 + while _aA < len(_OffMembers): + if _AnchorA + _aA >= _ElemN: + break + _nA, _tA = _OffMembers[_aA] + if self._MemberSlotAccepts(_tA, _OffEls[_AnchorA + _aA]): + _ScoreA += 1 + _aA += 1 + _ScoreB: int = 0 + if _AnchorB >= 0 and _AnchorB != _AnchorA and not self._IsRenumLayoutClass(_ResolvedClass): + _aB: int = 0 + while _aB < len(_OffMembers): + if _AnchorB + _aB >= _ElemN: + break + _nB, _tB = _OffMembers[_aB] + if self._MemberSlotAccepts(_tB, _OffEls[_AnchorB + _aB]): + _ScoreB += 1 + _aB += 1 + if _ScoreB > _ScoreA and _ScoreB > 0: + _TIdx: int = -1 + for _i5, (_n5, _) in enumerate(_OffMembers): + if _n5 == field_name: + _TIdx = _i5 + break + if _TIdx >= 0: + # 每类只留一次痕(大类的每个字段都会命中本路径) + _TailKey: str = _ResolvedClass + _TailRep: set = getattr(self, '_MemberTailAlignedReported', None) + if _TailRep is None: + _TailRep = set() + self._MemberTailAlignedReported = _TailRep + if _TailKey not in _TailRep: + _TailRep.add(_TailKey) + _vlog().warning( + f"[成员偏移] 类 '{_ResolvedClass}' 的成员表({len(_OffMembers)} 项)" + f"在 body({_ElemN} 槽)上按**尾部对齐**得分更高" + f"(尾部锚 {_AnchorB} 得 {_ScoreB} vs 前缀锚 {_AnchorA} 得 {_ScoreA})" + f"⇒ 判定该表缺的是**父类前缀**:字段 '{field_name}' = " + f"{_AnchorB + _TIdx}", + "MemberBase") + return _AnchorB + _TIdx + _Cursor: int = _BaseNow() + _PairedNames: set = set() + for _nmP, _mtP in _OffMembers: + if _Cursor >= _ElemN: + break + # 重名条目不是布局槽位(表构造/重载产生的重复项)⇒ 不占槽位 + if _nmP in _PairedNames: + continue + # None 成员在 body 里被物化为 i32(见 StructGen 建 body 分支), + # 配对类型必须同口径,否则该类字段会被整条跳过。 + _mtEff: Any = _mtP if _mtP is not None else ir.IntType(32) + _etP: Any = _OffEls[_Cursor] + _PairOk: bool = (_mtEff is _etP or str(_mtEff) == str(_etP)) + if (not _PairOk and isinstance(_mtEff, ir.PointerType) + and isinstance(_etP, ir.PointerType)): + # 联合类型字段在成员表里存成通用指针(如 AST.children = + # `list[rawptr[AST]] | t.CPtr` ⇒ i8*),而 body 槽是具体指针 + # (`%"...list[...]"*`):严格串比较会在这条**断链**,其后的 + # 所有条目都不再消费槽位(cursor 不动)⇒ 目标字段全部退化成 + # "base + 表内下标"(现场:ExceptHandler.name 表内下标 9 ⇒ + # 偏移 10,越界 fail-fast,而正确槽是 9)。判据与存储/建 body + # 侧同一套(_MemberSlotAccepts:两侧都是指针时按指针转换规则, + # i8* 通用指针放行);标量仍走严格串比较,不引入新的过配对。 + _PairOk = self._MemberSlotAccepts(_mtEff, _etP) + if _PairOk: + _PairedNames.add(_nmP) + if _nmP == field_name: + # 与旧下标法不一致时留痕:这正是"成员表与 struct 布局 + # 不自洽"的现场事实(每类每字段一次,便于定位漂移来源)。 + _OldIdx: int = -1 + for _i3, (_n3, _) in enumerate(_OffMembers): + if _n3 == field_name: + _OldIdx = _i3 + break + if _OldIdx >= 0 and _BaseNow() + _OldIdx != _Cursor: + _RepKey: tuple = (_ResolvedClass, field_name) + _RepSet: set = getattr(self, '_MemberSlotDriftReported', None) + if _RepSet is None: + _RepSet = set() + self._MemberSlotDriftReported = _RepSet + if _RepKey not in _RepSet: + _RepSet.add(_RepKey) + _vlog().warning( + f"[成员偏移] 类 '{_ResolvedClass}' 的成员表与 struct " + f"布局不自洽:字段 '{field_name}' 下标法={_BaseNow() + _OldIdx}" + f"(成员表 {len(_OffMembers)} 项) vs 布局配对比={_Cursor}" + f"(struct {_ElemN} 槽)——已采用布局配对结果", + "MemberBase") + return _Cursor + _Cursor += 1 + if _ResolvedClass and _ResolvedClass in self.class_members: + # 只有走到这里(布局配对未命中)才做一次重名检查:重名条目**不占布局槽**, + # 却会让 `base + 表内下标` 整体后移(现场:ExceptHandler 表 10 项、其中 + # 'children' 出现两次 ⇒ 'name' 下标 9 ⇒ 偏移 10 越界)。此处把事实报出来 + # (每类一次),并标注该返回值取自回退路径、可能失准。 + _MemList: list = self.class_members[_ResolvedClass] + _SeenNm: set = set() + _DupNm: list[str] = [] + for _nmC, _ in _MemList: + if _nmC in _SeenNm: + if _nmC not in _DupNm: + _DupNm.append(_nmC) + else: + _SeenNm.add(_nmC) + if _DupNm: + _DupRep: set = getattr(self, '_MemberDupNamesReported', None) + if _DupRep is None: + _DupRep = set() + self._MemberDupNamesReported = _DupRep + if _ResolvedClass not in _DupRep: + _DupRep.add(_ResolvedClass) + _vlog().warning( + f"[成员偏移] 类 '{_ResolvedClass}' 的成员表含**重名条目** " + f"{_DupNm!r}(表 {len(_MemList)} 项 / 去重 {len(_SeenNm)} 项)——" + f"重名项不是布局槽位,`base + 表内下标` 会因此整体后移;" + f"本次字段 '{field_name}' 走的是回退路径(未被 body 配对命中)," + f"返回值可能失准。重名来源通常是「同一字段被两条收集路径各登记一次" + f"(类内 AnnAssign 声明 + 方法体 self.x 赋值)」或继承前缀重复前置。", + "MemberBase") + # 回退取值也按"重名条目**不占槽位**"算(与上方配对路径同口径):直接取 + # `base + 原始下标` 会把重名条目也算进槽位,整体后移(现场:ExceptHandler + # 表 10 项、'children' 出现两次 ⇒ 'name' 原始下标 9 ⇒ 偏移 10 越界)。 + _SlotIdx: int = 0 + _Counted: set = set() + for name, _ in _MemList: + if name in _Counted: + continue + _Counted.add(name) + if name == field_name: + return _BaseNow() + _SlotIdx + _SlotIdx += 1 + # 继承链回退:当字段不在当前类的 class_members 中时(常见于跨模块导入 + # 父类未加载,字段被填充为 __inherited_N 占位名),沿 class_parent 链向上查找。 + # 子类 struct 布局为 [vtable?] + [基类字段...] + [子类字段...], + # 基类字段在子类 struct 中的起始偏移即为 base(子类), + # 因此祖先 class_members 中的索引 i 对应子类 struct 中的偏移 base + i。 + if ClassName and field_name: + _parent: str | None = self.class_parent.get(ClassName) + _visited: set = {ClassName} if ClassName else set() + _cls_short: str = ClassName.split('.')[-1] if ClassName else '' + while _parent and _parent not in _visited: + _visited.add(_parent) + # 父类候选键:class_parent 只登记**短名**(含泛型基类时是模板名, + # 如 'GSListNode'),而 class_members 里真实键可能是**实例化名** + # ('GSListNode[GlobalVariable]',见 includes/llvmlite/__module.vp + # `class GlobalVariable(GSListNode[GlobalVariable])`)或模板名。 + # 少这一步 ⇒ 泛型基类字段(Next/Prev 等)沿链永远查不到 → fast-fail + # ("属性 'Next' 在类 'GlobalVariable' 中找不到")。 + _p_tmpl: str = _parent.split('[')[0] + _cand_keys: list = [] + for _k in (_parent, _p_tmpl, f"{_p_tmpl}[{_cls_short}]" if _cls_short else _p_tmpl): + if _k and _k not in _cand_keys: + _cand_keys.append(_k) + # 跳过 __inherited_N 占位条目,只在真实字段名中查找 + _hit_idx: int = -1 + for _k in _cand_keys: + if _k in self.class_members: + _members: list = self.class_members[_k] + _has_placeholder: bool = any(n.startswith('__inherited_') for n, _ in _members) + if not _has_placeholder: + _hit_idx = self._MemberSlotIndex(_members, field_name) + if _hit_idx >= 0: + break + if _hit_idx >= 0: + # 基类字段位于子类 struct 的 base 起始处,索引 i 即基类内偏移 + return _BaseNow() + _hit_idx + # 父类 class_members 未加载或含占位条目,尝试从 stub 加载真实字段 + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) + for _k in _cand_keys: + for mod_name, mod_sha1 in sha1_map.items(): + stub_name: str = f"{mod_sha1}.stub.ll" + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(_k, stub_name, self) + if _k in self.class_members and not any(n.startswith('__inherited_') for n, _ in self.class_members[_k]): + _pIdx: int = self._MemberSlotIndex(self.class_members[_k], field_name) + if _pIdx >= 0: + return _BaseNow() + _pIdx + break + if _k in self.class_members and any(n == field_name for n, _ in self.class_members.get(_k, [])): + break + _parent = self.class_parent.get(_parent) + short_name: str | None = ClassName.split('.')[-1] if ClassName and '.' in ClassName else None + if short_name and short_name in self.class_members: + _sIdx: int = self._MemberSlotIndex(self.class_members[short_name], field_name) + if _sIdx >= 0: + return _BaseNow() + _sIdx + if ClassName and field_name: + if ClassName not in self.class_members or field_name not in [n for n, _ in self.class_members.get(ClassName, [])]: + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + sha1_map: dict[str, str] = getattr(self, 'ModuleSha1Map', {}) + for mod_name, mod_sha1 in sha1_map.items(): + stub_name: str = f"{mod_sha1}.stub.ll" + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(ClassName, stub_name, self) + if ClassName in self.class_members and len(self.class_members[ClassName]) > 0: + _rIdx: int = self._MemberSlotIndex(self.class_members[ClassName], field_name) + if _rIdx >= 0: + return _BaseNow() + _rIdx + break + if short_name: + for mod_name, mod_sha1 in sha1_map.items(): + stub_name: str = f"{mod_sha1}.stub.ll" + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_name, stub_name, self) + if short_name in self.class_members and len(self.class_members[short_name]) > 0: + _rIdx2: int = self._MemberSlotIndex(self.class_members[short_name], field_name) + if _rIdx2 >= 0: + return _BaseNow() + _rIdx2 + break + if ClassName and field_name: + for cn_key in self.class_members: + if cn_key == ClassName or cn_key.endswith(f'.{ClassName}') or (short_name and (cn_key == short_name or cn_key.endswith(f'.{short_name}'))): + _cIdx: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) + if _cIdx >= 0: + return _BaseNow() + _cIdx + # REnum 变体搜索: ClassName 是 REnum(如 MetaType),成员注册在变体结构 + # (MetaType_Var, MetaType_Concrete, MetaType_App)中。变体与 REnum 主结构 + # 共享 max_variant_struct 布局,偏移一致(__tag 在偏移 0,字段从偏移 1 开始) + if ClassName and field_name: + variant_prefix: str = f"{ClassName}_" + for cn_key in self.class_members: + if cn_key.startswith(variant_prefix): + _vIdx: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) + if _vIdx >= 0: + return _vIdx + if short_name: + variant_prefix = f"{short_name}_" + for cn_key in self.class_members: + if cn_key.startswith(variant_prefix): + _vIdx2: int = self._MemberSlotIndex(self.class_members[cn_key], field_name) + if _vIdx2 >= 0: + return _vIdx2 + if ClassName and ClassName in self.structs: + struct_type: ir.IdentifiedStructType = self.structs[ClassName] + if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None and len(struct_type.elements) > 0: + for cn_key, members in self.class_members.items(): + if cn_key == short_name or (short_name and cn_key.endswith(f'.{short_name}')): + _kIdx: int = self._MemberSlotIndex(members, field_name) + if 0 <= _kIdx < len(struct_type.elements): + return _kIdx + break + else: + if short_name: + for cn_key, members in self.class_members.items(): + sn: str = cn_key.split('.')[-1] if '.' in cn_key else cn_key + if sn == short_name: + _kIdx2: int = self._MemberSlotIndex(members, field_name) + if 0 <= _kIdx2 < len(struct_type.elements): + return _kIdx2 + break + if ClassName and field_name and ClassName not in self.class_members: + if self._Trans and hasattr(self._Trans, 'ImportHandler'): + for temp_dir_candidate in [getattr(self, '_temp_dir', None)]: + if temp_dir_candidate and os.path.isdir(temp_dir_candidate): + for pyi_file in os.listdir(temp_dir_candidate): + if pyi_file.endswith('.vpi') or pyi_file.endswith('.pyi'): + stub_name: str = pyi_file.replace('.vpi', '.stub.ll').replace('.pyi', '.stub.ll') + short_cn: str = short_name if short_name else ClassName + self._Trans.ImportHandler._TryLoadClassMembersFromPyi(short_cn, stub_name, self) + if short_cn in self.class_members and len(self.class_members[short_cn]) > 0: + _scIdx: int = self._MemberSlotIndex(self.class_members[short_cn], field_name) + if _scIdx >= 0: + return _BaseNow() + _scIdx + break + if ClassName and ClassName in self.structs and (ClassName not in self.class_members or len(self.class_members.get(ClassName, [])) == 0): + struct_type: ir.IdentifiedStructType = self.structs[ClassName] + if isinstance(struct_type, ir.IdentifiedStructType) and struct_type.elements is not None: + if self._Trans and hasattr(self._Trans, 'ClassHandler'): + class_handler: Any = self._Trans.ClassHandler + if self._current_tree: + for node in ast.iter_child_nodes(self._current_tree): + if isinstance(node, ast.ClassDef) and (node.name == ClassName or node.name == short_name): + if node.name not in self.class_members: + self.class_members[node.name] = [] + if len(self.class_members[node.name]) == 0: + class_handler._PreRegisterClassMembers(node, self) + if ClassName in self.class_members: + _prIdx: int = self._MemberSlotIndex(self.class_members[ClassName], field_name) + if _prIdx >= 0: + return _BaseNow() + _prIdx + if short_name and short_name in self.class_members: + _prIdx2: int = self._MemberSlotIndex(self.class_members[short_name], field_name) + if _prIdx2 >= 0: + return _BaseNow() + _prIdx2 + break + # 最后回退:当继承链回退失败但 struct body 已加载时, + # 从 struct elements 数和 class_members 数推断继承字段偏移。 + # 继承字段在 struct 开头(base 之后),此 fallback 仅适用于单继承字段的情况 + # (如 GSListNode[T] 的 Next 字段)。子类 struct 布局: + # [vtable?] + [基类字段...] + [子类字段...] + # 当 class_members 只含子类自身字段、基类 class_members 未加载时, + # 继承字段数 = struct_elements - class_members_count, + # 若该值 == 1,则所查字段(如 Next)的偏移 = base + 0。 + if ClassName and field_name: + _st_final = self.structs.get(ClassName) + if isinstance(_st_final, ir.IdentifiedStructType) and _st_final.elements is not None: + _num_elements_final: int = len(_st_final.elements) + _members_final: list = self.class_members.get(ClassName, []) + # 与 _member_base 的长度校准同口径:重名条目**不是布局槽位** ⇒ 按去重名数算 + _num_members_final: int = len({_Rn for _Rn, _ in _members_final}) + if _num_elements_final > _num_members_final: + # base 是 vtable 占位(struct[0] = i8*),不是继承字段,必须扣除: + # 不扣 base 时,凡"struct 元素数恰好多 1"的类,差值其实只是 vtable + # 本身(class_members 不含 vtable),却会对任意字段名返回 base+0。 + # 实例:ast.ImportFrom 的 level/module → 静默返回偏移 1(=parent, + # %"…AST"*)→ 调用点把 AST* 当 i32 传入 _resolve_relative_module + # → llvmlite "Type of #4 arg mismatch: i32 != %"…AST"*"。 + # 成员表与 struct 不一致(少一个非 vtable 槽)时,先强制按定义模块 + # 的 .vpi 重载一次成员表再按名查:_TryLoadClassMembersFromPyi 的 guard + # 会永久跳过"已存在但不完整"的表,此处即该冻结的解除点。重载后仍缺 + # 才走下方推断/fast-fail。每类只重载一次(避免递归与重复 IO)。 + _ReloadKey: tuple = (id(self), ClassName) + _ReloadTried: set = getattr(self, '_MemberReloadTried', None) + if _ReloadTried is None: + _ReloadTried = set() + self._MemberReloadTried = _ReloadTried + if _ReloadKey not in _ReloadTried: + _ReloadTried.add(_ReloadKey) + _Sha1Final: str | None = getattr(self, 'class_sha1_map', {}).get(ClassName) + if _Sha1Final and self._Trans and hasattr(self._Trans, 'ImportHandler'): + _OldMembers = _members_final + try: + self._Trans.ImportHandler._TryLoadClassMembersFromPyi( + ClassName, f"{_Sha1Final}.stub.ll", self, force=True) + except Exception: + pass + _ReloadedMembers: list = self.class_members.get(ClassName, []) + if len(_ReloadedMembers) < len(_OldMembers): + # 重载反而变少(.vpi 缺该类):回滚,避免丢字段 + self.class_members[ClassName] = _OldMembers + _ReloadedMembers = _OldMembers + _members_final = _ReloadedMembers + _num_members_final = len({_Rn for _Rn, _ in _members_final}) + _rfIdx: int = self._MemberSlotIndex(_members_final, field_name) + if _rfIdx >= 0: + return _BaseNow() + _rfIdx + _num_inherited_final: int = _num_elements_final - _num_members_final - _BaseNow() + if _num_inherited_final == 1: + # 推断(缺失槽位 = 位于 base+0 的未展平继承字段)仅在成员表 + # 尚未含展平继承字段时成立:若成员表首项本身就是任一已加载 + # 祖先的字段名(如 ImportFrom 'parent' ← Stmt),说明继承字段 + # 已展平、缺的是类自身字段,推断 base+0 必然读错槽位。 + _ParentNamesFinal: set = set() + for _ParentF in (getattr(self, 'class_parents', {}).get(ClassName) or []): + for _Np, _Tp in self.class_members.get(_ParentF, []) or []: + _ParentNamesFinal.add(_Np) + if _members_final and _members_final[0][0] in _ParentNamesFinal: + pass # 已含展平继承字段 → 落入下方 fast-fail 暴露真实缺失 + else: + # 单继承字段,偏移 = base(vtable 之后第一个位置) + return _BaseNow() + 0 + # fast-fail(治本修复 2026-08-10):字段未找到不再静默返回 None。 + # 静默 None 会让调用方(_gep_and_Load_member L26-27)直接 return None, + # AnnAssign InitValue 变 None → 变量 alloca 未初始化 → 运行期读垃圾指针 + # (如 HandlesImports.HandleImport 的 imp.names → 0xffffffffffffffff 崩溃)。 + # 抛出编译错误,暴露 class_members/struct 的真实状态便于定位根因。 + _members_diag: list = self.class_members.get(ClassName, []) if ClassName else [] + _struct_diag: str = 'not-registered' + if ClassName and ClassName in self.structs: + _st_diag = self.structs[ClassName] + if isinstance(_st_diag, ir.IdentifiedStructType): + _struct_diag = f"{len(_st_diag.elements)} elems" if _st_diag.elements is not None else 'opaque' + _member_names_diag: list = [n for n, _ in _members_diag][:16] + # ---- 取证(只读,不改变行为,2026-09-30)---- + # 现场:CallGenDispatch.vp:787 `len_val.Ty.Pointee` 里 `.Pointee` 被判成在类 + # 'ParamNode'(2 字段 Next/Ty)里找 —— 而 'Pointee' 本属 LLVMType 的 REnum + # 变体镜像字段。要一次定位"是谁把接收者记成了 ParamNode",报文需给出: + # ① 本类键 → struct 实例 IR 名(键/实例名不一致即"同名两份实例"); + # ② **真正声明了该字段**的类键(唯一者即"本该用的类"); + # ③ var_struct_class 里被登记为**本类**的变量(封顶 6 个)及其实际 pointee + # IR 名 —— 两者不一致即"记录类名与实际 IR 类型不符",直接圈定记录点。 + _extra_diag: str = '' + try: + _st_key = self.structs.get(ClassName) if ClassName else None + if isinstance(_st_key, ir.IdentifiedStructType): + _extra_diag += (f"\n 本类键 '{ClassName}' → struct 实例 IR 名 " + f"'{getattr(_st_key, 'name', '?')}'") + _owners: list = [] + for _ck2, _cm2 in self.class_members.items(): + if _ck2 == ClassName: + continue + if any(_n2 == field_name for _n2, _ in (_cm2 or [])): + _owners.append(_ck2) + _extra_diag += (f"\n 声明了 '{field_name}' 的其它类键({len(_owners)} 个): " + f"{_owners[:8]}{' …' if len(_owners) > 8 else ''}") + _vsc: dict = getattr(self, 'var_struct_class', {}) or {} + _vars_here: list = [v for v, cn in _vsc.items() if cn == ClassName] + if _vars_here: + _vdesc: list = [] + for _v2 in _vars_here[:6]: + _pointee_desc: str = '?' + for _src3 in (getattr(self, 'variables', {}) or {}, + getattr(self, '_reg_values', {}) or {}, + getattr(self, '_direct_values', {}) or {}): + _val3 = _src3.get(_v2) + if _val3 is not None and isinstance(getattr(_val3, 'type', None), ir.PointerType): + _pointee_desc = str(_val3.type.pointee) + break + _vdesc.append(f"{_v2}(pointee={_pointee_desc})") + _extra_diag += (f"\n var_struct_class 登记为 '{ClassName}' 的变量(" + f"{len(_vars_here)} 个,示前 6): {_vdesc}") + except Exception as _e_diag: + _extra_diag += f"\n (取证附加信息生成失败: {type(_e_diag).__name__}: {_e_diag})" + # 异常类型:MemberLookupFailFast(KeyError 子类 + 裸消息 __str__)—— + # 既保留 `except KeyError` 兜底点,又让报文不再被 repr 成"带引号 + \n 转义" + # 的不可读形式(原有 traceback.print_stack 调试转储一并移除,保持输出干净)。 + raise MemberLookupFailFast( + f"[fast-fail] 属性 '{field_name}' 在类 '{ClassName}' 中找不到" + f"(class_members={len(_members_diag)} 个: {_member_names_diag}, struct={_struct_diag})" + f"{_extra_diag}" + ) + + def _resolve_class_for_var(self, var_name: str) -> str | None: + for ClassName in self.class_methods: + if var_name == ClassName or var_name.startswith(f"__with_{ClassName}_"): + return ClassName + if var_name in self.variables: + var: ir.Value = self.variables[var_name] + if isinstance(var.type, ir.PointerType): + for ClassName, struct_type in self.structs.items(): + if var.type.pointee == struct_type: + return ClassName + if isinstance(var.type.pointee, ir.PointerType) and var.type.pointee.pointee == struct_type: + return ClassName + return None + + def _infer_return_type(self, FuncName: str) -> ir.Type: + if FuncName in self.functions: + return self.functions[FuncName].type.pointee.return_type + # 也尝试混淆后的名称 + mangled: str = self._mangle_func_name(FuncName) + if mangled != FuncName and mangled in self.functions: + return self.functions[mangled].type.pointee.return_type + if FuncName in self.known_return_types: + return self.known_return_types[FuncName] + if mangled != FuncName and mangled in self.known_return_types: + return self.known_return_types[mangled] + for ClassName, methods in self.class_methods.items(): + for method in methods: + if FuncName == method: + return ir.VoidType() + if self._import_handler_ref: + try: + # 先用原始名称查找,再用混淆名称查找 + stub_func_type: Any = self._import_handler_ref._LookupStubFuncType(FuncName, self) + if stub_func_type is None and mangled != FuncName: + stub_func_type = self._import_handler_ref._LookupStubFuncType(mangled, self) + if stub_func_type and hasattr(stub_func_type, 'return_type'): + return stub_func_type.return_type + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"查找桩函数返回类型失败: {_e}", "Exception") + return ir.IntType(32) + + def _get_or_declare_func(self, name: str, func_type: ir.FunctionType) -> ir.Function: + for fname, fobj in self.functions.items(): + if fname == name: + return fobj + if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): + return fobj + # module.globals 里可能已有同名函数而 Gen.functions 未登记(如 + # BaseGen._get_or_declare_function 的历史路径):直接新建必抛 llvmlite + # DuplicatedNameError(NameScope 按名去重)⇒ 统一走幂等入口。 + return self._CreateOrReuseFuncDecl(name, func_type) + + def get_or_declare_c_func(self, name: str, fallback_type: ir.FunctionType | None = None) -> ir.Function | None: + if name in self.functions: + return self.functions[name] + for fname, fobj in self.functions.items(): + if re.match(r'^[0-9a-f]{16,}\.' + re.escape(name) + r'$', fname): + return fobj + stub_func_type: ir.FunctionType | None = None + if self._import_handler_ref: + try: + stub_func_type = self._import_handler_ref._LookupStubFuncType(name, self) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"查找桩函数类型失败: {_e}", "Exception") + if stub_func_type: + return self._CreateOrReuseFuncDecl(name, stub_func_type) + if fallback_type is not None: + return self._CreateOrReuseFuncDecl(name, fallback_type) return None \ No newline at end of file diff --git a/lib/core/LLVMCG/MemoryOps.py b/lib/core/LLVMCG/MemoryOps.py index 1b609f5..4ffffcb 100644 --- a/lib/core/LLVMCG/MemoryOps.py +++ b/lib/core/LLVMCG/MemoryOps.py @@ -16,7 +16,7 @@ class _VolatileLoadInstr(ir.LoadInstr): `descr` 也不输出该关键字),故按本仓库既有做法(BaseGen.VaArgInstruction 自定义 `descr`)派生子类,只把 `load` 关键字扩成 `load volatile`。 输出格式与 llvmlite 原始实现逐字段一致(类型、指针、align、元数据), - 仅多一个 `volatile` 关键字;语义与 TransPyV 侧 `build_load(..., vol)` 对齐。 + 仅多一个 `volatile` 关键字;语义与 ViperC 侧 `build_load(..., vol)` 对齐。 """ def descr(self, buf: list[str]) -> None: @@ -147,7 +147,7 @@ class MemoryOpsMixin: # ============================================================ # 隐式转换公共判据:None(空值常量)/ 指针层级 / 基本类型 / 无损 # - # 语义唯一来源与 TransPyV 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。 + # 语义唯一来源与 ViperC 对齐(ExprGenBase.IsNoneValue、coerce_to_type)。 # 收敛于此,供赋值 / 返回 / 实参 / 数组元素等所有 coerce 入口复用,避免 # 各处各自演化出相反结论。 # ============================================================ @@ -197,7 +197,7 @@ class MemoryOpsMixin: @staticmethod def _ScalarConvertLossless(Src: Any, Dst: Any) -> bool: - """基本标量「无损」判定(与 TransPyV `_scalar_convert_reason` 同口径,逐条照搬): + """基本标量「无损」判定(与 ViperC `_scalar_convert_reason` 同口径,逐条照搬): - 整数 ↔ 整数:允许 ⇔ **目标位宽 ≥ 源位宽**(低→高无损;高→低是**有损窄化**,禁); - 浮点 ↔ 浮点:**所有方向都允许**; - 整数 → 浮点:允许 ⇔ **目标浮点位宽 ≥ 源整数位宽**(否则有损 → 禁); @@ -306,7 +306,7 @@ class MemoryOpsMixin: Node: Any = None) -> tuple[bool, str]: """隐式转换判据:返回 (Allowed, Reason)。 - 规则(用户决定,与 TransPyV 同口径): + 规则(用户决定,与 ViperC 同口径): ① 通用指针(t.CPtr/i8*)为通配:与任何 T* **双向**互转放行(须同级)。 ② T* → U*(皆非通用):允许 ⇔ 被指类型 T → U 无损;非基本类型 (结构体/类/联合)一律不允许 ⇒ Foo*→Bar* 禁止;**例外**有二: @@ -368,7 +368,7 @@ class MemoryOpsMixin: @staticmethod def _ValueNodeOf(Node: Any = None) -> Any: - """把「外层语句节点」下沉到其「值子表达式节点」(与 TransPyV _value_node_of 同口径)。 + """把「外层语句节点」下沉到其「值子表达式节点」(与 ViperC _value_node_of 同口径)。 Assign/AnnAssign/AugAssign/Expr/Return 取 .value;非语句节点原样返回。 规则③的「构造物化」豁免须看到**真正被转换的那个右值表达式**(如 `p: Point = Point()` @@ -392,7 +392,7 @@ class MemoryOpsMixin: def _IsFrameStackAddr(Node: Any, Gen: Any, TargetType: Any = None) -> bool: """AST 判据:Node 是否交出「当前帧栈上存储的地址」。 - 两种形态(与 TransPyV fail_if_stack_addr_escape 同口径): + 两种形态(与 ViperC fail_if_stack_addr_escape 同口径): (a) `&local`:UnaryOp(AddrOf, Name),且该 Name 是本帧局部 alloca(含形参); (b) 数组退让:裸本帧**数组**名出现在**指针**目标位置(如 `return arr` with `-> char*`), 交出的是首元素地址,同属栈地址。 @@ -695,7 +695,7 @@ class MemoryOpsMixin: val = self.builder.zext(val, ir.IntType(64), name="zext_ptr_store") return self.builder.inttoptr(val, target_type, name="int_to_ptr_store") if isinstance(target_type, ir.IntType) and isinstance(val.type, ir.PointerType): - # 指针 → 整数:隐式转换一律禁止(与 TransPyV coerce_to_type 同口径)。 + # 指针 → 整数:隐式转换一律禁止(与 ViperC coerce_to_type 同口径)。 # 原先三条路径都是静默的:同宽直接 load 指针所指物的位模式、≤8 位 load # 首字节、其余 ptrtoint 截断地址——把"类型写错"藏到运行期。 # None(空指针常量)已在上方短路为目标零值,不会落到这里。 diff --git a/lib/core/LLVMTypeMapper.py b/lib/core/LLVMTypeMapper.py index 9e5cd41..e3aa092 100644 --- a/lib/core/LLVMTypeMapper.py +++ b/lib/core/LLVMTypeMapper.py @@ -1,23 +1,23 @@ -"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器 - -从 SymbolTable 中提取的类型解析逻辑,职责单一: -将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。 - -原 SymbolTable 上的 4 个方法: - - _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str - - _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str - - _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str - - _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type -""" -from __future__ import annotations - -import vast as ast -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from lib.core.SymbolTable import SymbolTable - -from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap +"""LLVMTypeMapper — AST 注解节点到 LLVM IR 类型字符串的映射器 + +从 SymbolTable 中提取的类型解析逻辑,职责单一: +将 Python AST 注解节点翻译为 LLVM IR 类型字符串(如 'i32', 'i8*', '{ i32, i8* }')。 + +原 SymbolTable 上的 4 个方法: + - _GetLLVMTypeStr → LLVMTypeMapper.get_llvm_type_str + - _GetFuncRetTypeStr → LLVMTypeMapper.get_func_ret_type_str + - _GetFuncParamTypeStr → LLVMTypeMapper.get_func_param_type_str + - _ResolveTypedefValueType → LLVMTypeMapper.resolve_typedef_value_type +""" +from __future__ import annotations + +import vast as ast +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap from lib.core.SymbolUtils import ( IsTModule, IsListAnnotation, IsCPtrNode, IsCPtrSubscriptNode, IsRawPtrSubscriptNode, EnumIntWidth, @@ -110,347 +110,347 @@ class LLVMTypeMapper: resolved_b: str = self._ctype_to_llvm_str(entry) if resolved_b: return resolved_b - return '' - - # ================================================================== - # 公共 API - # ================================================================== - - def get_llvm_type_str(self, node: ast.AST | None) -> str: - """将 AST 注解节点转换为 LLVM IR 类型字符串""" - if node is None: - self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32") - return 'i32' - if isinstance(node, ast.Name): - return self._resolve_name(node) - elif isinstance(node, ast.Attribute): - return self._resolve_attribute(node) - elif isinstance(node, ast.Subscript): - return self._resolve_subscript(node) - elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - return self._resolve_bitor(node) - elif isinstance(node, ast.Constant): - if isinstance(node.value, bool): - return 'i8' - return 'i32' - elif isinstance(node, ast.Call): - return self._resolve_call(node) - self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32") - return 'i32' - - def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str: - """提取函数返回类型的 LLVM 字符串""" - if returns_node is None: - return 'i32' - if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr): - # 统一走 CPtr 感知的联合解析(int | t.CPtr → i32*、str | t.CPtr → i8**) - return self._resolve_bitor(returns_node) - result: str = self.get_llvm_type_str(returns_node) - return result if result else 'i32' - - def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str: - """提取函数参数类型的 LLVM 字符串。 - - 末尾的「不许静默变 i32」守卫:`Subscript` / 联合(`|`)形态的形参注解若解析结果 - 是 `'i32'`,只可能是**解析失败后的兜底** —— 合法的下标注解是容器 / 泛型 / 定长数组 - (解析出指针、数组或结构体指针),合法的联合写法是 `T | t.CPtr` 这类指针叠加,都 - 不会是 i32。该串会被 `_InsertFuncSymbol` 原样存进符号表(`FuncPtrParams`),再由 - `BuildLLVMFuncTypeFromSig` 物化成形参类型 —— 一旦是 i32,调用点就在 `_adjust_args` - 报「实参 %X* 不能隐式转为 i32」,把真因(**注解没解析出来**)掩盖成"调用点类型 - 不匹配"(现场:CallGenMixin.vp:149 调用 `_specialize_generic_class` 的 - `type_args: list[str]*`)。故此处响亮失败,并带上注解原文。 - """ - if annotation_node is None: - return 'i8*' - result: str - if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr): - # 统一走 CPtr 感知的联合解析 - result = self._resolve_bitor(annotation_node) - else: - result = self.get_llvm_type_str(annotation_node) - if not result: - result = 'i8*' - if result == 'i32' and isinstance(annotation_node, (ast.Subscript, ast.BinOp)): - _text: str - try: - _text = ast.unparse(annotation_node) - except Exception: - _text = f'<{type(annotation_node).__name__}>' - raise TypeNameUnresolvedError( - f"形参注解 '{_text}' 解析失败并落到 i32(下标 / 联合形态的合法结果应为" - f"指针、数组或结构体指针,不会是 i32)—— 拒绝静默降级为 i32") - return result - - def resolve_typedef_value_type(self, node: ast.AST | None) -> str: - """解析 typedef 值表达式的 C 类型字符串""" - if node is None: - return '' - if isinstance(node, ast.Name): - if node.id in self._symtab: - Entry: CTypeInfo = self._symtab[node.id] - if Entry.IsTypedef and Entry.OriginalType: - return Entry.OriginalType - # 短别名 / 内建速记(char/int/int64/uchar/str…):与注解位置 - # `_resolve_name` 同源。缺此分支时 typedef 值里的裸名解析为空, - # 整条 typedef 随之解析失败(现场:w32/win32base.vp 的 - # `LPCSTR: t.CTypedef = t.CConst | char | t.CPtr` ⇒ LPCSTR 不登记, - # 用到处报「裸名 LPCSTR 无法解析为类型」)。 - _binfo: CTypeInfo | None = self._lookup_builtins_typedef(node.id) - if _binfo is None: - _e: tuple[type, int] | None = BuiltinTypeMap.Get(node.id) - if _e: - _binfo = CTypeInfo() - _binfo.BaseType = _e[0] - _binfo.PtrCount = _e[1] - if _binfo is not None and _binfo.BaseType: - _bt: object = _binfo.BaseType - _nm: str = (_bt if isinstance(_bt, str) - else CTypeHelper.GetCName(_bt) if isinstance(_bt, type) - else type(_bt).__name__) - if _nm and _nm != 'CType': - return _nm + (' *' * (_binfo.PtrCount or 0)) - return '' - if isinstance(node, ast.Attribute): - if isinstance(node.value, ast.Name) and IsTModule(node.value.id): - # 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖) - if node.attr == 'CPtr': - return 'ptr' - if node.attr in _TYPEDEF_VALUE_QUALIFIERS: - # 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。 - # 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成 - # `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *', - # 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。 - return '' - CName: str | None = CTypeHelper.GetCName(node.attr) - if CName == 'CPtr': - return 'ptr' - if CName: - return CName - return '' - if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): - left_str: str = self.resolve_typedef_value_type(node.left) - right_str: str = self.resolve_typedef_value_type(node.right) - # 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。 - # 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。 - if not left_str: - return 'void *' if right_str == 'ptr' else right_str - if not right_str: - return 'void *' if left_str == 'ptr' else left_str - if right_str == 'ptr' or right_str.endswith('*'): - if left_str != 'ptr': - return left_str + ' *' - return right_str if right_str.endswith('*') else 'void *' - if left_str == 'ptr' or left_str.endswith('*'): - if right_str != 'ptr': - return right_str + ' *' - return left_str if left_str.endswith('*') else 'void *' - if right_str not in ('ptr', 'void', 'i32', 'i64'): - return right_str - if left_str not in ('ptr', 'void', 'i32', 'i64'): - return left_str - return left_str if left_str else right_str - return '' - - # ================================================================== - # 内部解析方法 - # ================================================================== - - def _ctype_to_llvm_str(self, info: CTypeInfo) -> str: - """从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。 - - void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。 - """ - if not info or not info.BaseType: - return '' - base_type = info.BaseType - base_llvm: str = '' - if isinstance(base_type, type) and issubclass(base_type, _t.CType): - base_llvm = CTypeRegistry.CTypeToLLVM(base_type) - elif isinstance(base_type, _t.CType): - base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type)) - if not base_llvm: - return '' - if info.PtrCount > 0: - if base_llvm == 'void': - return 'i8*' - return base_llvm + '*' * info.PtrCount - return base_llvm - - def _ctype_str_to_llvm(self, type_str: str) -> str: - """将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*') - - 支持: - - C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8' - - 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*' - - 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*' - 无法识别返回空字符串。 - """ - s: str = type_str.strip() - ptr_count: int = 0 - while s.endswith('*'): - ptr_count += 1 - s = s[:-1].strip() - base_llvm: str = '' - if s in ('void', 'CVoid'): - base_llvm = 'void' - else: - base_llvm = CTypeRegistry.NameToLLVM(s) - if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'): - base_llvm = s - if not base_llvm: - return '' - if ptr_count > 0: - if base_llvm == 'void': - return 'i8*' - return base_llvm + '*' * ptr_count - return base_llvm - - def _resolve_name(self, node: ast.Name) -> str: - """解析 ast.Name 节点""" - id_map: dict[str, str] = { - 'str': 'i8*', 'int': 'i32', 'bool': 'i8', - 'float': 'double', 'None': 'void', - 'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*', - 'UINT16PTR': 'i16*', 'INT16PTR': 'i16*', - 'UINT32PTR': 'i32*', 'INT32PTR': 'i32*', - 'UINT64PTR': 'i64*', 'INT64PTR': 'i64*', - } - if node.id in id_map: - return id_map[node.id] - # 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名 - builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id) - if builtin_entry: - TypeClass: type - base_ptr: int - TypeClass, base_ptr = builtin_entry - base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass) - if base_ptr > 0: - if base_llvm == 'void': - return 'i8*' - return base_llvm + '*' * base_ptr - return base_llvm if base_llvm else 'i32' - # 查找 SymbolTable 中的 typedef - if node.id in self._symtab: - entry: CTypeInfo = self._symtab[node.id] - if entry and entry.IsTypedef: - resolved_td: str = self._typedef_entry_to_llvm(entry) - if resolved_td: - return resolved_td - # --- 本模块声明的类型名 ------------------------------------------- - # 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径 - # (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。 - if node.id in self._ClassNames: - return f'%struct.{node.id}*' - # 枚举名:按登记的存储位宽物化(登记缺失时 32,与改前恒 i32 同值) - if node.id in self._EnumWidths: - return f'i{self._EnumWidths[node.id]}' - # --- 已加载模块(import / from-import)声明的类型名 ---------------- - if node.id in self._symtab: - entry_loaded: CTypeInfo = self._symtab[node.id] - if entry_loaded: - # IsStruct 覆盖 struct / renum / exception 三种类符号(见 _InsertClassSymbol) - if entry_loaded.IsStruct: - return f'%struct.{node.id}*' - if entry_loaded.IsEnum and not entry_loaded.IsRenum: - return f'i{EnumIntWidth(entry_loaded)}' - # --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ---- - # 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类** - # 声明同名符号(谁后插入谁占住裸名),两个真实例子: - # · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33 - # `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT` - # · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95 - # 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]` - # 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间 - # (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。 - _cand_sym: CTypeInfo | None - for _ns_syms in getattr(self._symtab, '_namespaces', {}).values(): - _cand_sym = _ns_syms.get(node.id) - if _cand_sym is None: - continue - if getattr(_cand_sym, 'IsTypedef', False): - _resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym) - if _resolved_ns: - return _resolved_ns - elif getattr(_cand_sym, 'IsStruct', False): - return f'%struct.{node.id}*' - elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False): - return f'i{EnumIntWidth(_cand_sym)}' - # --- builtins.vp 隐式 CTypedef ---------------------------------- - # int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss, - # 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径), - # 否则会误伤成下一条 fail-fast。 - builtin_info: CTypeInfo | None = self._lookup_builtins_typedef(node.id) - if builtin_info is not None: - builtin_llvm: str = self._ctype_to_llvm_str(builtin_info) - if builtin_llvm: - return builtin_llvm - # 存储类 / 类型限定符标记(CExport/CExtern/CStatic/CInline/CConst/ - # CVolatile/State 等,均以 `from t import ...` 裸名出现):自身不是类型, - # 只作标记 ⇒ 贡献空串,由 _resolve_bitor 取另一侧真实类型 - # (`CInt | CExport` → i32)。与 _resolve_attribute 的 marker 分支、 - # TypeAnnotationResolver._ResolveNameNode 的 STORAGE_CLASS/TYPE_QUALIFIER - # 判定同口径。缺此分支时裸名标记会落到下方 fail-fast —— 而带前缀写作 - # `t.CExport` 时却能通过(走 Attribute 路径),同一语义两种写法结果不同。 - # 现场:Test/AstTest `def main() -> CInt | CExport`(第 108 行)。 - _marker_cls: object = getattr(_t, node.id, None) - if (isinstance(_marker_cls, type) and _marker_cls is not _t.CType - and issubclass(_marker_cls, _t.CType)): - _pos_tags: object = getattr(_marker_cls, 'position', None) or () - if _t.CType.BASE not in _pos_tags and ( - _t.CType.STORAGE_CLASS in _pos_tags or _t.CType.TYPE_QUALIFIER in _pos_tags): - return '' - # fail-fast:类型信息缺失不再静默退化成 i32 - _node_line: object = getattr(node, 'lineno', None) - _node_col: object = getattr(node, 'col_offset', None) - _pos: str = "" - if _node_line: - _pos = f"(注解位置 第 {_node_line} 行" - _pos += f", 列 {_node_col})" if _node_col is not None else ")" - _loaded_cnt: int = len(getattr(self._symtab, '_symbols', None) or {}) - raise TypeNameUnresolvedError( - f"裸名 '{node.id}' 无法解析为类型{_pos}\n" - f" · 已尝试的解析来源(全部 miss):内置类型速记 / BuiltinTypeMap / " - f"builtins.vp 的 CTypedef / 符号表 typedef / " - f"本模块类名({len(self._ClassNames)} 个)/ 本模块枚举名({len(self._EnumWidths)} 个)/ " - f"符号表已加载条目({_loaded_cnt} 个)中的类与枚举\n" - f" · 若该名字确实是 typedef / 类:先确认声明它的模块已在本模块之前加载" - f"(未加载 ⇒ 符号表里没有它,与注解拼写无关);否则按「注解拼写错误或" - f"该类型不存在」处理") - - def _resolve_attribute(self, node: ast.Attribute) -> str: - """解析 ast.Attribute 节点""" - attr_name: str = node.attr if hasattr(node, 'attr') else '' - llvm_str: str | None = CTypeRegistry.NameToLLVM(attr_name) - if llvm_str is not None: - return llvm_str - if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'): - return '' - if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'): - return 'i8*' - if attr_name == 'CVoidPtr': - return 'i8*' - if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class: - return f'%struct.{attr_name}*' - if attr_name and attr_name not in CTypeRegistry._name_to_class: - return f'%struct.{attr_name}*' - self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32") - return 'i32' - - def _resolve_subscript(self, node: ast.Subscript) -> str: - """解析 ast.Subscript 节点""" - if isinstance(node.value, ast.Name) and node.value.id == 'tuple': - slice_node: ast.AST = node.slice - elem_types: list[str] = [] - if isinstance(slice_node, ast.Tuple): - elt: ast.AST - for elt in slice_node.elts: - elem_types.append(self.get_llvm_type_str(elt)) - else: - elem_types.append(self.get_llvm_type_str(slice_node)) - if elem_types: - return '{ ' + ', '.join(elem_types) + ' }' + return '' + + # ================================================================== + # 公共 API + # ================================================================== + + def get_llvm_type_str(self, node: ast.AST | None) -> str: + """将 AST 注解节点转换为 LLVM IR 类型字符串""" + if node is None: + self._symtab.diagnostics.warn('', 0, "类型注解为 None,回退到 i32") + return 'i32' + if isinstance(node, ast.Name): + return self._resolve_name(node) + elif isinstance(node, ast.Attribute): + return self._resolve_attribute(node) + elif isinstance(node, ast.Subscript): + return self._resolve_subscript(node) + elif isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + return self._resolve_bitor(node) + elif isinstance(node, ast.Constant): + if isinstance(node.value, bool): + return 'i8' + return 'i32' + elif isinstance(node, ast.Call): + return self._resolve_call(node) + self._symtab.diagnostics.warn('', 0, f"无法解析类型节点 {type(node).__name__},回退到 i32") + return 'i32' + + def get_func_ret_type_str(self, returns_node: ast.AST | None) -> str: + """提取函数返回类型的 LLVM 字符串""" + if returns_node is None: + return 'i32' + if isinstance(returns_node, ast.BinOp) and isinstance(returns_node.op, ast.BitOr): + # 统一走 CPtr 感知的联合解析(int | t.CPtr → i32*、str | t.CPtr → i8**) + return self._resolve_bitor(returns_node) + result: str = self.get_llvm_type_str(returns_node) + return result if result else 'i32' + + def get_func_param_type_str(self, annotation_node: ast.AST | None) -> str: + """提取函数参数类型的 LLVM 字符串。 + + 末尾的「不许静默变 i32」守卫:`Subscript` / 联合(`|`)形态的形参注解若解析结果 + 是 `'i32'`,只可能是**解析失败后的兜底** —— 合法的下标注解是容器 / 泛型 / 定长数组 + (解析出指针、数组或结构体指针),合法的联合写法是 `T | t.CPtr` 这类指针叠加,都 + 不会是 i32。该串会被 `_InsertFuncSymbol` 原样存进符号表(`FuncPtrParams`),再由 + `BuildLLVMFuncTypeFromSig` 物化成形参类型 —— 一旦是 i32,调用点就在 `_adjust_args` + 报「实参 %X* 不能隐式转为 i32」,把真因(**注解没解析出来**)掩盖成"调用点类型 + 不匹配"(现场:CallGenMixin.vp:149 调用 `_specialize_generic_class` 的 + `type_args: list[str]*`)。故此处响亮失败,并带上注解原文。 + """ + if annotation_node is None: + return 'i8*' + result: str + if isinstance(annotation_node, ast.BinOp) and isinstance(annotation_node.op, ast.BitOr): + # 统一走 CPtr 感知的联合解析 + result = self._resolve_bitor(annotation_node) + else: + result = self.get_llvm_type_str(annotation_node) + if not result: + result = 'i8*' + if result == 'i32' and isinstance(annotation_node, (ast.Subscript, ast.BinOp)): + _text: str + try: + _text = ast.unparse(annotation_node) + except Exception: + _text = f'<{type(annotation_node).__name__}>' + raise TypeNameUnresolvedError( + f"形参注解 '{_text}' 解析失败并落到 i32(下标 / 联合形态的合法结果应为" + f"指针、数组或结构体指针,不会是 i32)—— 拒绝静默降级为 i32") + return result + + def resolve_typedef_value_type(self, node: ast.AST | None) -> str: + """解析 typedef 值表达式的 C 类型字符串""" + if node is None: + return '' + if isinstance(node, ast.Name): + if node.id in self._symtab: + Entry: CTypeInfo = self._symtab[node.id] + if Entry.IsTypedef and Entry.OriginalType: + return Entry.OriginalType + # 短别名 / 内建速记(char/int/int64/uchar/str…):与注解位置 + # `_resolve_name` 同源。缺此分支时 typedef 值里的裸名解析为空, + # 整条 typedef 随之解析失败(现场:w32/win32base.vp 的 + # `LPCSTR: t.CTypedef = t.CConst | char | t.CPtr` ⇒ LPCSTR 不登记, + # 用到处报「裸名 LPCSTR 无法解析为类型」)。 + _binfo: CTypeInfo | None = self._lookup_builtins_typedef(node.id) + if _binfo is None: + _e: tuple[type, int] | None = BuiltinTypeMap.Get(node.id) + if _e: + _binfo = CTypeInfo() + _binfo.BaseType = _e[0] + _binfo.PtrCount = _e[1] + if _binfo is not None and _binfo.BaseType: + _bt: object = _binfo.BaseType + _nm: str = (_bt if isinstance(_bt, str) + else CTypeHelper.GetCName(_bt) if isinstance(_bt, type) + else type(_bt).__name__) + if _nm and _nm != 'CType': + return _nm + (' *' * (_binfo.PtrCount or 0)) + return '' + if isinstance(node, ast.Attribute): + if isinstance(node.value, ast.Name) and IsTModule(node.value.id): + # 直接判断属性名是否为 CPtr(消除对 CName=='*' 的依赖) + if node.attr == 'CPtr': + return 'ptr' + if node.attr in _TYPEDEF_VALUE_QUALIFIERS: + # 类型限定符标记(const/volatile)本身不是类型:它只修饰 `|` 另一侧。 + # 旧写法 `t.CConst | char | t.CPtr` 靠 char 一侧掩盖了这一点;一旦写成 + # `t.CConst | str`(`const str`),把 'CConst' 当基类型会得到 'CConst *', + # 于是别名登记失败、所有使用处报「裸名 … 无法解析为类型」。 + return '' + CName: str | None = CTypeHelper.GetCName(node.attr) + if CName == 'CPtr': + return 'ptr' + if CName: + return CName + return '' + if isinstance(node, ast.BinOp) and isinstance(node.op, ast.BitOr): + left_str: str = self.resolve_typedef_value_type(node.left) + right_str: str = self.resolve_typedef_value_type(node.right) + # 限定符侧解析为空串 ⇒ 另一侧就是完整类型(见 _TYPEDEF_VALUE_QUALIFIERS)。 + # 只在两侧都有类型信息时才按"基类型 + 指针标记"合并。 + if not left_str: + return 'void *' if right_str == 'ptr' else right_str + if not right_str: + return 'void *' if left_str == 'ptr' else left_str + if right_str == 'ptr' or right_str.endswith('*'): + if left_str != 'ptr': + return left_str + ' *' + return right_str if right_str.endswith('*') else 'void *' + if left_str == 'ptr' or left_str.endswith('*'): + if right_str != 'ptr': + return right_str + ' *' + return left_str if left_str.endswith('*') else 'void *' + if right_str not in ('ptr', 'void', 'i32', 'i64'): + return right_str + if left_str not in ('ptr', 'void', 'i32', 'i64'): + return left_str + return left_str if left_str else right_str + return '' + + # ================================================================== + # 内部解析方法 + # ================================================================== + + def _ctype_to_llvm_str(self, info: CTypeInfo) -> str: + """从 CTypeInfo 的 BaseType+PtrCount 生成归一化的 LLVM 类型字符串。 + + void* 在 LLVM IR 中归一化为 i8*;struct/enum/union 等非基础类型返回空字符串。 + """ + if not info or not info.BaseType: + return '' + base_type = info.BaseType + base_llvm: str = '' + if isinstance(base_type, type) and issubclass(base_type, _t.CType): + base_llvm = CTypeRegistry.CTypeToLLVM(base_type) + elif isinstance(base_type, _t.CType): + base_llvm = CTypeRegistry.CTypeToLLVM(type(base_type)) + if not base_llvm: + return '' + if info.PtrCount > 0: + if base_llvm == 'void': + return 'i8*' + return base_llvm + '*' * info.PtrCount + return base_llvm + + def _ctype_str_to_llvm(self, type_str: str) -> str: + """将 C 类型名字符串(如 'CUnsignedInt *')转换为 LLVM 类型字符串(如 'i32*') + + 支持: + - C 类名:'CUnsignedInt' → 'i32','CChar' → 'i8' + - 指针:'CUnsignedInt *' → 'i32*','void *' → 'i8*' + - 已是 LLVM 类型:'i32' → 'i32','i8*' → 'i8*' + 无法识别返回空字符串。 + """ + s: str = type_str.strip() + ptr_count: int = 0 + while s.endswith('*'): + ptr_count += 1 + s = s[:-1].strip() + base_llvm: str = '' + if s in ('void', 'CVoid'): + base_llvm = 'void' + else: + base_llvm = CTypeRegistry.NameToLLVM(s) + if not base_llvm and s in ('i8', 'i16', 'i32', 'i64', 'float', 'double', 'half', 'fp128', 'void'): + base_llvm = s + if not base_llvm: + return '' + if ptr_count > 0: + if base_llvm == 'void': + return 'i8*' + return base_llvm + '*' * ptr_count + return base_llvm + + def _resolve_name(self, node: ast.Name) -> str: + """解析 ast.Name 节点""" + id_map: dict[str, str] = { + 'str': 'i8*', 'int': 'i32', 'bool': 'i8', + 'float': 'double', 'None': 'void', + 'UINT8PTR': 'i8*', 'INT8PTR': 'i8*', 'BYTEPTR': 'i8*', + 'UINT16PTR': 'i16*', 'INT16PTR': 'i16*', + 'UINT32PTR': 'i32*', 'INT32PTR': 'i32*', + 'UINT64PTR': 'i64*', 'INT64PTR': 'i64*', + } + if node.id in id_map: + return id_map[node.id] + # 通过 BuiltinTypeMap 解析 VOIDPTR、INTPTR 等指针别名 + builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(node.id) + if builtin_entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = builtin_entry + base_llvm: str = CTypeRegistry.CTypeToLLVM(TypeClass) + if base_ptr > 0: + if base_llvm == 'void': + return 'i8*' + return base_llvm + '*' * base_ptr + return base_llvm if base_llvm else 'i32' + # 查找 SymbolTable 中的 typedef + if node.id in self._symtab: + entry: CTypeInfo = self._symtab[node.id] + if entry and entry.IsTypedef: + resolved_td: str = self._typedef_entry_to_llvm(entry) + if resolved_td: + return resolved_td + # --- 本模块声明的类型名 ------------------------------------------- + # 类名:裸类引用自身即指针 —— 与 _resolve_attribute 的 '%struct.X*' 同口径 + # (下游 TypeConvert._type_str_to_llvm 按短名命中已注册 struct ⇒ %"sha1.X"*)。 + if node.id in self._ClassNames: + return f'%struct.{node.id}*' + # 枚举名:按登记的存储位宽物化(登记缺失时 32,与改前恒 i32 同值) + if node.id in self._EnumWidths: + return f'i{self._EnumWidths[node.id]}' + # --- 已加载模块(import / from-import)声明的类型名 ---------------- + if node.id in self._symtab: + entry_loaded: CTypeInfo = self._symtab[node.id] + if entry_loaded: + # IsStruct 覆盖 struct / renum / exception 三种类符号(见 _InsertClassSymbol) + if entry_loaded.IsStruct: + return f'%struct.{node.id}*' + if entry_loaded.IsEnum and not entry_loaded.IsRenum: + return f'i{EnumIntWidth(entry_loaded)}' + # --- 同名冲突:平坦条目被 define / 枚举成员 / 函数占位时找回类型 ---- + # 平坦表(无前缀)一个名字只能留一个条目,而不同模块可能用**不同类型种类** + # 声明同名符号(谁后插入谁占住裸名),两个真实例子: + # · stdint.vp:9 `INT: t.CTypedef = t.CInt` vs argparse.vp:33 + # `define INT = 1`(sig 里是 `INT: t.CDefine = 1`)—— 注解 `oflag: INT` + # · llvmlite/__function.vp:77 `class Function` vs ast/tokens.vp:95 + # 枚举成员 `Function: t.State` —— 注解 `func: rawptr[Function]` + # 注解位置只可能是 typedef / 类 / 枚举 ⇒ 平坦条目不是这三种时,跨命名空间 + # (各模块的声明登记在 _namespaces[模块名] 下)再找同名条目。 + _cand_sym: CTypeInfo | None + for _ns_syms in getattr(self._symtab, '_namespaces', {}).values(): + _cand_sym = _ns_syms.get(node.id) + if _cand_sym is None: + continue + if getattr(_cand_sym, 'IsTypedef', False): + _resolved_ns: str = self._typedef_entry_to_llvm(_cand_sym) + if _resolved_ns: + return _resolved_ns + elif getattr(_cand_sym, 'IsStruct', False): + return f'%struct.{node.id}*' + elif getattr(_cand_sym, 'IsEnum', False) and not getattr(_cand_sym, 'IsRenum', False): + return f'i{EnumIntWidth(_cand_sym)}' + # --- builtins.vp 隐式 CTypedef ---------------------------------- + # int64/uint64/size_t 等:用户文件无需 import 即用,符号表必定 miss, + # 必须在此按权威定义展开(与 TypeConvert.L454-468 同一口径), + # 否则会误伤成下一条 fail-fast。 + builtin_info: CTypeInfo | None = self._lookup_builtins_typedef(node.id) + if builtin_info is not None: + builtin_llvm: str = self._ctype_to_llvm_str(builtin_info) + if builtin_llvm: + return builtin_llvm + # 存储类 / 类型限定符标记(CExport/CExtern/CStatic/CInline/CConst/ + # CVolatile/State 等,均以 `from t import ...` 裸名出现):自身不是类型, + # 只作标记 ⇒ 贡献空串,由 _resolve_bitor 取另一侧真实类型 + # (`CInt | CExport` → i32)。与 _resolve_attribute 的 marker 分支、 + # TypeAnnotationResolver._ResolveNameNode 的 STORAGE_CLASS/TYPE_QUALIFIER + # 判定同口径。缺此分支时裸名标记会落到下方 fail-fast —— 而带前缀写作 + # `t.CExport` 时却能通过(走 Attribute 路径),同一语义两种写法结果不同。 + # 现场:Test/AstTest `def main() -> CInt | CExport`(第 108 行)。 + _marker_cls: object = getattr(_t, node.id, None) + if (isinstance(_marker_cls, type) and _marker_cls is not _t.CType + and issubclass(_marker_cls, _t.CType)): + _pos_tags: object = getattr(_marker_cls, 'position', None) or () + if _t.CType.BASE not in _pos_tags and ( + _t.CType.STORAGE_CLASS in _pos_tags or _t.CType.TYPE_QUALIFIER in _pos_tags): + return '' + # fail-fast:类型信息缺失不再静默退化成 i32 + _node_line: object = getattr(node, 'lineno', None) + _node_col: object = getattr(node, 'col_offset', None) + _pos: str = "" + if _node_line: + _pos = f"(注解位置 第 {_node_line} 行" + _pos += f", 列 {_node_col})" if _node_col is not None else ")" + _loaded_cnt: int = len(getattr(self._symtab, '_symbols', None) or {}) + raise TypeNameUnresolvedError( + f"裸名 '{node.id}' 无法解析为类型{_pos}\n" + f" · 已尝试的解析来源(全部 miss):内置类型速记 / BuiltinTypeMap / " + f"builtins.vp 的 CTypedef / 符号表 typedef / " + f"本模块类名({len(self._ClassNames)} 个)/ 本模块枚举名({len(self._EnumWidths)} 个)/ " + f"符号表已加载条目({_loaded_cnt} 个)中的类与枚举\n" + f" · 若该名字确实是 typedef / 类:先确认声明它的模块已在本模块之前加载" + f"(未加载 ⇒ 符号表里没有它,与注解拼写无关);否则按「注解拼写错误或" + f"该类型不存在」处理") + + def _resolve_attribute(self, node: ast.Attribute) -> str: + """解析 ast.Attribute 节点""" + attr_name: str = node.attr if hasattr(node, 'attr') else '' + llvm_str: str | None = CTypeRegistry.NameToLLVM(attr_name) + if llvm_str is not None: + return llvm_str + if attr_name in ('CState', 'CDefine', 'CTypedef', 'CExtern', 'CStatic', 'CConst', 'State'): + return '' + if attr_name in ('CCharPtr', 'CIntPtr', 'CVoidPtr', 'CArrayPtr'): + return 'i8*' + if attr_name == 'CVoidPtr': + return 'i8*' + if attr_name and attr_name[0].isupper() and attr_name not in CTypeRegistry._name_to_class: + return f'%struct.{attr_name}*' + if attr_name and attr_name not in CTypeRegistry._name_to_class: + return f'%struct.{attr_name}*' + self._symtab.diagnostics.warn('', 0, f"无法解析 Attribute 类型 '{attr_name}',回退到 i32") + return 'i32' + + def _resolve_subscript(self, node: ast.Subscript) -> str: + """解析 ast.Subscript 节点""" + if isinstance(node.value, ast.Name) and node.value.id == 'tuple': + slice_node: ast.AST = node.slice + elem_types: list[str] = [] + if isinstance(slice_node, ast.Tuple): + elt: ast.AST + for elt in slice_node.elts: + elem_types.append(self.get_llvm_type_str(elt)) + else: + elem_types.append(self.get_llvm_type_str(slice_node)) + if elem_types: + return '{ ' + ', '.join(elem_types) + ' }' if IsCPtrSubscriptNode(node): - # t.CPtr[T] → Ptr(resolve(T))(对齐 TPV Subscript 分支 + # t.CPtr[T] → Ptr(resolve(T))(对齐 VPC Subscript 分支 # TypeGenResolve.vp:2769-2790;void 首层归一化为 i8*) return self._add_pointer_level(self.get_llvm_type_str(node.slice)) if IsRawPtrSubscriptNode(node): @@ -458,115 +458,115 @@ class LLVMTypeMapper: # 合并语义同口径(裸类引用自身即指针,吸收这一层;其余逐层外包)。 # 此前只认 IsCPtrSubscriptNode → rawptr[T] 落到下方 warn 并退化成 i32。 return self._rawptr_subscript_type_str(node) - if IsListAnnotation(node): - slice_node: ast.AST = node.slice - elem_type: str - if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1: - elem_type = self.get_llvm_type_str(slice_node.elts[0]) - else: - elem_type = self.get_llvm_type_str(slice_node) - return elem_type + '*' - # 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。 - # 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见 - # SymbolUtils.ResolveGenericClassSpecName;输出与 _resolve_name / - # _resolve_attribute 的类引用**同一表示**(%struct.<名>*,短名交给 - # TypeConvert._type_str_to_llvm_base 按 _struct_sha1_map 物化到 - # %"{模板sha1}.<特化名>"*)。此前单参泛型下标落到下方 warn + 'i32' ⇒ - # 形参类型串成 i32(现场:ClassGenGeneric._specialize_generic_class 的 - # 形参 `type_args: list[str]*`)。 - _Spec: str | None = ResolveGenericClassSpecName( - node, self._symtab, - GetStructSha1MapFromSymtab(self._symtab), - GetTCAliasMapFromSymtab(self._symtab)) - if _Spec: - return f'%struct.{_Spec}*' - self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32") - return 'i32' - - def _resolve_bitor(self, node: ast.BinOp) -> str: - """解析 ast.BinOp(BitOr) 节点(CPtr 感知的指针层级叠加)。 - - T | t.CPtr 一侧为 t.CPtr 标记时,另一侧追加一层 '*': - int | t.CPtr → i32* (原逻辑误降级为 i8*) - str | t.CPtr → i8** (原逻辑误降级为 i8*) - (int | t.CPtr) | t.CPtr → i32**(嵌套递归叠加) - T | t.CPtr[...] 一侧为下标指针类型时,另一侧叠加该下标的指针层级 N - (N = 1 + 切片自身层数,不含 struct 吸收,对齐 TPV BinOp 下标分支 - TypeGenResolve.vp:2596-2639): - t.CUInt8T | t.CPtr[t.CPtr] → i8**(原逻辑误降级为首段 i8) - """ - left: str = self.get_llvm_type_str(node.left) - right: str = self.get_llvm_type_str(node.right) - left_is_cptr: bool = IsCPtrNode(node.left) - right_is_cptr: bool = IsCPtrNode(node.right) - if right_is_cptr: - return self._add_pointer_level(left) - if left_is_cptr: - return self._add_pointer_level(right) - # t.CPtr[...] 下标指针标记:X | t.CPtr[...] → Ptr^N(X) - if IsCPtrSubscriptNode(node.right): - return self._add_pointer_levels(left, self._cptr_subscript_ptr_level(node.right)) - if IsCPtrSubscriptNode(node.left): - return self._add_pointer_levels(right, self._cptr_subscript_ptr_level(node.left)) - if right and right.endswith('*'): - return right - if left and left.endswith('*'): - return left - if left and left not in ('i32', 'void', 'i64'): - return left - if right and right not in ('i32', 'void', 'i64'): - return right - return left if left else right - - def _add_pointer_level(self, ty: str) -> str: - """给类型字符串追加一层指针 '*'(void/void* → i8*)。""" - if not ty: - return 'i8*' - if ty == 'void' or ty == 'void*': - return 'i8*' - if ty.endswith('*'): - return ty + '*' - return ty + '*' - - def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str: - """rawptr[T](内置裸指针下标)→ LLVM 类型串。 - - 与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' / - '%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层; - 其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标) - 逐层外包(void → i8*)。 - """ - inner: ast.AST = node.slice - inner_type: str = self.get_llvm_type_str(inner) - if (not isinstance(inner, ast.BinOp) - and not IsRawPtrSubscriptNode(inner) - and not IsCPtrSubscriptNode(inner) - and inner_type.startswith('%') and inner_type.endswith('*')): - return inner_type - return self._add_pointer_level(inner_type) - - def _add_pointer_levels(self, ty: str, count: int) -> str: - """给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。""" - result: str = ty - for _ in range(count): - result = self._add_pointer_level(result) - return result - - def _cptr_subscript_ptr_level(self, node: ast.AST) -> int: - """t.CPtr[T] 下标指针标记贡献的指针层级 N(= 1 + 切片自身层数)。 - - 复用定义侧解析器 TypeAnnotationResolver.from_node(其 _from_node_subscript - 已实现 t.CPtr[T] → Ptr(T) 的层级叠加,见 TypeAnnotationResolver.py:756-783), - 与 HandlesTypeMerge._HandleSubscript(HandlesTypeMerge.py:512-531)同口径。 - 调用前须经 IsCPtrSubscriptNode 判据过滤,故 PtrCount 至少为 1。 - """ - from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR - return _TAR.from_node(node, self._symtab).PtrCount - - def _resolve_call(self, node: ast.Call) -> str: - """解析 ast.Call 节点""" - if isinstance(node.func, ast.Attribute): - if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id): - return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr)) - self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32") - return 'i32' + if IsListAnnotation(node): + slice_node: ast.AST = node.slice + elem_type: str + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) >= 1: + elem_type = self.get_llvm_type_str(slice_node.elts[0]) + else: + elem_type = self.get_llvm_type_str(slice_node) + return elem_type + '*' + # 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。 + # 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见 + # SymbolUtils.ResolveGenericClassSpecName;输出与 _resolve_name / + # _resolve_attribute 的类引用**同一表示**(%struct.<名>*,短名交给 + # TypeConvert._type_str_to_llvm_base 按 _struct_sha1_map 物化到 + # %"{模板sha1}.<特化名>"*)。此前单参泛型下标落到下方 warn + 'i32' ⇒ + # 形参类型串成 i32(现场:ClassGenGeneric._specialize_generic_class 的 + # 形参 `type_args: list[str]*`)。 + _Spec: str | None = ResolveGenericClassSpecName( + node, self._symtab, + GetStructSha1MapFromSymtab(self._symtab), + GetTCAliasMapFromSymtab(self._symtab)) + if _Spec: + return f'%struct.{_Spec}*' + self._symtab.diagnostics.warn('', 0, "无法解析 Subscript 类型,回退到 i32") + return 'i32' + + def _resolve_bitor(self, node: ast.BinOp) -> str: + """解析 ast.BinOp(BitOr) 节点(CPtr 感知的指针层级叠加)。 + + T | t.CPtr 一侧为 t.CPtr 标记时,另一侧追加一层 '*': + int | t.CPtr → i32* (原逻辑误降级为 i8*) + str | t.CPtr → i8** (原逻辑误降级为 i8*) + (int | t.CPtr) | t.CPtr → i32**(嵌套递归叠加) + T | t.CPtr[...] 一侧为下标指针类型时,另一侧叠加该下标的指针层级 N + (N = 1 + 切片自身层数,不含 struct 吸收,对齐 VPC BinOp 下标分支 + TypeGenResolve.vp:2596-2639): + t.CUInt8T | t.CPtr[t.CPtr] → i8**(原逻辑误降级为首段 i8) + """ + left: str = self.get_llvm_type_str(node.left) + right: str = self.get_llvm_type_str(node.right) + left_is_cptr: bool = IsCPtrNode(node.left) + right_is_cptr: bool = IsCPtrNode(node.right) + if right_is_cptr: + return self._add_pointer_level(left) + if left_is_cptr: + return self._add_pointer_level(right) + # t.CPtr[...] 下标指针标记:X | t.CPtr[...] → Ptr^N(X) + if IsCPtrSubscriptNode(node.right): + return self._add_pointer_levels(left, self._cptr_subscript_ptr_level(node.right)) + if IsCPtrSubscriptNode(node.left): + return self._add_pointer_levels(right, self._cptr_subscript_ptr_level(node.left)) + if right and right.endswith('*'): + return right + if left and left.endswith('*'): + return left + if left and left not in ('i32', 'void', 'i64'): + return left + if right and right not in ('i32', 'void', 'i64'): + return right + return left if left else right + + def _add_pointer_level(self, ty: str) -> str: + """给类型字符串追加一层指针 '*'(void/void* → i8*)。""" + if not ty: + return 'i8*' + if ty == 'void' or ty == 'void*': + return 'i8*' + if ty.endswith('*'): + return ty + '*' + return ty + '*' + + def _rawptr_subscript_type_str(self, node: ast.Subscript) -> str: + """rawptr[T](内置裸指针下标)→ LLVM 类型串。 + + 与定义端同口径(`T | t.CPtr` 的合并):裸类引用('%struct.X*' / + '%"sha1.X"*' / 泛型特化等以 '%' 开头的结构指针)自身即指针,吸收这一层; + 其余(标量、已是指针的 typedef、联合写法、嵌套 rawptr / t.CPtr 下标) + 逐层外包(void → i8*)。 + """ + inner: ast.AST = node.slice + inner_type: str = self.get_llvm_type_str(inner) + if (not isinstance(inner, ast.BinOp) + and not IsRawPtrSubscriptNode(inner) + and not IsCPtrSubscriptNode(inner) + and inner_type.startswith('%') and inner_type.endswith('*')): + return inner_type + return self._add_pointer_level(inner_type) + + def _add_pointer_levels(self, ty: str, count: int) -> str: + """给类型字符串叠加 count 层指针(void/void* 首层归一化为 i8*)。""" + result: str = ty + for _ in range(count): + result = self._add_pointer_level(result) + return result + + def _cptr_subscript_ptr_level(self, node: ast.AST) -> int: + """t.CPtr[T] 下标指针标记贡献的指针层级 N(= 1 + 切片自身层数)。 + + 复用定义侧解析器 TypeAnnotationResolver.from_node(其 _from_node_subscript + 已实现 t.CPtr[T] → Ptr(T) 的层级叠加,见 TypeAnnotationResolver.py:756-783), + 与 HandlesTypeMerge._HandleSubscript(HandlesTypeMerge.py:512-531)同口径。 + 调用前须经 IsCPtrSubscriptNode 判据过滤,故 PtrCount 至少为 1。 + """ + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR + return _TAR.from_node(node, self._symtab).PtrCount + + def _resolve_call(self, node: ast.Call) -> str: + """解析 ast.Call 节点""" + if isinstance(node.func, ast.Attribute): + if hasattr(node.func.value, 'id') and IsTModule(node.func.value.id): + return self.get_llvm_type_str(ast.Attribute(value=ast.Name(id='t'), attr=node.func.attr)) + self._symtab.diagnostics.warn('', 0, "无法解析 Call 类型,回退到 i32") + return 'i32' diff --git a/lib/core/SymbolExtractor.py b/lib/core/SymbolExtractor.py index 333412e..f63b41f 100644 --- a/lib/core/SymbolExtractor.py +++ b/lib/core/SymbolExtractor.py @@ -1,320 +1,320 @@ -"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息""" -from __future__ import annotations -import vast as ast -from typing import TYPE_CHECKING -if TYPE_CHECKING: - from lib.core.SymbolTable import SymbolTable - -from lib.core.SymbolData import ( - ModuleSymbols, ClassSymbolData, TypedefSymbolData, - FuncSymbolData, DefineSymbolData, AnonymousTypeData -) -from lib.core.Handles.HandlesBase import CTypeInfo +"""Pass 1: ASTSymbolExtractor — 从 AST 提取原始符号信息""" +from __future__ import annotations +import vast as ast +from typing import TYPE_CHECKING +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + +from lib.core.SymbolData import ( + ModuleSymbols, ClassSymbolData, TypedefSymbolData, + FuncSymbolData, DefineSymbolData, AnonymousTypeData +) +from lib.core.Handles.HandlesBase import CTypeInfo from lib.core.SymbolUtils import AnnotationContainsTType, CheckAnnotationHasCInline, IsTModule, AnnotationContainsName -from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH -from lib.includes import t - - -class ASTSymbolExtractor: - """从 Python 源文件的 AST 中提取所有符号信息""" - - def __init__(self, symbol_table: SymbolTable) -> None: - self._symtab: SymbolTable = symbol_table - - def extract(self, file_path: str) -> ModuleSymbols: - """提取文件中的所有符号信息""" - with open(file_path, 'r', encoding='utf-8') as f: - code: str = f.read() - tree: ast.Module = ast.parse(code, file_path) - - result: ModuleSymbols = ModuleSymbols(file_path=file_path) - - # 第一步:收集所有类的成员和匿名类型 - class_members: dict[str, dict[str, CTypeInfo]] = {} - for node in tree.body: - if isinstance(node, ast.ClassDef): - members: dict[str, CTypeInfo] = {} - self._collect_members(node, members, result.anonymous_types) - class_members[node.name] = members - - # 第二步:从每个顶层节点提取符号信息 - for node in tree.body: - if isinstance(node, ast.ClassDef): - members_info: dict[str, CTypeInfo] = class_members.get(node.name, {}) - result.classes.append(self._extract_class(node, members_info)) - elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): - self._extract_ann_assign(node, result) - elif isinstance(node, ast.FunctionDef): - result.functions.append(self._extract_function(node)) - - return result - - # ------------------------------------------------------------------ - # 成员收集(递归处理嵌套匿名类) - # ------------------------------------------------------------------ - - def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None: - """递归收集类成员,包括嵌套匿名类型""" - for item in class_node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - member_name: str = item.target.id - full_name: str = f'{prefix}{member_name}' if prefix else member_name - member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab) - if member_type is None: - member_type = CTypeInfo() - member_type.BaseType = t.CInt() - is_ptr: bool = member_type.IsPtr - if isinstance(item.annotation, ast.BinOp): - if AnnotationContainsName(item.annotation, 'CPtr'): - is_ptr = True - members[full_name] = member_type.Copy() - - elif isinstance(item, ast.ClassDef): - is_anonymous: bool = any( - any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases) - ) if item.bases else False - - if is_anonymous: - is_union: bool = any( - any(AnnotationContainsTType(base, 'CUnion') for base in item.bases) - ) - # 也检查装饰器形式 @t.CUnion - if not is_union and hasattr(item, 'decorator_list') and item.decorator_list: - for deco in item.decorator_list: - if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion': - is_union = True - break - elif isinstance(deco, ast.Name): - cls2: type | None = getattr(t, deco.id, None) - if cls2 is None and hasattr(self._symtab, '_t_type_symbols'): - cls2 = self._symtab._t_type_symbols.get(deco.id) - if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion): - is_union = True - break - - nested_name: str = f'{prefix}{item.name}' if prefix else item.name - members[nested_name] = CTypeInfo() - members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}' - members[nested_name].PtrCount = 0 - members[nested_name].ArrayDims = [] - - new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.' - self._collect_members(item, members, anonymous_types, new_prefix) - - anonymous_members: dict[str, CTypeInfo] = {} - for full_name, member_info in members.items(): - if full_name.startswith(f"{nested_name}."): - member_name: str = full_name[len(nested_name) + 1:] - anonymous_members[member_name] = member_info - - if item.name not in anonymous_types: - anonymous_types[item.name] = AnonymousTypeData( - name=item.name, - is_union=is_union, - members=anonymous_members, - lineno=item.lineno - ) - - # ------------------------------------------------------------------ - # 类符号提取 - # ------------------------------------------------------------------ - - def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData: - """从 ClassDef 节点提取类符号数据""" - type_kind: str = 'struct' - is_cpython_object: bool = False - is_packed: bool = False - - # 检查装饰器 - if hasattr(node, 'decorator_list') and node.decorator_list: - for decorator in node.decorator_list: - if isinstance(decorator, ast.Attribute): - if (hasattr(decorator.value, 'id') and - IsTModule(decorator.value.id, self._symtab)): - if decorator.attr == 'Object': - is_cpython_object = True - elif decorator.attr == 'CUnion': - type_kind = 'union' - elif decorator.attr == 'CEnum': - type_kind = 'enum' - elif decorator.attr == 'REnum': - type_kind = 'renum' - elif decorator.attr == 'CStruct': - type_kind = 'struct' - elif isinstance(decorator, ast.Name): - # from t import CStruct / CUnion / CEnum / REnum - cls: type | None = getattr(t, decorator.id, None) - if cls is None and hasattr(self._symtab, '_t_type_symbols'): - cls = self._symtab._t_type_symbols.get(decorator.id) - if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): - if issubclass(cls, t.CUnion): - type_kind = 'union' - elif issubclass(cls, t.REnum): - type_kind = 'renum' - elif issubclass(cls, t.CEnum): - type_kind = 'enum' - elif issubclass(cls, t.CStruct): - type_kind = 'struct' - elif decorator.id == 'Object': - is_cpython_object = True - elif isinstance(decorator, ast.Call): - if isinstance(decorator.func, ast.Attribute): - if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute': - for arg in decorator.args: - if isinstance(arg, ast.Attribute): - if isinstance(arg.value, ast.Attribute): - if hasattr(arg.value.value, 'id') and IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed': - is_packed = True - - # 检查基类 - for base in node.bases: - if AnnotationContainsTType(base, 'CUnion'): - type_kind = 'union' - break - elif AnnotationContainsTType(base, 'CEnum'): - type_kind = 'enum' - break - elif AnnotationContainsTType(base, 'REnum'): - type_kind = 'renum' - break - elif AnnotationContainsTType(base, 'CStruct'): - type_kind = 'struct' - break - elif AnnotationContainsTType(base, 'Object'): - is_cpython_object = True - type_kind = 'struct' - break - elif isinstance(base, ast.Name) and base.id == 'Exception': - type_kind = 'exception' - break - elif isinstance(base, ast.Name): - base_entry: CTypeInfo | None = self._symtab.get(base.id) - if base_entry and base_entry.IsExceptionClass: - type_kind = 'exception' - break - - # 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端 - # HandlesClassDef._RegisterEnumMembers / TPV ClassGenEnum 同一规则源)。 - # 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源 - # (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle), - # 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读 - # 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。 - enum_members: list[tuple[str, int, int]] = [] - enum_width: int = _ENUM_DEFAULT_INT_WIDTH - if type_kind == 'enum': - enum_width = EnumClassIntWidth(node.body, self._symtab) - next_enum_value: int = 0 - for item in node.body: - if isinstance(item, ast.Assign): - if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): - member_name: str = item.targets[0].id - if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - next_enum_value = item.value.value + 1 - else: - next_enum_value += 1 - enum_members.append((member_name, next_enum_value - 1, item.lineno)) - elif isinstance(item, ast.AnnAssign): - if isinstance(item.target, ast.Name): - member_name: str = item.target.id - if item.value: - if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - next_enum_value = item.value.value + 1 - else: - next_enum_value += 1 - else: - next_enum_value += 1 - enum_members.append((member_name, next_enum_value - 1, item.lineno)) - - # REnum 变体提取(嵌套 ClassDef + Assign 显式值) - # 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐: - # - 先收集 Assign 显式值 - # - ClassDef 变体从 max(explicit)+1 开始顺序编号 - # - Assign 无显式值时用独立计数器 variant_index - if type_kind == 'renum': - explicit_values: dict[str, int] = {} - for item in node.body: - if isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name): - if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - explicit_values[target.id] = item.value.value - next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0 - variant_index: int = 0 - for item in node.body: - if isinstance(item, ast.ClassDef): - variant_name_r: str = item.name - tag: int = next_class_tag - next_class_tag += 1 - enum_members.append((variant_name_r, tag, item.lineno)) - elif isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name): - var_name: str = target.id - if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): - value: int = item.value.value - else: - value = variant_index - variant_index += 1 - enum_members.append((var_name, value, item.lineno)) - - return ClassSymbolData( - name=node.name, - type_kind=type_kind, - lineno=node.lineno, - members=members_info, - is_cpython_object=is_cpython_object, - is_packed=is_packed, - enum_members=enum_members, - enum_width=enum_width, - ) - - # ------------------------------------------------------------------ - # AnnAssign 提取(typedef / define) - # ------------------------------------------------------------------ - - def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None: - """从 AnnAssign 节点提取 typedef 或 define 数据""" - var_name: str = node.target.id - has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False - has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False - has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False - - if has_cdefine: - result.defines.append(DefineSymbolData( - name=var_name, - lineno=node.lineno, - value_node=node.value, - )) - else: - # rawptr 是编译器内置语法(TPC/TPV 双端),不可重定义: - # 忽略任何对 rawptr 的 typedef 声明,防止覆盖内置裸指针语义 - if var_name == 'rawptr': - return - result.typedefs.append(TypedefSymbolData( - name=var_name, - lineno=node.lineno, - annotation=node.annotation, - value=node.value, - has_cdefine=has_cdefine, - has_postdef=has_postdef, - has_ctypedef=has_ctypedef, - )) - - # ------------------------------------------------------------------ - # 函数提取 - # ------------------------------------------------------------------ - - def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData: - """从 FunctionDef 节点提取函数符号数据""" - params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args] - is_variadic: bool = node.args.vararg is not None - is_inline: bool = CheckAnnotationHasCInline(node.returns) - - return FuncSymbolData( - name=node.name, - lineno=node.lineno, - returns_node=node.returns, - params=params, - is_variadic=is_variadic, - is_inline=is_inline, - ) +from lib.core.SymbolUtils import EnumClassIntWidth, _ENUM_DEFAULT_INT_WIDTH +from lib.includes import t + + +class ASTSymbolExtractor: + """从 Python 源文件的 AST 中提取所有符号信息""" + + def __init__(self, symbol_table: SymbolTable) -> None: + self._symtab: SymbolTable = symbol_table + + def extract(self, file_path: str) -> ModuleSymbols: + """提取文件中的所有符号信息""" + with open(file_path, 'r', encoding='utf-8') as f: + code: str = f.read() + tree: ast.Module = ast.parse(code, file_path) + + result: ModuleSymbols = ModuleSymbols(file_path=file_path) + + # 第一步:收集所有类的成员和匿名类型 + class_members: dict[str, dict[str, CTypeInfo]] = {} + for node in tree.body: + if isinstance(node, ast.ClassDef): + members: dict[str, CTypeInfo] = {} + self._collect_members(node, members, result.anonymous_types) + class_members[node.name] = members + + # 第二步:从每个顶层节点提取符号信息 + for node in tree.body: + if isinstance(node, ast.ClassDef): + members_info: dict[str, CTypeInfo] = class_members.get(node.name, {}) + result.classes.append(self._extract_class(node, members_info)) + elif isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name): + self._extract_ann_assign(node, result) + elif isinstance(node, ast.FunctionDef): + result.functions.append(self._extract_function(node)) + + return result + + # ------------------------------------------------------------------ + # 成员收集(递归处理嵌套匿名类) + # ------------------------------------------------------------------ + + def _collect_members(self, class_node: ast.ClassDef, members: dict[str, CTypeInfo], anonymous_types: dict[str, AnonymousTypeData], prefix: str = '') -> None: + """递归收集类成员,包括嵌套匿名类型""" + for item in class_node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + member_name: str = item.target.id + full_name: str = f'{prefix}{member_name}' if prefix else member_name + member_type: CTypeInfo | None = CTypeInfo.FromNode(item.annotation, self._symtab) + if member_type is None: + member_type = CTypeInfo() + member_type.BaseType = t.CInt() + is_ptr: bool = member_type.IsPtr + if isinstance(item.annotation, ast.BinOp): + if AnnotationContainsName(item.annotation, 'CPtr'): + is_ptr = True + members[full_name] = member_type.Copy() + + elif isinstance(item, ast.ClassDef): + is_anonymous: bool = any( + any(AnnotationContainsTType(base, 'Anonymous') for base in item.bases) + ) if item.bases else False + + if is_anonymous: + is_union: bool = any( + any(AnnotationContainsTType(base, 'CUnion') for base in item.bases) + ) + # 也检查装饰器形式 @t.CUnion + if not is_union and hasattr(item, 'decorator_list') and item.decorator_list: + for deco in item.decorator_list: + if isinstance(deco, ast.Attribute) and hasattr(deco.value, 'id') and IsTModule(deco.value.id, self._symtab) and deco.attr == 'CUnion': + is_union = True + break + elif isinstance(deco, ast.Name): + cls2: type | None = getattr(t, deco.id, None) + if cls2 is None and hasattr(self._symtab, '_t_type_symbols'): + cls2 = self._symtab._t_type_symbols.get(deco.id) + if cls2 is not None and isinstance(cls2, type) and issubclass(cls2, t.CUnion): + is_union = True + break + + nested_name: str = f'{prefix}{item.name}' if prefix else item.name + members[nested_name] = CTypeInfo() + members[nested_name].BaseType = f'union {item.name}' if is_union else f'struct {item.name}' + members[nested_name].PtrCount = 0 + members[nested_name].ArrayDims = [] + + new_prefix: str = f'{prefix}{item.name}.' if prefix else f'{item.name}.' + self._collect_members(item, members, anonymous_types, new_prefix) + + anonymous_members: dict[str, CTypeInfo] = {} + for full_name, member_info in members.items(): + if full_name.startswith(f"{nested_name}."): + member_name: str = full_name[len(nested_name) + 1:] + anonymous_members[member_name] = member_info + + if item.name not in anonymous_types: + anonymous_types[item.name] = AnonymousTypeData( + name=item.name, + is_union=is_union, + members=anonymous_members, + lineno=item.lineno + ) + + # ------------------------------------------------------------------ + # 类符号提取 + # ------------------------------------------------------------------ + + def _extract_class(self, node: ast.ClassDef, members_info: dict[str, CTypeInfo]) -> ClassSymbolData: + """从 ClassDef 节点提取类符号数据""" + type_kind: str = 'struct' + is_cpython_object: bool = False + is_packed: bool = False + + # 检查装饰器 + if hasattr(node, 'decorator_list') and node.decorator_list: + for decorator in node.decorator_list: + if isinstance(decorator, ast.Attribute): + if (hasattr(decorator.value, 'id') and + IsTModule(decorator.value.id, self._symtab)): + if decorator.attr == 'Object': + is_cpython_object = True + elif decorator.attr == 'CUnion': + type_kind = 'union' + elif decorator.attr == 'CEnum': + type_kind = 'enum' + elif decorator.attr == 'REnum': + type_kind = 'renum' + elif decorator.attr == 'CStruct': + type_kind = 'struct' + elif isinstance(decorator, ast.Name): + # from t import CStruct / CUnion / CEnum / REnum + cls: type | None = getattr(t, decorator.id, None) + if cls is None and hasattr(self._symtab, '_t_type_symbols'): + cls = self._symtab._t_type_symbols.get(decorator.id) + if cls is not None and isinstance(cls, type) and issubclass(cls, t.CType): + if issubclass(cls, t.CUnion): + type_kind = 'union' + elif issubclass(cls, t.REnum): + type_kind = 'renum' + elif issubclass(cls, t.CEnum): + type_kind = 'enum' + elif issubclass(cls, t.CStruct): + type_kind = 'struct' + elif decorator.id == 'Object': + is_cpython_object = True + elif isinstance(decorator, ast.Call): + if isinstance(decorator.func, ast.Attribute): + if hasattr(decorator.func.value, 'id') and decorator.func.value.id == 'c' and decorator.func.attr == 'Attribute': + for arg in decorator.args: + if isinstance(arg, ast.Attribute): + if isinstance(arg.value, ast.Attribute): + if hasattr(arg.value.value, 'id') and IsTModule(arg.value.value.id, self._symtab) and arg.value.attr == 'attr' and arg.attr == 'packed': + is_packed = True + + # 检查基类 + for base in node.bases: + if AnnotationContainsTType(base, 'CUnion'): + type_kind = 'union' + break + elif AnnotationContainsTType(base, 'CEnum'): + type_kind = 'enum' + break + elif AnnotationContainsTType(base, 'REnum'): + type_kind = 'renum' + break + elif AnnotationContainsTType(base, 'CStruct'): + type_kind = 'struct' + break + elif AnnotationContainsTType(base, 'Object'): + is_cpython_object = True + type_kind = 'struct' + break + elif isinstance(base, ast.Name) and base.id == 'Exception': + type_kind = 'exception' + break + elif isinstance(base, ast.Name): + base_entry: CTypeInfo | None = self._symtab.get(base.id) + if base_entry and base_entry.IsExceptionClass: + type_kind = 'exception' + break + + # 枚举成员提取 + 存储宽度推导("最宽成员类型",与定义端 + # HandlesClassDef._RegisterEnumMembers / VPC ClassGenEnum 同一规则源)。 + # 本条通路(LoadModuleSymbols → 消费 .vpi/.pyi)是**跨模块符号表**的来源 + # (Phase2Translator._build_shared_symbol_data → _shared_sym.json pickle), + # 宽度在此推导后随条目序列化,跨模块读取端(HandlesImports 的 .vpi 直读 + # 路径 + _enum_int_type)即得到同一宽度;旧产物/老 pickle 无该字段 → 退 32。 + enum_members: list[tuple[str, int, int]] = [] + enum_width: int = _ENUM_DEFAULT_INT_WIDTH + if type_kind == 'enum': + enum_width = EnumClassIntWidth(node.body, self._symtab) + next_enum_value: int = 0 + for item in node.body: + if isinstance(item, ast.Assign): + if len(item.targets) == 1 and isinstance(item.targets[0], ast.Name): + member_name: str = item.targets[0].id + if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + next_enum_value = item.value.value + 1 + else: + next_enum_value += 1 + enum_members.append((member_name, next_enum_value - 1, item.lineno)) + elif isinstance(item, ast.AnnAssign): + if isinstance(item.target, ast.Name): + member_name: str = item.target.id + if item.value: + if isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + next_enum_value = item.value.value + 1 + else: + next_enum_value += 1 + else: + next_enum_value += 1 + enum_members.append((member_name, next_enum_value - 1, item.lineno)) + + # REnum 变体提取(嵌套 ClassDef + Assign 显式值) + # 与 HandlesClassDef._EmitREnumLlvm 的 tag 分配逻辑对齐: + # - 先收集 Assign 显式值 + # - ClassDef 变体从 max(explicit)+1 开始顺序编号 + # - Assign 无显式值时用独立计数器 variant_index + if type_kind == 'renum': + explicit_values: dict[str, int] = {} + for item in node.body: + if isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + explicit_values[target.id] = item.value.value + next_class_tag: int = max(explicit_values.values()) + 1 if explicit_values else 0 + variant_index: int = 0 + for item in node.body: + if isinstance(item, ast.ClassDef): + variant_name_r: str = item.name + tag: int = next_class_tag + next_class_tag += 1 + enum_members.append((variant_name_r, tag, item.lineno)) + elif isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name): + var_name: str = target.id + if item.value and isinstance(item.value, ast.Constant) and isinstance(item.value.value, int): + value: int = item.value.value + else: + value = variant_index + variant_index += 1 + enum_members.append((var_name, value, item.lineno)) + + return ClassSymbolData( + name=node.name, + type_kind=type_kind, + lineno=node.lineno, + members=members_info, + is_cpython_object=is_cpython_object, + is_packed=is_packed, + enum_members=enum_members, + enum_width=enum_width, + ) + + # ------------------------------------------------------------------ + # AnnAssign 提取(typedef / define) + # ------------------------------------------------------------------ + + def _extract_ann_assign(self, node: ast.AnnAssign, result: ModuleSymbols) -> None: + """从 AnnAssign 节点提取 typedef 或 define 数据""" + var_name: str = node.target.id + has_cdefine: bool = AnnotationContainsTType(node.annotation, 'CDefine') if node.annotation else False + has_postdef: bool = AnnotationContainsTType(node.annotation, 'Postdefinition') if node.annotation else False + has_ctypedef: bool = AnnotationContainsTType(node.annotation, 'CTypedef') if node.annotation else False + + if has_cdefine: + result.defines.append(DefineSymbolData( + name=var_name, + lineno=node.lineno, + value_node=node.value, + )) + else: + # rawptr 是编译器内置语法(TPC/VPC 双端),不可重定义: + # 忽略任何对 rawptr 的 typedef 声明,防止覆盖内置裸指针语义 + if var_name == 'rawptr': + return + result.typedefs.append(TypedefSymbolData( + name=var_name, + lineno=node.lineno, + annotation=node.annotation, + value=node.value, + has_cdefine=has_cdefine, + has_postdef=has_postdef, + has_ctypedef=has_ctypedef, + )) + + # ------------------------------------------------------------------ + # 函数提取 + # ------------------------------------------------------------------ + + def _extract_function(self, node: ast.FunctionDef) -> FuncSymbolData: + """从 FunctionDef 节点提取函数符号数据""" + params: list[tuple[str, ast.AST | None]] = [(arg.arg, arg.annotation) for arg in node.args.args] + is_variadic: bool = node.args.vararg is not None + is_inline: bool = CheckAnnotationHasCInline(node.returns) + + return FuncSymbolData( + name=node.name, + lineno=node.lineno, + returns_node=node.returns, + params=params, + is_variadic=is_variadic, + is_inline=is_inline, + ) diff --git a/lib/core/Translator/ConstEval.py b/lib/core/Translator/ConstEval.py index 9c452ea..2c86d04 100644 --- a/lib/core/Translator/ConstEval.py +++ b/lib/core/Translator/ConstEval.py @@ -1,318 +1,318 @@ -from __future__ import annotations - -import vast as ast -from typing import TYPE_CHECKING -import llvmlite.ir as ir - -from lib.core.ConstEvaluator import ConstEvaluator - -if TYPE_CHECKING: - from lib.core.LlvmCodeGenerator import LlvmCodeGenerator - - -class ConstEvalMixin: - """常量表达式求值 Mixin - - 提供数组初始化常量构建与常量表达式求值能力。 - """ - - @staticmethod - def _bswap_constant_value(val: int, width: int) -> int: - """编译期字节序交换""" - if width == 16: - return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF) - elif width == 32: - return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF) - elif width == 64: - return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF) - return val - - def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None: - _zv: ir.Constant | None = Gen._zero_value_for_type(ElemType) - _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined) - if not isinstance(value_node, (ast.List, ast.Set)): - return None - # 显式空元素判据(与 TPV AssignGenMixin._init_global_array_from_list 的裸空指针 - # 判据两侧一致):Python 侧的空 AST 元素就是 None(TPV 侧是裸 0)。列表元素计数 - # 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值; - # 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。 - for _gi, _elt in enumerate(value_node.elts): - if _elt is None: - raise TypeError( - f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。' - f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);' - f'拒绝静默补零生成残缺初始化,已终止编译。' - f'位置 line {getattr(value_node, "lineno", 0)}' - f':{getattr(value_node, "col_offset", 0)}' - ) - if isinstance(ElemType, ir.ArrayType): - constants: list[ir.Constant] = [] - for elt in value_node.elts: - if isinstance(elt, (ast.List, ast.Set)): - inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen) - if inner_consts: - try: - constants.append(ir.Constant(ElemType, inner_consts)) - except Exception: # fallback to _fallback on constant construction failure - constants.append(_fallback) - else: - constants.append(_fallback) - else: - constants.append(_fallback) - while len(constants) < ArrayCount: - constants.append(_fallback) - return constants[:ArrayCount] - StructName: str | None = None - if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: - StructName = elem_type_node.id - constants = [] - for elt in value_node.elts: - if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName: - const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen) - if const: - constants.append(const) - else: - constants.append(_fallback) - elif isinstance(elt, ast.Constant): - # 字符串常量:根据引号类型决定 i8 或 i8* - if isinstance(elt.value, str): - const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen) - if const: - constants.append(const) - else: - constants.append(_fallback) - else: - const = self._BuildScalarConstantUnified(elt, ElemType, Gen) - if const: - constants.append(const) - else: - constants.append(_fallback) - elif isinstance(elt, ast.UnaryOp): - const = self._BuildScalarConstantUnified(elt, ElemType, Gen) - if const: - constants.append(const) - else: - constants.append(_fallback) - elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr': - if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int): - try: - constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF)) - except Exception: # fallback to _fallback on constant construction failure - constants.append(_fallback) - else: - constants.append(_fallback) - elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord': - if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1: - try: - constants.append(ir.Constant(ElemType, ord(elt.args[0].value))) - except Exception: # fallback to _fallback on constant construction failure - constants.append(_fallback) - else: - constants.append(_fallback) - else: - const_val: int | float | str | None = self._try_eval_const_call(elt, Gen) - if const_val is not None: - try: - constants.append(ir.Constant(ElemType, const_val)) - except Exception: # fallback to _fallback on constant construction failure - constants.append(_fallback) - else: - constants.append(_fallback) - while len(constants) < ArrayCount: - constants.append(_fallback) - if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64): - swapped: list[ir.Constant] = [] - for c in constants: - if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int): - swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width) - swapped.append(ir.Constant(ElemType, swapped_val)) - else: - swapped.append(c) - constants = swapped - return constants[:ArrayCount] - - def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None: - """根据引号类型决定字符串常量的类型: - - - 单引号单字符 → i8 (char 值) - - 双引号/单引号多字符/三重引号 → i8* (C 字符串指针) - - 空字符串 → 0 (None),传入 ptr 目标自动隐式转换 - """ - if not isinstance(node.value, str): - return None - s: str = node.value - # 空字符串 → 0 (None) - if len(s) == 0: - try: - return ir.Constant(target_type, 0) - except Exception: - return None - # 获取源代码以判断引号类型 - quote_char: str | None = None - is_triple: bool = False - col_offset: int = getattr(node, 'col_offset', 0) - node_lineno: int = getattr(node, 'lineno', 0) - # AST 的 lineno 可能不准确(常量节点可能指向下一行), - # 因此在 lineno 和 lineno-1 两行中查找引号 - for try_lineno in (node_lineno, node_lineno - 1): - source_line: str | None = Gen._get_source_line(try_lineno) - if source_line and col_offset < len(source_line): - ch: str = source_line[col_offset] - if ch in ("'", '"'): - quote_char = ch - if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'): - is_triple = True - break - # 判断是否为 char (i8):单引号、非三重、单字符 - is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1) - if is_char: - # 单引号单字符 → i8 - if isinstance(target_type, ir.IntType): - try: - return ir.Constant(target_type, ord(s)) - except Exception: - return None - # 目标类型不是整数 → 类型不匹配 - return None - else: - # 双引号/多字符/三重 → i8* (C 字符串) - if isinstance(target_type, ir.PointerType): - str_val: str = s + '\x00' - str_bytes: bytes = str_val.encode('utf-8') - arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) - gv_name: str = f"str_const_{id(node)}" - gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) - gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) - gv.linkage = 'internal' - return ir.Constant(target_type, gv.get_reference()) - # 目标类型不是指针 → 类型不匹配 - return None - - def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None: - """统一的标量常量构建方法。 - - 合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性: - - 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False) - - 字符串处理委托给 _BuildStringLiteralConstant - - 显式拒绝 BaseStructType - - 全部 try/except 保护 - """ - # 显式拒绝结构体类型 - if isinstance(target_type, ir.BaseStructType): - return None - # 处理常量节点 - if isinstance(node, ast.Constant): - # bool 必须在 int 之前判断(bool 是 int 的子类) - if isinstance(node.value, bool): - try: - return ir.Constant(target_type, 1 if node.value else 0) - except Exception: - return None - elif isinstance(node.value, int): - try: - return ir.Constant(target_type, node.value) - except Exception: - return None - elif isinstance(node.value, float): - try: - return ir.Constant(target_type, node.value) - except Exception: - return None - elif isinstance(node.value, str): - # 字符串处理委托给 _BuildStringLiteralConstant - const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen) - if const is not None: - return const - # 回退:直接创建字符串全局变量(i8*) - try: - return Gen._create_string_global(node.value) - except Exception: - return None - # 处理一元负号 - elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub): - if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int): - try: - return ir.Constant(target_type, -node.operand.value) - except Exception: - return None - # 处理 True/False 名称节点 - elif isinstance(node, ast.Name): - if node.id == 'True': - try: - return ir.Constant(target_type, 1) - except Exception: - return None - elif node.id == 'False': - try: - if isinstance(target_type, ir.PointerType): - return ir.Constant(target_type, None) - return ir.Constant(target_type, 0) - except Exception: - return None - return None - - def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None: - if isinstance(node, ast.Call): - func_name: str | None = None - if isinstance(node.func, ast.Name): - func_name = node.func.id - elif isinstance(node.func, ast.Attribute): - func_name = node.func.attr - if func_name: - arg_vals: list[int | float | str | None] = [] - for arg in node.args: - v: int | float | str | None = self._try_eval_const_expr(arg, Gen) - if v is None: - return None - arg_vals.append(v) - if func_name == 'COLOR_RGB' and len(arg_vals) == 3: - r: int | float | str | None - g: int | float | str | None - b: int | float | str | None - r, g, b = arg_vals - return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r) - if func_name in Gen._DefineConstants: - return Gen._DefineConstants[func_name] - for key in Gen.functions: - if key.endswith(f'__{func_name}'): - return None - return None - return self._try_eval_const_expr(node, Gen) - - def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None: - return ConstEvaluator.eval_with_symtab(node, self.SymbolTable) - - def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None: - inner_type: ir.Type = ArrayType.element - count: int = ArrayType.count - elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else [] - _zv: ir.Constant | None = Gen._zero_value_for_type(inner_type) - _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined) - constants: list[ir.Constant] = [] - for elt in elts: - if isinstance(inner_type, ir.ArrayType): - if isinstance(elt, (ast.List, ast.Set)): - inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen) - if inner_consts: - constants.append(ir.Constant(inner_type, inner_consts)) - else: - constants.append(_fallback) - else: - constants.append(_fallback) - elif isinstance(elt, ast.Constant): - const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen) - if const: - constants.append(const) - else: - constants.append(_fallback) - elif isinstance(elt, ast.UnaryOp): - const = self._BuildScalarConstantUnified(elt, inner_type, Gen) - if const: - constants.append(const) - else: - constants.append(_fallback) - else: - constants.append(_fallback) - while len(constants) < count: - constants.append(_fallback) - return constants[:count] +from __future__ import annotations + +import vast as ast +from typing import TYPE_CHECKING +import llvmlite.ir as ir + +from lib.core.ConstEvaluator import ConstEvaluator + +if TYPE_CHECKING: + from lib.core.LlvmCodeGenerator import LlvmCodeGenerator + + +class ConstEvalMixin: + """常量表达式求值 Mixin + + 提供数组初始化常量构建与常量表达式求值能力。 + """ + + @staticmethod + def _bswap_constant_value(val: int, width: int) -> int: + """编译期字节序交换""" + if width == 16: + return ((val & 0xFF) << 8) | ((val >> 8) & 0xFF) + elif width == 32: + return ((val & 0xFF) << 24) | ((val & 0xFF00) << 8) | ((val >> 8) & 0xFF00) | ((val >> 24) & 0xFF) + elif width == 64: + return ((val & 0xFF) << 56) | ((val & 0xFF00) << 40) | ((val & 0xFF0000) << 24) | ((val & 0xFF000000) << 8) | ((val >> 8) & 0xFF000000) | ((val >> 24) & 0xFF0000) | ((val >> 40) & 0xFF00) | ((val >> 56) & 0xFF) + return val + + def _BuildArrayInitConstants(self, value_node: ast.AST, ElemType: ir.Type, elem_type_node: ast.AST, ArrayCount: int, Gen: LlvmCodeGenerator, byte_order: str = '') -> list[ir.Constant] | None: + _zv: ir.Constant | None = Gen._zero_value_for_type(ElemType) + _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(ElemType, ir.Undefined) + if not isinstance(value_node, (ast.List, ast.Set)): + return None + # 显式空元素判据(与 VPC AssignGenMixin._init_global_array_from_list 的裸空指针 + # 判据两侧一致):Python 侧的空 AST 元素就是 None(VPC 侧是裸 0)。列表元素计数 + # 与实际填充不一致(存在 None 槽)时,按下标取元素会静默产出错误常量/回退值; + # 故在此响亮失败(不静默跳过、不静默补 fallback),并指名下标与位置。 + for _gi, _elt in enumerate(value_node.elts): + if _elt is None: + raise TypeError( + f'[TransPyC] 数组初始化列表字面量第 {_gi} 个元素为空 AST 元素(None)。' + f'上层某处产生了空 AST 元素(元素计数与实际填充不一致);' + f'拒绝静默补零生成残缺初始化,已终止编译。' + f'位置 line {getattr(value_node, "lineno", 0)}' + f':{getattr(value_node, "col_offset", 0)}' + ) + if isinstance(ElemType, ir.ArrayType): + constants: list[ir.Constant] = [] + for elt in value_node.elts: + if isinstance(elt, (ast.List, ast.Set)): + inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, ElemType, Gen) + if inner_consts: + try: + constants.append(ir.Constant(ElemType, inner_consts)) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + else: + constants.append(_fallback) + while len(constants) < ArrayCount: + constants.append(_fallback) + return constants[:ArrayCount] + StructName: str | None = None + if isinstance(elem_type_node, ast.Name) and elem_type_node.id in Gen.structs: + StructName = elem_type_node.id + constants = [] + for elt in value_node.elts: + if StructName and isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == StructName: + const: ir.Constant | None = self.ClassHandler._BuildStructConstant(elt, StructName, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Constant): + # 字符串常量:根据引号类型决定 i8 或 i8* + if isinstance(elt.value, str): + const: ir.Constant | None = self._BuildStringLiteralConstant(elt, ElemType, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + else: + const = self._BuildScalarConstantUnified(elt, ElemType, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.UnaryOp): + const = self._BuildScalarConstantUnified(elt, ElemType, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'chr': + if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, int): + try: + constants.append(ir.Constant(ElemType, elt.args[0].value & 0xFF)) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Call) and isinstance(elt.func, ast.Name) and elt.func.id == 'ord': + if elt.args and isinstance(elt.args[0], ast.Constant) and isinstance(elt.args[0].value, str) and len(elt.args[0].value) == 1: + try: + constants.append(ir.Constant(ElemType, ord(elt.args[0].value))) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + else: + const_val: int | float | str | None = self._try_eval_const_call(elt, Gen) + if const_val is not None: + try: + constants.append(ir.Constant(ElemType, const_val)) + except Exception: # fallback to _fallback on constant construction failure + constants.append(_fallback) + else: + constants.append(_fallback) + while len(constants) < ArrayCount: + constants.append(_fallback) + if byte_order == 'big' and isinstance(ElemType, ir.IntType) and ElemType.width in (16, 32, 64): + swapped: list[ir.Constant] = [] + for c in constants: + if isinstance(c, ir.Constant) and isinstance(c.type, ir.IntType) and isinstance(c.constant, int): + swapped_val: int = self._bswap_constant_value(int(c.constant), ElemType.width) + swapped.append(ir.Constant(ElemType, swapped_val)) + else: + swapped.append(c) + constants = swapped + return constants[:ArrayCount] + + def _BuildStringLiteralConstant(self, node: ast.Constant, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None: + """根据引号类型决定字符串常量的类型: + + - 单引号单字符 → i8 (char 值) + - 双引号/单引号多字符/三重引号 → i8* (C 字符串指针) + - 空字符串 → 0 (None),传入 ptr 目标自动隐式转换 + """ + if not isinstance(node.value, str): + return None + s: str = node.value + # 空字符串 → 0 (None) + if len(s) == 0: + try: + return ir.Constant(target_type, 0) + except Exception: + return None + # 获取源代码以判断引号类型 + quote_char: str | None = None + is_triple: bool = False + col_offset: int = getattr(node, 'col_offset', 0) + node_lineno: int = getattr(node, 'lineno', 0) + # AST 的 lineno 可能不准确(常量节点可能指向下一行), + # 因此在 lineno 和 lineno-1 两行中查找引号 + for try_lineno in (node_lineno, node_lineno - 1): + source_line: str | None = Gen._get_source_line(try_lineno) + if source_line and col_offset < len(source_line): + ch: str = source_line[col_offset] + if ch in ("'", '"'): + quote_char = ch + if col_offset + 2 < len(source_line) and source_line[col_offset:col_offset + 3] in ("'''", '"""'): + is_triple = True + break + # 判断是否为 char (i8):单引号、非三重、单字符 + is_char: bool = (not is_triple) and (quote_char == "'") and (len(s) == 1) + if is_char: + # 单引号单字符 → i8 + if isinstance(target_type, ir.IntType): + try: + return ir.Constant(target_type, ord(s)) + except Exception: + return None + # 目标类型不是整数 → 类型不匹配 + return None + else: + # 双引号/多字符/三重 → i8* (C 字符串) + if isinstance(target_type, ir.PointerType): + str_val: str = s + '\x00' + str_bytes: bytes = str_val.encode('utf-8') + arr_type: ir.ArrayType = ir.ArrayType(ir.IntType(8), len(str_bytes)) + gv_name: str = f"str_const_{id(node)}" + gv: ir.GlobalVariable = ir.GlobalVariable(Gen.module, arr_type, name=gv_name) + gv.initializer = ir.Constant(arr_type, bytearray(str_bytes)) + gv.linkage = 'internal' + return ir.Constant(target_type, gv.get_reference()) + # 目标类型不是指针 → 类型不匹配 + return None + + def _BuildScalarConstantUnified(self, node: ast.AST, target_type: ir.Type, Gen: LlvmCodeGenerator) -> ir.Constant | None: + """统一的标量常量构建方法。 + + 合并原 HandlesClassDef._BuildScalarConstant 与 HandlesImports._BuildScalarConstant 的特性: + - 支持 bool/int/float/str/ast.UnaryOp(USub)/ast.Name(True/False) + - 字符串处理委托给 _BuildStringLiteralConstant + - 显式拒绝 BaseStructType + - 全部 try/except 保护 + """ + # 显式拒绝结构体类型 + if isinstance(target_type, ir.BaseStructType): + return None + # 处理常量节点 + if isinstance(node, ast.Constant): + # bool 必须在 int 之前判断(bool 是 int 的子类) + if isinstance(node.value, bool): + try: + return ir.Constant(target_type, 1 if node.value else 0) + except Exception: + return None + elif isinstance(node.value, int): + try: + return ir.Constant(target_type, node.value) + except Exception: + return None + elif isinstance(node.value, float): + try: + return ir.Constant(target_type, node.value) + except Exception: + return None + elif isinstance(node.value, str): + # 字符串处理委托给 _BuildStringLiteralConstant + const: ir.Constant | None = self._BuildStringLiteralConstant(node, target_type, Gen) + if const is not None: + return const + # 回退:直接创建字符串全局变量(i8*) + try: + return Gen._create_string_global(node.value) + except Exception: + return None + # 处理一元负号 + elif isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub): + if isinstance(node.operand, ast.Constant) and isinstance(node.operand.value, int): + try: + return ir.Constant(target_type, -node.operand.value) + except Exception: + return None + # 处理 True/False 名称节点 + elif isinstance(node, ast.Name): + if node.id == 'True': + try: + return ir.Constant(target_type, 1) + except Exception: + return None + elif node.id == 'False': + try: + if isinstance(target_type, ir.PointerType): + return ir.Constant(target_type, None) + return ir.Constant(target_type, 0) + except Exception: + return None + return None + + def _try_eval_const_call(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | None: + if isinstance(node, ast.Call): + func_name: str | None = None + if isinstance(node.func, ast.Name): + func_name = node.func.id + elif isinstance(node.func, ast.Attribute): + func_name = node.func.attr + if func_name: + arg_vals: list[int | float | str | None] = [] + for arg in node.args: + v: int | float | str | None = self._try_eval_const_expr(arg, Gen) + if v is None: + return None + arg_vals.append(v) + if func_name == 'COLOR_RGB' and len(arg_vals) == 3: + r: int | float | str | None + g: int | float | str | None + b: int | float | str | None + r, g, b = arg_vals + return (255 << 24) | (int(b) << 16) | (int(g) << 8) | int(r) + if func_name in Gen._DefineConstants: + return Gen._DefineConstants[func_name] + for key in Gen.functions: + if key.endswith(f'__{func_name}'): + return None + return None + return self._try_eval_const_expr(node, Gen) + + def _try_eval_const_expr(self, node: ast.AST, Gen: LlvmCodeGenerator) -> int | float | str | None: + return ConstEvaluator.eval_with_symtab(node, self.SymbolTable) + + def _BuildMultiDimArrayInitConstants(self, value_node: ast.AST, ArrayType: ir.ArrayType, Gen: LlvmCodeGenerator) -> list[ir.Constant] | None: + inner_type: ir.Type = ArrayType.element + count: int = ArrayType.count + elts: list[ast.AST] = list(value_node.elts) if hasattr(value_node, 'elts') else [] + _zv: ir.Constant | None = Gen._zero_value_for_type(inner_type) + _fallback: ir.Constant = _zv if _zv is not None else ir.Constant(inner_type, ir.Undefined) + constants: list[ir.Constant] = [] + for elt in elts: + if isinstance(inner_type, ir.ArrayType): + if isinstance(elt, (ast.List, ast.Set)): + inner_consts: list[ir.Constant] | None = self._BuildMultiDimArrayInitConstants(elt, inner_type, Gen) + if inner_consts: + constants.append(ir.Constant(inner_type, inner_consts)) + else: + constants.append(_fallback) + else: + constants.append(_fallback) + elif isinstance(elt, ast.Constant): + const: ir.Constant | None = self._BuildScalarConstantUnified(elt, inner_type, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + elif isinstance(elt, ast.UnaryOp): + const = self._BuildScalarConstantUnified(elt, inner_type, Gen) + if const: + constants.append(const) + else: + constants.append(_fallback) + else: + constants.append(_fallback) + while len(constants) < count: + constants.append(_fallback) + return constants[:count] diff --git a/lib/core/Translator/LlvmGenerator.py b/lib/core/Translator/LlvmGenerator.py index a82a23f..9260423 100644 --- a/lib/core/Translator/LlvmGenerator.py +++ b/lib/core/Translator/LlvmGenerator.py @@ -1,779 +1,779 @@ -from __future__ import annotations - -import vast as ast -import re -import llvmlite.ir as ir - -from lib.core.LlvmCodeGenerator import LlvmCodeGenerator -from lib.core.Handles.HandlesBase import CTypeInfo -from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, CTypeHelper -from lib.includes import t -from lib.constants.config import mode as _config_mode -from lib.core.VLogger import get_logger as _vlog -from lib.core.DecoratorPass import run as _run_decorator_pass +from __future__ import annotations + +import vast as ast +import re +import llvmlite.ir as ir + +from lib.core.LlvmCodeGenerator import LlvmCodeGenerator +from lib.core.Handles.HandlesBase import CTypeInfo +from lib.core.Handles.HandlesBase import CTypeInfo as _CTypeInfo, CTypeHelper +from lib.includes import t +from lib.constants.config import mode as _config_mode +from lib.core.VLogger import get_logger as _vlog +from lib.core.DecoratorPass import run as _run_decorator_pass from lib.core.SymbolUtils import ( IsTModule, AnnotationContainsName, RequireDefineConstStorable, DefineIntWidthOf, DefineIntSignednessOf, -) -# 治本修复:导入 ConstEvaluator 用于预扫描 CDefine 常量的复杂表达式求值 -from lib.core.ConstEvaluator import ConstEvaluator - -# `_InsertModuleLevelLLVMIR` 解析模块级 LLVM IR 文本用的**预编译**正则 -# (性能:该函数逐行解析 IR,字面量正则在调用点每次都要做模式串哈希与 re 缓存查找)。 -_RE_MODULE_FLAGS: re.Pattern[str] = re.compile(r'!llvm\.module\.flags\s*=\s*!\{(!\d+)\}') -_RE_METADATA_DEF: re.Pattern[str] = re.compile(r'(!\d+)\s*=\s*!\{(.+)\}') -_RE_METADATA_FIELD_SEP: re.Pattern[str] = re.compile(r',\s*') -_RE_METADATA_FIELD_I32: re.Pattern[str] = re.compile(r'^i32\s+(\d+)$') -_RE_METADATA_FIELD_STR: re.Pattern[str] = re.compile(r'^"([^"]*)"$') -_RE_METADATA_FIELD_MDSTR: re.Pattern[str] = re.compile(r'^!"([^"]*)"$') -_RE_METADATA_FIELD_INT: re.Pattern[str] = re.compile(r'^(\d+)$') -_RE_METADATA_FIELD_REF: re.Pattern[str] = re.compile(r'^(!\d+)$') - - -class LlvmGeneratorMixin: - """LLVM IR 生成 Mixin - - 提供 LLVM IR 直接生成、模块级 LLVM IR 处理与 C 代码生成入口能力。 - """ - - def GenerateLlvmDirect(self, Tree: ast.Module) -> str: - Gen: LlvmCodeGenerator = self.LlvmGen - - # === 新增:轻量 AST 遍历,收集类图信息,解决前向引用 === - Gen._class_graph: dict[str, dict[str, bool | list[str]]] = {} - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.ClassDef): - bases: list[str] = [] - for base in Node.bases: - if hasattr(base, 'id'): - bases.append(base.id) - elif hasattr(base, 'attr'): - bases.append(base.attr) - has_methods: bool = any(isinstance(item, ast.FunctionDef) for item in Node.body) - # 检测 @t.NoVTable 装饰器(非多态继承基类标记) - has_novtable: bool = False - if hasattr(Node, 'decorator_list') and Node.decorator_list: - for decorator in Node.decorator_list: - if isinstance(decorator, ast.Attribute): - if getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable': - has_novtable = True - elif isinstance(decorator, ast.Name): - if decorator.id == 'NoVTable': - has_novtable = True - Gen._class_graph[Node.name] = { - 'bases': bases, - 'has_methods': has_methods, - 'has_novtable': has_novtable, - } - # === 结束新增 === - - # 预扫描:为所有 -> str 注解的函数注册正确的返回类型 i8* - # 这样在类方法中调用这些函数时,前向声明会使用正确的类型 - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.FunctionDef): - if Node.returns and isinstance(Node.returns, ast.Name) and Node.returns.id == 'str': - Gen.known_return_types[Node.name] = ir.PointerType(ir.IntType(8)) - - # 首先收集所有 CDefine 常量,确保类定义中可以引用 - def _extract_const_value(node: ast.AST) -> int | str | float | None: - """求值 CDefine 常量表达式,支持 Constant、BinOp、UnaryOp、Name(引用其他 define)、Call(类型构造)。 - - 治本修复:原 _extract_call_const_val 只支持 t.XxxType(Constant) 形式, - 无法处理 FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE - 等引用其他 CDefine 的 BinOp 表达式,导致依赖常量无法被预扫描。 - 现使用 ConstEvaluator.eval_with_symtab 统一求值。 - """ - if isinstance(node, ast.Constant): - return node.value - # 使用 ConstEvaluator 处理 BinOp/UnaryOp/Name/Attribute/Call - return ConstEvaluator.eval_with_symtab(node, self.SymbolTable) - - def _is_cdefine_type(TypeInfo: CTypeInfo | None) -> bool: - """判断 CTypeInfo 是否对应 t.CDefine 注解""" - return bool(TypeInfo and TypeInfo.BaseType and - ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) - or isinstance(TypeInfo.BaseType, t.CDefine) or TypeInfo.IsDefine)) - - def _register_cdefine_constant(NameNode: ast.Name, Annotation: ast.expr, Value: ast.expr) -> None: - """注册 CDefine 编译期常量(_define_constants + SymbolTable)""" - TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Annotation) - if not _is_cdefine_type(TypeInfo): - return - val: int | str | float | None = _extract_const_value(Value) - if val is not None: - # fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储 - # (自动推导形态,本语言无 64 位以上整数类型)——此前超大值会被静默 - # 截断/生成非法 IR 文本。唯一判据见 SymbolUtils.RequireDefineIntStorable。 - RequireDefineConstStorable(Gen, self, NameNode, TypeInfo.BaseType, - Annotation, val, NameNode.id) - if not hasattr(Gen, '_define_constants'): - Gen._define_constants: dict[str, int | str | float | bool] = {} - Gen._define_constants[NameNode.id] = val - # 带类型注册:记录声明整数类型(位宽 + 符号性),供使用点按该类型物化 - # (如 `define X: DWORD = 5` → i32/u32,而不是按值大小猜 i32/i64)。 - if isinstance(val, int) and not isinstance(val, bool): - _w: int | None = DefineIntWidthOf(TypeInfo.BaseType) - if _w is not None: - _s: bool | None = DefineIntSignednessOf(TypeInfo.BaseType) - if not hasattr(Gen, '_define_decl_types'): - Gen._define_decl_types: dict[str, tuple[int, bool]] = {} - Gen._define_decl_types[NameNode.id] = (_w, bool(_s) if _s is not None else False) - existing = self.SymbolTable.lookup(NameNode.id) - if not (existing and existing.IsTypedef and existing.OriginalType): - sym_info: _CTypeInfo = _CTypeInfo() - sym_info.IsDefine = True - sym_info.DefineValue = val - self.SymbolTable.insert(NameNode.id, sym_info) - - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name) and Node.value: - TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) - # 治本修复:检查 BaseType 是否为 t.CDefine 实例/子类,或 IsDefine 标志 - # IsDefine 由 GetCTypeInfo 对 t.CDefine 注解自动设置 - if _is_cdefine_type(TypeInfo): - _register_cdefine_constant(Node.target, Node.annotation, Node.value) - elif isinstance(Node, ast.ClassDef): - # 类体 CDefine 常量(2026-08-10):供方法内裸名(如 Mixin 的 - # ISINSTANCE_CLASS_MAX/MRO_MAX: t.CDefine = N)编译期解析为常量值。 - # 原预扫描只用 iter_child_nodes 扫顶层,类体常量不注册 → 方法内 - # 裸名解析为 0 → pool.alloc(8*0) 失败(TPV 编译测试 SUBS-NONE)。 - for _citem in Node.body: - if isinstance(_citem, ast.AnnAssign) and isinstance(_citem.target, ast.Name) and _citem.value: - _register_cdefine_constant(_citem.target, _citem.annotation, _citem.value) - elif isinstance(Node, ast.Assign): - for target in Node.targets: - if isinstance(target, ast.Name): - TypeInfo: CTypeInfo | None = None - if Node.type_comment: - try: - tc_ast: ast.Expression = ast.parse(Node.type_comment, '', mode='eval') - TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body) - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析类型注释失败: {_e}", "Exception") - if TypeInfo and TypeInfo.BaseType and ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) or isinstance(TypeInfo.BaseType, t.CDefine)): - val: int | str | float | None = _extract_const_value(Node.value) - if val is not None: - if not hasattr(Gen, '_define_constants'): - Gen._define_constants: dict[str, int | str | float | bool] = {} - Gen._define_constants[target.id] = val - sym_info: _CTypeInfo = _CTypeInfo() - sym_info.IsDefine = True - sym_info.DefineValue = val - self.SymbolTable.insert(target.id, sym_info) - - # 预扫描所有顶层函数定义,填充 FunctionDefCache(不创建 LLVM 声明) - # 这样类方法编译时遇到未声明的函数可以按需前向声明 - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.FunctionDef): - FuncName: str = Node.name - if FuncName not in self.FunctionDefCache: - self.FunctionDefCache[FuncName] = Node - elif isinstance(Node, ast.AsyncFunctionDef): - # 协程:注册到 AsyncHandler(生成 create_/resume_,不进 FunctionDefCache, - # 避免被当作普通函数前向声明) - self.AsyncHandler._RegisterAsyncFunc(Node.name, Node) - - # 首先处理所有导入语句,确保模块在类型解析前被加载 - for Node in ast.iter_child_nodes(Tree): - Gen._set_node_info(Node) - try: - if isinstance(Node, ast.Import): - self.ImportHandler._EmitImportDeclarationsLlvm(Node, Gen) - elif isinstance(Node, ast.ImportFrom): - self.ImportHandler._EmitImportFromDeclarationsLlvm(Node, Gen) - except Exception as e: - # fail-fast:import 模块不存在(ImportError)必须立即终止编译, - # 不能吞进 _ErrorStack(该栈无消费点,静默容忍导致 import 路径 - # 错误延迟到链接期才暴露,如 ClassGenGeneric 漏改 import 路径)。 - if isinstance(e, ImportError): - raise - lineno: int = getattr(Node, 'lineno', 0) - # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, - # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator); - # 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。 - _line_offset: int = int(getattr(self, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - source_line: str | None = Gen._get_source_line(lineno) - src_file: str = getattr(Gen, '_current_source_file', '') - src_path: str = src_file if src_file else 'unknown' - line_info: str = "源代码 (%s, line %d)" % (src_path, lineno) - if source_line: - line_info += ":\n %d | %s" % (lineno, source_line) - self._ErrorStack.append((type(e).__name__, str(e), line_info)) - - # 本模块泛型类模板必须在前置声明之前注册(与 includes 侧注册判据同源, - # 见 HandlesClassDef.PrescanModuleGenericClassTemplates):前置声明要解析 - # 参数/返回注解里的泛型类下标(`def f(b: Box[Node])`),而类处理阶段 - # (下方 _EmitClassLlvm)才注册模板 → 注解解析为空 → VoidType → - # "无法识别的参数类型注解"。放在 import 扫描之后,保证本地类覆盖同名 - # 导入类(与 _EmitClassLlvm 的覆盖顺序一致)。 - self.ClassHandler.PrescanModuleGenericClassTemplates(ast.iter_child_nodes(Tree)) - - # 前向声明所有顶层函数,确保全局变量初始化时能引用函数指针 - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.FunctionDef): - Gen._set_node_info(Node) - self.FunctionHandler._EmitFunctionForwardDeclLlvm(Node, Gen) - - # 预创建全局变量(带初始化器),确保类方法编译时能引用全局数组等 - Gen._current_tree: ast.Module = Tree - for Node in ast.iter_child_nodes(Tree): - Gen._set_node_info(Node) - if isinstance(Node, ast.AnnAssign): - self.AssignHandler._EmitGlobalAnnAssignLlvm(Node, Gen) - elif isinstance(Node, ast.Assign): - self.AssignHandler._EmitGlobalAssignLlvm(Node, Gen) - - # 首先处理所有类定义(包括 CUnion),确保类型定义在使用前完成 - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.ClassDef): - Gen._set_node_info(Node) - self.ClassHandler._EmitClassLlvm(Node, Gen) - - Gen._generate_structs() - Gen._create_Vtable_globals() - - for Node in ast.iter_child_nodes(Tree): - Gen._set_node_info(Node) - try: - if isinstance(Node, ast.Import): - continue - elif isinstance(Node, ast.ImportFrom): - continue - elif isinstance(Node, ast.ClassDef): - continue - elif isinstance(Node, ast.FunctionDef): - self.FunctionHandler._EmitFunctionLlvm(Node, Gen) - elif isinstance(Node, ast.AsyncFunctionDef): - self.AsyncHandler._EmitAsyncFunctionLlvm(Node, Gen) - elif isinstance(Node, ast.AnnAssign): - self.AssignHandler._EmitGlobalAnnAssignLlvm(Node, Gen) - elif isinstance(Node, ast.Assign): - self.AssignHandler._EmitGlobalAssignLlvm(Node, Gen) - elif isinstance(Node, ast.If): - pass - elif isinstance(Node, ast.Expr): - self._HandleModuleLevelExprLlvm(Node, Gen) - except Exception as e: - lineno: int = getattr(Node, 'lineno', 0) - # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, - # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator); - # 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。 - _line_offset: int = int(getattr(self, '_line_offset', 0) or 0) - if _line_offset > 0 and lineno > _line_offset: - lineno -= _line_offset - source_line: str | None = Gen._get_source_line(lineno) - src_file: str = getattr(Gen, '_current_source_file', '') - src_path: str = src_file if src_file else 'unknown' - line_info: str = "源代码 (%s, line %d)" % (src_path, lineno) - if source_line: - line_info += ":\n %d | %s" % (lineno, source_line) - self._ErrorStack.append((type(e).__name__, str(e), line_info)) - raise - Gen._set_Vtable_initializers() - # 执行 DecoratorPass:为带自定义装饰器的函数生成 wrapper - _run_decorator_pass(Gen) - # 调用点热点统计与每函数计时(--hotspot-sites):在装饰器 wrapper 生成之后 - # 统一收口;未启用时不执行(零影响)。hotspot_sites_module_local=True 走 - # Projectrans 多模块路径(每模块 @llvm.global_ctors 自注册),否则走种子 - # CLI 单模块路径(在 main 入口注册)。 - if Gen.hotspot_sites_file: - if Gen.hotspot_sites_module_local: - Gen._InstallHotspotSitesModuleLocal() - else: - Gen._InstallHotspotSites() - ir_code: str = Gen.finalize() - return ir_code - - def _HandleModuleLevelExprLlvm(self, Node: ast.Expr, Gen: LlvmCodeGenerator) -> None: - if not isinstance(Node.value, ast.Call): - return - call: ast.Call = Node.value - if isinstance(call.func, ast.Name): - # 模块级裸名 __llvmir__(...) 等价 c.LLVMIR(...) - if call.func.id == '__llvmir__': - self._HandleModuleLevelLLVMIR(call, Gen) - return - if not isinstance(call.func, ast.Attribute): - return - if not isinstance(call.func.value, ast.Name) or call.func.value.id != 'c': - return - func_name: str = call.func.attr - if func_name == 'LLVMIR': - self._HandleModuleLevelLLVMIR(call, Gen) - - def _HandleModuleLevelLLVMIR(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> None: - if not Node.args: - return - first_arg: ast.expr = Node.args[0] - if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): - ir_text: str = first_arg.value - elif isinstance(first_arg, ast.JoinedStr): - parts: list[str] = [] - for value in first_arg.values: - if isinstance(value, ast.Constant) and isinstance(value.value, str): - parts.append(value.value) - elif isinstance(value, ast.FormattedValue): - parts.append('') - ir_text = ''.join(parts) - else: - return - self._InsertModuleLevelLLVMIR(Gen, ir_text) - - def _InsertModuleLevelLLVMIR(self, Gen: LlvmCodeGenerator, ir_text: str) -> None: - for line in ir_text.strip().split('\n'): - line = line.strip() - if not line: - continue - module_flags_match: re.Match[str] | None = _RE_MODULE_FLAGS.match(line) - if module_flags_match: - ref: str = module_flags_match.group(1) - if not hasattr(Gen, '_pending_module_flags'): - Gen._pending_module_flags: list = [] - Gen._pending_module_flags_refs: str = ref - continue - metadata_match: re.Match[str] | None = _RE_METADATA_DEF.match(line) - if metadata_match: - md_id: str = metadata_match.group(1) - md_body: str = metadata_match.group(2) - fields: list[ir.Constant | ir.MetaDataString | str] = [] - for field_str in _RE_METADATA_FIELD_SEP.split(md_body): - field_str = field_str.strip() - if not field_str: - continue - int_m: re.Match[str] | None = _RE_METADATA_FIELD_I32.match(field_str) - if int_m: - fields.append(ir.Constant(ir.IntType(32), int(int_m.group(1)))) - continue - str_m: re.Match[str] | None = _RE_METADATA_FIELD_STR.match(field_str) - if str_m: - fields.append(ir.MetaDataString(Gen.module, str_m.group(1))) - continue - mdstr_m: re.Match[str] | None = _RE_METADATA_FIELD_MDSTR.match(field_str) - if mdstr_m: - fields.append(ir.MetaDataString(Gen.module, mdstr_m.group(1))) - continue - int_m2: re.Match[str] | None = _RE_METADATA_FIELD_INT.match(field_str) - if int_m2: - fields.append(ir.Constant(ir.IntType(32), int(int_m2.group(1)))) - continue - ref_m: re.Match[str] | None = _RE_METADATA_FIELD_REF.match(field_str) - if ref_m: - fields.append(ref_m.group(1)) - continue - if fields: - if not hasattr(Gen, '_pending_metadata'): - Gen._pending_metadata: list[tuple[str, list]] = [] - Gen._pending_metadata.append((md_id, fields)) - continue - self._FlushModuleMetadata(Gen) - - def _FlushModuleMetadata(self, Gen: LlvmCodeGenerator) -> None: - if not hasattr(Gen, '_pending_metadata') or not Gen._pending_metadata: - return - md_map: dict[str, ir.MetaDataString] = {} - for md_id, fields in Gen._pending_metadata: - resolved: list[ir.Constant | ir.MetaDataString] = [] - for f in fields: - if isinstance(f, str) and f.startswith('!'): - ref: ir.MetaDataString | None = md_map.get(f) - if ref: - resolved.append(ref) - else: - resolved.append(f) - md_value: ir.MetaDataString = Gen.module.add_metadata(resolved) - md_map[md_id] = md_value - if hasattr(Gen, '_pending_module_flags_refs'): - ref: str = Gen._pending_module_flags_refs - md_node: ir.MetaDataString | None = md_map.get(ref) - if md_node: - Gen.module.add_named_metadata('llvm.module.flags', md_node) - Gen._pending_metadata = [] - Gen._pending_module_flags_refs = None - - def GenerateCCode(self, Tree: ast.Module, target: str = 'llvm') -> str: - """生成代码 - - Args: - Tree: Python AST 树 - target: 目标代码类型,仅支持 'llvm' - - Returns: - 生成的代码字符串 - """ - self._generated_vars: set[str] = set() - self.VarScopes = [{}] # 全局作用域 - - # 预扫描 import 语句,注册别名,确保类型解析时能正确解析模块别名 - if not getattr(self, '_ImportedModules', None): - self._ImportedModules: set[str] = set() - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.Import): - for alias in Node.names: - name: str = alias.name - if name in ('c', 't'): - continue - self._ImportedModules.add(name) - if alias.asname: - self.SymbolTable.import_aliases[alias.asname] = name - elif isinstance(Node, ast.ImportFrom): - module_name: str | None = Node.module - if module_name and module_name not in ('c', 't'): - self._ImportedModules.add(module_name) - for alias in Node.names: - if alias.asname: - self.SymbolTable.import_aliases[alias.asname] = f"{module_name}.{alias.name}" - - for Node in ast.iter_child_nodes(Tree): - if isinstance(Node, ast.ClassDef): - ClassName: str = Node.name - - # 检查是否是特殊类型(枚举或联合体) - IsSpecialType: bool = False - if Node.bases: - for base in Node.bases: - if hasattr(base, 'attr'): - if base.attr in ['CEnum', 'Enum', 'CUnion', 'REnum']: - IsSpecialType = True - break - elif hasattr(base, 'id'): - if base.id in ['CEnum', 'Enum', 'CUnion', 'REnum']: - IsSpecialType = True - break - - # 如果是特殊类型,跳过这里的处理(由 HandleClassDef 处理) - if IsSpecialType: - continue - - members: dict[str, CTypeInfo] = {} - IsTypedef: bool = False - TypedefName: str | None = None - - if hasattr(Node, 'annotation') and Node.annotation: - try: - if AnnotationContainsName(Node.annotation, 'CTypedef'): - IsTypedef = True - if isinstance(Node.annotation, ast.Call): - if Node.annotation.args: - if isinstance(Node.annotation.args[0], ast.Constant): - TypedefName = Node.annotation.args[0].value - except Exception as _e: - if _config_mode == "strict": - raise - _vlog().warning(f"解析 typedef 注解失败: {_e}", "Exception") - - if not IsTypedef: - for item in Node.body: - if isinstance(item, ast.Assign): - for target in item.targets: - if isinstance(target, ast.Name) and target.id == '__annotations__': - if isinstance(item.value, ast.Dict): - for key, value in zip(item.value.keys, item.value.values): - if isinstance(key, ast.Constant) and key.value == '__type__': - if AnnotationContainsName(value, 'CTypedef'): - IsTypedef = True - - if IsTypedef: - TypedefKey: str = TypedefName if TypedefName else ClassName - TypedefNode: CTypeInfo = CTypeInfo() - TypedefNode.Name = TypedefKey - TypedefNode.IsTypedef = True - TypedefNode.OriginalType = f'struct {ClassName}' - TypedefNode.set('IsComplete', True) - TypedefNode.file = '' - self.SymbolTable.insert(TypedefKey, TypedefNode) - - for item in Node.body: - if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): - VarName: str = item.target.id - try: - TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(item.annotation) - if TypeInfo is None: - TypeInfo = CTypeInfo() - TypeInfo.BaseType = t.CInt() - IsPtr: bool = TypeInfo.IsPtr - if not IsPtr: - if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): - if AnnotationContainsName(item.annotation, 'CPtr'): - IsPtr = True - dims: list[int] = [] - if hasattr(item.annotation, 'slice') and isinstance(item.annotation.slice, ast.Constant): - if isinstance(item.annotation.value, ast.Name): - if IsTModule(item.annotation.value.id, self.SymbolTable): - if hasattr(item.annotation, 'slice'): - try: - dims.append(int(item.annotation.slice.value)) - except Exception as _e: - if _config_mode == "strict": - self.LogWarning(f"异常被忽略: {_e}") - MemberInfo: CTypeInfo = TypeInfo.Copy() - if dims: - MemberInfo.ArrayDims = [str(d) for d in dims] - members[VarName] = MemberInfo - except Exception as _e: - if _config_mode == "strict": - self.LogWarning(f"异常被忽略: {_e}") - - if not IsTypedef: - StructNode: CTypeInfo = CTypeInfo() - StructNode.Name = ClassName - StructNode.IsStruct = True - StructNode.Members = members or {} - StructNode.file = '' - existing: CTypeInfo | None = self.SymbolTable.lookup(ClassName) - if existing: - if hasattr(existing, 'IsCpythonObject') and existing.IsCpythonObject: - StructNode.IsCpythonObject = True - self.SymbolTable.insert(ClassName, StructNode) - elif isinstance(Node, ast.FunctionDef): - FuncName: str = Node.name - if not self.SymbolTable.has(FuncName): - FuncNode: CTypeInfo = CTypeInfo() - FuncNode.Name = FuncName - FuncNode.IsFunction = True - FuncNode.file = '' - self.SymbolTable.insert(FuncName, FuncNode) - elif isinstance(Node, ast.AsyncFunctionDef): - FuncName = Node.name - if not self.SymbolTable.has(FuncName): - FuncNode = CTypeInfo() - FuncNode.Name = FuncName - FuncNode.IsFunction = True - FuncNode.file = '' - self.SymbolTable.insert(FuncName, FuncNode) - elif isinstance(Node, ast.AnnAssign): - if isinstance(Node.target, ast.Name): - VarName: str = Node.target.id - - VarType: str = 'unknown' - IsPtr: bool = False - IsTypedef_assignment: bool = False - - if Node.annotation: - try: - if AnnotationContainsName(Node.annotation, 'CTypedef'): - IsTypedef_assignment = True - except Exception as _e: - if _config_mode == "strict": - self.LogWarning(f"异常被忽略: {_e}") - - if IsTypedef_assignment and Node.value: - if isinstance(Node.value, ast.Name): - OriginalType: str = Node.value.id - if OriginalType in self.GeneratedTypes: - OriginalInfo: CTypeInfo | None = self.SymbolTable.lookup(OriginalType) - if OriginalInfo: - if isinstance(OriginalInfo, dict): - actual_type: str = OriginalInfo.get('type', 'struct') - elif isinstance(OriginalInfo, CTypeInfo): - if OriginalInfo.IsEnum: - actual_type = 'enum' - elif OriginalInfo.IsTypedef: - actual_type = 'typedef' - elif OriginalInfo.IsStruct: - actual_type = 'struct' - else: - actual_type = 'struct' - else: - actual_type = 'struct' - if actual_type == 'enum': - TypedefNode: CTypeInfo = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - TypedefNode.OriginalType = f'enum {OriginalType}' - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - elif actual_type == 'typedef': - TypedefNode = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - if isinstance(OriginalInfo, dict): - TypedefNode.OriginalType = OriginalInfo.get('OriginalType', f'struct {OriginalType}') - elif isinstance(OriginalInfo, CTypeInfo) and OriginalInfo.OriginalType: - TypedefNode.OriginalType = OriginalInfo.OriginalType - else: - TypedefNode.OriginalType = f'struct {OriginalType}' - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - else: - TypedefNode = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - TypedefNode.OriginalType = f'struct {OriginalType}' - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - self.GeneratedTypes.add(VarName) - continue - else: - OriginalInfo: CTypeInfo | None = self.SymbolTable.lookup(OriginalType) - if OriginalInfo: - if isinstance(OriginalInfo, dict): - actual_type: str = OriginalInfo.get('type', 'struct') - elif isinstance(OriginalInfo, CTypeInfo): - if OriginalInfo.IsEnum: - actual_type = 'enum' - elif OriginalInfo.IsTypedef: - actual_type = 'typedef' - elif OriginalInfo.IsStruct: - actual_type = 'struct' - else: - actual_type = 'struct' - else: - actual_type = 'struct' - if actual_type == 'typedef': - TypedefNode: CTypeInfo = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - if isinstance(OriginalInfo, dict): - TypedefNode.OriginalType = OriginalInfo.get('OriginalType', f'struct {OriginalType}') - elif isinstance(OriginalInfo, CTypeInfo) and OriginalInfo.OriginalType: - TypedefNode.OriginalType = OriginalInfo.OriginalType - else: - TypedefNode.OriginalType = f'struct {OriginalType}' - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - self.GeneratedTypes.add(VarName) - if isinstance(OriginalInfo, dict): - OriginalInfo['skip_generation'] = True - continue - elif actual_type == 'struct': - TypedefNode = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - TypedefNode.OriginalType = f'struct {OriginalType}' - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - self.GeneratedTypes.add(VarName) - if isinstance(OriginalInfo, dict): - OriginalInfo['skip_generation'] = True - continue - elif actual_type == 'enum': - TypedefNode = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - TypedefNode.OriginalType = f'enum {OriginalType}' - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - self.GeneratedTypes.add(VarName) - if isinstance(OriginalInfo, dict): - OriginalInfo['skip_generation'] = True - continue - elif isinstance(Node.value, ast.Attribute): - CName: str | None = CTypeHelper.GetCName(Node.value.attr) - if CName: - TypedefNode: CTypeInfo = CTypeInfo() - TypedefNode.Name = VarName - TypedefNode.IsTypedef = True - TypedefNode.OriginalType = CName - TypedefNode.file = '' - self.SymbolTable.insert(VarName, TypedefNode) - self.GeneratedTypes.add(VarName) - continue - - if Node.annotation: - try: - TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) - IsPtr = TypeInfo.IsPtr if TypeInfo else False - except Exception as _e: - if _config_mode == "strict": - self.LogWarning(f"异常被忽略: {_e}") - # 如果符号已经存在且是 typedef,不要覆盖它 - existing = self.SymbolTable.lookup(VarName) - if existing and existing.IsTypedef and existing.OriginalType: - # typedef 已正确插入,保持现有条目不覆盖 - pass - else: - var_node: CTypeInfo = CTypeInfo() - var_node.Name = VarName - var_node.IsVariable = True - var_node.PtrCount = 1 if IsPtr else 0 - var_node.file = '' - self.SymbolTable.insert(VarName, var_node) - - self.LlvmGen: LlvmCodeGenerator = LlvmCodeGenerator( - triple=getattr(self, 'triple', None), - datalayout=getattr(self, 'datalayout', None), - ) - self.LlvmGen._Trans = self - self.LlvmGen._import_handler_ref = self.ImportHandler - if hasattr(self, '_module_sha1'): - self.LlvmGen.module_sha1 = self._module_sha1 - if hasattr(self, '_ModuleSha1Map'): - self.LlvmGen.ModuleSha1Map = self._ModuleSha1Map - if hasattr(self, '_struct_sha1_map'): - self.LlvmGen._struct_sha1_map = self._struct_sha1_map - if hasattr(self, '_temp_dir'): - self.LlvmGen._temp_dir = self._temp_dir - if hasattr(self, '_export_extern_funcs'): - self.LlvmGen._export_extern_funcs = self._export_extern_funcs - # 调用点热点统计与每函数计时(--hotspot-sites):透传落盘路径与注册策略; - # 未启用时不产生任何 IR 改动 - self.LlvmGen.hotspot_sites_file = getattr(self, 'HotspotSitesFile', None) - self.LlvmGen.hotspot_sites_all = getattr(self, 'HotspotSitesAll', False) - self.LlvmGen.hotspot_callees = getattr(self, 'HotspotCallees', None) - self.LlvmGen.hotspot_sites_module_local = getattr(self, 'HotspotSitesModuleLocal', False) - # 【本模块声明的类:SHA1 前缀钉死为本模块(2026-09-30)】 - # 必须**先于** setup_from_symbol_table —— 其内部 _generate_structs 会为符号表里的 - # 每个类建 struct,IR 名前缀取自 `class_sha1_map`(空)→ `_struct_sha1_map` - # (Phase2 扫描各 .vpi 得到的"裸类名 → 定义模块 SHA1")。两张表都按**裸类名** - # 索引,两个模块各定义同名类时只能留一个:现场 includes/ast/astaux.vp 的 - # `class Keyword(AST)` 与 includes/ast/tokens.vp 的 `class Keyword(t.CEnum)` - # 同名,`_build_struct_sha1_map` 的"每类只登记一次"判据误用了 sha1 键(恒假、 - # 实为后扫到的覆盖先扫到的)⇒ astaux 自己那份被命名成 - # `%"fef783da4d24330d.Keyword"`(= tokens 的 sha1)。于是同一类在本模块里出现 - # 两个 struct 实例:`__new__`/`__init__` 用按本模块前缀另建的那份 - # `%"8312ce86a2fd82c0.Keyword"*`,而 `HandlesClassDef._EmitNewFunctionLlvm` - # 生成的 `__before_init__` 用 `Gen.structs['Keyword']`(错名前缀那份)⇒ 声明与 - # 调用点分叉,llvmlite 报 - # "Type of #1 arg mismatch: %"fef783da4d24330d.Keyword"* != %"8312ce86a2fd82c0.Keyword"*" - # (报文是 `形参 != 实参`,下标 1-based ⇒ 指的其实是第 0 个形参 self)。 - # "本文件顶层 ClassDef 名单"是**正向事实**(就在本次要翻译的 AST 里,且 - # class_sha1_map 是 Gen 实例级、每模块重建)⇒ 命中即钉死为本模块 SHA1; - # 非本模块声明的类不受影响,仍走原来的全局映射。 - _DefSha1: str | None = getattr(self, '_module_sha1', None) - if _DefSha1: - for _Nd in ast.iter_child_nodes(Tree): - if isinstance(_Nd, ast.ClassDef) and getattr(_Nd, 'name', None): - self.LlvmGen.class_sha1_map[_Nd.name] = _DefSha1 - self.LlvmGen.setup_from_symbol_table(self.SymbolTable) - if hasattr(self, 'CurrentFile') and self.CurrentFile: - self.LlvmGen._set_source_info(self.CurrentFile) - # 函数级 DWARF 调试元数据(--dwarf / --asan):透传开关给本模块的 - # LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg, - # 生成的 .ll 与改动前逐字节一致(零影响) +) +# 治本修复:导入 ConstEvaluator 用于预扫描 CDefine 常量的复杂表达式求值 +from lib.core.ConstEvaluator import ConstEvaluator + +# `_InsertModuleLevelLLVMIR` 解析模块级 LLVM IR 文本用的**预编译**正则 +# (性能:该函数逐行解析 IR,字面量正则在调用点每次都要做模式串哈希与 re 缓存查找)。 +_RE_MODULE_FLAGS: re.Pattern[str] = re.compile(r'!llvm\.module\.flags\s*=\s*!\{(!\d+)\}') +_RE_METADATA_DEF: re.Pattern[str] = re.compile(r'(!\d+)\s*=\s*!\{(.+)\}') +_RE_METADATA_FIELD_SEP: re.Pattern[str] = re.compile(r',\s*') +_RE_METADATA_FIELD_I32: re.Pattern[str] = re.compile(r'^i32\s+(\d+)$') +_RE_METADATA_FIELD_STR: re.Pattern[str] = re.compile(r'^"([^"]*)"$') +_RE_METADATA_FIELD_MDSTR: re.Pattern[str] = re.compile(r'^!"([^"]*)"$') +_RE_METADATA_FIELD_INT: re.Pattern[str] = re.compile(r'^(\d+)$') +_RE_METADATA_FIELD_REF: re.Pattern[str] = re.compile(r'^(!\d+)$') + + +class LlvmGeneratorMixin: + """LLVM IR 生成 Mixin + + 提供 LLVM IR 直接生成、模块级 LLVM IR 处理与 C 代码生成入口能力。 + """ + + def GenerateLlvmDirect(self, Tree: ast.Module) -> str: + Gen: LlvmCodeGenerator = self.LlvmGen + + # === 新增:轻量 AST 遍历,收集类图信息,解决前向引用 === + Gen._class_graph: dict[str, dict[str, bool | list[str]]] = {} + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.ClassDef): + bases: list[str] = [] + for base in Node.bases: + if hasattr(base, 'id'): + bases.append(base.id) + elif hasattr(base, 'attr'): + bases.append(base.attr) + has_methods: bool = any(isinstance(item, ast.FunctionDef) for item in Node.body) + # 检测 @t.NoVTable 装饰器(非多态继承基类标记) + has_novtable: bool = False + if hasattr(Node, 'decorator_list') and Node.decorator_list: + for decorator in Node.decorator_list: + if isinstance(decorator, ast.Attribute): + if getattr(decorator.value, 'id', None) == 't' and decorator.attr == 'NoVTable': + has_novtable = True + elif isinstance(decorator, ast.Name): + if decorator.id == 'NoVTable': + has_novtable = True + Gen._class_graph[Node.name] = { + 'bases': bases, + 'has_methods': has_methods, + 'has_novtable': has_novtable, + } + # === 结束新增 === + + # 预扫描:为所有 -> str 注解的函数注册正确的返回类型 i8* + # 这样在类方法中调用这些函数时,前向声明会使用正确的类型 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.FunctionDef): + if Node.returns and isinstance(Node.returns, ast.Name) and Node.returns.id == 'str': + Gen.known_return_types[Node.name] = ir.PointerType(ir.IntType(8)) + + # 首先收集所有 CDefine 常量,确保类定义中可以引用 + def _extract_const_value(node: ast.AST) -> int | str | float | None: + """求值 CDefine 常量表达式,支持 Constant、BinOp、UnaryOp、Name(引用其他 define)、Call(类型构造)。 + + 治本修复:原 _extract_call_const_val 只支持 t.XxxType(Constant) 形式, + 无法处理 FOREGROUND_WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE + 等引用其他 CDefine 的 BinOp 表达式,导致依赖常量无法被预扫描。 + 现使用 ConstEvaluator.eval_with_symtab 统一求值。 + """ + if isinstance(node, ast.Constant): + return node.value + # 使用 ConstEvaluator 处理 BinOp/UnaryOp/Name/Attribute/Call + return ConstEvaluator.eval_with_symtab(node, self.SymbolTable) + + def _is_cdefine_type(TypeInfo: CTypeInfo | None) -> bool: + """判断 CTypeInfo 是否对应 t.CDefine 注解""" + return bool(TypeInfo and TypeInfo.BaseType and + ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) + or isinstance(TypeInfo.BaseType, t.CDefine) or TypeInfo.IsDefine)) + + def _register_cdefine_constant(NameNode: ast.Name, Annotation: ast.expr, Value: ast.expr) -> None: + """注册 CDefine 编译期常量(_define_constants + SymbolTable)""" + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Annotation) + if not _is_cdefine_type(TypeInfo): + return + val: int | str | float | None = _extract_const_value(Value) + if val is not None: + # fail-fast:常量值必须能落进声明类型(define 带类型形态)/ 64 位存储 + # (自动推导形态,本语言无 64 位以上整数类型)——此前超大值会被静默 + # 截断/生成非法 IR 文本。唯一判据见 SymbolUtils.RequireDefineIntStorable。 + RequireDefineConstStorable(Gen, self, NameNode, TypeInfo.BaseType, + Annotation, val, NameNode.id) + if not hasattr(Gen, '_define_constants'): + Gen._define_constants: dict[str, int | str | float | bool] = {} + Gen._define_constants[NameNode.id] = val + # 带类型注册:记录声明整数类型(位宽 + 符号性),供使用点按该类型物化 + # (如 `define X: DWORD = 5` → i32/u32,而不是按值大小猜 i32/i64)。 + if isinstance(val, int) and not isinstance(val, bool): + _w: int | None = DefineIntWidthOf(TypeInfo.BaseType) + if _w is not None: + _s: bool | None = DefineIntSignednessOf(TypeInfo.BaseType) + if not hasattr(Gen, '_define_decl_types'): + Gen._define_decl_types: dict[str, tuple[int, bool]] = {} + Gen._define_decl_types[NameNode.id] = (_w, bool(_s) if _s is not None else False) + existing = self.SymbolTable.lookup(NameNode.id) + if not (existing and existing.IsTypedef and existing.OriginalType): + sym_info: _CTypeInfo = _CTypeInfo() + sym_info.IsDefine = True + sym_info.DefineValue = val + self.SymbolTable.insert(NameNode.id, sym_info) + + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.AnnAssign) and isinstance(Node.target, ast.Name) and Node.value: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) + # 治本修复:检查 BaseType 是否为 t.CDefine 实例/子类,或 IsDefine 标志 + # IsDefine 由 GetCTypeInfo 对 t.CDefine 注解自动设置 + if _is_cdefine_type(TypeInfo): + _register_cdefine_constant(Node.target, Node.annotation, Node.value) + elif isinstance(Node, ast.ClassDef): + # 类体 CDefine 常量(2026-08-10):供方法内裸名(如 Mixin 的 + # ISINSTANCE_CLASS_MAX/MRO_MAX: t.CDefine = N)编译期解析为常量值。 + # 原预扫描只用 iter_child_nodes 扫顶层,类体常量不注册 → 方法内 + # 裸名解析为 0 → pool.alloc(8*0) 失败(VPC 编译测试 SUBS-NONE)。 + for _citem in Node.body: + if isinstance(_citem, ast.AnnAssign) and isinstance(_citem.target, ast.Name) and _citem.value: + _register_cdefine_constant(_citem.target, _citem.annotation, _citem.value) + elif isinstance(Node, ast.Assign): + for target in Node.targets: + if isinstance(target, ast.Name): + TypeInfo: CTypeInfo | None = None + if Node.type_comment: + try: + tc_ast: ast.Expression = ast.parse(Node.type_comment, '', mode='eval') + TypeInfo = self.TypeMergeHandler.GetCTypeInfo(tc_ast.body) + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析类型注释失败: {_e}", "Exception") + if TypeInfo and TypeInfo.BaseType and ((isinstance(TypeInfo.BaseType, type) and issubclass(TypeInfo.BaseType, t.CDefine)) or isinstance(TypeInfo.BaseType, t.CDefine)): + val: int | str | float | None = _extract_const_value(Node.value) + if val is not None: + if not hasattr(Gen, '_define_constants'): + Gen._define_constants: dict[str, int | str | float | bool] = {} + Gen._define_constants[target.id] = val + sym_info: _CTypeInfo = _CTypeInfo() + sym_info.IsDefine = True + sym_info.DefineValue = val + self.SymbolTable.insert(target.id, sym_info) + + # 预扫描所有顶层函数定义,填充 FunctionDefCache(不创建 LLVM 声明) + # 这样类方法编译时遇到未声明的函数可以按需前向声明 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.FunctionDef): + FuncName: str = Node.name + if FuncName not in self.FunctionDefCache: + self.FunctionDefCache[FuncName] = Node + elif isinstance(Node, ast.AsyncFunctionDef): + # 协程:注册到 AsyncHandler(生成 create_/resume_,不进 FunctionDefCache, + # 避免被当作普通函数前向声明) + self.AsyncHandler._RegisterAsyncFunc(Node.name, Node) + + # 首先处理所有导入语句,确保模块在类型解析前被加载 + for Node in ast.iter_child_nodes(Tree): + Gen._set_node_info(Node) + try: + if isinstance(Node, ast.Import): + self.ImportHandler._EmitImportDeclarationsLlvm(Node, Gen) + elif isinstance(Node, ast.ImportFrom): + self.ImportHandler._EmitImportFromDeclarationsLlvm(Node, Gen) + except Exception as e: + # fail-fast:import 模块不存在(ImportError)必须立即终止编译, + # 不能吞进 _ErrorStack(该栈无消费点,静默容忍导致 import 路径 + # 错误延迟到链接期才暴露,如 ClassGenGeneric 漏改 import 路径)。 + if isinstance(e, ImportError): + raise + lineno: int = getattr(Node, 'lineno', 0) + # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, + # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator); + # 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。 + _line_offset: int = int(getattr(self, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + source_line: str | None = Gen._get_source_line(lineno) + src_file: str = getattr(Gen, '_current_source_file', '') + src_path: str = src_file if src_file else 'unknown' + line_info: str = "源代码 (%s, line %d)" % (src_path, lineno) + if source_line: + line_info += ":\n %d | %s" % (lineno, source_line) + self._ErrorStack.append((type(e).__name__, str(e), line_info)) + + # 本模块泛型类模板必须在前置声明之前注册(与 includes 侧注册判据同源, + # 见 HandlesClassDef.PrescanModuleGenericClassTemplates):前置声明要解析 + # 参数/返回注解里的泛型类下标(`def f(b: Box[Node])`),而类处理阶段 + # (下方 _EmitClassLlvm)才注册模板 → 注解解析为空 → VoidType → + # "无法识别的参数类型注解"。放在 import 扫描之后,保证本地类覆盖同名 + # 导入类(与 _EmitClassLlvm 的覆盖顺序一致)。 + self.ClassHandler.PrescanModuleGenericClassTemplates(ast.iter_child_nodes(Tree)) + + # 前向声明所有顶层函数,确保全局变量初始化时能引用函数指针 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.FunctionDef): + Gen._set_node_info(Node) + self.FunctionHandler._EmitFunctionForwardDeclLlvm(Node, Gen) + + # 预创建全局变量(带初始化器),确保类方法编译时能引用全局数组等 + Gen._current_tree: ast.Module = Tree + for Node in ast.iter_child_nodes(Tree): + Gen._set_node_info(Node) + if isinstance(Node, ast.AnnAssign): + self.AssignHandler._EmitGlobalAnnAssignLlvm(Node, Gen) + elif isinstance(Node, ast.Assign): + self.AssignHandler._EmitGlobalAssignLlvm(Node, Gen) + + # 首先处理所有类定义(包括 CUnion),确保类型定义在使用前完成 + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.ClassDef): + Gen._set_node_info(Node) + self.ClassHandler._EmitClassLlvm(Node, Gen) + + Gen._generate_structs() + Gen._create_Vtable_globals() + + for Node in ast.iter_child_nodes(Tree): + Gen._set_node_info(Node) + try: + if isinstance(Node, ast.Import): + continue + elif isinstance(Node, ast.ImportFrom): + continue + elif isinstance(Node, ast.ClassDef): + continue + elif isinstance(Node, ast.FunctionDef): + self.FunctionHandler._EmitFunctionLlvm(Node, Gen) + elif isinstance(Node, ast.AsyncFunctionDef): + self.AsyncHandler._EmitAsyncFunctionLlvm(Node, Gen) + elif isinstance(Node, ast.AnnAssign): + self.AssignHandler._EmitGlobalAnnAssignLlvm(Node, Gen) + elif isinstance(Node, ast.Assign): + self.AssignHandler._EmitGlobalAssignLlvm(Node, Gen) + elif isinstance(Node, ast.If): + pass + elif isinstance(Node, ast.Expr): + self._HandleModuleLevelExprLlvm(Node, Gen) + except Exception as e: + lineno: int = getattr(Node, 'lineno', 0) + # 注入行偏移还原:AST 行号含 _inject_auto_imports 在文件头注入的 N 行, + # 先按精确偏移减回磁盘真实行号(N 由 Phase2Translator 计算并挂到 translator); + # 报文行号与源码行必须同用还原后的 lineno,二者一致(否则会显示下一行源码)。 + _line_offset: int = int(getattr(self, '_line_offset', 0) or 0) + if _line_offset > 0 and lineno > _line_offset: + lineno -= _line_offset + source_line: str | None = Gen._get_source_line(lineno) + src_file: str = getattr(Gen, '_current_source_file', '') + src_path: str = src_file if src_file else 'unknown' + line_info: str = "源代码 (%s, line %d)" % (src_path, lineno) + if source_line: + line_info += ":\n %d | %s" % (lineno, source_line) + self._ErrorStack.append((type(e).__name__, str(e), line_info)) + raise + Gen._set_Vtable_initializers() + # 执行 DecoratorPass:为带自定义装饰器的函数生成 wrapper + _run_decorator_pass(Gen) + # 调用点热点统计与每函数计时(--hotspot-sites):在装饰器 wrapper 生成之后 + # 统一收口;未启用时不执行(零影响)。hotspot_sites_module_local=True 走 + # Projectrans 多模块路径(每模块 @llvm.global_ctors 自注册),否则走种子 + # CLI 单模块路径(在 main 入口注册)。 + if Gen.hotspot_sites_file: + if Gen.hotspot_sites_module_local: + Gen._InstallHotspotSitesModuleLocal() + else: + Gen._InstallHotspotSites() + ir_code: str = Gen.finalize() + return ir_code + + def _HandleModuleLevelExprLlvm(self, Node: ast.Expr, Gen: LlvmCodeGenerator) -> None: + if not isinstance(Node.value, ast.Call): + return + call: ast.Call = Node.value + if isinstance(call.func, ast.Name): + # 模块级裸名 __llvmir__(...) 等价 c.LLVMIR(...) + if call.func.id == '__llvmir__': + self._HandleModuleLevelLLVMIR(call, Gen) + return + if not isinstance(call.func, ast.Attribute): + return + if not isinstance(call.func.value, ast.Name) or call.func.value.id != 'c': + return + func_name: str = call.func.attr + if func_name == 'LLVMIR': + self._HandleModuleLevelLLVMIR(call, Gen) + + def _HandleModuleLevelLLVMIR(self, Node: ast.Call, Gen: LlvmCodeGenerator) -> None: + if not Node.args: + return + first_arg: ast.expr = Node.args[0] + if isinstance(first_arg, ast.Constant) and isinstance(first_arg.value, str): + ir_text: str = first_arg.value + elif isinstance(first_arg, ast.JoinedStr): + parts: list[str] = [] + for value in first_arg.values: + if isinstance(value, ast.Constant) and isinstance(value.value, str): + parts.append(value.value) + elif isinstance(value, ast.FormattedValue): + parts.append('') + ir_text = ''.join(parts) + else: + return + self._InsertModuleLevelLLVMIR(Gen, ir_text) + + def _InsertModuleLevelLLVMIR(self, Gen: LlvmCodeGenerator, ir_text: str) -> None: + for line in ir_text.strip().split('\n'): + line = line.strip() + if not line: + continue + module_flags_match: re.Match[str] | None = _RE_MODULE_FLAGS.match(line) + if module_flags_match: + ref: str = module_flags_match.group(1) + if not hasattr(Gen, '_pending_module_flags'): + Gen._pending_module_flags: list = [] + Gen._pending_module_flags_refs: str = ref + continue + metadata_match: re.Match[str] | None = _RE_METADATA_DEF.match(line) + if metadata_match: + md_id: str = metadata_match.group(1) + md_body: str = metadata_match.group(2) + fields: list[ir.Constant | ir.MetaDataString | str] = [] + for field_str in _RE_METADATA_FIELD_SEP.split(md_body): + field_str = field_str.strip() + if not field_str: + continue + int_m: re.Match[str] | None = _RE_METADATA_FIELD_I32.match(field_str) + if int_m: + fields.append(ir.Constant(ir.IntType(32), int(int_m.group(1)))) + continue + str_m: re.Match[str] | None = _RE_METADATA_FIELD_STR.match(field_str) + if str_m: + fields.append(ir.MetaDataString(Gen.module, str_m.group(1))) + continue + mdstr_m: re.Match[str] | None = _RE_METADATA_FIELD_MDSTR.match(field_str) + if mdstr_m: + fields.append(ir.MetaDataString(Gen.module, mdstr_m.group(1))) + continue + int_m2: re.Match[str] | None = _RE_METADATA_FIELD_INT.match(field_str) + if int_m2: + fields.append(ir.Constant(ir.IntType(32), int(int_m2.group(1)))) + continue + ref_m: re.Match[str] | None = _RE_METADATA_FIELD_REF.match(field_str) + if ref_m: + fields.append(ref_m.group(1)) + continue + if fields: + if not hasattr(Gen, '_pending_metadata'): + Gen._pending_metadata: list[tuple[str, list]] = [] + Gen._pending_metadata.append((md_id, fields)) + continue + self._FlushModuleMetadata(Gen) + + def _FlushModuleMetadata(self, Gen: LlvmCodeGenerator) -> None: + if not hasattr(Gen, '_pending_metadata') or not Gen._pending_metadata: + return + md_map: dict[str, ir.MetaDataString] = {} + for md_id, fields in Gen._pending_metadata: + resolved: list[ir.Constant | ir.MetaDataString] = [] + for f in fields: + if isinstance(f, str) and f.startswith('!'): + ref: ir.MetaDataString | None = md_map.get(f) + if ref: + resolved.append(ref) + else: + resolved.append(f) + md_value: ir.MetaDataString = Gen.module.add_metadata(resolved) + md_map[md_id] = md_value + if hasattr(Gen, '_pending_module_flags_refs'): + ref: str = Gen._pending_module_flags_refs + md_node: ir.MetaDataString | None = md_map.get(ref) + if md_node: + Gen.module.add_named_metadata('llvm.module.flags', md_node) + Gen._pending_metadata = [] + Gen._pending_module_flags_refs = None + + def GenerateCCode(self, Tree: ast.Module, target: str = 'llvm') -> str: + """生成代码 + + Args: + Tree: Python AST 树 + target: 目标代码类型,仅支持 'llvm' + + Returns: + 生成的代码字符串 + """ + self._generated_vars: set[str] = set() + self.VarScopes = [{}] # 全局作用域 + + # 预扫描 import 语句,注册别名,确保类型解析时能正确解析模块别名 + if not getattr(self, '_ImportedModules', None): + self._ImportedModules: set[str] = set() + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.Import): + for alias in Node.names: + name: str = alias.name + if name in ('c', 't'): + continue + self._ImportedModules.add(name) + if alias.asname: + self.SymbolTable.import_aliases[alias.asname] = name + elif isinstance(Node, ast.ImportFrom): + module_name: str | None = Node.module + if module_name and module_name not in ('c', 't'): + self._ImportedModules.add(module_name) + for alias in Node.names: + if alias.asname: + self.SymbolTable.import_aliases[alias.asname] = f"{module_name}.{alias.name}" + + for Node in ast.iter_child_nodes(Tree): + if isinstance(Node, ast.ClassDef): + ClassName: str = Node.name + + # 检查是否是特殊类型(枚举或联合体) + IsSpecialType: bool = False + if Node.bases: + for base in Node.bases: + if hasattr(base, 'attr'): + if base.attr in ['CEnum', 'Enum', 'CUnion', 'REnum']: + IsSpecialType = True + break + elif hasattr(base, 'id'): + if base.id in ['CEnum', 'Enum', 'CUnion', 'REnum']: + IsSpecialType = True + break + + # 如果是特殊类型,跳过这里的处理(由 HandleClassDef 处理) + if IsSpecialType: + continue + + members: dict[str, CTypeInfo] = {} + IsTypedef: bool = False + TypedefName: str | None = None + + if hasattr(Node, 'annotation') and Node.annotation: + try: + if AnnotationContainsName(Node.annotation, 'CTypedef'): + IsTypedef = True + if isinstance(Node.annotation, ast.Call): + if Node.annotation.args: + if isinstance(Node.annotation.args[0], ast.Constant): + TypedefName = Node.annotation.args[0].value + except Exception as _e: + if _config_mode == "strict": + raise + _vlog().warning(f"解析 typedef 注解失败: {_e}", "Exception") + + if not IsTypedef: + for item in Node.body: + if isinstance(item, ast.Assign): + for target in item.targets: + if isinstance(target, ast.Name) and target.id == '__annotations__': + if isinstance(item.value, ast.Dict): + for key, value in zip(item.value.keys, item.value.values): + if isinstance(key, ast.Constant) and key.value == '__type__': + if AnnotationContainsName(value, 'CTypedef'): + IsTypedef = True + + if IsTypedef: + TypedefKey: str = TypedefName if TypedefName else ClassName + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = TypedefKey + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'struct {ClassName}' + TypedefNode.set('IsComplete', True) + TypedefNode.file = '' + self.SymbolTable.insert(TypedefKey, TypedefNode) + + for item in Node.body: + if isinstance(item, ast.AnnAssign) and isinstance(item.target, ast.Name): + VarName: str = item.target.id + try: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(item.annotation) + if TypeInfo is None: + TypeInfo = CTypeInfo() + TypeInfo.BaseType = t.CInt() + IsPtr: bool = TypeInfo.IsPtr + if not IsPtr: + if isinstance(item.annotation, ast.BinOp) and isinstance(item.annotation.op, ast.BitOr): + if AnnotationContainsName(item.annotation, 'CPtr'): + IsPtr = True + dims: list[int] = [] + if hasattr(item.annotation, 'slice') and isinstance(item.annotation.slice, ast.Constant): + if isinstance(item.annotation.value, ast.Name): + if IsTModule(item.annotation.value.id, self.SymbolTable): + if hasattr(item.annotation, 'slice'): + try: + dims.append(int(item.annotation.slice.value)) + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + MemberInfo: CTypeInfo = TypeInfo.Copy() + if dims: + MemberInfo.ArrayDims = [str(d) for d in dims] + members[VarName] = MemberInfo + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + + if not IsTypedef: + StructNode: CTypeInfo = CTypeInfo() + StructNode.Name = ClassName + StructNode.IsStruct = True + StructNode.Members = members or {} + StructNode.file = '' + existing: CTypeInfo | None = self.SymbolTable.lookup(ClassName) + if existing: + if hasattr(existing, 'IsCpythonObject') and existing.IsCpythonObject: + StructNode.IsCpythonObject = True + self.SymbolTable.insert(ClassName, StructNode) + elif isinstance(Node, ast.FunctionDef): + FuncName: str = Node.name + if not self.SymbolTable.has(FuncName): + FuncNode: CTypeInfo = CTypeInfo() + FuncNode.Name = FuncName + FuncNode.IsFunction = True + FuncNode.file = '' + self.SymbolTable.insert(FuncName, FuncNode) + elif isinstance(Node, ast.AsyncFunctionDef): + FuncName = Node.name + if not self.SymbolTable.has(FuncName): + FuncNode = CTypeInfo() + FuncNode.Name = FuncName + FuncNode.IsFunction = True + FuncNode.file = '' + self.SymbolTable.insert(FuncName, FuncNode) + elif isinstance(Node, ast.AnnAssign): + if isinstance(Node.target, ast.Name): + VarName: str = Node.target.id + + VarType: str = 'unknown' + IsPtr: bool = False + IsTypedef_assignment: bool = False + + if Node.annotation: + try: + if AnnotationContainsName(Node.annotation, 'CTypedef'): + IsTypedef_assignment = True + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + + if IsTypedef_assignment and Node.value: + if isinstance(Node.value, ast.Name): + OriginalType: str = Node.value.id + if OriginalType in self.GeneratedTypes: + OriginalInfo: CTypeInfo | None = self.SymbolTable.lookup(OriginalType) + if OriginalInfo: + if isinstance(OriginalInfo, dict): + actual_type: str = OriginalInfo.get('type', 'struct') + elif isinstance(OriginalInfo, CTypeInfo): + if OriginalInfo.IsEnum: + actual_type = 'enum' + elif OriginalInfo.IsTypedef: + actual_type = 'typedef' + elif OriginalInfo.IsStruct: + actual_type = 'struct' + else: + actual_type = 'struct' + else: + actual_type = 'struct' + if actual_type == 'enum': + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'enum {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + elif actual_type == 'typedef': + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + if isinstance(OriginalInfo, dict): + TypedefNode.OriginalType = OriginalInfo.get('OriginalType', f'struct {OriginalType}') + elif isinstance(OriginalInfo, CTypeInfo) and OriginalInfo.OriginalType: + TypedefNode.OriginalType = OriginalInfo.OriginalType + else: + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + else: + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + continue + else: + OriginalInfo: CTypeInfo | None = self.SymbolTable.lookup(OriginalType) + if OriginalInfo: + if isinstance(OriginalInfo, dict): + actual_type: str = OriginalInfo.get('type', 'struct') + elif isinstance(OriginalInfo, CTypeInfo): + if OriginalInfo.IsEnum: + actual_type = 'enum' + elif OriginalInfo.IsTypedef: + actual_type = 'typedef' + elif OriginalInfo.IsStruct: + actual_type = 'struct' + else: + actual_type = 'struct' + else: + actual_type = 'struct' + if actual_type == 'typedef': + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + if isinstance(OriginalInfo, dict): + TypedefNode.OriginalType = OriginalInfo.get('OriginalType', f'struct {OriginalType}') + elif isinstance(OriginalInfo, CTypeInfo) and OriginalInfo.OriginalType: + TypedefNode.OriginalType = OriginalInfo.OriginalType + else: + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + if isinstance(OriginalInfo, dict): + OriginalInfo['skip_generation'] = True + continue + elif actual_type == 'struct': + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'struct {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + if isinstance(OriginalInfo, dict): + OriginalInfo['skip_generation'] = True + continue + elif actual_type == 'enum': + TypedefNode = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = f'enum {OriginalType}' + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + if isinstance(OriginalInfo, dict): + OriginalInfo['skip_generation'] = True + continue + elif isinstance(Node.value, ast.Attribute): + CName: str | None = CTypeHelper.GetCName(Node.value.attr) + if CName: + TypedefNode: CTypeInfo = CTypeInfo() + TypedefNode.Name = VarName + TypedefNode.IsTypedef = True + TypedefNode.OriginalType = CName + TypedefNode.file = '' + self.SymbolTable.insert(VarName, TypedefNode) + self.GeneratedTypes.add(VarName) + continue + + if Node.annotation: + try: + TypeInfo: CTypeInfo | None = self.TypeMergeHandler.GetCTypeInfo(Node.annotation) + IsPtr = TypeInfo.IsPtr if TypeInfo else False + except Exception as _e: + if _config_mode == "strict": + self.LogWarning(f"异常被忽略: {_e}") + # 如果符号已经存在且是 typedef,不要覆盖它 + existing = self.SymbolTable.lookup(VarName) + if existing and existing.IsTypedef and existing.OriginalType: + # typedef 已正确插入,保持现有条目不覆盖 + pass + else: + var_node: CTypeInfo = CTypeInfo() + var_node.Name = VarName + var_node.IsVariable = True + var_node.PtrCount = 1 if IsPtr else 0 + var_node.file = '' + self.SymbolTable.insert(VarName, var_node) + + self.LlvmGen: LlvmCodeGenerator = LlvmCodeGenerator( + triple=getattr(self, 'triple', None), + datalayout=getattr(self, 'datalayout', None), + ) + self.LlvmGen._Trans = self + self.LlvmGen._import_handler_ref = self.ImportHandler + if hasattr(self, '_module_sha1'): + self.LlvmGen.module_sha1 = self._module_sha1 + if hasattr(self, '_ModuleSha1Map'): + self.LlvmGen.ModuleSha1Map = self._ModuleSha1Map + if hasattr(self, '_struct_sha1_map'): + self.LlvmGen._struct_sha1_map = self._struct_sha1_map + if hasattr(self, '_temp_dir'): + self.LlvmGen._temp_dir = self._temp_dir + if hasattr(self, '_export_extern_funcs'): + self.LlvmGen._export_extern_funcs = self._export_extern_funcs + # 调用点热点统计与每函数计时(--hotspot-sites):透传落盘路径与注册策略; + # 未启用时不产生任何 IR 改动 + self.LlvmGen.hotspot_sites_file = getattr(self, 'HotspotSitesFile', None) + self.LlvmGen.hotspot_sites_all = getattr(self, 'HotspotSitesAll', False) + self.LlvmGen.hotspot_callees = getattr(self, 'HotspotCallees', None) + self.LlvmGen.hotspot_sites_module_local = getattr(self, 'HotspotSitesModuleLocal', False) + # 【本模块声明的类:SHA1 前缀钉死为本模块(2026-09-30)】 + # 必须**先于** setup_from_symbol_table —— 其内部 _generate_structs 会为符号表里的 + # 每个类建 struct,IR 名前缀取自 `class_sha1_map`(空)→ `_struct_sha1_map` + # (Phase2 扫描各 .vpi 得到的"裸类名 → 定义模块 SHA1")。两张表都按**裸类名** + # 索引,两个模块各定义同名类时只能留一个:现场 includes/ast/astaux.vp 的 + # `class Keyword(AST)` 与 includes/ast/tokens.vp 的 `class Keyword(t.CEnum)` + # 同名,`_build_struct_sha1_map` 的"每类只登记一次"判据误用了 sha1 键(恒假、 + # 实为后扫到的覆盖先扫到的)⇒ astaux 自己那份被命名成 + # `%"fef783da4d24330d.Keyword"`(= tokens 的 sha1)。于是同一类在本模块里出现 + # 两个 struct 实例:`__new__`/`__init__` 用按本模块前缀另建的那份 + # `%"8312ce86a2fd82c0.Keyword"*`,而 `HandlesClassDef._EmitNewFunctionLlvm` + # 生成的 `__before_init__` 用 `Gen.structs['Keyword']`(错名前缀那份)⇒ 声明与 + # 调用点分叉,llvmlite 报 + # "Type of #1 arg mismatch: %"fef783da4d24330d.Keyword"* != %"8312ce86a2fd82c0.Keyword"*" + # (报文是 `形参 != 实参`,下标 1-based ⇒ 指的其实是第 0 个形参 self)。 + # "本文件顶层 ClassDef 名单"是**正向事实**(就在本次要翻译的 AST 里,且 + # class_sha1_map 是 Gen 实例级、每模块重建)⇒ 命中即钉死为本模块 SHA1; + # 非本模块声明的类不受影响,仍走原来的全局映射。 + _DefSha1: str | None = getattr(self, '_module_sha1', None) + if _DefSha1: + for _Nd in ast.iter_child_nodes(Tree): + if isinstance(_Nd, ast.ClassDef) and getattr(_Nd, 'name', None): + self.LlvmGen.class_sha1_map[_Nd.name] = _DefSha1 + self.LlvmGen.setup_from_symbol_table(self.SymbolTable) + if hasattr(self, 'CurrentFile') and self.CurrentFile: + self.LlvmGen._set_source_info(self.CurrentFile) + # 函数级 DWARF 调试元数据(--dwarf / --asan):透传开关给本模块的 + # LlvmCodeGenerator;未启用时 dwarf_enabled=False → 不构造 DI、不挂 !dbg, + # 生成的 .ll 与改动前逐字节一致(零影响) self.LlvmGen.dwarf_enabled = bool(getattr(self, 'DwarfEnabled', False)) # Ctrl+C 调用栈(--traceback):透传"本模块需注入 Install() 启动期构造器" # 标记(仅 includes/traceback.vp 的翻译上下文为 True);未启用时为 False # → finalize() 不做任何事,生成的 .ll 与改动前逐字节一致(零影响) - self.LlvmGen.traceback_ctor = bool(getattr(self, 'TracebackCtor', False)) - - # --- 修复:共享 vtable 状态跨模块 --- - # 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性, - # 不跨模块共享。导致模块 B 编译时 _specialize_generic_class 触发的 - # pool.alloc(48) 找不到 MemManager 的虚表状态,直接调用 stub 返回 NULL → 段错误。 - # 修复:将 vtable 状态存储在 Translator 对象上并赋值给每个新 Gen。 - if not hasattr(self, '_shared_class_vtable'): - self._shared_class_vtable: set[str] = set() - self._shared_cross_module_vtable: set[str] = set() - self._shared_class_methods: dict[str, list[str]] = {} - self._shared_cross_module_novtable: set[str] = set() - # 合并 setup_from_symbol_table 创建的实例级 class_methods 到共享 dict - # (仅添加新类,不覆盖已有方法列表) - for _cls_name, _methods in self.LlvmGen.class_methods.items(): - if _cls_name not in self._shared_class_methods: - self._shared_class_methods[_cls_name] = _methods - else: - for _m in _methods: - if _m not in self._shared_class_methods[_cls_name]: - self._shared_class_methods[_cls_name].append(_m) - # 替换为共享状态 - self.LlvmGen.class_vtable = self._shared_class_vtable - self.LlvmGen._cross_module_vtable_classes = self._shared_cross_module_vtable - self.LlvmGen.class_methods = self._shared_class_methods - self.LlvmGen._cross_module_novtable = self._shared_cross_module_novtable - # --- 修复结束 --- - - return self.GenerateLlvmDirect(Tree) + self.LlvmGen.traceback_ctor = bool(getattr(self, 'TracebackCtor', False)) + + # --- 修复:共享 vtable 状态跨模块 --- + # 根因:LlvmGen 每模块重新创建,class_vtable/class_methods 等是实例级属性, + # 不跨模块共享。导致模块 B 编译时 _specialize_generic_class 触发的 + # pool.alloc(48) 找不到 MemManager 的虚表状态,直接调用 stub 返回 NULL → 段错误。 + # 修复:将 vtable 状态存储在 Translator 对象上并赋值给每个新 Gen。 + if not hasattr(self, '_shared_class_vtable'): + self._shared_class_vtable: set[str] = set() + self._shared_cross_module_vtable: set[str] = set() + self._shared_class_methods: dict[str, list[str]] = {} + self._shared_cross_module_novtable: set[str] = set() + # 合并 setup_from_symbol_table 创建的实例级 class_methods 到共享 dict + # (仅添加新类,不覆盖已有方法列表) + for _cls_name, _methods in self.LlvmGen.class_methods.items(): + if _cls_name not in self._shared_class_methods: + self._shared_class_methods[_cls_name] = _methods + else: + for _m in _methods: + if _m not in self._shared_class_methods[_cls_name]: + self._shared_class_methods[_cls_name].append(_m) + # 替换为共享状态 + self.LlvmGen.class_vtable = self._shared_class_vtable + self.LlvmGen._cross_module_vtable_classes = self._shared_cross_module_vtable + self.LlvmGen.class_methods = self._shared_class_methods + self.LlvmGen._cross_module_novtable = self._shared_cross_module_novtable + # --- 修复结束 --- + + return self.GenerateLlvmDirect(Tree) diff --git a/lib/core/TypeAnnotationResolver.py b/lib/core/TypeAnnotationResolver.py index db97b30..e3538d7 100644 --- a/lib/core/TypeAnnotationResolver.py +++ b/lib/core/TypeAnnotationResolver.py @@ -1,1319 +1,1319 @@ -from __future__ import annotations -import os -import re -import threading -import vast as ast -import warnings -from typing import TYPE_CHECKING - -from lib.core.Handles.HandlesBase import CTypeInfo, BuiltinTypeMap, CTypeHelper -from lib.core.ConstEvaluator import ConstEvaluator +from __future__ import annotations +import os +import re +import threading +import vast as ast +import warnings +from typing import TYPE_CHECKING + +from lib.core.Handles.HandlesBase import CTypeInfo, BuiltinTypeMap, CTypeHelper +from lib.core.ConstEvaluator import ConstEvaluator from lib.core.SymbolUtils import ( IsTModule, IsTModulePath, IsListAnnotation, - ResolveGenericClassSpecName, GetStructSha1MapFromSymtab, GetTCAliasMapFromSymtab) -from lib.includes import t -from lib.constants.config import mode as _config_mode - -if TYPE_CHECKING: - from lib.core.SymbolTable import SymbolTable - - -# --------------------------------------------------------------- -# `_scan_builtins` 的 `import X as Y` 解析正则**预编译**(性能:避免每次 -# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 -# 语义与逐字写法完全一致,模式串一字未改)。 -# --------------------------------------------------------------- -_RE_IMPORT_AS: re.Pattern[str] = re.compile(r'import\s+([^\s]+)\s+as\s+([^\s]+)') - - -class TypeAnnotationResolver: - """从 AST 节点解析类型注解,独立于 CTypeInfo 数据模型。""" - - # 编译器内置语法:rawptr(TPC/TPV 双端内置,不可重定义) - # rawptr[T] 固定表示"裸指针,指向原始类型 T"(等价 t.CPtr[T]); - # 单独 rawptr 等价 t.CPtr(void*)。 - _BUILTIN_ROWPTR_NAME: str = 'rawptr' - _BUILTIN_ROWPTR_VALUE: str = 't.CPtr' - - @classmethod - def _builtin_rawptr_info(cls) -> CTypeInfo: - """返回内置 rawptr 的 CTypeInfo(t.CPtr 语义:BaseType=CVoid, PtrCount=1)""" - info: CTypeInfo = CTypeInfo() - info.BaseType = t.CVoid() - info.PtrCount = 1 - return info - - @staticmethod - def from_type_name(TypeName: str) -> CTypeInfo: - """从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)""" - # Handle pointer types like 'CUInt32T *', 'void *', etc. - ptr_count: int = 0 - base_name: str = TypeName - while base_name.endswith(' *') or base_name.endswith('*'): - ptr_count += 1 - base_name = base_name.rstrip(' *').rstrip() - - entry: tuple[type, int] | None = BuiltinTypeMap.Get(base_name) - if entry: - TypeClass: type - base_ptr: int - TypeClass, base_ptr = entry - info: CTypeInfo = CTypeInfo() - info.BaseType = TypeClass - info.PtrCount = base_ptr + ptr_count - return info - # Handle LLVM primitive type names (e.g., 'i64', 'double' from generic type inference) - llvm_map: dict = CTypeInfo._get_llvm_primitive_map() - llvm_entry: tuple[type, int] | None = llvm_map.get(base_name) - if llvm_entry: - TypeClass: type - base_ptr: int - TypeClass, base_ptr = llvm_entry - info: CTypeInfo = CTypeInfo() - info.BaseType = TypeClass - info.PtrCount = base_ptr + ptr_count - return info - # Fallback with pointer count - info: CTypeInfo = CTypeInfo.CreateFromTypeName(TypeName) - if ptr_count > 0: - info.PtrCount = ptr_count - return info - - @staticmethod - def try_eval_const_expr(node: ast.AST, SymbolTable: SymbolTable) -> int | float | str | None: - return ConstEvaluator.eval_with_symtab(node, SymbolTable) - - @staticmethod - def from_node(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """从 AST 节点解析 CTypeInfo - - Args: - Node: AST 节点(如 ast.Name, ast.Attribute 等) - SymbolTable: 符号表字典 - """ - if isinstance(Node, ast.Constant): - return TypeAnnotationResolver._from_node_constant(Node, SymbolTable) - if isinstance(Node, ast.Name): - return TypeAnnotationResolver._from_node_name(Node, SymbolTable) - if isinstance(Node, ast.Call): - return TypeAnnotationResolver._from_node_call(Node, SymbolTable) - if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): - return TypeAnnotationResolver._from_node_binop(Node, SymbolTable) - if isinstance(Node, ast.Subscript): - return TypeAnnotationResolver._from_node_subscript(Node, SymbolTable) - if isinstance(Node, ast.Attribute): - return TypeAnnotationResolver._from_node_attribute(Node, SymbolTable) - return CTypeInfo() - - # ============================================================ - # builtins 最后匹配(对齐 TPV TypeGenResolve._lookup_builtins_typedef): - # 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的 typedef - # 声明作为最终兜底(rawptr/sizet/uint64 等)。 - # builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随 - # from_imports 注入,故需按源文件扫描兜底。 - # 必须同时兼容两种等价写法: - # - 语法糖 `typedef 名字 = 类型`(builtins.vp 现用形态) - # - 注解形态 `名字: t.CTypedef = 类型`(stdint.vp 等库模块) - # 只认注解形态时语法糖声明全被跳过 → 兜底表为空 → 裸名退化为 - # `%"{mod}.名字"*` 假结构体(sizet 曾被当成结构体,BinOp 报 - # "Operands must be the same type, got (%"…sizet"*, i64)")。 - # ============================================================ - _builtins_scan_cache: dict | None = None - - # builtins 解析链的**全链**重入深度上限与线程局部计数。 - # 既有 `_resolve_builtins_type_str` 的 `depth`/`visited` 只在自身递归内传递; - # 而复合值(含 `|` / `[`)分支会经 `from_node` 重新进入 - # `_from_node_name → _lookup_builtins_typedef`,把 depth/visited 归零 ⇒ 该方向 - # 可绕过环检测无限递归(A = B | t.CPtr、B = A 一类声明)。此计数器覆盖 - # `_lookup_builtins_typedef → _resolve_builtins_type_str → from_node → …` 全程, - # 超上限即判为成环并 fail-fast(不再沿链继续)。合法链通常 ≤ 4 层。 - _BUILTINS_CHAIN_MAX_DEPTH: int = 32 - _chain_state: threading.local = threading.local() - - # 解析失败(成环/悬空)的 builtins typedef 名 → 错误报文缓存: - # 同一失败不再被反复重试(每次重试都会重新扫描/解析整条链)。 - # builtins.vp 在一次编译进程内不变,故无需失效。 - _builtins_failure_cache: dict[str, str] = {} - - @classmethod - def _chain_depth_enter(cls) -> int: - depth: int = getattr(cls._chain_state, 'depth', 0) - cls._chain_state.depth = depth + 1 - return depth - - @classmethod - def _chain_depth_exit(cls, prev: int) -> None: - cls._chain_state.depth = prev - - # `typedef 名字 = 类型` 语法糖(对应 SymbolUtils._ExpandTypedefs 的展开形态) - _TYPEDEF_SUGAR_RE: re.Pattern = re.compile( - r'^typedef\s+([A-Za-z_]\w*)\s*=(?!=)\s*(.+?)\s*$') - - @staticmethod - def _NormalizeTypedefRhsText(text: str) -> str: - """把 typedef 右值的 C 式写法归一成规范 `|` 联合文本(供**文本回退路径**使用)。 - - 磁盘原文里 `X: t.CTypedef = const str` / `= const uint16*` 的右值带描述符与指针 - 后缀,而本路径(_parse_typedef_line → _resolve_builtins_type_str)按表达式文本解析, - 故先归一: - - 前导 `const` / `volatile`(可组合) → `t.CConst` / `t.CVolatile` 前缀; - - 末尾 `*` / `**` 串 → 逐层 `| t.CPtr`。 - 不含上述形态的文本原样返回(幂等)。无算术语义风险:typedef 右值不含乘法。 - """ - s: str = text.strip() - if not s: - return text - markers: list[str] = [] - while True: - m: re.Match | None = re.match(r'^(const|volatile)\s+', s) - if m is None: - break - markers.append('t.CConst' if m.group(1) == 'const' else 't.CVolatile') - s = s[m.end():].lstrip() - ptrs: int = 0 - while s.endswith('*'): - s = s[:-1].rstrip() - ptrs += 1 - if not markers and ptrs == 0: - return text - parts: list[str] = markers + [s] + ['t.CPtr'] * ptrs - return ' | '.join(p for p in parts if p) - - @staticmethod - def _parse_typedef_line(stripped: str) -> tuple[str, str] | None: - """单行源码文本 → typedef 声明 (名字, 类型值);非 typedef 行返回 None - - 兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。 - 右值的 C 式写法(`const T` / `T*`)经 _NormalizeTypedefRhsText 归一为 `|` 联合。 - """ - m: re.Match | None = TypeAnnotationResolver._TYPEDEF_SUGAR_RE.match(stripped) - if m is not None: - return m.group(1), TypeAnnotationResolver._NormalizeTypedefRhsText(m.group(2)) - if ':' not in stripped or 't.CTypedef' not in stripped: - return None - name, _, rest = stripped.partition(':') - if 't.CTypedef' not in rest: - return None - value: str = rest.partition('=')[2].strip() - name = name.strip() - if not name or not value: - return None - return name, TypeAnnotationResolver._NormalizeTypedefRhsText(value) - - @staticmethod - def _builtins_include_dirs() -> list[str]: - """includes 目录列表(builtins.vp 定位用)——只认项目配置 includes, - 禁止仓库硬编码回退(重组/缺失时会产生错误类型解析,如 uint32→struct 退化)""" - dirs: list[str] = [] - try: - from lib.Projectrans.Phase2Translator import Phase2Translator - dirs = list(Phase2Translator.PROJECT_INCLUDE_DIRS) - except Exception: - pass - return dirs - - @staticmethod - def _find_include_file(mod: str) -> str | None: - """按模块名定位 includes 源文件(mod 的 '.'→'/',附加 .vp)""" - rel: str = mod.replace('.', '/') + '.vp' - for inc_dir in TypeAnnotationResolver._builtins_include_dirs(): - cand: str = os.path.join(inc_dir, rel) - if os.path.isfile(cand): - return cand - return None - - @classmethod - def _scan_builtins(cls) -> dict: - """扫描 includes/builtins.vp:返回 {typedefs: {name: type_str}, aliases: {alias: target}}""" - if cls._builtins_scan_cache is not None: - return cls._builtins_scan_cache - typedefs: dict[str, str] = {} - aliases: dict[str, str] = {} - path: str | None = TypeAnnotationResolver._find_include_file('builtins') - if path is not None: - try: - with open(path, 'r', encoding='utf-8') as f: - content: str = f.read() - except Exception: - content = '' - for raw_line in content.split('\n'): - line: str = raw_line.rstrip('\r') - stripped: str = line.strip() - if not stripped or stripped.startswith('#'): - continue - # import X as Y → 别名表(__stdint → stdint) - if stripped.startswith('import '): - m: re.Match | None = _RE_IMPORT_AS.match(stripped) - if m: - aliases[m.group(2)] = m.group(1) - continue - td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped) - if td is not None: - typedefs[td[0]] = td[1] - # 只缓存"扫到了东西"的结果:includes 目录尚未装配(PROJECT_INCLUDE_DIRS 为空) - # 或 builtins.vp 未定位时,缓存空表会让本进程内后续所有 builtins typedef 解析 - # (sizet(x)/uint64(x) 这类 cast 调用)永久 miss,只在真正扫到内容后落缓存。 - if not typedefs and not aliases: - return {'typedefs': typedefs, 'aliases': aliases} - cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases} - return cls._builtins_scan_cache - - @classmethod - def _scan_library_typedef(cls, mod: str, attr: str) -> str | None: - """查库模块源中 typedef 声明,返回类型值(权威定义)或 None - - 兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。 - """ - path: str | None = TypeAnnotationResolver._find_include_file(mod) - if path is None: - return None - try: - with open(path, 'r', encoding='utf-8') as f: - content: str = f.read() - except Exception: - return None - for raw_line in content.split('\n'): - line: str = raw_line.rstrip('\r') - stripped: str = line.strip() - if not stripped or stripped.startswith('#'): - continue - td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped) - if td is not None and td[0] == attr: - return td[1] - return None - - @classmethod - def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str], - SymbolTable, depth: int = 0, - visited: frozenset[str] | None = None) -> CTypeInfo | None: - """解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名) - - Args: - type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT / sizet) - aliases: builtins.vp 的 import 别名表(__stdint → stdint) - depth: 递归深度守卫(防 typedef 值自引用成环) - visited: 本次展开链上已展开的 typedef 名(环检测)。命中环/自引用返回 - None(而不是退化成 %"{mod}.{名}"* 假结构体),由调用方 - _lookup_builtins_typedef fail-fast 报出根因。 - """ - if not type_str or depth > 8: - return None - type_str = type_str.strip() - if not type_str: - return None - # 复合表达式(联合/泛型)交给常规 AST 解析 - if '|' in type_str or '[' in type_str: - try: - node: ast.AST | None = TypeAnnotationResolver._parse_type_expr(type_str) - except Exception: - node = None - if node is None: - return None - return TypeAnnotationResolver.from_node(node, SymbolTable) - if '.' in type_str: - mod: str - attr: str - mod, _, attr = type_str.rpartition('.') - mod = mod.strip() - attr = attr.strip() - real_mod: str = aliases.get(mod, mod) - # t/c 内建命名空间:**优先按内建类名直接解析**,不依赖调用方 SymbolTable - # 的装配进度(import 注册阶段 t 尚未入表 ⇒ 原先返回 None,被上层误归因为 - # "自引用/循环引用";实测 builtins typedef 'sizet' → 't.CSizeT' 即卡在此处)。 - if real_mod in ('t', 'c'): - _bi: CTypeInfo | None = TypeAnnotationResolver.from_type_name(attr) - if _bi is not None: - return _bi - node = TypeAnnotationResolver._parse_type_expr(type_str) - if node is not None: - return TypeAnnotationResolver.from_node(node, SymbolTable) - return None - # 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描) - lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr) - if lib_val is not None: - return TypeAnnotationResolver._resolve_builtins_type_str( - lib_val, aliases, SymbolTable, depth + 1, visited) - # 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable) - node = TypeAnnotationResolver._parse_type_expr(type_str) - if node is not None: - return TypeAnnotationResolver.from_node(node, SymbolTable) - return None - # 裸名:名字本身即类型(t/C 类名、BuiltinTypeMap 的 C 速记名、裸 LLVM 标量名) - # → 直接解析;否则它可能是**另一个 typedef 的引用**,沿 builtins typedef 表 - # 继续展开到基础类型。 - # 依据:stdint.vp 的 `size_t/SIZE_T: t.CTypedef = sizet` 与 builtins.vp 的 - # `size_t/uzt: t.CTypedef = sizet` 都是"别名 → 权威名 → t 类型"的链式写法, - # 与 TPV resolve_typedef_or_map 的 CTypedef 表递归(TypeGenResolve.vp:723-734) - # 同口径。此前裸名一律走 from_type_name:链中名字命中不到基础类型时退化成 - # %"{mod}.{名}"* 假结构体(sizet/size_t → "Type of #N arg mismatch: - # %…sizet"* != i32" 的根因)。 - if TypeAnnotationResolver._is_direct_type_name(type_str): - return TypeAnnotationResolver.from_type_name(type_str) - chain: str | None = TypeAnnotationResolver._scan_builtins()['typedefs'].get(type_str) - if chain is None: - # 非 builtins typedef 名的裸名(str/bytes/None 等):保留既有兜底 - return TypeAnnotationResolver.from_type_name(type_str) - seen: frozenset[str] = visited or frozenset() - if type_str in seen or chain == type_str: - # 自引用/环(如 `sizet: t.CTypedef = sizet`):无信息可展开,返回 None - return None - return TypeAnnotationResolver._resolve_builtins_type_str( - chain, aliases, SymbolTable, depth + 1, seen | {type_str}) - - @staticmethod - def _is_direct_type_name(name: str) -> bool: - """名字本身即类型(非"指向另一个 typedef 的别名") - - 覆盖 t 的 CType 子类名(CInt/CChar/CSizeT/CInt64T 等)、BuiltinTypeMap 的 - C 速记名(int/char/bool)与裸 LLVM 标量名(i64/double…,泛型推导产物)。 - """ - if not name: - return False - if BuiltinTypeMap.Get(name) is not None: - return True - if name in CTypeInfo._get_llvm_primitive_map(): - return True - cls: type | None = CTypeHelper.GetTModuleCType(name) - if cls is None: - cls = getattr(t, name, None) - return isinstance(cls, type) and issubclass(cls, t.CType) and cls is not t.CType - - @staticmethod - def _parse_type_expr(text: str) -> ast.AST | None: - """vast mode='eval' 解析单个表达式,返回表达式节点或 None""" - try: - expr_tree: ast.AST = ast.parse(text, '', mode='eval') - body = getattr(expr_tree, 'body', None) - if body is not None and not isinstance(body, list): - return body - if isinstance(body, list) and body: - return body[0] - return None - except Exception: - return None - - @classmethod - def builtins_typedef_value(cls, TypeName: str) -> str | None: - """返回 builtins.vp 中 CTypedef 声明的原始类型值(如 rawptr → 't.CPtr') - - 供 stub 生成 / 类型字符串消费端判断"base[arg]"下标是否为 builtins 指针 - typedef(rawptr[AST] → AST*),防止字符串化产物被误建为 phantom 结构体。 - rawptr 已内置于编译器(语法保留字):即使 builtins.vp 不再声明、 - 或用户自行 typedef,也始终返回内置值(不可重定义)。 - """ - if not TypeName: - return None - if TypeName == cls._BUILTIN_ROWPTR_NAME: - return cls._BUILTIN_ROWPTR_VALUE - data: dict = cls._scan_builtins() - return data['typedefs'].get(TypeName) - - @classmethod - def _lookup_builtins_typedef(cls, TypeName: str, SymbolTable) -> CTypeInfo | None: - """builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明 - - 命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo, - miss(该名字不是 builtins typedef)返回 None(交由调用方走原有兜底)。 - rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。 - 名字在表内却解析不出基础类型 = 声明本身无信息/成环(自引用)⇒ fail-fast, - 不再静默退化为 %"{mod}.{名}"* 假结构体(下游仅报 "Type of #N arg - mismatch: %…* != …",无法归因到 typedef 声明)。 - """ - if not TypeName: - return None - if TypeName == cls._BUILTIN_ROWPTR_NAME: - return cls._builtin_rawptr_info() - # 同一失败不再反复解析:命中失败缓存则直接按既有报文 fail-fast, - # 不再重扫 builtins.vp / 重走整条解析链。 - _cached_err: str | None = cls._builtins_failure_cache.get(TypeName) - if _cached_err is not None: - raise ValueError(_cached_err) - # 全链重入深度守卫:覆盖 `_lookup_builtins_typedef → _resolve_builtins_type_str - # → from_node → _from_node_name → _lookup_builtins_typedef` 的所有回路 - # (含 `|`/`[` 复合值分支导致的 depth/visited 归零绕过)。超上限即成环。 - _prev_depth: int = cls._chain_depth_enter() - try: - if _prev_depth >= cls._BUILTINS_CHAIN_MAX_DEPTH: - _err: str = ( - f"builtins typedef 解析链成环或过深(>{cls._BUILTINS_CHAIN_MAX_DEPTH} 层)," - f"解析 '{TypeName}' 时终止:includes/builtins.vp 中相关 typedef 声明" - f"(含 `|` / `[...]` 复合值)互相引用成环。请改为指向真实基础类型" - f"(如 t.CSizeT),或断开环上引用。") - cls._builtins_failure_cache[TypeName] = _err - raise ValueError(_err) - data: dict = cls._scan_builtins() - type_str: str | None = data['typedefs'].get(TypeName) - if type_str is None: - return None - info: CTypeInfo | None = cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable) - if info is None: - # 不再一律归因"自引用/循环引用"(那正是把 t.CSizeT 解析失败误报成环、 - # 把人引向错误方向的根因):把声明值与该值解析失败的形态如实报出。 - _err = ( - f"builtins typedef '{TypeName}' 无法解析到基础类型:includes/builtins.vp " - f"声明 '{TypeName}: t.CTypedef = {type_str}',其值 '{type_str}' 解析失败" - f"(既不是内建类型名,也不是链上可展开的 typedef)。" - f"请把该声明指向真实类型(如 t.CSizeT),或确认该类型名存在。") - cls._builtins_failure_cache[TypeName] = _err - raise ValueError(_err) - return info - finally: - cls._chain_depth_exit(_prev_depth) - - @staticmethod - def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """处理 ast.Constant 节点""" - TypeName: str = Node.value - return TypeAnnotationResolver.from_type_name(TypeName) - - @staticmethod - def _RecoverTypeFromNamespaces(TypeName: str, SymbolTable: dict | SymbolTable) -> CTypeInfo | None: - """平坦条目不是"类型"时,按命名空间找回同名**类型**条目(找不到 → None)。 - - 与 LLVMTypeMapper._resolve_name 的"同名冲突:平坦条目被 define / 枚举成员 / - 函数占位时找回类型"逐分支同构(同一条规则源):优先 typedef,其次结构体 - (struct / renum / exception 均置 IsStruct),再次枚举。 - - 必要性:平坦表(无前缀)一个名字只留一个条目,而 `import path as X` 的**模块 - 别名**与模块内 `class X` 同名(TransPyV 的 Generator 47 个 `rawptr[X.X]` 字段 - 全是这种形态)。条目被别名占住时,本解析器返回"无基类型"的空 CTypeInfo ⇒ - `rawptr[X.X]` 退化成 void 指针链(i8*/i8**),而 stub 声明侧是 - `%".X"*` ⇒ struct 布局里该槽被 set_body 冻结成 i8**(不可重设), - 赋值时报"指针隐式转换已禁止(层数不同)"。 - """ - _Namespaces: Any = getattr(SymbolTable, '_namespaces', None) - if not isinstance(_Namespaces, dict): - return None - for _NsSyms in _Namespaces.values(): - if not isinstance(_NsSyms, dict): - continue - _Cand: Any = _NsSyms.get(TypeName) - if _Cand is None: - continue - if getattr(_Cand, 'IsTypedef', False): - _R: CTypeInfo = _Cand.Copy() - _R.IsTypedef = True - if _R.BaseType is None: - _R.BaseType = t._CTypedef(TypeName) - return _R - if getattr(_Cand, 'IsStruct', False) or getattr(_Cand, 'IsRenum', False): - # 结构体表示与本文件既有 promotion(_from_node_name / _from_node_attribute - # 的 `BaseType = t.CStruct(name=...); IsStruct = True`)**逐字同口径**: - # 类引用不额外加指针层(PtrCount 保持 0)——本文件里 `rawptr[X]` 的层数 - # 由 rawptr 分支自己 +1,若此处再加一层会得到 `X**`。 - _R = CTypeInfo() - _R.BaseType = t.CStruct(name=TypeName) - _R.IsStruct = True - _R.Name = TypeName - return _R - if getattr(_Cand, 'IsEnum', False): - _R = CTypeInfo() - _R.BaseType = t.CInt() - _R.IsEnum = True - _R.Name = TypeName - return _R - return None - - @staticmethod - def _from_node_name(Node: ast.Name, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """处理 ast.Name 节点""" - TypeName: str = Node.id - # 编译器内置语法 rawptr(不可重定义):单独 rawptr 等价 t.CPtr(void*), - # 优先于 SymbolTable/任何 typedef 声明。 - if TypeName == TypeAnnotationResolver._BUILTIN_ROWPTR_NAME: - return TypeAnnotationResolver._builtin_rawptr_info() - TypeObj: type | None = CTypeHelper.GetTModuleCType(TypeName) - if TypeObj is None: - TypeObj = getattr(t, TypeName, None) - if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType) and TypeObj is not t.CType: - if TypeObj == t.CPtr: - Result: CTypeInfo = CTypeInfo() - Result.PtrCount = 1 - Result.BaseType = t.CVoid() - return Result - if TypeObj == t.State: - Result: CTypeInfo = CTypeInfo() - Result.IsState = True - Result.BaseType = t.CVoid() - return Result - Inst: t.CType = TypeObj() - Result: CTypeInfo = CTypeInfo() - Result.Name = type(Inst).__name__ - # 存储类修饰符(CExtern, CExport, CInline, CThreadLocal 等): - # position 含 STORAGE_CLASS 且不含 BASE → 纯修饰符,无实际类型 - if t.CType.STORAGE_CLASS in TypeObj.position and t.CType.BASE not in TypeObj.position: - Result.BaseType = t.CVoid() - Result.Storage = Inst - return Result - # 类型限定符(CConst, CVolatile 等) - if t.CType.TYPE_QUALIFIER in TypeObj.position and t.CType.BASE not in TypeObj.position: - Result.BaseType = t.CVoid() - if TypeObj == t.CConst: - Result.DataConst = True - elif TypeObj == t.CVolatile: - Result.DataVolatile = True - return Result - Result.BaseType = TypeObj - if hasattr(Inst, 'IsSigned'): - Result.IsSigned = Inst.IsSigned - return Result - # 检查 BuiltinTypeMap(覆盖 VOIDPTR, BYTEPTR 等指针别名) - builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(TypeName) - if builtin_entry: - TypeClass: type - base_ptr: int - TypeClass, base_ptr = builtin_entry - Result: CTypeInfo = CTypeInfo() - Result.BaseType = TypeClass - Result.PtrCount = base_ptr - return Result - if TypeName in SymbolTable: - Entry: CTypeInfo | dict = SymbolTable[TypeName] - if Entry: - if isinstance(Entry, CTypeInfo): - if Entry.IsTypedef: - if Entry.BaseType and (not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0): - Result = Entry.Copy() - Result.IsTypedef = True - Result.Name = TypeName - return Result - if Entry.OriginalType: - if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] - Result.IsTypedef = True - Result.Name = TypeName - return Result - elif isinstance(Entry.OriginalType, CTypeInfo): - Resolved = Entry.OriginalType.Copy() - elif isinstance(Entry.OriginalType, str) and Entry.OriginalType == 'Callable': - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = [] - Result.IsTypedef = True - Result.Name = TypeName - return Result - elif isinstance(Entry.OriginalType, str): - Resolved = TypeAnnotationResolver.from_type_name(Entry.OriginalType) - else: - Resolved = Entry.OriginalType - Resolved.IsTypedef = True - Resolved.Name = TypeName - return Resolved - TypeEntry = BuiltinTypeMap.Get(TypeName) - if TypeEntry: - Result = CTypeInfo() - Result.BaseType = TypeEntry[0]() - Result.PtrCount = TypeEntry[1] - Result.IsTypedef = True - Result.Name = TypeName - return Result - return Entry - if Entry.IsEnum: - Result = CTypeInfo() - Result.BaseType = t.CInt() - Result.IsEnum = True - Result.Name = TypeName - return Result - if Entry.IsExceptionClass: - Result = CTypeInfo() - Result.BaseType = t.CInt() - Result.IsExceptionClass = True - Result.Name = TypeName - return Result - if Entry.BaseType is None and (Entry.IsStruct or Entry.Name): - Entry.BaseType = t.CStruct(name=TypeName) - Entry.IsStruct = True - if (Entry.BaseType is None and not Entry.IsStruct and not Entry.IsEnum - and not Entry.IsTypedef and not Entry.IsCpythonObject and not Entry.Name): - # 平坦条目不是"类型"(同名**模块别名** / 函数 / 变量 / define / - # 枚举成员占住裸名):直接 return Entry 会得到"无基类型"的空 - # CTypeInfo ⇒ `rawptr[X]` 之类注解退化成 void 指针链 - # (i8*/i8**),struct 布局里该槽被 set_body 永久冻结。 - # 与上方 _from_node_attribute 及 LLVMTypeMapper._resolve_name - # 的"同名冲突找回类型"同一条规则源。 - _RecoveredName: CTypeInfo | None = TypeAnnotationResolver._RecoverTypeFromNamespaces( - TypeName, SymbolTable) - if _RecoveredName is not None: - return _RecoveredName - return Entry - elif isinstance(Entry, dict): - if Entry.get('type') == 'typedef': - OriginalType = Entry.get('OriginalType', '') - if OriginalType: - if OriginalType == 'Callable': - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = [] - Result.IsTypedef = True - Result.Name = TypeName - return Result - Resolved = TypeAnnotationResolver.from_type_name(OriginalType) - Resolved.IsTypedef = True - Resolved.Name = TypeName - return Resolved - TypeEntry = BuiltinTypeMap.Get(TypeName) - if TypeEntry: - Result = CTypeInfo() - Result.BaseType = TypeEntry[0]() - Result.PtrCount = TypeEntry[1] - Result.IsTypedef = True - Result.Name = TypeName - return Result - Result = CTypeInfo() - Result.BaseType = t._CTypedef(TypeName) - Result.IsTypedef = True - Result.Name = TypeName - return Result - # 检查 t 类型别名(from t import CInt as myint) - if hasattr(SymbolTable, '_t_type_symbols'): - t_cls = SymbolTable._t_type_symbols.get(TypeName) - if t_cls is not None: - Result = CTypeInfo() - Result.BaseType = t_cls - return Result - # 特殊处理 str 类型,它应该是 char*(指针) - if TypeName in ('str', 'bytes'): - Result = CTypeInfo() - Result.BaseType = t.CChar() - Result.PtrCount = 1 - return Result - # [builtins 最后匹配] 常规路径全部 miss 后的最终兜底: - # 扫描 includes/builtins.vp 的 CTypedef 声明(rawptr/size_t 等, - # 对齐 TPV TypeGenResolve)。命中返回解析结果,miss 走 from_type_name。 - builtins_info: CTypeInfo | None = TypeAnnotationResolver._lookup_builtins_typedef(TypeName, SymbolTable) - if builtins_info is not None: - return builtins_info - return TypeAnnotationResolver.from_type_name(TypeName) - - @staticmethod - def _from_node_call(Node: ast.Call, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """处理 ast.Call 节点""" - if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': - Result: CTypeInfo = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = [] - try: - if len(Node.args) > 0: - RetInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.args[0], SymbolTable) - if RetInfo: - Result.FuncPtrReturn = RetInfo - kw: ast.keyword - for kw in Node.keywords: - ParamInfo: CTypeInfo = TypeAnnotationResolver.from_node(kw.value, SymbolTable) - if ParamInfo: - Result.FuncPtrParams.append((kw.arg or '', ParamInfo)) - except Exception as _e: - if _config_mode == "strict": - warnings.warn(f"异常被忽略: {_e}") - return Result - if isinstance(Node.func, ast.Attribute): - if isinstance(Node.func.value, ast.Name) and IsTModule(Node.func.value.id, SymbolTable): - if Node.func.attr == 'Bit' and len(Node.args) > 0: - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t.CInt() - Result.IsBitField = True - if isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, int): - Result.BitWidth = Node.args[0].value - else: - Result.BitWidth = 1 - return Result - return CTypeInfo() - - @staticmethod - def _from_node_binop(Node: ast.BinOp, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """处理 ast.BinOp (BitOr) 节点""" - LeftInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.left, SymbolTable) - RightInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.right, SymbolTable) - - if LeftInfo.IsState or RightInfo.IsState: - Result: CTypeInfo = CTypeInfo() - Result.IsState = True - NonStateInfo: CTypeInfo = LeftInfo if not LeftInfo.IsState else RightInfo - StateInfo: CTypeInfo = LeftInfo if LeftInfo.IsState else RightInfo - if NonStateInfo.BaseType and (not isinstance(NonStateInfo.BaseType, (t._CTypedef,)) or NonStateInfo.PtrCount > 0): - Result.BaseType = NonStateInfo.BaseType - elif NonStateInfo.IsTypedef and NonStateInfo.Name: - if NonStateInfo.BaseType and (not isinstance(NonStateInfo.BaseType, (t._CTypedef,)) or NonStateInfo.PtrCount > 0): - Result.BaseType = NonStateInfo.BaseType - Result.PtrCount = NonStateInfo.PtrCount - Result.IsTypedef = True - Result.Name = NonStateInfo.Name - if NonStateInfo.OriginalType: - Result.OriginalType = NonStateInfo.OriginalType - else: - Result.BaseType = t.CStruct(name=NonStateInfo.Name) - Result.IsStruct = True - Result.IsTypedef = True - Result.Name = NonStateInfo.Name - if NonStateInfo.OriginalType: - Result.OriginalType = NonStateInfo.OriginalType - else: - Result.BaseType = t.CVoid() - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - if not Result.Storage: - Result.Storage = t.CExport() - if LeftInfo.Storage and not isinstance(LeftInfo.Storage, t.CExport): - Result.Storage = LeftInfo.Storage - elif RightInfo.Storage and not isinstance(RightInfo.Storage, t.CExport): - Result.Storage = RightInfo.Storage - if LeftInfo.DataConst or RightInfo.DataConst: - Result.DataConst = True - if LeftInfo.VarConst or RightInfo.VarConst: - Result.VarConst = True - return Result - - if LeftInfo.IsPtr or RightInfo.IsPtr: - Result = CTypeInfo() - # 当双方都是指针时(如 (AST | t.CPtr) | t.CPtr),PtrCount 应相加 - # 而非取 max,否则 AST** 会被折叠为 AST*,导致对象切片问题 - if LeftInfo.IsPtr and RightInfo.IsPtr: - Result.PtrCount = LeftInfo.PtrCount + RightInfo.PtrCount - # BaseType 优先取非 void 侧(如 AST),而非 t.CPtr 的 void - if LeftInfo.BaseType and not isinstance(LeftInfo.BaseType, t.CVoid): - Result.BaseType = LeftInfo.BaseType - elif RightInfo.BaseType and not isinstance(RightInfo.BaseType, t.CVoid): - Result.BaseType = RightInfo.BaseType - else: - Result.BaseType = t.CUnsignedChar() - else: - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - NonPtrInfo = LeftInfo if not LeftInfo.IsPtr else RightInfo - PtrInfo = RightInfo if RightInfo.IsPtr else LeftInfo - if NonPtrInfo.BaseType and (not isinstance(NonPtrInfo.BaseType, (t._CTypedef,)) or NonPtrInfo.PtrCount > 0): - Result.BaseType = NonPtrInfo.BaseType - elif NonPtrInfo.IsTypedef and NonPtrInfo.Name: - if NonPtrInfo.BaseType and (not isinstance(NonPtrInfo.BaseType, (t._CTypedef,)) or NonPtrInfo.PtrCount > 0): - Result.BaseType = NonPtrInfo.BaseType - Result.IsTypedef = True - Result.Name = NonPtrInfo.Name - if NonPtrInfo.OriginalType: - Result.OriginalType = NonPtrInfo.OriginalType - else: - Result.BaseType = t.CStruct(name=NonPtrInfo.Name) - Result.IsStruct = True - elif PtrInfo.BaseType and (not isinstance(PtrInfo.BaseType, (t._CTypedef,)) or PtrInfo.PtrCount > 0): - Result.BaseType = PtrInfo.BaseType - else: - Result.BaseType = t.CUnsignedChar() - if LeftInfo.DataConst or RightInfo.DataConst: - Result.DataConst = True - if LeftInfo.VarConst or RightInfo.VarConst: - Result.VarConst = True - if LeftInfo.DataVolatile or RightInfo.DataVolatile: - Result.DataVolatile = True - if LeftInfo.VarVolatile or RightInfo.VarVolatile: - Result.VarVolatile = True - if LeftInfo.Storage: - Result.Storage = LeftInfo.Storage - elif RightInfo.Storage: - Result.Storage = RightInfo.Storage - return Result - - if LeftInfo.DataConst or RightInfo.DataConst: - Result = CTypeInfo() - BaseTypeSide = LeftInfo if not LeftInfo.DataConst else RightInfo - QualSide = LeftInfo if LeftInfo.DataConst else RightInfo - Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else QualSide.BaseType - if not Result.BaseType: - Result.BaseType = t.CInt() - Result.DataConst = True - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - if LeftInfo.VarConst or RightInfo.VarConst: - Result.VarConst = True - if LeftInfo.DataVolatile or RightInfo.DataVolatile: - Result.DataVolatile = True - if LeftInfo.Storage: - Result.Storage = LeftInfo.Storage - elif RightInfo.Storage: - Result.Storage = RightInfo.Storage - return Result - - if LeftInfo.DataVolatile or RightInfo.DataVolatile: - Result = CTypeInfo() - BaseTypeSide = LeftInfo if not LeftInfo.DataVolatile else RightInfo - QualSide = LeftInfo if LeftInfo.DataVolatile else RightInfo - Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else QualSide.BaseType - if not Result.BaseType: - Result.BaseType = t.CInt() - Result.DataVolatile = True - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - if LeftInfo.VarConst or RightInfo.VarConst: - Result.VarConst = True - if LeftInfo.Storage: - Result.Storage = LeftInfo.Storage - elif RightInfo.Storage: - Result.Storage = RightInfo.Storage - return Result - - if LeftInfo.Storage or RightInfo.Storage: - Result = CTypeInfo() - BaseTypeSide = LeftInfo if not LeftInfo.Storage else RightInfo - StorageSide = LeftInfo if LeftInfo.Storage else RightInfo - # CExtern | CExport 等价于 t.State:两边都有 Storage 时设置 IsState - if BaseTypeSide.IsState or StorageSide.IsState or (LeftInfo.Storage and RightInfo.Storage): - Result.IsState = True - Result.BaseType = t.CVoid() - Result.Storage = LeftInfo.Storage if LeftInfo.Storage else RightInfo.Storage - return Result - Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else StorageSide.BaseType - if not Result.BaseType: - Result.BaseType = t.CInt() - Result.Storage = LeftInfo.Storage if LeftInfo.Storage else RightInfo.Storage - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - if LeftInfo.DataConst or RightInfo.DataConst: - Result.DataConst = True - if LeftInfo.VarConst or RightInfo.VarConst: - Result.VarConst = True - return Result - - # 检查是否有位域类型 (t.Bit) - if LeftInfo.IsBitField: - Result = CTypeInfo() - Result.BaseType = LeftInfo.BaseType if LeftInfo.BaseType else t.CInt() - Result.IsBitField = True - Result.BitWidth = LeftInfo.BitWidth - return Result - if RightInfo.IsBitField: - Result = CTypeInfo() - Result.BaseType = RightInfo.BaseType if RightInfo.BaseType else t.CInt() - Result.IsBitField = True - Result.BitWidth = RightInfo.BitWidth - return Result - - # 处理字节序类型 (t.BigEndian, t.LittleEndian) - # 合并两侧:保留非字节序侧的实际类型(如 CUInt64T),取字节序侧的 ByteOrder - if LeftInfo.ByteOrder or RightInfo.ByteOrder: - Result = CTypeInfo() - ByteOrderSide: CTypeInfo = LeftInfo if LeftInfo.ByteOrder else RightInfo - OtherSide: CTypeInfo = RightInfo if LeftInfo.ByteOrder else LeftInfo - if OtherSide.BaseType and (not isinstance(OtherSide.BaseType, (t._CTypedef,)) or OtherSide.PtrCount > 0): - Result.BaseType = OtherSide.BaseType - elif ByteOrderSide.BaseType and (not isinstance(ByteOrderSide.BaseType, (t._CTypedef,)) or ByteOrderSide.PtrCount > 0): - Result.BaseType = ByteOrderSide.BaseType - else: - Result.BaseType = t.CInt() - Result.ByteOrder = LeftInfo.ByteOrder or RightInfo.ByteOrder - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - return Result - - if LeftInfo.IsFuncPtr or RightInfo.IsFuncPtr: - Result = CTypeInfo() - FuncPtrSide = LeftInfo if LeftInfo.IsFuncPtr else RightInfo - Result.IsFuncPtr = True - Result.FuncPtrParams = list(FuncPtrSide.FuncPtrParams) - Result.FuncPtrReturn = FuncPtrSide.FuncPtrReturn - if LeftInfo.Storage: - Result.Storage = LeftInfo.Storage - elif RightInfo.Storage: - Result.Storage = RightInfo.Storage - return Result - - if LeftInfo.IsTypedef and RightInfo.BaseType: - if LeftInfo.BaseType and (not isinstance(LeftInfo.BaseType, (t._CTypedef,)) or LeftInfo.PtrCount > 0): - Result = LeftInfo.Copy() - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - if RightInfo.Storage: - Result.Storage = RightInfo.Storage - return Result - return RightInfo - if RightInfo.IsTypedef and LeftInfo.BaseType: - if RightInfo.BaseType and (not isinstance(RightInfo.BaseType, (t._CTypedef,)) or RightInfo.PtrCount > 0): - Result = RightInfo.Copy() - Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) - if LeftInfo.Storage: - Result.Storage = LeftInfo.Storage - return Result - return LeftInfo - - if LeftInfo.BaseType: - return LeftInfo - elif RightInfo.BaseType: - return RightInfo - return CTypeInfo() - - @staticmethod - def _from_node_subscript(Node: ast.Subscript, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """处理 ast.Subscript 节点""" - base: ast.AST = Node.value - if isinstance(base, ast.Attribute): - if isinstance(base.value, ast.Name) and IsTModule(base.value.id, SymbolTable) and base.attr == 'Bit': - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t.CInt() - Result.IsBitField = True - if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): - Result.BitWidth = Node.slice.value - else: - Result.BitWidth = 1 - return Result - if IsListAnnotation(Node): - slice_node: ast.AST = Node.slice - elts: list[ast.AST] = [] - if isinstance(slice_node, ast.Tuple): - elts = slice_node.elts - elif isinstance(slice_node, (ast.Attribute, ast.Name, ast.Subscript)): - elts = [slice_node] - if elts: - ElemInfo: CTypeInfo = TypeAnnotationResolver.from_node(elts[0], SymbolTable) - if ElemInfo and ElemInfo.BaseType: - Result: CTypeInfo = CTypeInfo() - Result.BaseType = ElemInfo.BaseType - Result.PtrCount = ElemInfo.PtrCount - Result.ArrayDims = list(ElemInfo.ArrayDims) - if len(elts) >= 2: - count_val: int | None = TypeAnnotationResolver.try_eval_const_expr(elts[1], SymbolTable) - if count_val is not None and isinstance(count_val, int) and count_val > 0: - Result.ArrayDims.insert(0, str(count_val)) - else: - Result.PtrCount += 1 - else: - # 单参数 t.CArray[T](= t.CArray[T, None]):长度自动推导数组。 - # 指针位置语义为"元素指针" Ptr(T),需在元素类型上再加一层指针。 - # 与裸名 CArray[T](HandlesTypeMerge.CollectAll 的 - # `ElementType.PtrCount + 1`)、stub 侧(LLVMTypeMapper - # IsListAnnotation 的 `elem_type + '*'`)、声明端 - # (DeclarationGenerator `[0 x T]` → _decay_array_to_ptr = T*) - # 以及 TPV(TypeGenResolve 单参 → Ptr(elem_ty))同口径。 - # 此前缺这一层:t.CArray[str] 得 i8*(即 str 本身), - # result[k] 退化成 i8 → 写字符串指针触发 ptr→int fail-fast。 - Result.PtrCount += 1 - if ElemInfo.IsPtr and not ElemInfo.ArrayDims: - Result.PtrCount = max(Result.PtrCount, 1) - return Result - # 函数指针构造子的两种书写形态逐点等价(对齐 TPV - # TypeGenResolve._IsCallableSubscriptValue): - # t.Callable[...] / c.Callable[...] → 模块限定 Attribute - # callable[...] / Callable[...] → 内建裸名(无需 import) - # 裸名形态此前落进下方泛型特化兜底(ResolveGenericClassSpecName)未命中 ⇒ - # 空 CTypeInfo ⇒ 槽位类型退化 i32(现场 Repl.vp:307 - # `ReplFragFn: callable[[rawptr], int]`,赋 i8* 时被「指针隐式转整数」拦下)。 - parts: list[str] = [] - IsBareCallable: bool = False - if isinstance(base, ast.Attribute): - current: ast.AST = base - while isinstance(current, ast.Attribute): - parts.insert(0, current.attr) - current = current.value - if isinstance(current, ast.Name): - parts.insert(0, current.id) - elif isinstance(base, ast.Name) and (base.id == 'callable' or base.id == 'Callable'): - # 裸名只在注解位置当函数指针构造子(非类型位置仍是普通标识符) - IsBareCallable = True - if isinstance(base, ast.Attribute) or IsBareCallable: - if (not IsBareCallable) and parts and IsTModule(parts[0], SymbolTable) and parts[-1] == t.CPtr.__name__: - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t.CVoid() - Result.PtrCount = 1 - slice_node: ast.AST = Node.slice - if isinstance(slice_node, ast.Subscript): - InnerInfo: CTypeInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) - if InnerInfo: - if InnerInfo.PtrCount > 0: - Result.PtrCount += InnerInfo.PtrCount - if InnerInfo.BaseType and not isinstance(InnerInfo.BaseType, t.CVoid): - Result.BaseType = InnerInfo.BaseType - elif isinstance(slice_node, ast.Attribute): - SliceInfo: CTypeInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) - if SliceInfo: - if SliceInfo.IsPtr: - Result.PtrCount += SliceInfo.PtrCount - elif SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid): - Result.BaseType = SliceInfo.BaseType - elif isinstance(slice_node, ast.Name): - SliceType: type | None = getattr(t, slice_node.id, None) - if SliceType == t.CPtr: - Result.PtrCount += 1 - else: - SliceInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) - if SliceInfo and SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid): - Result.BaseType = SliceInfo.BaseType - return Result - if IsBareCallable or (parts and IsTModule(parts[0], SymbolTable) and parts[-1] == 'Callable'): - Result = CTypeInfo() - Result.IsFuncPtr = True - slice_node = Node.slice - if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: - params_list = slice_node.elts[0] - return_node = slice_node.elts[1] - ParamTypes = [] - _ParamsIsList: bool = isinstance(params_list, ast.List) - if _ParamsIsList: - for elt in params_list.elts: - ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable) - if ParamTypeInfo: - ParamTypes.append(('', ParamTypeInfo)) - # 空参数表 `t.Callable[[], R]` 语义 = 零形参:LLVM 端即 `R ()*`, - # 调用点按零实参间接 call。此前在此注入 void 占位形参 → 得 - # `R (void)*`(void 作形参本身不是合法 LLVM 函数类型),调用点 - # 实参个数(0) 与形参个数(1) 不符 → llvmlite CallInstr - # `arg = args[i]` IndexError(Repl.vp 模块级 t.Callable 槽 - # `ReplFragFn()` 翻译崩溃根因);与本文件另三处 Callable 形态 - # (:378 / :422 / :471)的 `FuncPtrParams = []` 口径统一。 - # 非 List 形态(遗留写法 t.Callable[A, R])保留原 void 占位行为。 - if not ParamTypes and not _ParamsIsList: - ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void'))) - Result.FuncPtrParams = ParamTypes - ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable) - Result.FuncPtrReturn = ReturnTypeInfo if ReturnTypeInfo else CTypeInfo.VoidTypeInfo() - return Result - # [builtins 最后匹配] 裸名 builtins typedef 下标(rawptr[int] → Ptr(int)): - # 与 TPV Subscript 分支同口径——base 是 builtins typedef(rawptr → t.CPtr), - # 结果 = 切片类型外包 base 的指针层数。 - if isinstance(base, ast.Name): - base_info: CTypeInfo | None = TypeAnnotationResolver._lookup_builtins_typedef(base.id, SymbolTable) - if base_info is not None and base_info.PtrCount > 0: - slice_node = Node.slice - SliceInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) - Result = CTypeInfo() - Result.BaseType = SliceInfo.BaseType if (SliceInfo and SliceInfo.BaseType) else t.CVoid() - Result.PtrCount = (SliceInfo.PtrCount if SliceInfo else 0) + base_info.PtrCount - return Result - # 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。 - # 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见 - # SymbolUtils.ResolveGenericClassSpecName。此前单参泛型下标在下方直接落到 - # "未知类型"空 CTypeInfo ⇒ 形参/成员类型退化成 i32 或 void 指针链 - # (现场:type_args: list[str]* 的形参 i32;CTypedefNames: list[str] | t.CPtr → i8*)。 - # 表示口径与本文件既有结构体 promotion 逐字一致(BaseType=CStruct + IsStruct + - # Name,PtrCount 保持 0):`X | t.CPtr` 由 _from_node_binop 取 max → 1 层, - # `rawptr[X]` 由上方分支 +1 → 1 层,均与 declare 侧 %"{sha}.X[*]"* 一致。 - _Spec: str | None = ResolveGenericClassSpecName( - Node, SymbolTable, - GetStructSha1MapFromSymtab(SymbolTable), - GetTCAliasMapFromSymtab(SymbolTable)) - if _Spec: - Result = CTypeInfo() - Result.BaseType = t.CStruct(name=_Spec) - Result.IsStruct = True - Result.Name = _Spec - return Result - return CTypeInfo() - - @staticmethod - def _from_node_attribute(Node: ast.Attribute, SymbolTable: dict | SymbolTable) -> CTypeInfo: - """处理 ast.Attribute 节点""" - ModuleParts: list[str] = [] - Current: ast.AST = Node - while isinstance(Current, ast.Attribute): - ModuleParts.insert(0, Current.attr) - Current = Current.value - - if isinstance(Current, ast.Name): - ModuleParts.insert(0, Current.id) - - TypeName: str - ModulePath: str | None - if len(ModuleParts) >= 2: - TypeName = ModuleParts[-1] - ModulePath = '.'.join(ModuleParts[:-1]) - elif len(ModuleParts) == 1: - TypeName = ModuleParts[0] - ModulePath = None - else: - return CTypeInfo() - - # 解析 import 别名: 如 import vpsdk.window as window -> window -> vpsdk.window - if ModulePath: - resolved: bool = False - import_aliases: dict = SymbolTable.import_aliases - if ModulePath in import_aliases: - ModulePath = import_aliases[ModulePath] - resolved = True - if not resolved and ModulePath in SymbolTable: - entry: CTypeInfo = SymbolTable[ModulePath] - if isinstance(entry, CTypeInfo) and entry.IsModuleAlias: - resolved_name = entry.ResolvedModule - if resolved_name: - ModulePath = resolved_name - - if ModulePath and IsTModulePath(ModulePath): - # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`):非真实类,仅按名字 - # 识别为「一层指针」(与 t.CPtr 同形),使联合形态 `T | t.CRef` 解析为 T*、 - # `void | t.CRef` 解析为 void*(i8*)。模式语义(ref/temp)由注解侧识别。 - if TypeName in ('CRef', 'CTempRef'): - Info: CTypeInfo = CTypeInfo() - Info.PtrCount = 1 - Info.BaseType = t.CVoid - return Info - TypeClass: type | None = CTypeHelper.GetTModuleCType(TypeName) - if TypeClass is None: - TypeClass = getattr(t, TypeName, None) - if TypeClass is not None and TypeClass.HasPosition(t.CType.POINTER): - Info: CTypeInfo = CTypeInfo() - Info.PtrCount = 1 - if TypeClass == t.CArrayPtr: - Info.IsArrayPtr = True - Info.BaseType = t.CVoid - return Info - if TypeClass is not None and TypeClass.IsStorageClass(): - Info: CTypeInfo = CTypeInfo() - Info.Storage = TypeClass() - if TypeClass == t.State: - Info.IsState = True - Info.BaseType = t.CVoid() - return Info - if TypeClass is not None and TypeClass.IsTypeQualifier(): - Info: CTypeInfo = CTypeInfo() - if TypeClass == t.CConst: - Info.DataConst = True - elif TypeClass == t.CVolatile: - Info.DataVolatile = True - return Info - if TypeClass is not None and TypeClass in (t.BigEndian, t.LittleEndian): - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t.CInt() - Result.ByteOrder = 'big' if TypeClass == t.BigEndian else 'little' - return Result - if (TypeClass is not None - and isinstance(TypeClass, type) - and issubclass(TypeClass, t.CType) - and TypeClass is not t.CType - and TypeClass.HasPosition(t.CType.BASE)): - if TypeClass == t.State: - Result: CTypeInfo = CTypeInfo() - Result.IsState = True - Result.BaseType = t.CVoid() - return Result - Inst: t.CType = TypeClass() - Result: CTypeInfo = CTypeInfo() - Result.BaseType = TypeClass - Result.Name = type(Inst).__name__ - if hasattr(Inst, 'IsSigned'): - Result.IsSigned = Inst.IsSigned - return Result - CNAME: str | None = CTypeHelper.GetCName(TypeName) - if CNAME: - return TypeAnnotationResolver.from_type_name(CNAME) - - # 处理字节序类型 - if TypeName == 'BigEndian': - Result = CTypeInfo() - Result.BaseType = t.CInt() - Result.ByteOrder = 'big' - return Result - if TypeName == 'LittleEndian': - Result = CTypeInfo() - Result.BaseType = t.CInt() - Result.ByteOrder = 'little' - return Result - - FullName: str = f"{ModulePath}.{TypeName}" if ModulePath else TypeName - - if TypeName in SymbolTable: - Entry: CTypeInfo = SymbolTable[TypeName] - if Entry and Entry.IsTypedef: - Resolved: CTypeInfo = Entry.Copy() - if Resolved.BaseType is None: - Resolved.BaseType = t._CTypedef(TypeName) - return Resolved - if Entry and Entry.IsEnum: - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t.CInt() - Result.IsEnum = True - Result.Name = TypeName - return Result - if Entry and Entry.IsExceptionClass: - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t.CInt() - Result.IsExceptionClass = True - Result.Name = TypeName - return Result - if Entry: - Result: CTypeInfo = Entry.Copy() - if Result.BaseType is None and (Result.IsStruct or Result.IsCpythonObject or Result.Name): - Result.BaseType = t.CStruct(name=TypeName) - Result.IsStruct = True - if (Result.BaseType is None and not Result.IsStruct and not Result.IsEnum - and not Result.IsTypedef and not Result.IsCpythonObject and not Result.Name): - # 平坦条目不是"类型"(同名**模块别名** / 函数 / 变量 / define / - # 枚举成员占住了裸名):Copy 得到的是"无基类型"的空 CTypeInfo ⇒ - # `rawptr[Mod.Cls]` 之类注解退化成 void 指针链(i8*/i8**), - # struct 布局里该槽被 set_body 永久冻结(现场:Generator 的 - # ExprGenFstr 槽 = i8**,而声明槽是 %"…ExprGenFstr"*,赋值时报 - # "层数不同")。与 LLVMTypeMapper._resolve_name 的"同名冲突找回 - # 类型"同一条规则源:按命名空间找回同名 typedef/结构体/枚举。 - _Recovered: CTypeInfo | None = TypeAnnotationResolver._RecoverTypeFromNamespaces( - TypeName, SymbolTable) - if _Recovered is not None: - return _Recovered - return Result - _Recovered2: CTypeInfo | None = TypeAnnotationResolver._RecoverTypeFromNamespaces( - TypeName, SymbolTable) - if _Recovered2 is not None: - return _Recovered2 - return CTypeInfo() - - # 嵌套类 / 嵌套 struct:限定名 `外类.内类`(ModulePath 是**类名**而非模块名)。 - # 命名口径见 HandlesClassDef._EmitNestedClassDefs —— 嵌套类以扁平名 - # `{外类}_{内类}` 发射,并由 _RegisterNestedClassName 把扁平名与 `外类.内类` - # 一并插入 SymbolTable。本分支按**扁平名**取条目:BaseType/Name 必须是扁平名 - # (若走下方 FullName 分支,会按短名拼出 `t.CStruct(name='Inner')`,与 - # 声明端的 `%"{sha}.外类_内类"` 不同源 ⇒ 布局/字段偏移分叉)。 - if ModulePath and TypeName: - NestedKey: str = f"{ModulePath}_{TypeName}" - if NestedKey in SymbolTable: - NestedEntry: CTypeInfo = SymbolTable[NestedKey] - if NestedEntry is not None: - Result = NestedEntry.Copy() - Result.Name = NestedKey - if Result.BaseType is None: - Result.BaseType = t.CStruct(name=NestedKey) - Result.IsStruct = True - return Result - - if FullName in SymbolTable: - Info: CTypeInfo = SymbolTable[FullName] - if Info and Info.IsTypedef: - # 优先用 BaseType 解析(如 VOIDPTR 已解析为 BaseType=CVoid, PtrCount=1) - if Info.BaseType and (not isinstance(Info.BaseType, (t._CTypedef,)) or Info.PtrCount > 0): - Result = Info.Copy() - Result.IsTypedef = True - Result.Name = FullName - return Result - # 再用 OriginalType 解析(字符串如 'void *',或 CTypeInfo) - if Info.OriginalType: - if isinstance(Info.OriginalType, CTypeInfo) and Info.OriginalType.IsFuncPtr: - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = Info.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = list(Info.OriginalType.FuncPtrParams) if Info.OriginalType.FuncPtrParams else [] - Result.IsTypedef = True - Result.Name = FullName - return Result - elif isinstance(Info.OriginalType, CTypeInfo): - Resolved: CTypeInfo = Info.OriginalType.Copy() - elif isinstance(Info.OriginalType, str) and Info.OriginalType == 'Callable': - Result = CTypeInfo() - Result.IsFuncPtr = True - Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() - Result.FuncPtrParams = [] - Result.IsTypedef = True - Result.Name = FullName - return Result - elif isinstance(Info.OriginalType, str): - Resolved = TypeAnnotationResolver.from_type_name(Info.OriginalType) - else: - Resolved = Info.OriginalType - if Resolved and Resolved.BaseType: - Resolved.IsTypedef = True - Resolved.Name = FullName - return Resolved - # 回退:保持原逻辑但设置 Name = FullName(供后续 TypeConvert 查找) - Result: CTypeInfo = CTypeInfo() - Result.BaseType = t._CTypedef(TypeName) - Result.IsTypedef = True - Result.Name = FullName - return Result - if Info: - Result: CTypeInfo = Info.Copy() - if Result.BaseType is None and (Result.IsStruct or Result.IsCpythonObject or Result.Name): - Result.BaseType = t.CStruct(name=TypeName) - Result.IsStruct = True - return Result - return CTypeInfo() - - return CTypeInfo() + ResolveGenericClassSpecName, GetStructSha1MapFromSymtab, GetTCAliasMapFromSymtab) +from lib.includes import t +from lib.constants.config import mode as _config_mode + +if TYPE_CHECKING: + from lib.core.SymbolTable import SymbolTable + + +# --------------------------------------------------------------- +# `_scan_builtins` 的 `import X as Y` 解析正则**预编译**(性能:避免每次 +# `re.match(字面量, ...)` 的"模式串哈希 → re 模块级缓存查找"。 +# 语义与逐字写法完全一致,模式串一字未改)。 +# --------------------------------------------------------------- +_RE_IMPORT_AS: re.Pattern[str] = re.compile(r'import\s+([^\s]+)\s+as\s+([^\s]+)') + + +class TypeAnnotationResolver: + """从 AST 节点解析类型注解,独立于 CTypeInfo 数据模型。""" + + # 编译器内置语法:rawptr(TPC/VPC 双端内置,不可重定义) + # rawptr[T] 固定表示"裸指针,指向原始类型 T"(等价 t.CPtr[T]); + # 单独 rawptr 等价 t.CPtr(void*)。 + _BUILTIN_ROWPTR_NAME: str = 'rawptr' + _BUILTIN_ROWPTR_VALUE: str = 't.CPtr' + + @classmethod + def _builtin_rawptr_info(cls) -> CTypeInfo: + """返回内置 rawptr 的 CTypeInfo(t.CPtr 语义:BaseType=CVoid, PtrCount=1)""" + info: CTypeInfo = CTypeInfo() + info.BaseType = t.CVoid() + info.PtrCount = 1 + return info + + @staticmethod + def from_type_name(TypeName: str) -> CTypeInfo: + """从简单类型名构造 CTypeInfo(不经过 FromStr 字符串解析)""" + # Handle pointer types like 'CUInt32T *', 'void *', etc. + ptr_count: int = 0 + base_name: str = TypeName + while base_name.endswith(' *') or base_name.endswith('*'): + ptr_count += 1 + base_name = base_name.rstrip(' *').rstrip() + + entry: tuple[type, int] | None = BuiltinTypeMap.Get(base_name) + if entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = entry + info: CTypeInfo = CTypeInfo() + info.BaseType = TypeClass + info.PtrCount = base_ptr + ptr_count + return info + # Handle LLVM primitive type names (e.g., 'i64', 'double' from generic type inference) + llvm_map: dict = CTypeInfo._get_llvm_primitive_map() + llvm_entry: tuple[type, int] | None = llvm_map.get(base_name) + if llvm_entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = llvm_entry + info: CTypeInfo = CTypeInfo() + info.BaseType = TypeClass + info.PtrCount = base_ptr + ptr_count + return info + # Fallback with pointer count + info: CTypeInfo = CTypeInfo.CreateFromTypeName(TypeName) + if ptr_count > 0: + info.PtrCount = ptr_count + return info + + @staticmethod + def try_eval_const_expr(node: ast.AST, SymbolTable: SymbolTable) -> int | float | str | None: + return ConstEvaluator.eval_with_symtab(node, SymbolTable) + + @staticmethod + def from_node(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """从 AST 节点解析 CTypeInfo + + Args: + Node: AST 节点(如 ast.Name, ast.Attribute 等) + SymbolTable: 符号表字典 + """ + if isinstance(Node, ast.Constant): + return TypeAnnotationResolver._from_node_constant(Node, SymbolTable) + if isinstance(Node, ast.Name): + return TypeAnnotationResolver._from_node_name(Node, SymbolTable) + if isinstance(Node, ast.Call): + return TypeAnnotationResolver._from_node_call(Node, SymbolTable) + if isinstance(Node, ast.BinOp) and isinstance(Node.op, ast.BitOr): + return TypeAnnotationResolver._from_node_binop(Node, SymbolTable) + if isinstance(Node, ast.Subscript): + return TypeAnnotationResolver._from_node_subscript(Node, SymbolTable) + if isinstance(Node, ast.Attribute): + return TypeAnnotationResolver._from_node_attribute(Node, SymbolTable) + return CTypeInfo() + + # ============================================================ + # builtins 最后匹配(对齐 VPC TypeGenResolve._lookup_builtins_typedef): + # 所有常规注解解析路径 miss 后,扫描 includes/builtins.vp 的 typedef + # 声明作为最终兜底(rawptr/sizet/uint64 等)。 + # builtins 始终纳入编译链但不在 App 文件 import 闭包,其 typedef 不随 + # from_imports 注入,故需按源文件扫描兜底。 + # 必须同时兼容两种等价写法: + # - 语法糖 `typedef 名字 = 类型`(builtins.vp 现用形态) + # - 注解形态 `名字: t.CTypedef = 类型`(stdint.vp 等库模块) + # 只认注解形态时语法糖声明全被跳过 → 兜底表为空 → 裸名退化为 + # `%"{mod}.名字"*` 假结构体(sizet 曾被当成结构体,BinOp 报 + # "Operands must be the same type, got (%"…sizet"*, i64)")。 + # ============================================================ + _builtins_scan_cache: dict | None = None + + # builtins 解析链的**全链**重入深度上限与线程局部计数。 + # 既有 `_resolve_builtins_type_str` 的 `depth`/`visited` 只在自身递归内传递; + # 而复合值(含 `|` / `[`)分支会经 `from_node` 重新进入 + # `_from_node_name → _lookup_builtins_typedef`,把 depth/visited 归零 ⇒ 该方向 + # 可绕过环检测无限递归(A = B | t.CPtr、B = A 一类声明)。此计数器覆盖 + # `_lookup_builtins_typedef → _resolve_builtins_type_str → from_node → …` 全程, + # 超上限即判为成环并 fail-fast(不再沿链继续)。合法链通常 ≤ 4 层。 + _BUILTINS_CHAIN_MAX_DEPTH: int = 32 + _chain_state: threading.local = threading.local() + + # 解析失败(成环/悬空)的 builtins typedef 名 → 错误报文缓存: + # 同一失败不再被反复重试(每次重试都会重新扫描/解析整条链)。 + # builtins.vp 在一次编译进程内不变,故无需失效。 + _builtins_failure_cache: dict[str, str] = {} + + @classmethod + def _chain_depth_enter(cls) -> int: + depth: int = getattr(cls._chain_state, 'depth', 0) + cls._chain_state.depth = depth + 1 + return depth + + @classmethod + def _chain_depth_exit(cls, prev: int) -> None: + cls._chain_state.depth = prev + + # `typedef 名字 = 类型` 语法糖(对应 SymbolUtils._ExpandTypedefs 的展开形态) + _TYPEDEF_SUGAR_RE: re.Pattern = re.compile( + r'^typedef\s+([A-Za-z_]\w*)\s*=(?!=)\s*(.+?)\s*$') + + @staticmethod + def _NormalizeTypedefRhsText(text: str) -> str: + """把 typedef 右值的 C 式写法归一成规范 `|` 联合文本(供**文本回退路径**使用)。 + + 磁盘原文里 `X: t.CTypedef = const str` / `= const uint16*` 的右值带描述符与指针 + 后缀,而本路径(_parse_typedef_line → _resolve_builtins_type_str)按表达式文本解析, + 故先归一: + - 前导 `const` / `volatile`(可组合) → `t.CConst` / `t.CVolatile` 前缀; + - 末尾 `*` / `**` 串 → 逐层 `| t.CPtr`。 + 不含上述形态的文本原样返回(幂等)。无算术语义风险:typedef 右值不含乘法。 + """ + s: str = text.strip() + if not s: + return text + markers: list[str] = [] + while True: + m: re.Match | None = re.match(r'^(const|volatile)\s+', s) + if m is None: + break + markers.append('t.CConst' if m.group(1) == 'const' else 't.CVolatile') + s = s[m.end():].lstrip() + ptrs: int = 0 + while s.endswith('*'): + s = s[:-1].rstrip() + ptrs += 1 + if not markers and ptrs == 0: + return text + parts: list[str] = markers + [s] + ['t.CPtr'] * ptrs + return ' | '.join(p for p in parts if p) + + @staticmethod + def _parse_typedef_line(stripped: str) -> tuple[str, str] | None: + """单行源码文本 → typedef 声明 (名字, 类型值);非 typedef 行返回 None + + 兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。 + 右值的 C 式写法(`const T` / `T*`)经 _NormalizeTypedefRhsText 归一为 `|` 联合。 + """ + m: re.Match | None = TypeAnnotationResolver._TYPEDEF_SUGAR_RE.match(stripped) + if m is not None: + return m.group(1), TypeAnnotationResolver._NormalizeTypedefRhsText(m.group(2)) + if ':' not in stripped or 't.CTypedef' not in stripped: + return None + name, _, rest = stripped.partition(':') + if 't.CTypedef' not in rest: + return None + value: str = rest.partition('=')[2].strip() + name = name.strip() + if not name or not value: + return None + return name, TypeAnnotationResolver._NormalizeTypedefRhsText(value) + + @staticmethod + def _builtins_include_dirs() -> list[str]: + """includes 目录列表(builtins.vp 定位用)——只认项目配置 includes, + 禁止仓库硬编码回退(重组/缺失时会产生错误类型解析,如 uint32→struct 退化)""" + dirs: list[str] = [] + try: + from lib.Projectrans.Phase2Translator import Phase2Translator + dirs = list(Phase2Translator.PROJECT_INCLUDE_DIRS) + except Exception: + pass + return dirs + + @staticmethod + def _find_include_file(mod: str) -> str | None: + """按模块名定位 includes 源文件(mod 的 '.'→'/',附加 .vp)""" + rel: str = mod.replace('.', '/') + '.vp' + for inc_dir in TypeAnnotationResolver._builtins_include_dirs(): + cand: str = os.path.join(inc_dir, rel) + if os.path.isfile(cand): + return cand + return None + + @classmethod + def _scan_builtins(cls) -> dict: + """扫描 includes/builtins.vp:返回 {typedefs: {name: type_str}, aliases: {alias: target}}""" + if cls._builtins_scan_cache is not None: + return cls._builtins_scan_cache + typedefs: dict[str, str] = {} + aliases: dict[str, str] = {} + path: str | None = TypeAnnotationResolver._find_include_file('builtins') + if path is not None: + try: + with open(path, 'r', encoding='utf-8') as f: + content: str = f.read() + except Exception: + content = '' + for raw_line in content.split('\n'): + line: str = raw_line.rstrip('\r') + stripped: str = line.strip() + if not stripped or stripped.startswith('#'): + continue + # import X as Y → 别名表(__stdint → stdint) + if stripped.startswith('import '): + m: re.Match | None = _RE_IMPORT_AS.match(stripped) + if m: + aliases[m.group(2)] = m.group(1) + continue + td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped) + if td is not None: + typedefs[td[0]] = td[1] + # 只缓存"扫到了东西"的结果:includes 目录尚未装配(PROJECT_INCLUDE_DIRS 为空) + # 或 builtins.vp 未定位时,缓存空表会让本进程内后续所有 builtins typedef 解析 + # (sizet(x)/uint64(x) 这类 cast 调用)永久 miss,只在真正扫到内容后落缓存。 + if not typedefs and not aliases: + return {'typedefs': typedefs, 'aliases': aliases} + cls._builtins_scan_cache = {'typedefs': typedefs, 'aliases': aliases} + return cls._builtins_scan_cache + + @classmethod + def _scan_library_typedef(cls, mod: str, attr: str) -> str | None: + """查库模块源中 typedef 声明,返回类型值(权威定义)或 None + + 兼容 `typedef 名字 = 类型`(语法糖)与 `名字: t.CTypedef = 类型`(注解)。 + """ + path: str | None = TypeAnnotationResolver._find_include_file(mod) + if path is None: + return None + try: + with open(path, 'r', encoding='utf-8') as f: + content: str = f.read() + except Exception: + return None + for raw_line in content.split('\n'): + line: str = raw_line.rstrip('\r') + stripped: str = line.strip() + if not stripped or stripped.startswith('#'): + continue + td: tuple[str, str] | None = TypeAnnotationResolver._parse_typedef_line(stripped) + if td is not None and td[0] == attr: + return td[1] + return None + + @classmethod + def _resolve_builtins_type_str(cls, type_str: str, aliases: dict[str, str], + SymbolTable, depth: int = 0, + visited: frozenset[str] | None = None) -> CTypeInfo | None: + """解析 builtins typedef 值(支持 t.XXX / mod.attr / 裸名;mod 支持 import 别名) + + Args: + type_str: typedef 值(如 t.CPtr / __stdint.size_t / CSizeT / sizet) + aliases: builtins.vp 的 import 别名表(__stdint → stdint) + depth: 递归深度守卫(防 typedef 值自引用成环) + visited: 本次展开链上已展开的 typedef 名(环检测)。命中环/自引用返回 + None(而不是退化成 %"{mod}.{名}"* 假结构体),由调用方 + _lookup_builtins_typedef fail-fast 报出根因。 + """ + if not type_str or depth > 8: + return None + type_str = type_str.strip() + if not type_str: + return None + # 复合表达式(联合/泛型)交给常规 AST 解析 + if '|' in type_str or '[' in type_str: + try: + node: ast.AST | None = TypeAnnotationResolver._parse_type_expr(type_str) + except Exception: + node = None + if node is None: + return None + return TypeAnnotationResolver.from_node(node, SymbolTable) + if '.' in type_str: + mod: str + attr: str + mod, _, attr = type_str.rpartition('.') + mod = mod.strip() + attr = attr.strip() + real_mod: str = aliases.get(mod, mod) + # t/c 内建命名空间:**优先按内建类名直接解析**,不依赖调用方 SymbolTable + # 的装配进度(import 注册阶段 t 尚未入表 ⇒ 原先返回 None,被上层误归因为 + # "自引用/循环引用";实测 builtins typedef 'sizet' → 't.CSizeT' 即卡在此处)。 + if real_mod in ('t', 'c'): + _bi: CTypeInfo | None = TypeAnnotationResolver.from_type_name(attr) + if _bi is not None: + return _bi + node = TypeAnnotationResolver._parse_type_expr(type_str) + if node is not None: + return TypeAnnotationResolver.from_node(node, SymbolTable) + return None + # 库模块权威 typedef(stdint.size_t → 读 stdint.vp 扫描) + lib_val: str | None = TypeAnnotationResolver._scan_library_typedef(real_mod, attr) + if lib_val is not None: + return TypeAnnotationResolver._resolve_builtins_type_str( + lib_val, aliases, SymbolTable, depth + 1, visited) + # 库源未命中:回退常规解析(模块可能已由 import 闭包注入 SymbolTable) + node = TypeAnnotationResolver._parse_type_expr(type_str) + if node is not None: + return TypeAnnotationResolver.from_node(node, SymbolTable) + return None + # 裸名:名字本身即类型(t/C 类名、BuiltinTypeMap 的 C 速记名、裸 LLVM 标量名) + # → 直接解析;否则它可能是**另一个 typedef 的引用**,沿 builtins typedef 表 + # 继续展开到基础类型。 + # 依据:stdint.vp 的 `size_t/SIZE_T: t.CTypedef = sizet` 与 builtins.vp 的 + # `size_t/uzt: t.CTypedef = sizet` 都是"别名 → 权威名 → t 类型"的链式写法, + # 与 VPC resolve_typedef_or_map 的 CTypedef 表递归(TypeGenResolve.vp:723-734) + # 同口径。此前裸名一律走 from_type_name:链中名字命中不到基础类型时退化成 + # %"{mod}.{名}"* 假结构体(sizet/size_t → "Type of #N arg mismatch: + # %…sizet"* != i32" 的根因)。 + if TypeAnnotationResolver._is_direct_type_name(type_str): + return TypeAnnotationResolver.from_type_name(type_str) + chain: str | None = TypeAnnotationResolver._scan_builtins()['typedefs'].get(type_str) + if chain is None: + # 非 builtins typedef 名的裸名(str/bytes/None 等):保留既有兜底 + return TypeAnnotationResolver.from_type_name(type_str) + seen: frozenset[str] = visited or frozenset() + if type_str in seen or chain == type_str: + # 自引用/环(如 `sizet: t.CTypedef = sizet`):无信息可展开,返回 None + return None + return TypeAnnotationResolver._resolve_builtins_type_str( + chain, aliases, SymbolTable, depth + 1, seen | {type_str}) + + @staticmethod + def _is_direct_type_name(name: str) -> bool: + """名字本身即类型(非"指向另一个 typedef 的别名") + + 覆盖 t 的 CType 子类名(CInt/CChar/CSizeT/CInt64T 等)、BuiltinTypeMap 的 + C 速记名(int/char/bool)与裸 LLVM 标量名(i64/double…,泛型推导产物)。 + """ + if not name: + return False + if BuiltinTypeMap.Get(name) is not None: + return True + if name in CTypeInfo._get_llvm_primitive_map(): + return True + cls: type | None = CTypeHelper.GetTModuleCType(name) + if cls is None: + cls = getattr(t, name, None) + return isinstance(cls, type) and issubclass(cls, t.CType) and cls is not t.CType + + @staticmethod + def _parse_type_expr(text: str) -> ast.AST | None: + """vast mode='eval' 解析单个表达式,返回表达式节点或 None""" + try: + expr_tree: ast.AST = ast.parse(text, '', mode='eval') + body = getattr(expr_tree, 'body', None) + if body is not None and not isinstance(body, list): + return body + if isinstance(body, list) and body: + return body[0] + return None + except Exception: + return None + + @classmethod + def builtins_typedef_value(cls, TypeName: str) -> str | None: + """返回 builtins.vp 中 CTypedef 声明的原始类型值(如 rawptr → 't.CPtr') + + 供 stub 生成 / 类型字符串消费端判断"base[arg]"下标是否为 builtins 指针 + typedef(rawptr[AST] → AST*),防止字符串化产物被误建为 phantom 结构体。 + rawptr 已内置于编译器(语法保留字):即使 builtins.vp 不再声明、 + 或用户自行 typedef,也始终返回内置值(不可重定义)。 + """ + if not TypeName: + return None + if TypeName == cls._BUILTIN_ROWPTR_NAME: + return cls._BUILTIN_ROWPTR_VALUE + data: dict = cls._scan_builtins() + return data['typedefs'].get(TypeName) + + @classmethod + def _lookup_builtins_typedef(cls, TypeName: str, SymbolTable) -> CTypeInfo | None: + """builtins 最后匹配:查 includes/builtins.vp 的 CTypedef 声明 + + 命中(该名字确为 builtins typedef)返回解析出的 CTypeInfo, + miss(该名字不是 builtins typedef)返回 None(交由调用方走原有兜底)。 + rawptr 为编译器内置语法:优先于一切外部声明(不可重定义)。 + 名字在表内却解析不出基础类型 = 声明本身无信息/成环(自引用)⇒ fail-fast, + 不再静默退化为 %"{mod}.{名}"* 假结构体(下游仅报 "Type of #N arg + mismatch: %…* != …",无法归因到 typedef 声明)。 + """ + if not TypeName: + return None + if TypeName == cls._BUILTIN_ROWPTR_NAME: + return cls._builtin_rawptr_info() + # 同一失败不再反复解析:命中失败缓存则直接按既有报文 fail-fast, + # 不再重扫 builtins.vp / 重走整条解析链。 + _cached_err: str | None = cls._builtins_failure_cache.get(TypeName) + if _cached_err is not None: + raise ValueError(_cached_err) + # 全链重入深度守卫:覆盖 `_lookup_builtins_typedef → _resolve_builtins_type_str + # → from_node → _from_node_name → _lookup_builtins_typedef` 的所有回路 + # (含 `|`/`[` 复合值分支导致的 depth/visited 归零绕过)。超上限即成环。 + _prev_depth: int = cls._chain_depth_enter() + try: + if _prev_depth >= cls._BUILTINS_CHAIN_MAX_DEPTH: + _err: str = ( + f"builtins typedef 解析链成环或过深(>{cls._BUILTINS_CHAIN_MAX_DEPTH} 层)," + f"解析 '{TypeName}' 时终止:includes/builtins.vp 中相关 typedef 声明" + f"(含 `|` / `[...]` 复合值)互相引用成环。请改为指向真实基础类型" + f"(如 t.CSizeT),或断开环上引用。") + cls._builtins_failure_cache[TypeName] = _err + raise ValueError(_err) + data: dict = cls._scan_builtins() + type_str: str | None = data['typedefs'].get(TypeName) + if type_str is None: + return None + info: CTypeInfo | None = cls._resolve_builtins_type_str(type_str, data['aliases'], SymbolTable) + if info is None: + # 不再一律归因"自引用/循环引用"(那正是把 t.CSizeT 解析失败误报成环、 + # 把人引向错误方向的根因):把声明值与该值解析失败的形态如实报出。 + _err = ( + f"builtins typedef '{TypeName}' 无法解析到基础类型:includes/builtins.vp " + f"声明 '{TypeName}: t.CTypedef = {type_str}',其值 '{type_str}' 解析失败" + f"(既不是内建类型名,也不是链上可展开的 typedef)。" + f"请把该声明指向真实类型(如 t.CSizeT),或确认该类型名存在。") + cls._builtins_failure_cache[TypeName] = _err + raise ValueError(_err) + return info + finally: + cls._chain_depth_exit(_prev_depth) + + @staticmethod + def _from_node_constant(Node: ast.AST, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Constant 节点""" + TypeName: str = Node.value + return TypeAnnotationResolver.from_type_name(TypeName) + + @staticmethod + def _RecoverTypeFromNamespaces(TypeName: str, SymbolTable: dict | SymbolTable) -> CTypeInfo | None: + """平坦条目不是"类型"时,按命名空间找回同名**类型**条目(找不到 → None)。 + + 与 LLVMTypeMapper._resolve_name 的"同名冲突:平坦条目被 define / 枚举成员 / + 函数占位时找回类型"逐分支同构(同一条规则源):优先 typedef,其次结构体 + (struct / renum / exception 均置 IsStruct),再次枚举。 + + 必要性:平坦表(无前缀)一个名字只留一个条目,而 `import path as X` 的**模块 + 别名**与模块内 `class X` 同名(ViperC 的 Generator 47 个 `rawptr[X.X]` 字段 + 全是这种形态)。条目被别名占住时,本解析器返回"无基类型"的空 CTypeInfo ⇒ + `rawptr[X.X]` 退化成 void 指针链(i8*/i8**),而 stub 声明侧是 + `%".X"*` ⇒ struct 布局里该槽被 set_body 冻结成 i8**(不可重设), + 赋值时报"指针隐式转换已禁止(层数不同)"。 + """ + _Namespaces: Any = getattr(SymbolTable, '_namespaces', None) + if not isinstance(_Namespaces, dict): + return None + for _NsSyms in _Namespaces.values(): + if not isinstance(_NsSyms, dict): + continue + _Cand: Any = _NsSyms.get(TypeName) + if _Cand is None: + continue + if getattr(_Cand, 'IsTypedef', False): + _R: CTypeInfo = _Cand.Copy() + _R.IsTypedef = True + if _R.BaseType is None: + _R.BaseType = t._CTypedef(TypeName) + return _R + if getattr(_Cand, 'IsStruct', False) or getattr(_Cand, 'IsRenum', False): + # 结构体表示与本文件既有 promotion(_from_node_name / _from_node_attribute + # 的 `BaseType = t.CStruct(name=...); IsStruct = True`)**逐字同口径**: + # 类引用不额外加指针层(PtrCount 保持 0)——本文件里 `rawptr[X]` 的层数 + # 由 rawptr 分支自己 +1,若此处再加一层会得到 `X**`。 + _R = CTypeInfo() + _R.BaseType = t.CStruct(name=TypeName) + _R.IsStruct = True + _R.Name = TypeName + return _R + if getattr(_Cand, 'IsEnum', False): + _R = CTypeInfo() + _R.BaseType = t.CInt() + _R.IsEnum = True + _R.Name = TypeName + return _R + return None + + @staticmethod + def _from_node_name(Node: ast.Name, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Name 节点""" + TypeName: str = Node.id + # 编译器内置语法 rawptr(不可重定义):单独 rawptr 等价 t.CPtr(void*), + # 优先于 SymbolTable/任何 typedef 声明。 + if TypeName == TypeAnnotationResolver._BUILTIN_ROWPTR_NAME: + return TypeAnnotationResolver._builtin_rawptr_info() + TypeObj: type | None = CTypeHelper.GetTModuleCType(TypeName) + if TypeObj is None: + TypeObj = getattr(t, TypeName, None) + if isinstance(TypeObj, type) and issubclass(TypeObj, t.CType) and TypeObj is not t.CType: + if TypeObj == t.CPtr: + Result: CTypeInfo = CTypeInfo() + Result.PtrCount = 1 + Result.BaseType = t.CVoid() + return Result + if TypeObj == t.State: + Result: CTypeInfo = CTypeInfo() + Result.IsState = True + Result.BaseType = t.CVoid() + return Result + Inst: t.CType = TypeObj() + Result: CTypeInfo = CTypeInfo() + Result.Name = type(Inst).__name__ + # 存储类修饰符(CExtern, CExport, CInline, CThreadLocal 等): + # position 含 STORAGE_CLASS 且不含 BASE → 纯修饰符,无实际类型 + if t.CType.STORAGE_CLASS in TypeObj.position and t.CType.BASE not in TypeObj.position: + Result.BaseType = t.CVoid() + Result.Storage = Inst + return Result + # 类型限定符(CConst, CVolatile 等) + if t.CType.TYPE_QUALIFIER in TypeObj.position and t.CType.BASE not in TypeObj.position: + Result.BaseType = t.CVoid() + if TypeObj == t.CConst: + Result.DataConst = True + elif TypeObj == t.CVolatile: + Result.DataVolatile = True + return Result + Result.BaseType = TypeObj + if hasattr(Inst, 'IsSigned'): + Result.IsSigned = Inst.IsSigned + return Result + # 检查 BuiltinTypeMap(覆盖 VOIDPTR, BYTEPTR 等指针别名) + builtin_entry: tuple[type, int] | None = BuiltinTypeMap.Get(TypeName) + if builtin_entry: + TypeClass: type + base_ptr: int + TypeClass, base_ptr = builtin_entry + Result: CTypeInfo = CTypeInfo() + Result.BaseType = TypeClass + Result.PtrCount = base_ptr + return Result + if TypeName in SymbolTable: + Entry: CTypeInfo | dict = SymbolTable[TypeName] + if Entry: + if isinstance(Entry, CTypeInfo): + if Entry.IsTypedef: + if Entry.BaseType and (not isinstance(Entry.BaseType, (t._CTypedef,)) or Entry.PtrCount > 0): + Result = Entry.Copy() + Result.IsTypedef = True + Result.Name = TypeName + return Result + if Entry.OriginalType: + if isinstance(Entry.OriginalType, CTypeInfo) and Entry.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Entry.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Entry.OriginalType.FuncPtrParams) if Entry.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, CTypeInfo): + Resolved = Entry.OriginalType.Copy() + elif isinstance(Entry.OriginalType, str) and Entry.OriginalType == 'Callable': + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + elif isinstance(Entry.OriginalType, str): + Resolved = TypeAnnotationResolver.from_type_name(Entry.OriginalType) + else: + Resolved = Entry.OriginalType + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + TypeEntry = BuiltinTypeMap.Get(TypeName) + if TypeEntry: + Result = CTypeInfo() + Result.BaseType = TypeEntry[0]() + Result.PtrCount = TypeEntry[1] + Result.IsTypedef = True + Result.Name = TypeName + return Result + return Entry + if Entry.IsEnum: + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsEnum = True + Result.Name = TypeName + return Result + if Entry.IsExceptionClass: + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsExceptionClass = True + Result.Name = TypeName + return Result + if Entry.BaseType is None and (Entry.IsStruct or Entry.Name): + Entry.BaseType = t.CStruct(name=TypeName) + Entry.IsStruct = True + if (Entry.BaseType is None and not Entry.IsStruct and not Entry.IsEnum + and not Entry.IsTypedef and not Entry.IsCpythonObject and not Entry.Name): + # 平坦条目不是"类型"(同名**模块别名** / 函数 / 变量 / define / + # 枚举成员占住裸名):直接 return Entry 会得到"无基类型"的空 + # CTypeInfo ⇒ `rawptr[X]` 之类注解退化成 void 指针链 + # (i8*/i8**),struct 布局里该槽被 set_body 永久冻结。 + # 与上方 _from_node_attribute 及 LLVMTypeMapper._resolve_name + # 的"同名冲突找回类型"同一条规则源。 + _RecoveredName: CTypeInfo | None = TypeAnnotationResolver._RecoverTypeFromNamespaces( + TypeName, SymbolTable) + if _RecoveredName is not None: + return _RecoveredName + return Entry + elif isinstance(Entry, dict): + if Entry.get('type') == 'typedef': + OriginalType = Entry.get('OriginalType', '') + if OriginalType: + if OriginalType == 'Callable': + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + Result.IsTypedef = True + Result.Name = TypeName + return Result + Resolved = TypeAnnotationResolver.from_type_name(OriginalType) + Resolved.IsTypedef = True + Resolved.Name = TypeName + return Resolved + TypeEntry = BuiltinTypeMap.Get(TypeName) + if TypeEntry: + Result = CTypeInfo() + Result.BaseType = TypeEntry[0]() + Result.PtrCount = TypeEntry[1] + Result.IsTypedef = True + Result.Name = TypeName + return Result + Result = CTypeInfo() + Result.BaseType = t._CTypedef(TypeName) + Result.IsTypedef = True + Result.Name = TypeName + return Result + # 检查 t 类型别名(from t import CInt as myint) + if hasattr(SymbolTable, '_t_type_symbols'): + t_cls = SymbolTable._t_type_symbols.get(TypeName) + if t_cls is not None: + Result = CTypeInfo() + Result.BaseType = t_cls + return Result + # 特殊处理 str 类型,它应该是 char*(指针) + if TypeName in ('str', 'bytes'): + Result = CTypeInfo() + Result.BaseType = t.CChar() + Result.PtrCount = 1 + return Result + # [builtins 最后匹配] 常规路径全部 miss 后的最终兜底: + # 扫描 includes/builtins.vp 的 CTypedef 声明(rawptr/size_t 等, + # 对齐 VPC TypeGenResolve)。命中返回解析结果,miss 走 from_type_name。 + builtins_info: CTypeInfo | None = TypeAnnotationResolver._lookup_builtins_typedef(TypeName, SymbolTable) + if builtins_info is not None: + return builtins_info + return TypeAnnotationResolver.from_type_name(TypeName) + + @staticmethod + def _from_node_call(Node: ast.Call, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Call 节点""" + if isinstance(Node.func, ast.Name) and Node.func.id == 'callable': + Result: CTypeInfo = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + try: + if len(Node.args) > 0: + RetInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.args[0], SymbolTable) + if RetInfo: + Result.FuncPtrReturn = RetInfo + kw: ast.keyword + for kw in Node.keywords: + ParamInfo: CTypeInfo = TypeAnnotationResolver.from_node(kw.value, SymbolTable) + if ParamInfo: + Result.FuncPtrParams.append((kw.arg or '', ParamInfo)) + except Exception as _e: + if _config_mode == "strict": + warnings.warn(f"异常被忽略: {_e}") + return Result + if isinstance(Node.func, ast.Attribute): + if isinstance(Node.func.value, ast.Name) and IsTModule(Node.func.value.id, SymbolTable): + if Node.func.attr == 'Bit' and len(Node.args) > 0: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsBitField = True + if isinstance(Node.args[0], ast.Constant) and isinstance(Node.args[0].value, int): + Result.BitWidth = Node.args[0].value + else: + Result.BitWidth = 1 + return Result + return CTypeInfo() + + @staticmethod + def _from_node_binop(Node: ast.BinOp, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.BinOp (BitOr) 节点""" + LeftInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.left, SymbolTable) + RightInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.right, SymbolTable) + + if LeftInfo.IsState or RightInfo.IsState: + Result: CTypeInfo = CTypeInfo() + Result.IsState = True + NonStateInfo: CTypeInfo = LeftInfo if not LeftInfo.IsState else RightInfo + StateInfo: CTypeInfo = LeftInfo if LeftInfo.IsState else RightInfo + if NonStateInfo.BaseType and (not isinstance(NonStateInfo.BaseType, (t._CTypedef,)) or NonStateInfo.PtrCount > 0): + Result.BaseType = NonStateInfo.BaseType + elif NonStateInfo.IsTypedef and NonStateInfo.Name: + if NonStateInfo.BaseType and (not isinstance(NonStateInfo.BaseType, (t._CTypedef,)) or NonStateInfo.PtrCount > 0): + Result.BaseType = NonStateInfo.BaseType + Result.PtrCount = NonStateInfo.PtrCount + Result.IsTypedef = True + Result.Name = NonStateInfo.Name + if NonStateInfo.OriginalType: + Result.OriginalType = NonStateInfo.OriginalType + else: + Result.BaseType = t.CStruct(name=NonStateInfo.Name) + Result.IsStruct = True + Result.IsTypedef = True + Result.Name = NonStateInfo.Name + if NonStateInfo.OriginalType: + Result.OriginalType = NonStateInfo.OriginalType + else: + Result.BaseType = t.CVoid() + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if not Result.Storage: + Result.Storage = t.CExport() + if LeftInfo.Storage and not isinstance(LeftInfo.Storage, t.CExport): + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage and not isinstance(RightInfo.Storage, t.CExport): + Result.Storage = RightInfo.Storage + if LeftInfo.DataConst or RightInfo.DataConst: + Result.DataConst = True + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + return Result + + if LeftInfo.IsPtr or RightInfo.IsPtr: + Result = CTypeInfo() + # 当双方都是指针时(如 (AST | t.CPtr) | t.CPtr),PtrCount 应相加 + # 而非取 max,否则 AST** 会被折叠为 AST*,导致对象切片问题 + if LeftInfo.IsPtr and RightInfo.IsPtr: + Result.PtrCount = LeftInfo.PtrCount + RightInfo.PtrCount + # BaseType 优先取非 void 侧(如 AST),而非 t.CPtr 的 void + if LeftInfo.BaseType and not isinstance(LeftInfo.BaseType, t.CVoid): + Result.BaseType = LeftInfo.BaseType + elif RightInfo.BaseType and not isinstance(RightInfo.BaseType, t.CVoid): + Result.BaseType = RightInfo.BaseType + else: + Result.BaseType = t.CUnsignedChar() + else: + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + NonPtrInfo = LeftInfo if not LeftInfo.IsPtr else RightInfo + PtrInfo = RightInfo if RightInfo.IsPtr else LeftInfo + if NonPtrInfo.BaseType and (not isinstance(NonPtrInfo.BaseType, (t._CTypedef,)) or NonPtrInfo.PtrCount > 0): + Result.BaseType = NonPtrInfo.BaseType + elif NonPtrInfo.IsTypedef and NonPtrInfo.Name: + if NonPtrInfo.BaseType and (not isinstance(NonPtrInfo.BaseType, (t._CTypedef,)) or NonPtrInfo.PtrCount > 0): + Result.BaseType = NonPtrInfo.BaseType + Result.IsTypedef = True + Result.Name = NonPtrInfo.Name + if NonPtrInfo.OriginalType: + Result.OriginalType = NonPtrInfo.OriginalType + else: + Result.BaseType = t.CStruct(name=NonPtrInfo.Name) + Result.IsStruct = True + elif PtrInfo.BaseType and (not isinstance(PtrInfo.BaseType, (t._CTypedef,)) or PtrInfo.PtrCount > 0): + Result.BaseType = PtrInfo.BaseType + else: + Result.BaseType = t.CUnsignedChar() + if LeftInfo.DataConst or RightInfo.DataConst: + Result.DataConst = True + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + if LeftInfo.DataVolatile or RightInfo.DataVolatile: + Result.DataVolatile = True + if LeftInfo.VarVolatile or RightInfo.VarVolatile: + Result.VarVolatile = True + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.DataConst or RightInfo.DataConst: + Result = CTypeInfo() + BaseTypeSide = LeftInfo if not LeftInfo.DataConst else RightInfo + QualSide = LeftInfo if LeftInfo.DataConst else RightInfo + Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else QualSide.BaseType + if not Result.BaseType: + Result.BaseType = t.CInt() + Result.DataConst = True + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + if LeftInfo.DataVolatile or RightInfo.DataVolatile: + Result.DataVolatile = True + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.DataVolatile or RightInfo.DataVolatile: + Result = CTypeInfo() + BaseTypeSide = LeftInfo if not LeftInfo.DataVolatile else RightInfo + QualSide = LeftInfo if LeftInfo.DataVolatile else RightInfo + Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else QualSide.BaseType + if not Result.BaseType: + Result.BaseType = t.CInt() + Result.DataVolatile = True + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.Storage or RightInfo.Storage: + Result = CTypeInfo() + BaseTypeSide = LeftInfo if not LeftInfo.Storage else RightInfo + StorageSide = LeftInfo if LeftInfo.Storage else RightInfo + # CExtern | CExport 等价于 t.State:两边都有 Storage 时设置 IsState + if BaseTypeSide.IsState or StorageSide.IsState or (LeftInfo.Storage and RightInfo.Storage): + Result.IsState = True + Result.BaseType = t.CVoid() + Result.Storage = LeftInfo.Storage if LeftInfo.Storage else RightInfo.Storage + return Result + Result.BaseType = BaseTypeSide.BaseType if BaseTypeSide.BaseType and (not isinstance(BaseTypeSide.BaseType, t.CVoid) or BaseTypeSide.PtrCount > 0) else StorageSide.BaseType + if not Result.BaseType: + Result.BaseType = t.CInt() + Result.Storage = LeftInfo.Storage if LeftInfo.Storage else RightInfo.Storage + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.DataConst or RightInfo.DataConst: + Result.DataConst = True + if LeftInfo.VarConst or RightInfo.VarConst: + Result.VarConst = True + return Result + + # 检查是否有位域类型 (t.Bit) + if LeftInfo.IsBitField: + Result = CTypeInfo() + Result.BaseType = LeftInfo.BaseType if LeftInfo.BaseType else t.CInt() + Result.IsBitField = True + Result.BitWidth = LeftInfo.BitWidth + return Result + if RightInfo.IsBitField: + Result = CTypeInfo() + Result.BaseType = RightInfo.BaseType if RightInfo.BaseType else t.CInt() + Result.IsBitField = True + Result.BitWidth = RightInfo.BitWidth + return Result + + # 处理字节序类型 (t.BigEndian, t.LittleEndian) + # 合并两侧:保留非字节序侧的实际类型(如 CUInt64T),取字节序侧的 ByteOrder + if LeftInfo.ByteOrder or RightInfo.ByteOrder: + Result = CTypeInfo() + ByteOrderSide: CTypeInfo = LeftInfo if LeftInfo.ByteOrder else RightInfo + OtherSide: CTypeInfo = RightInfo if LeftInfo.ByteOrder else LeftInfo + if OtherSide.BaseType and (not isinstance(OtherSide.BaseType, (t._CTypedef,)) or OtherSide.PtrCount > 0): + Result.BaseType = OtherSide.BaseType + elif ByteOrderSide.BaseType and (not isinstance(ByteOrderSide.BaseType, (t._CTypedef,)) or ByteOrderSide.PtrCount > 0): + Result.BaseType = ByteOrderSide.BaseType + else: + Result.BaseType = t.CInt() + Result.ByteOrder = LeftInfo.ByteOrder or RightInfo.ByteOrder + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + return Result + + if LeftInfo.IsFuncPtr or RightInfo.IsFuncPtr: + Result = CTypeInfo() + FuncPtrSide = LeftInfo if LeftInfo.IsFuncPtr else RightInfo + Result.IsFuncPtr = True + Result.FuncPtrParams = list(FuncPtrSide.FuncPtrParams) + Result.FuncPtrReturn = FuncPtrSide.FuncPtrReturn + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + elif RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + + if LeftInfo.IsTypedef and RightInfo.BaseType: + if LeftInfo.BaseType and (not isinstance(LeftInfo.BaseType, (t._CTypedef,)) or LeftInfo.PtrCount > 0): + Result = LeftInfo.Copy() + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if RightInfo.Storage: + Result.Storage = RightInfo.Storage + return Result + return RightInfo + if RightInfo.IsTypedef and LeftInfo.BaseType: + if RightInfo.BaseType and (not isinstance(RightInfo.BaseType, (t._CTypedef,)) or RightInfo.PtrCount > 0): + Result = RightInfo.Copy() + Result.PtrCount = max(LeftInfo.PtrCount, RightInfo.PtrCount) + if LeftInfo.Storage: + Result.Storage = LeftInfo.Storage + return Result + return LeftInfo + + if LeftInfo.BaseType: + return LeftInfo + elif RightInfo.BaseType: + return RightInfo + return CTypeInfo() + + @staticmethod + def _from_node_subscript(Node: ast.Subscript, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Subscript 节点""" + base: ast.AST = Node.value + if isinstance(base, ast.Attribute): + if isinstance(base.value, ast.Name) and IsTModule(base.value.id, SymbolTable) and base.attr == 'Bit': + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsBitField = True + if isinstance(Node.slice, ast.Constant) and isinstance(Node.slice.value, int): + Result.BitWidth = Node.slice.value + else: + Result.BitWidth = 1 + return Result + if IsListAnnotation(Node): + slice_node: ast.AST = Node.slice + elts: list[ast.AST] = [] + if isinstance(slice_node, ast.Tuple): + elts = slice_node.elts + elif isinstance(slice_node, (ast.Attribute, ast.Name, ast.Subscript)): + elts = [slice_node] + if elts: + ElemInfo: CTypeInfo = TypeAnnotationResolver.from_node(elts[0], SymbolTable) + if ElemInfo and ElemInfo.BaseType: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = ElemInfo.BaseType + Result.PtrCount = ElemInfo.PtrCount + Result.ArrayDims = list(ElemInfo.ArrayDims) + if len(elts) >= 2: + count_val: int | None = TypeAnnotationResolver.try_eval_const_expr(elts[1], SymbolTable) + if count_val is not None and isinstance(count_val, int) and count_val > 0: + Result.ArrayDims.insert(0, str(count_val)) + else: + Result.PtrCount += 1 + else: + # 单参数 t.CArray[T](= t.CArray[T, None]):长度自动推导数组。 + # 指针位置语义为"元素指针" Ptr(T),需在元素类型上再加一层指针。 + # 与裸名 CArray[T](HandlesTypeMerge.CollectAll 的 + # `ElementType.PtrCount + 1`)、stub 侧(LLVMTypeMapper + # IsListAnnotation 的 `elem_type + '*'`)、声明端 + # (DeclarationGenerator `[0 x T]` → _decay_array_to_ptr = T*) + # 以及 VPC(TypeGenResolve 单参 → Ptr(elem_ty))同口径。 + # 此前缺这一层:t.CArray[str] 得 i8*(即 str 本身), + # result[k] 退化成 i8 → 写字符串指针触发 ptr→int fail-fast。 + Result.PtrCount += 1 + if ElemInfo.IsPtr and not ElemInfo.ArrayDims: + Result.PtrCount = max(Result.PtrCount, 1) + return Result + # 函数指针构造子的两种书写形态逐点等价(对齐 VPC + # TypeGenResolve._IsCallableSubscriptValue): + # t.Callable[...] / c.Callable[...] → 模块限定 Attribute + # callable[...] / Callable[...] → 内建裸名(无需 import) + # 裸名形态此前落进下方泛型特化兜底(ResolveGenericClassSpecName)未命中 ⇒ + # 空 CTypeInfo ⇒ 槽位类型退化 i32(现场 Repl.vp:307 + # `ReplFragFn: callable[[rawptr], int]`,赋 i8* 时被「指针隐式转整数」拦下)。 + parts: list[str] = [] + IsBareCallable: bool = False + if isinstance(base, ast.Attribute): + current: ast.AST = base + while isinstance(current, ast.Attribute): + parts.insert(0, current.attr) + current = current.value + if isinstance(current, ast.Name): + parts.insert(0, current.id) + elif isinstance(base, ast.Name) and (base.id == 'callable' or base.id == 'Callable'): + # 裸名只在注解位置当函数指针构造子(非类型位置仍是普通标识符) + IsBareCallable = True + if isinstance(base, ast.Attribute) or IsBareCallable: + if (not IsBareCallable) and parts and IsTModule(parts[0], SymbolTable) and parts[-1] == t.CPtr.__name__: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CVoid() + Result.PtrCount = 1 + slice_node: ast.AST = Node.slice + if isinstance(slice_node, ast.Subscript): + InnerInfo: CTypeInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + if InnerInfo: + if InnerInfo.PtrCount > 0: + Result.PtrCount += InnerInfo.PtrCount + if InnerInfo.BaseType and not isinstance(InnerInfo.BaseType, t.CVoid): + Result.BaseType = InnerInfo.BaseType + elif isinstance(slice_node, ast.Attribute): + SliceInfo: CTypeInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + if SliceInfo: + if SliceInfo.IsPtr: + Result.PtrCount += SliceInfo.PtrCount + elif SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid): + Result.BaseType = SliceInfo.BaseType + elif isinstance(slice_node, ast.Name): + SliceType: type | None = getattr(t, slice_node.id, None) + if SliceType == t.CPtr: + Result.PtrCount += 1 + else: + SliceInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + if SliceInfo and SliceInfo.BaseType and not isinstance(SliceInfo.BaseType, t.CVoid): + Result.BaseType = SliceInfo.BaseType + return Result + if IsBareCallable or (parts and IsTModule(parts[0], SymbolTable) and parts[-1] == 'Callable'): + Result = CTypeInfo() + Result.IsFuncPtr = True + slice_node = Node.slice + if isinstance(slice_node, ast.Tuple) and len(slice_node.elts) == 2: + params_list = slice_node.elts[0] + return_node = slice_node.elts[1] + ParamTypes = [] + _ParamsIsList: bool = isinstance(params_list, ast.List) + if _ParamsIsList: + for elt in params_list.elts: + ParamTypeInfo = TypeAnnotationResolver.from_node(elt, SymbolTable) + if ParamTypeInfo: + ParamTypes.append(('', ParamTypeInfo)) + # 空参数表 `t.Callable[[], R]` 语义 = 零形参:LLVM 端即 `R ()*`, + # 调用点按零实参间接 call。此前在此注入 void 占位形参 → 得 + # `R (void)*`(void 作形参本身不是合法 LLVM 函数类型),调用点 + # 实参个数(0) 与形参个数(1) 不符 → llvmlite CallInstr + # `arg = args[i]` IndexError(Repl.vp 模块级 t.Callable 槽 + # `ReplFragFn()` 翻译崩溃根因);与本文件另三处 Callable 形态 + # (:378 / :422 / :471)的 `FuncPtrParams = []` 口径统一。 + # 非 List 形态(遗留写法 t.Callable[A, R])保留原 void 占位行为。 + if not ParamTypes and not _ParamsIsList: + ParamTypes.append(('', TypeAnnotationResolver.from_type_name('void'))) + Result.FuncPtrParams = ParamTypes + ReturnTypeInfo = TypeAnnotationResolver.from_node(return_node, SymbolTable) + Result.FuncPtrReturn = ReturnTypeInfo if ReturnTypeInfo else CTypeInfo.VoidTypeInfo() + return Result + # [builtins 最后匹配] 裸名 builtins typedef 下标(rawptr[int] → Ptr(int)): + # 与 VPC Subscript 分支同口径——base 是 builtins typedef(rawptr → t.CPtr), + # 结果 = 切片类型外包 base 的指针层数。 + if isinstance(base, ast.Name): + base_info: CTypeInfo | None = TypeAnnotationResolver._lookup_builtins_typedef(base.id, SymbolTable) + if base_info is not None and base_info.PtrCount > 0: + slice_node = Node.slice + SliceInfo = TypeAnnotationResolver.from_node(slice_node, SymbolTable) + Result = CTypeInfo() + Result.BaseType = SliceInfo.BaseType if (SliceInfo and SliceInfo.BaseType) else t.CVoid() + Result.PtrCount = (SliceInfo.PtrCount if SliceInfo else 0) + base_info.PtrCount + return Result + # 泛型类下标(单/多参):list[str] / dict[str][int] / 用户泛型 Box[Node]。 + # 特化名与声明端(DeclarationGenerator 泛型分支)同口径,见 + # SymbolUtils.ResolveGenericClassSpecName。此前单参泛型下标在下方直接落到 + # "未知类型"空 CTypeInfo ⇒ 形参/成员类型退化成 i32 或 void 指针链 + # (现场:type_args: list[str]* 的形参 i32;CTypedefNames: list[str] | t.CPtr → i8*)。 + # 表示口径与本文件既有结构体 promotion 逐字一致(BaseType=CStruct + IsStruct + + # Name,PtrCount 保持 0):`X | t.CPtr` 由 _from_node_binop 取 max → 1 层, + # `rawptr[X]` 由上方分支 +1 → 1 层,均与 declare 侧 %"{sha}.X[*]"* 一致。 + _Spec: str | None = ResolveGenericClassSpecName( + Node, SymbolTable, + GetStructSha1MapFromSymtab(SymbolTable), + GetTCAliasMapFromSymtab(SymbolTable)) + if _Spec: + Result = CTypeInfo() + Result.BaseType = t.CStruct(name=_Spec) + Result.IsStruct = True + Result.Name = _Spec + return Result + return CTypeInfo() + + @staticmethod + def _from_node_attribute(Node: ast.Attribute, SymbolTable: dict | SymbolTable) -> CTypeInfo: + """处理 ast.Attribute 节点""" + ModuleParts: list[str] = [] + Current: ast.AST = Node + while isinstance(Current, ast.Attribute): + ModuleParts.insert(0, Current.attr) + Current = Current.value + + if isinstance(Current, ast.Name): + ModuleParts.insert(0, Current.id) + + TypeName: str + ModulePath: str | None + if len(ModuleParts) >= 2: + TypeName = ModuleParts[-1] + ModulePath = '.'.join(ModuleParts[:-1]) + elif len(ModuleParts) == 1: + TypeName = ModuleParts[0] + ModulePath = None + else: + return CTypeInfo() + + # 解析 import 别名: 如 import vpsdk.window as window -> window -> vpsdk.window + if ModulePath: + resolved: bool = False + import_aliases: dict = SymbolTable.import_aliases + if ModulePath in import_aliases: + ModulePath = import_aliases[ModulePath] + resolved = True + if not resolved and ModulePath in SymbolTable: + entry: CTypeInfo = SymbolTable[ModulePath] + if isinstance(entry, CTypeInfo) and entry.IsModuleAlias: + resolved_name = entry.ResolvedModule + if resolved_name: + ModulePath = resolved_name + + if ModulePath and IsTModulePath(ModulePath): + # 值引用参数标记(CRef = `&T`、CTempRef = `&temp T`):非真实类,仅按名字 + # 识别为「一层指针」(与 t.CPtr 同形),使联合形态 `T | t.CRef` 解析为 T*、 + # `void | t.CRef` 解析为 void*(i8*)。模式语义(ref/temp)由注解侧识别。 + if TypeName in ('CRef', 'CTempRef'): + Info: CTypeInfo = CTypeInfo() + Info.PtrCount = 1 + Info.BaseType = t.CVoid + return Info + TypeClass: type | None = CTypeHelper.GetTModuleCType(TypeName) + if TypeClass is None: + TypeClass = getattr(t, TypeName, None) + if TypeClass is not None and TypeClass.HasPosition(t.CType.POINTER): + Info: CTypeInfo = CTypeInfo() + Info.PtrCount = 1 + if TypeClass == t.CArrayPtr: + Info.IsArrayPtr = True + Info.BaseType = t.CVoid + return Info + if TypeClass is not None and TypeClass.IsStorageClass(): + Info: CTypeInfo = CTypeInfo() + Info.Storage = TypeClass() + if TypeClass == t.State: + Info.IsState = True + Info.BaseType = t.CVoid() + return Info + if TypeClass is not None and TypeClass.IsTypeQualifier(): + Info: CTypeInfo = CTypeInfo() + if TypeClass == t.CConst: + Info.DataConst = True + elif TypeClass == t.CVolatile: + Info.DataVolatile = True + return Info + if TypeClass is not None and TypeClass in (t.BigEndian, t.LittleEndian): + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.ByteOrder = 'big' if TypeClass == t.BigEndian else 'little' + return Result + if (TypeClass is not None + and isinstance(TypeClass, type) + and issubclass(TypeClass, t.CType) + and TypeClass is not t.CType + and TypeClass.HasPosition(t.CType.BASE)): + if TypeClass == t.State: + Result: CTypeInfo = CTypeInfo() + Result.IsState = True + Result.BaseType = t.CVoid() + return Result + Inst: t.CType = TypeClass() + Result: CTypeInfo = CTypeInfo() + Result.BaseType = TypeClass + Result.Name = type(Inst).__name__ + if hasattr(Inst, 'IsSigned'): + Result.IsSigned = Inst.IsSigned + return Result + CNAME: str | None = CTypeHelper.GetCName(TypeName) + if CNAME: + return TypeAnnotationResolver.from_type_name(CNAME) + + # 处理字节序类型 + if TypeName == 'BigEndian': + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.ByteOrder = 'big' + return Result + if TypeName == 'LittleEndian': + Result = CTypeInfo() + Result.BaseType = t.CInt() + Result.ByteOrder = 'little' + return Result + + FullName: str = f"{ModulePath}.{TypeName}" if ModulePath else TypeName + + if TypeName in SymbolTable: + Entry: CTypeInfo = SymbolTable[TypeName] + if Entry and Entry.IsTypedef: + Resolved: CTypeInfo = Entry.Copy() + if Resolved.BaseType is None: + Resolved.BaseType = t._CTypedef(TypeName) + return Resolved + if Entry and Entry.IsEnum: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsEnum = True + Result.Name = TypeName + return Result + if Entry and Entry.IsExceptionClass: + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t.CInt() + Result.IsExceptionClass = True + Result.Name = TypeName + return Result + if Entry: + Result: CTypeInfo = Entry.Copy() + if Result.BaseType is None and (Result.IsStruct or Result.IsCpythonObject or Result.Name): + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + if (Result.BaseType is None and not Result.IsStruct and not Result.IsEnum + and not Result.IsTypedef and not Result.IsCpythonObject and not Result.Name): + # 平坦条目不是"类型"(同名**模块别名** / 函数 / 变量 / define / + # 枚举成员占住了裸名):Copy 得到的是"无基类型"的空 CTypeInfo ⇒ + # `rawptr[Mod.Cls]` 之类注解退化成 void 指针链(i8*/i8**), + # struct 布局里该槽被 set_body 永久冻结(现场:Generator 的 + # ExprGenFstr 槽 = i8**,而声明槽是 %"…ExprGenFstr"*,赋值时报 + # "层数不同")。与 LLVMTypeMapper._resolve_name 的"同名冲突找回 + # 类型"同一条规则源:按命名空间找回同名 typedef/结构体/枚举。 + _Recovered: CTypeInfo | None = TypeAnnotationResolver._RecoverTypeFromNamespaces( + TypeName, SymbolTable) + if _Recovered is not None: + return _Recovered + return Result + _Recovered2: CTypeInfo | None = TypeAnnotationResolver._RecoverTypeFromNamespaces( + TypeName, SymbolTable) + if _Recovered2 is not None: + return _Recovered2 + return CTypeInfo() + + # 嵌套类 / 嵌套 struct:限定名 `外类.内类`(ModulePath 是**类名**而非模块名)。 + # 命名口径见 HandlesClassDef._EmitNestedClassDefs —— 嵌套类以扁平名 + # `{外类}_{内类}` 发射,并由 _RegisterNestedClassName 把扁平名与 `外类.内类` + # 一并插入 SymbolTable。本分支按**扁平名**取条目:BaseType/Name 必须是扁平名 + # (若走下方 FullName 分支,会按短名拼出 `t.CStruct(name='Inner')`,与 + # 声明端的 `%"{sha}.外类_内类"` 不同源 ⇒ 布局/字段偏移分叉)。 + if ModulePath and TypeName: + NestedKey: str = f"{ModulePath}_{TypeName}" + if NestedKey in SymbolTable: + NestedEntry: CTypeInfo = SymbolTable[NestedKey] + if NestedEntry is not None: + Result = NestedEntry.Copy() + Result.Name = NestedKey + if Result.BaseType is None: + Result.BaseType = t.CStruct(name=NestedKey) + Result.IsStruct = True + return Result + + if FullName in SymbolTable: + Info: CTypeInfo = SymbolTable[FullName] + if Info and Info.IsTypedef: + # 优先用 BaseType 解析(如 VOIDPTR 已解析为 BaseType=CVoid, PtrCount=1) + if Info.BaseType and (not isinstance(Info.BaseType, (t._CTypedef,)) or Info.PtrCount > 0): + Result = Info.Copy() + Result.IsTypedef = True + Result.Name = FullName + return Result + # 再用 OriginalType 解析(字符串如 'void *',或 CTypeInfo) + if Info.OriginalType: + if isinstance(Info.OriginalType, CTypeInfo) and Info.OriginalType.IsFuncPtr: + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = Info.OriginalType.FuncPtrReturn or CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = list(Info.OriginalType.FuncPtrParams) if Info.OriginalType.FuncPtrParams else [] + Result.IsTypedef = True + Result.Name = FullName + return Result + elif isinstance(Info.OriginalType, CTypeInfo): + Resolved: CTypeInfo = Info.OriginalType.Copy() + elif isinstance(Info.OriginalType, str) and Info.OriginalType == 'Callable': + Result = CTypeInfo() + Result.IsFuncPtr = True + Result.FuncPtrReturn = CTypeInfo.VoidTypeInfo() + Result.FuncPtrParams = [] + Result.IsTypedef = True + Result.Name = FullName + return Result + elif isinstance(Info.OriginalType, str): + Resolved = TypeAnnotationResolver.from_type_name(Info.OriginalType) + else: + Resolved = Info.OriginalType + if Resolved and Resolved.BaseType: + Resolved.IsTypedef = True + Resolved.Name = FullName + return Resolved + # 回退:保持原逻辑但设置 Name = FullName(供后续 TypeConvert 查找) + Result: CTypeInfo = CTypeInfo() + Result.BaseType = t._CTypedef(TypeName) + Result.IsTypedef = True + Result.Name = FullName + return Result + if Info: + Result: CTypeInfo = Info.Copy() + if Result.BaseType is None and (Result.IsStruct or Result.IsCpythonObject or Result.Name): + Result.BaseType = t.CStruct(name=TypeName) + Result.IsStruct = True + return Result + return CTypeInfo() + + return CTypeInfo() diff --git a/lib/vast/Parser.py b/lib/vast/Parser.py index 1d5be0a..b30598a 100644 --- a/lib/vast/Parser.py +++ b/lib/vast/Parser.py @@ -32,9 +32,9 @@ from lib.vast.Tokenizer import ( # novirtual → `@t.NoVTable`。本层只"合成装饰器节点",不新增第二套表示: # - 类级:CVTable 强制有 vtable / NoVTable 强制无 vtable # (TPC:HandlesClassDef._resolve_class_flags 读 ClassDef.decorator_list -# 的 CVTable/NoVTable;TPV:ClassGenBase._detect_vtable_status) +# 的 CVTable/NoVTable;VPC:ClassGenBase._detect_vtable_status) # - 方法级:CVTable 进虚表槽 / NoVTable 不进虚表槽 -# (TPV:ClassGenBase._should_method_be_virtual;TPC:同判据见 +# (VPC:ClassGenBase._should_method_be_virtual;TPC:同判据见 # SymbolUtils.ClassVtableSlotMethods / 方法收集处) # # 行号守恒:合成节点的位置取"修饰符 token"的位置,ClassDef/FunctionDef 自身的 @@ -419,7 +419,7 @@ class Parser(ExprParserMixin): 用于把 typedef 的右值按**类型位置**解析(`X: t.CTypedef = const uint16*` 里的 `*` 后缀由 _ParseTypeAnnotation 消费,与注解位同一规则;描述符 `const`/`volatile` 已由 SymbolUtils._ExpandTypeDescriptors 在文本层展开)。 - 仅按标记名判定,不依赖 import 别名表(与 TPV `_IsCTypedefAnnotation` 同口径)。 + 仅按标记名判定,不依赖 import 别名表(与 VPC `_IsCTypedefAnnotation` 同口径)。 """ if annotation is None: return False @@ -761,7 +761,7 @@ class Parser(ExprParserMixin): nxt: Token = self._Peek(1) marker: str = _VIRTUAL_MARKERS[mod.Value] # 后随必须是 class / def;def 的别名 fn/fun/func/function 同样接受 - # (与 TPV 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的 + # (与 VPC 的 _is_funcdef_kw 口径一致)。注意:ExpandSoftKeywords 的 # fn→def 展开只命中**行首**的 fn,`virtual fn m()` 不会被先展开, # 故此处必须显式接受别名(否则两端对同一源码一端报错一端通过)。 if nxt.Type != NAME or (nxt.Value != 'class' @@ -820,7 +820,7 @@ class Parser(ExprParserMixin): def _ParseStructDef(self, decorators: list[N.expr] | None = None) -> N.ClassDef: """`struct X:` / `struct X(Base):` → 与 `@t.NoVTable class X(t.CStruct):` - 逐字段同形的 ClassDef(与 TPV 的 _parse_structdef 同口径)。 + 逐字段同形的 ClassDef(与 VPC 的 _parse_structdef 同口径)。 实现:复用 _ParseClassDef 的完整解析路径(类型参数 / 基类 / 关键字实参 / 类体套件),只在**解析前后**补三件事: @@ -974,7 +974,7 @@ class Parser(ExprParserMixin): return node def _ParseStaticDef(self, decorators: list[N.expr] | None = None) -> N.FunctionDef: - """解析 static 函数定义(等价 ``@t.CStatic``,与 TransPyV 的 + """解析 static 函数定义(等价 ``@t.CStatic``,与 ViperC 的 `_ParseStaticModified` 同源)。 允许语法(与 export/extern 对称,配不配 def 都一样): @@ -1038,7 +1038,7 @@ class Parser(ExprParserMixin): define A(x: int): ... 产出标准 FunctionDef 节点,返回注解改写为 ``<原类型> | t.CDefine`` - (无显式返回注解时单独 ``t.CDefine``),使 TPC/TPV 复用既有的 + (无显式返回注解时单独 ``t.CDefine``),使 TPC/VPC 复用既有的 CDefine 宏函数识别路径(StubGen 原样保留生成 C #define、 alwaysinline 内联);并设自定义属性 ``Define = True`` 供 Unparser 还原 define 语法。