fix(generic): 恢复泛型特化发射主路径

补回 _specialize_generic_class 的 deepcopy/改名/字段与方法 T 替换/_EmitClassLlvm 发射逻辑,list[...].__len__ 与 get 正常生成;源码不再支持 CPtr[...],统一写 T*;编译错误只由主进程输出一次。
This commit is contained in:
TermiNexus committed 2026-10-18 19:50:32 +08:00
1 parent 09553401e7
commit 369afb5011
13 files changed
+413 -95

No files matched your search

+104
View File
@@ -235,6 +235,110 @@ class ClassHandle(BaseHandle):
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)
# 特化 AST:复制模板 → 清空 type_params → 改名为特化名,再整体替换
# T(注解 + 方法体)。之后走与普通类完全相同的 _EmitClassLlvm 发射
# (declare_only=False 时逐方法 _EmitFunctionLlvm 生成方法体)。
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 替换后的真实形态可能是
# Attribute 链('<sha1>.GNode' → Name('<sha1>').GNode,
# 见 _make_replacement_node/_make_name_node),不是 ast.Name,
# 故按裸名判据(_BareClassAnnotationName)判定;已经是指针
# 下标(CPtr[T] / rawptr[T])的注解返回 None,不重复加层。
raw_name: str | None = self._BareClassAnnotationName(arg.annotation)
if raw_name is not None and raw_name in class_type_names:
arg.annotation = ast.Subscript(
value=ast.Name(id='CPtr', ctx=ast.Load()),
slice=arg.annotation,
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.Subscript(
value=ast.Name(id='CPtr', ctx=ast.Load()),
slice=item.returns,
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 即
# 类名 → 定义模块表,Phase2 从各 .vpi 扫描构建):与声明端
# 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)
except BaseException:
# 失败时撤销预注册缓存:否则后续调用会把「特化到一半」当成已完成
# (declare_only=False),方法体永远不会再补发,错误被伪装成
# "没有 __len__ 方法 / Undefined method"。异常本身不吞,继续上抛。
Gen._generic_class_specializations.pop(spec_key, None)
raise
finally:
# 确保 Gen 状态恢复,即使 _EmitClassLlvm 抛出异常(异常本身不吞,
# 由调用方记录到 Gen._generic_spec_error 或直接上抛)。
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:
"""解析基类名称的类型种类
+4 -2
View File
@@ -3068,8 +3068,10 @@ class ExprCallHandle(BaseHandle):
base_name, cached_type_args, Gen,
type_names=cached_type_names, declare_only=False
)
except Exception:
# 失败时恢复 declare_only 标记,避免后续调用跳过补发
except Exception as _spec_err:
# 不静默吞特化异常:暴露到 Gen._generic_spec_error(Undefined method /
# len 失败时随报文输出),并恢复 declare_only 标记避免后续调用跳过补发。
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
Gen._generic_class_specializations[spec_key] = (cached_name, True)
Gen.builder = _saved_builder
Gen.func = _saved_func
+4 -4
View File
@@ -181,8 +181,8 @@ class ExprUtils:
# 泛型特化方法尚未生成时,先补发特化
try:
self.Trans.ExprCallHandle._try_complete_generic_specialization(ClassName, op_name, Gen)
except Exception:
pass
except Exception as _spec_err:
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
FullMethodName = f'{ClassName}.{op_name}'
if not Gen._has_function(FullMethodName):
FullMethodName = f'{ClassName}.{op_name}__'
@@ -224,8 +224,8 @@ class ExprUtils:
# 缓存机制会从 declare_only 升级为完整特化),补发后重新检查
try:
self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen)
except Exception:
pass
except Exception as _spec_err:
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
_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', []):
+36 -18
View File
@@ -591,8 +591,14 @@ class FunctionHandle(BaseHandle):
# 避免过早触发完整特化(此时 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)
try:
spec_name2: str | None = self.Trans.ClassHandler._specialize_generic_class(
ret_class, ret_type_args, Gen, type_names=ret_type_names, declare_only=True)
except Exception as _spec_err:
# 不静默吞特化异常:记录到 Gen._generic_spec_error 后再
# 交给外层返回类型回退(否则只剩一个错误的 CInt 返回类型)。
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
raise
if spec_name2 and spec_name2 in Gen.structs:
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CStruct(name=spec_name2)
@@ -995,30 +1001,29 @@ class FunctionHandle(BaseHandle):
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[CPtr[AST]] 中的 T)在表达式上下文中
# 需替换为 `CPtr[AST]` 下标,使 __sizeof__ 等处理器能正确识别
# 指针语义并直接返回 8,避免触发 struct 加载和重入编译
# 指针类型参数(如 list[CPtr[AST]] 中的 T):表达式里 T 的唯一
# 用法是 `T.__sizeof__()`,指针大小恒为 8。替换成**裸 CPtr**
# (而不是 CPtr[<mangled 串>]):mangled 串含 '.'/'*',既不是
# 合法 Name,也会让 __sizeof__ 的 Subscript 分支(只认泛型切片)
# 落空后把 CPtr 当变量求值 → "未定义名称: 'CPtr'"。
if cptr_params and node.func.value.id in cptr_params:
node.func.value = ast.Subscript(
value=ast.Name(id='CPtr', ctx=ast.Load()),
slice=self._make_name_node(replacement),
ctx=ast.Load()
)
node.func.value = ast.Name(id='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)
elif isinstance(node.func, ast.Subscript):
# `T*(expr)` / `list[T](...)`:被调者/转换目标的切片也要替换。
self._apply_type_map_to_expr(node.func, 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.Subscript(
value=ast.Name(id='CPtr', ctx=ast.Load()),
slice=self._make_name_node(replacement),
ctx=ast.Load()
)
# 同上:指针类型参数的表达式用法(T.__sizeof__ 等)只需
# "指针语义",裸 CPtr 即指针大小 8;不要拼 mangled 串。
node.value = ast.Name(id='CPtr', ctx=ast.Load())
else:
node.value = ast.Name(id=replacement, ctx=ast.Load())
else:
@@ -1041,7 +1046,14 @@ class FunctionHandle(BaseHandle):
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)
# `T*(expr)` 的指针转换在 AST 里是 Call(func=Subscript(CPtr, T)):
# 切片上的 T 必须整体替换成 `_make_replacement_node` 的产物(如
# CPtr[<sha1>.AST]),不能走 Name 分支直接改 id(mangled 串含 '.'/'*'
# 不是合法标识符),否则会留下裸 T → 生成幻影结构体 %"<sha1>.T"。
if isinstance(node.slice, ast.Name) and node.slice.id in type_map:
node.slice = self._make_replacement_node(type_map[node.slice.id])
else:
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]
@@ -1504,8 +1516,14 @@ class FunctionHandle(BaseHandle):
# 返回类型解析只需 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)
try:
spec_name2: str | None = self.Trans.ClassHandler._specialize_generic_class(
ret_class, ret_type_args, Gen, type_names=ret_type_names, declare_only=True)
except Exception as _spec_err:
# 不静默吞特化异常:记录到 Gen._generic_spec_error 后再
# 交给外层返回类型回退(否则只剩一个错误的 CInt 返回类型)。
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
raise
if spec_name2 and spec_name2 in Gen.structs:
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CStruct(name=spec_name2)
+4 -1
View File
@@ -4256,7 +4256,10 @@ class ImportHandle(BaseHandle):
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:
except Exception as _spec_err:
# 不静默吞特化异常:记录现场,供后续 Undefined method /
# len 失败路径把真因带进报错。
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
gc_spec = None
Gen.module_sha1 = saved_module_sha1
self.Trans._module_sha1 = saved_trans_sha1
+15 -6
View File
@@ -4,7 +4,8 @@ if TYPE_CHECKING:
from lib.core.translator import Translator
from lib.core.Handles.HandlesBase import BaseHandle, CTypeHelper, CTypeInfo
from lib.core.SymbolUtils import (
IsListAnnotation, IsPtrSubscriptNode, IsCPtrMarkerNode)
IsListAnnotation, IsPtrSubscriptNode, IsCPtrMarkerNode,
MangleTypeArgByRegistry)
from lib.core import TypeSystem
import lib._bootstrap # noqa: F401 设置 sys.path(项目根 + lib)
import vast as ast
@@ -67,7 +68,7 @@ class HandlesTypeMerge(BaseHandle):
Line: int = getattr(Node, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
f"类型注解只能是单一类型:指针请写 T*(如 int*),修饰符请用关键字")
Result = self._GetCTypeInfoInternal(Node)
if isinstance(Result, CTypeInfo):
Info = Result
@@ -83,7 +84,7 @@ class HandlesTypeMerge(BaseHandle):
Line: int = getattr(Node, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
f"类型注解只能是单一类型:指针请写 T*(如 int*),修饰符请用关键字")
LineNum = getattr(Node, 'lineno', None)
if getattr(self.Trans, 'DebugFile', None):
@@ -119,6 +120,9 @@ class HandlesTypeMerge(BaseHandle):
Info.BaseType = TypeSystem.CVoid
return Info
if BuiltinCls == TypeSystem.CPtr:
# 裸 CPtr 仅来自内部类型字符串;源码注解写 CPtr / CPtr[...]
# 已在解析层拒绝(提示改写 T*)。内部 Subscript(CPtr, T)
# 仍由后续 Subscript 分支解析。
Info.PtrCount = 1
Info.BaseType = TypeSystem.CVoid
return Info
@@ -800,13 +804,18 @@ class HandlesTypeMerge(BaseHandle):
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))
_raw_arg: str = slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(elt)
# 与 _mangle_generic_class_name 同口径(CInt → i32、
# CFloat64T → f64):否则注解侧拼出 list[CInt],
# 实际特化名却是 list[i32] → 同名结构体分裂。
arg_parts.append(MangleTypeArgByRegistry(_raw_arg))
# 必须用 '][' 分隔以匹配 _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)
_raw_arg = slice_res[0] if slice_res else self.Trans.ExprCallHandle._resolve_type_arg_str(slice_node)
type_arg_str = MangleTypeArgByRegistry(_raw_arg)
full_spec_name: str = f'{value_node.id}[{type_arg_str}]'
ResultInfo.BaseType = TypeSystem.CStruct(name=full_spec_name)
ResultInfo.Name = full_spec_name
@@ -915,4 +924,4 @@ class HandlesTypeMerge(BaseHandle):
Line: int = getattr(Node, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
f"类型注解只能是单一类型:指针请写 T*(如 int*),修饰符请用关键字")
+2 -1
View File
@@ -2141,6 +2141,7 @@ _ANNOTATION_MARKER_ALIASES: dict[str, str] = {
}
# 指针/引用下标的 base 名:注解里出现这些名字即表示"指针层"(等价旧 CPtr 标记)。
# CPtr 仅作为内部表示(`T*` 脱糖产物);源码写 CPtr[...] 已在解析层拒绝。
_POINTER_SUBSCRIPT_NAMES: frozenset[str] = frozenset({
'CPtr', 'rawptr', 'CRef', 'CTempRef', 'CArrayPtr', 'CAutoPtr',
})
@@ -2152,7 +2153,7 @@ def RejectTypeUnionAnnotation(node: ast.AST | None) -> None:
Line: int = getattr(node, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
f"类型注解只能是单一类型:指针请写 T*(如 int*),修饰符请用关键字")
def AnnotationContainsMarker(node: ast.AST, MarkerName: str) -> bool:
+6 -2
View File
@@ -145,7 +145,7 @@ class TypeAnnotationResolver:
Line: int = getattr(Node, 'lineno', 0) or 0
raise TypeError(
f"[TPC] 不支持类型联合 'T1 | T2'(第 {Line} 行);"
f"类型注解只能是单一类型:指针请写 CPtr[T],修饰符请用关键字")
f"类型注解只能是单一类型:指针请写 T*(如 int*),修饰符请用关键字")
if isinstance(Node, ast.Constant):
Info: CTypeInfo = TypeAnnotationResolver._from_node_constant(Node, SymbolTable)
elif isinstance(Node, ast.Name):
@@ -575,6 +575,8 @@ class TypeAnnotationResolver:
TypeObj = getattr(TypeSystem, TypeName, None)
if isinstance(TypeObj, type) and issubclass(TypeObj, TypeSystem.CType) and TypeObj is not TypeSystem.CType:
if TypeObj == TypeSystem.CPtr:
# CPtr 名仅来自内部类型字符串(builtins typedef 值等);源码
# 注解写 CPtr / CPtr[...] 已在解析层拒绝,提示改写为 T*。
Result: CTypeInfo = CTypeInfo()
Result.PtrCount = 1
Result.BaseType = TypeSystem.CVoid()
@@ -790,6 +792,8 @@ class TypeAnnotationResolver:
MarkerName = base.id
elif isinstance(base, ast.Attribute):
MarkerName = base.attr
# 指针/引用下标:CPtr 仅由 `T*` 脱糖产生(内部表示),源码写 CPtr[...]
# 已在解析层拒绝;rawptr / CRef / CTempRef / CArrayPtr / CAutoPtr 仍可直写。
if MarkerName in ('CPtr', 'rawptr'):
SliceInfo: CTypeInfo = TypeAnnotationResolver.from_node(Node.slice, SymbolTable)
Result: CTypeInfo = CTypeInfo()
@@ -980,7 +984,7 @@ class TypeAnnotationResolver:
Where: str = f'{File}:{Line}:{Col}' if File else f'第 {Line} 行第 {Col} 列'
raise TypeError(
f"[TPC] 类型注解不支持模块前缀 \"{FullName}\"({Where})。"
f"请改写为 \"{TypeName}\";指针写 CPtr[T] / rawptr[T]。")
f"请改写为 \"{TypeName}\";指针写 T*(如 int*)/ rawptr[T]。")
# 解析 import 别名: 如 import vpsdk.window as window -> window -> vpsdk.window
if ModulePath:
resolved: bool = False