修正了 TPC 的一些错误,包括 Test 维护后 TPV 无法重新编译或重编译后越界的部分问题

This commit is contained in:
TermiNexus committed 2026-07-20 11:12:30 +08:00
1 parent ab73420b4f
commit a277ded8d4
476 files changed
+4000 -3439

No files matched your search

+167 -7
View File
@@ -1272,7 +1272,13 @@ class ExprCallHandle(BaseHandle):
if expected_type.width < val.type.width:
return Gen.builder.trunc(val, expected_type)
else:
return Gen.builder.zext(val, expected_type)
if val.type.width == 1:
# i1 是布尔值,无符号语义,必须 zext
return Gen.builder.zext(val, expected_type, name="zext")
is_unsigned: bool = id(val) in Gen._unsigned_results
if is_unsigned:
return Gen.builder.zext(val, expected_type, name="zext")
return Gen.builder.sext(val, expected_type, name="sext")
if isinstance(val.type, ir.PointerType) and isinstance(expected_type, ir.IntType):
if expected_type.width == 64:
return Gen.builder.ptrtoint(val, expected_type)
@@ -1625,7 +1631,14 @@ class ExprCallHandle(BaseHandle):
return ArgVal
if isinstance(TargetType, ir.IntType) and isinstance(ArgVal.type, ir.IntType):
if ArgVal.type.width < TargetType.width:
return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast")
if ArgVal.type.width == 1:
# i1 是布尔值,无符号语义,必须 zext
return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast")
# C cast 语义:源有符号 → sext;源无符号 → zext;目标符号性仅影响解释
is_unsigned: bool = (Node.args and Gen._check_node_unsigned(Node.args[0])) or id(ArgVal) in Gen._unsigned_results
if is_unsigned:
return Gen.builder.zext(ArgVal, TargetType, name=f"zext_{TypedefName}_cast")
return Gen.builder.sext(ArgVal, TargetType, name=f"sext_{TypedefName}_cast")
elif ArgVal.type.width > TargetType.width:
return Gen.builder.trunc(ArgVal, TargetType, name=f"trunc_{TypedefName}_cast")
return ArgVal
@@ -1754,7 +1767,15 @@ class ExprCallHandle(BaseHandle):
if default_val.type != param_type:
if isinstance(param_type, ir.IntType) and isinstance(default_val.type, ir.IntType):
if default_val.type.width < param_type.width:
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
if default_val.type.width == 1:
# i1 是布尔值,无符号语义,必须 zext
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
else:
is_unsigned: bool = Gen._check_node_unsigned(default_node) or id(default_val) in Gen._unsigned_results
if is_unsigned:
default_val = Gen.builder.zext(default_val, param_type, name=f"zext_default_{i}")
else:
default_val = Gen.builder.sext(default_val, param_type, name=f"sext_default_{i}")
elif default_val.type.width > param_type.width:
default_val = Gen.builder.trunc(default_val, param_type, name=f"trunc_default_{i}")
elif isinstance(param_type, ir.PointerType) and isinstance(default_val.type, ir.IntType):
@@ -1823,7 +1844,16 @@ class ExprCallHandle(BaseHandle):
elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.IntType):
adjusted.append(Gen.builder.ptrtoint(arg, param_type))
elif isinstance(arg.type, ir.IntType) and arg.type.width < param_type.width:
adjusted.append(Gen.builder.zext(arg, param_type))
if arg.type.width == 1:
# i1 是布尔值,无符号语义,必须 zext
adjusted.append(Gen.builder.zext(arg, param_type, name=f"zext_vararg_fixed_{i}"))
else:
arg_node = Node.args[i] if i < len(Node.args) else None
is_unsigned: bool = (arg_node is not None and Gen._check_node_unsigned(arg_node)) or id(arg) in Gen._unsigned_results
if is_unsigned:
adjusted.append(Gen.builder.zext(arg, param_type, name=f"zext_vararg_fixed_{i}"))
else:
adjusted.append(Gen.builder.sext(arg, param_type, name=f"sext_vararg_fixed_{i}"))
elif isinstance(arg.type, ir.PointerType) and isinstance(param_type, ir.PointerType):
adjusted.append(Gen.builder.bitcast(arg, param_type, name=f"cast_arg_{i}"))
else:
@@ -1994,6 +2024,23 @@ class ExprCallHandle(BaseHandle):
else:
# 跨模块基类 — 查找基类的 sha1
base_sha1 = struct_sha1_map.get(base_name)
# 回退:若 _struct_sha1_map 未命中(普通类导入时不填充),
# 扫描 temp/ 下所有 .pyi 查找包含该基类定义的文件
if base_sha1 is None:
temp_dir = os.path.join(project_root, 'temp')
try:
pyi_files = os.listdir(temp_dir)
except OSError:
pyi_files = []
for pyi_file in pyi_files:
if not pyi_file.endswith('.pyi'):
continue
candidate_sha1 = pyi_file[:-4]
candidate_map = load_pyi(candidate_sha1)
if candidate_map and base_name in candidate_map:
base_sha1 = candidate_sha1
struct_sha1_map[base_name] = base_sha1
break
if base_sha1:
base_methods, base_cvtable, base_novtable, _ = collect_class_methods(base_name, base_sha1, visited)
if base_novtable:
@@ -2049,6 +2096,63 @@ class ExprCallHandle(BaseHandle):
except Exception:
pass
def _try_complete_generic_specialization(self, ClassName: str, MethodName: str, Gen: LlvmGeneratorMixin) -> None:
"""泛型特化类方法按需完整特化。
当 ClassName 是泛型特化名(如 ndarray[double])且缓存中只有
declare_only 版本时,补发方法体。这样方法调用能找到 define,
避免链接时 undefined reference。
触发场景:numpy 模块内部函数(如 np_sum)调用 a.sum(),
其中 a 的类型注解 ndarray[t.CDouble] | t.CPtr 在类型解析时
只触发了 declare_only=True 特化(创建 struct + __before_init__),
方法体(fill/delete/dot/sum/...)未被编译。
"""
# 仅对特化名(含 '[' 且以 ']' 结尾)触发
if '[' not in ClassName or not ClassName.endswith(']'):
return
# 反推模板类名(如 ndarray[double] → ndarray)
base_name: str = ClassName.split('[')[0]
# 检查是否是已注册的泛型模板
if not (hasattr(self.Trans.ClassHandler, '_generic_class_templates') and base_name in self.Trans.ClassHandler._generic_class_templates):
return
# 构造 spec_key(与 _specialize_generic_class 一致:ClassName<arg1,arg2,...>)
spec_key: str = base_name + '<' + ','.join(ClassName[len(base_name) + 1:-1].split('][')) + '>'
spec_cache = getattr(Gen, '_generic_class_specializations', {})
cached = spec_cache.get(spec_key)
if not isinstance(cached, tuple) or len(cached) < 2:
return
cached_name: str = cached[0]
was_declare_only: bool = cached[1]
# 仅当缓存是 declare_only 版本时才需要补发
if not was_declare_only:
return
# 检查方法是否已有 define(blocks 非空表示已编译方法体)
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(cached_name)
full_method_name: str = f'{class_sha1}.{cached_name}.{MethodName}' if class_sha1 else f'{cached_name}.{MethodName}'
existing_func = Gen._find_function(full_method_name)
if existing_func is not None:
existing_blocks = getattr(existing_func, 'blocks', [])
if existing_blocks:
return # 方法体已编译
# 从缓存获取 type_args/type_names
type_info = getattr(Gen, '_generic_class_spec_type_info', {}).get(spec_key)
if type_info is None:
return
cached_type_args, cached_type_names = type_info
# 不预先标记:_specialize_generic_class 内部缓存逻辑会检测
# cached_declare_only=True 且 declare_only=False,自动补发方法体
# 并更新缓存为 (spec_name, False)。预注册机制(L117)防止递归。
try:
self.Trans.ClassHandler._specialize_generic_class(
base_name, cached_type_args, Gen,
type_names=cached_type_names, declare_only=False
)
except Exception:
# 失败时恢复 declare_only 标记,避免后续调用跳过补发
Gen._generic_class_specializations[spec_key] = (cached_name, True)
def _HandleMethodCallLlvm(self, Node: ast.Call) -> ir.Value:
Gen = self.Trans.LlvmGen
MethodName = Node.func.attr
@@ -2425,6 +2529,25 @@ class ExprCallHandle(BaseHandle):
break
if not ClassName:
return ir.Constant(ir.IntType(32), 0)
# 泛型特化 fallback:当 ClassName 是 opaque 基础名(如 'ndarray',不含 '[')
# 且注解未指定类型参数时,遍历 Gen.structs 查找以 'ClassName[' 开头的特化版本
# (如 'ndarray[double]'),优先选有对应方法的特化。这样方法调用能正确分派到
# 特化版本的方法(如 ndarray[double].delete),而非查找 opaque stub declare。
if '[' not in ClassName:
SpecClass: str | None = None
for CN in Gen.structs.keys():
if not CN.startswith(f'{ClassName}['):
continue
struct_sha1_tmp = getattr(Gen, '_struct_sha1_map', {}).get(CN)
if struct_sha1_tmp:
if Gen._has_function(f'{struct_sha1_tmp}.{CN}.{MethodName}'):
SpecClass = CN
break
if Gen._has_function(f'{CN}.{MethodName}'):
SpecClass = CN
break
if SpecClass:
ClassName = SpecClass
struct_sha1_map = getattr(Gen, '_struct_sha1_map', {})
class_sha1 = struct_sha1_map.get(ClassName)
if class_sha1:
@@ -2432,6 +2555,11 @@ class ExprCallHandle(BaseHandle):
else:
FullMethodName = f'{ClassName}.{MethodName}'
# 泛型特化类方法按需完整特化:当 ClassName 是特化名(如 ndarray[double])
# 且缓存中只有 declare_only 版本时,补发方法体。否则方法调用会查找
# stub declare(无 define),链接时报 undefined reference。
self._try_complete_generic_specialization(ClassName, MethodName, Gen)
# 提前查找函数以获取参数信息
func_for_kw = Gen._find_function(FullMethodName)
if not func_for_kw:
@@ -2505,6 +2633,22 @@ class ExprCallHandle(BaseHandle):
if func:
break
p = Gen.class_parent.get(p)
# 修复:vtable 分派需要 func 的函数类型。当 func=None 时(如 __new__
# 编译较早,MemManager.alloc 的 declare 尚未加入 Gen.functions),
# 从 stub 获取函数类型并创建 declare,确保 vtable 分派不被跳过
# 回退到直接调用(直接调用基类 MemManager.alloc 返回 NULL → 段错误)。
if not func and class_sha1:
stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(FullMethodName, Gen)
if stub_func_type:
func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen)
if not func and class_sha1:
plain_name = f'{ClassName}.{MethodName}'
stub_func_type = self.Trans.ImportHandler._LookupStubFuncType(plain_name, Gen)
if stub_func_type:
func = self.GetOrCreateStubFuncDecl(FullMethodName, stub_func_type, Gen)
if not func:
plain_name = f'{ClassName}.{MethodName}'
func = Gen._find_function(plain_name)
if func:
VtableSlotPtr = Gen.builder.gep(ObjVal, [ir.Constant(ir.IntType(32), 0), ir.Constant(ir.IntType(32), 0)], name=f"vtable_slot_{ClassName}")
VtablePtr = Gen._load(VtableSlotPtr, name=f"vtable_ptr_{ClassName}")
@@ -2790,8 +2934,18 @@ class ExprCallHandle(BaseHandle):
return (ts, tn)
# BinOp (如 AST | CPtr) 或 Attribute (如 t.CPtr / t.CInt)
type_str: str = self._resolve_type_arg_str(sn)
# type_name 取原始名称(不带模块前缀),用于 T 替换
raw_name: str = self._get_raw_type_name(sn)
# type_name 保留模块前缀(如 't.CUnsignedChar'),避免 T(0) 替换为
# CUnsignedChar(0) 后因 CUnsignedChar 不在顶层作用域而报
# "Undefined function: 'CUnsignedChar'"。
# t.CPtr 特殊处理:保留 'CPtr' 不带前缀,因为 cptr_params 检测依赖
# _BUILTIN_TYPE_NAMES 中的 'CPtr' 来识别指针类型参数
if isinstance(sn, ast.Attribute) and isinstance(sn.value, ast.Name):
if sn.attr == 'CPtr':
raw_name: str = 'CPtr'
else:
raw_name: str = f'{sn.value.id}.{sn.attr}'
else:
raw_name: str = self._get_raw_type_name(sn)
return (type_str, raw_name)
def _ResolveGenericSlice(self, Sub: ast.Subscript) -> str | None:
@@ -2823,7 +2977,13 @@ class ExprCallHandle(BaseHandle):
while len(type_args) < len(type_param_names):
type_args.append('int')
type_names.append('CInt')
return self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names)
# 类型注解解析(如 Float64Array: t.CTypedef = ndarray[t.CDouble])
# 只需 struct 定义,不需方法体;使用 declare_only=True 避免过早触发
# 完整特化(此时被引用的函数如 printf 可能尚未由 from X import Y 注册,
# 导致 "Undefined function" 错误)。完整特化由后续类实例化
# (_HandleClassNewLlvm → _specialize_generic_class(declare_only=False))
# 触发,缓存机制会补发方法体。
return self.Trans.ClassHandler._specialize_generic_class(ClassName, type_args, Gen, type_names=type_names, declare_only=True)
@staticmethod
def _AnnotationContainsCPtr(Node: ast.AST) -> bool: