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:
1 parent
b3f72d675b
commit
14af03577c
3 files changed
+116
-16
No files matched your search
+85
-12
@@ -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 节点"""
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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*')
|
||||
|
||||
|
||||
Reference in new issue
Block a user