From 14af03577c88409879dfff1569f7fe9250dc5479 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Mon, 28 Sep 2026 18:06:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(tpc):=20=E8=B7=A8=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=AC=A6=E5=8F=B7=E8=A1=A8=E4=BF=9D=E7=95=99=E7=B1=BB=E5=90=8D?= =?UTF-8?q?=EF=BC=8C=E5=BD=A2=E5=8F=82=E6=B3=A8=E8=A7=A3=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E9=80=80=E5=8C=96=E6=88=90=20i32*?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit a 类(编译器缺陷):.vpi/.pyi 派生的函数签名经 LLVMTypeMapper._resolve_name 解析, 该方法只认内置名 / BuiltinTypeMap / typedef,不认类名 ⇒ rawptr[Constant] 被 _rawptr_subscript_type_str 变成 i32*,写进跨模块符号表(temp/_shared_sym.json 的 FuncPtrParams arg0=i32*),调用点据此声明出 i32 形参,与实参 %"sha1.Constant"* 冲突, 在指针隐式转换规则下报成"源码问题"(掩盖根因)。 修法:TypeResolver.resolve 注入本模块类名/枚举宽度;_resolve_name 按 本模块类名 → %struct.X*、 枚举 → i{width}、已加载模块类/枚举 → 同上、builtins CTypedef → 按定义展开;其余一律 raise TypeNameUnresolvedError(不再 warn + i32)。SymbolTable._LoadSymbolsFromFile 对该异常 re-raise(不再吞成"加载模块失败"返回空符号表)。 验证(vpi 探针):name_get_id/constant_get_str_val 形参 i32* → %struct.Name* / %struct.Constant*; binop_get_left/binop_get_right/attribute_get_value 返回 %struct.AST*(跨模块类经符号表解析); int64/size_t/sizet → i64;未知名 → 报错终止。 --- lib/core/LLVMTypeMapper.py | 97 +++++++++++++++++++++++++++++----- lib/core/SymbolTable.py | 7 ++- lib/core/SymbolTypeResolver.py | 28 ++++++++-- 3 files changed, 116 insertions(+), 16 deletions(-) diff --git a/lib/core/LLVMTypeMapper.py b/lib/core/LLVMTypeMapper.py index cab6056..d481f62 100644 --- a/lib/core/LLVMTypeMapper.py +++ b/lib/core/LLVMTypeMapper.py @@ -20,16 +20,61 @@ if TYPE_CHECKING: from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper, BuiltinTypeMap from lib.core.SymbolUtils import ( IsTModule, IsListAnnotation, IsCPtrNode, IsCPtrSubscriptNode, - IsRawPtrSubscriptNode) -from lib.includes import t as _t -from lib.includes.t import CTypeRegistry - - -class LLVMTypeMapper: - """AST 注解节点 → LLVM IR 类型字符串 映射器""" - - def __init__(self, symtab: SymbolTable): - self._symtab = symtab + IsRawPtrSubscriptNode, EnumIntWidth) +from lib.includes import t as _t +from lib.includes.t import CTypeRegistry + + +class TypeNameUnresolvedError(NameError): + """类型注解名无法解析(fail-fast,取代旧的"warn + 回退 i32")。 + + 触发点:LLVMTypeMapper._resolve_name —— 注解引用的裸名既不是内置类型 / + builtins typedef / 符号表 typedef,也不是本模块或已加载模块的类/枚举。 + + 为什么必须 fail-fast:旧口径把这类名字降级成 i32,而 `rawptr[Constant]` 这类 + 写法会随之变成 i32*(类信息全丢),经 SymbolInserter 记入跨模块符号表 + (temp/_shared_sym.json 的 `('arg0','i32*')`),最终在调用点被 FuncGen 的指针 + 隐式转换规则拦下 —— 报错指向**源码**(形参 i32* 对不上实参 %"sha1.X"*), + 而根因在上游符号表,属于掩盖根因的兜底。层数:TypeResolver.resolve → + SymbolTable._LoadSymbolsFromFile(直接 re-raise,终止编译)。 + """ + + def __str__(self) -> str: + if self.args and isinstance(self.args[0], str): + return self.args[0] + return super().__str__() + + +class LLVMTypeMapper: + """AST 注解节点 → LLVM IR 类型字符串 映射器""" + + def __init__(self, symtab: SymbolTable): + self._symtab = symtab + # 本模块声明的类型名(由 TypeResolver.resolve 注入)。函数签名里的裸类引用 + # (rawptr[X] / X / t.CPtr[X])依赖它解析成类指针:缺了它,X 落到 + # _resolve_name 的解析失败分支,rawptr[X] 会退化成 i32*(类信息丢失)。 + self._ClassNames: set[str] = set() + # 本模块枚举名 → 存储位宽(bit);按登记的宽度物化(默认 32,与改前同值) + self._EnumWidths: dict[str, int] = {} + + def set_known_type_names(self, ClassNames: set[str], + EnumWidths: dict[str, int]) -> None: + """注入"本模块声明的类型名"(由 TypeResolver.resolve 在签名解析前调用)。 + + ClassNames: 本模块类名(struct / renum / exception —— 即 IsStruct 口径) + EnumWidths: 本模块枚举名 → 存储位宽(bit) + """ + self._ClassNames = ClassNames + self._EnumWidths = EnumWidths + + def _lookup_builtins_typedef(self, name: str) -> CTypeInfo | None: + """查 includes/builtins.vp 的 CTypedef 声明(int64/size_t/uint64 等)。 + + 这些名字用户文件无需 import 即用 ⇒ 常规路径(符号表)必定 miss,必须在此 + 展开,否则会误进 fail-fast。本地导入避免与 TypeAnnotationResolver 成环。 + """ + from lib.core.TypeAnnotationResolver import TypeAnnotationResolver as _TAR + return _TAR._lookup_builtins_typedef(name, self._symtab) # ================================================================== # 公共 API @@ -216,8 +261,36 @@ class LLVMTypeMapper: resolved = self._ctype_to_llvm_str(entry) if resolved: return resolved - self._symtab.diagnostics.warn('', 0, f"无法解析 Name 类型 '{node.id}',回退到 i32") - return 'i32' + # --- 本模块声明的类型名 ------------------------------------------- + # 类名:裸类引用自身即指针 —— 与 _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)}' + # --- 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 + # fail-fast:类型信息缺失不再静默退化成 i32 + raise TypeNameUnresolvedError( + f"类型注解 '{node.id}' 无法解析:既不是内置类型 / 符号表 typedef / " + f"builtins typedef,也不是本模块或已加载模块的类/枚举") def _resolve_attribute(self, node: ast.Attribute) -> str: """解析 ast.Attribute 节点""" diff --git a/lib/core/SymbolTable.py b/lib/core/SymbolTable.py index 7147715..98b1e36 100644 --- a/lib/core/SymbolTable.py +++ b/lib/core/SymbolTable.py @@ -10,7 +10,7 @@ from lib.core.Handles.HandlesBase import CTypeInfo from lib.includes import t from lib.core.VLogger import get_logger as _vlog from lib.core.DiagnosticCollector import DiagnosticCollector -from lib.core.LLVMTypeMapper import LLVMTypeMapper +from lib.core.LLVMTypeMapper import LLVMTypeMapper, TypeNameUnresolvedError from lib.core.SymbolExtractor import ASTSymbolExtractor from lib.core.SymbolTypeResolver import TypeResolver from lib.core.SymbolInserter import SymbolInserter @@ -400,6 +400,11 @@ class SymbolTable: return loaded + except TypeNameUnresolvedError: + # 类型解析 fail-fast:**不再**吞成"加载模块失败"后返回空符号表 + # (那会让该模块的全部符号缺失,错误在下游以"找不到类/函数"的面目炸开, + # 根因被掩盖)。直接上抛 → 上层终止编译并打印带函数名/形参名的报文。 + raise except Exception as e: _vlog().error(traceback.format_exc()) self.diagnostics.error(FilePath, lineno, f"加载模块失败: {e}") diff --git a/lib/core/SymbolTypeResolver.py b/lib/core/SymbolTypeResolver.py index 18b43db..fa0ed6d 100644 --- a/lib/core/SymbolTypeResolver.py +++ b/lib/core/SymbolTypeResolver.py @@ -9,7 +9,7 @@ from lib.core.SymbolData import ( ModuleSymbols, TypedefSymbolData, FuncSymbolData, DefineSymbolData ) from lib.core.Handles.HandlesBase import CTypeInfo, CTypeHelper -from lib.core.LLVMTypeMapper import LLVMTypeMapper +from lib.core.LLVMTypeMapper import LLVMTypeMapper, TypeNameUnresolvedError from lib.core.SymbolUtils import IsTModule from lib.includes import t @@ -28,6 +28,18 @@ class TypeResolver: # 构建 class_members 查找表(typedef Postdefinition 需要) class_members: dict[str, dict[str, CTypeInfo]] = {cls.name: cls.members for cls in module_symbols.classes} + # 注入"本模块声明的类型名"(函数/typedef 签名解析需要)。 + # 本模块类名在 Pass 3 插入符号表之前**不可见** ⇒ 若不注入,裸类引用 + # (rawptr[Constant] / Constant)在 _resolve_name 里解析不出,旧口径退化成 + # i32(rawptr[X] → i32*),类信息丢失后经跨模块符号表传到调用点,报错会 + # 指向源码(形参 i32* 对不上实参 %"sha1.X"*)而掩盖根因。 + # 枚举名按 type_kind == 'enum' 单独登记宽度(IsStruct 口径不含枚举)。 + self._type_mapper.set_known_type_names( + {cls.name for cls in module_symbols.classes if cls.type_kind != 'enum'}, + {cls.name: (cls.enum_width or 32) for cls in module_symbols.classes + if cls.type_kind == 'enum'}, + ) + # 解析 typedef(按顺序记录,供后续 typedef 引用同模块已解析的 typedef) td: TypedefSymbolData for td in module_symbols.typedefs: @@ -203,7 +215,12 @@ class TypeResolver: def _resolve_function(self, func: FuncSymbolData): """解析函数的返回类型和参数类型""" - func.ret_type = self._type_mapper.get_func_ret_type_str(func.returns_node) + try: + func.ret_type = self._type_mapper.get_func_ret_type_str(func.returns_node) + except TypeNameUnresolvedError as _e: + # fail-fast:返回类型无法解析 → 终止编译(附函数名定位,不降级为 i32) + raise TypeNameUnresolvedError( + f"函数 '{func.name}' 的返回类型无法解析:{_e}") from None # 同时解析为 CTypeInfo,保留符号性(LLVM 类型字符串 'i64' 不区分有无符号) if func.returns_node is not None: try: @@ -215,7 +232,12 @@ class TypeResolver: annotation: ast.expr | None for _arg_name, annotation in func.params: if annotation: - func.param_types.append(self._type_mapper.get_func_param_type_str(annotation)) + try: + func.param_types.append(self._type_mapper.get_func_param_type_str(annotation)) + except TypeNameUnresolvedError as _e: + # fail-fast:形参类型无法解析 → 终止编译(附函数名/形参名定位) + raise TypeNameUnresolvedError( + f"函数 '{func.name}' 的形参 '{_arg_name}' 类型无法解析:{_e}") from None else: func.param_types.append('i8*')