fix(tpc): 跨模块符号表保留类名,形参注解不再退化成 i32*

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;未知名 → 报错终止。
This commit is contained in:
TermiNexus committed 2026-09-28 18:06:07 +08:00
1 parent b3f72d675b
commit 14af03577c
3 files changed
+116 -16

No files matched your search

+85 -12
View File
@@ -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 节点"""
+6 -1
View File
@@ -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}")
+25 -3
View File
@@ -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*')