fix(generic): 特化实参统一 mangling,失败立即终止

泛型实参在特化名/spec_key/type_map 中统一为 i32/指针形态,list[int].get 返回 i32;特化与翻译失败不再吞异常,include 缺失 .ll 直接终止。
This commit is contained in:
TermiNexus committed 2026-10-18 20:40:23 +08:00
1 parent 88645fe7da
commit de89b26bb7
8 files changed
+108 -56

No files matched your search

+5 -1
View File
@@ -3913,8 +3913,12 @@ class Phase2Translator:
for item in need_translate:
src_path, sha1, module_name, ll_path, obj_path, precompiled_obj = item
if not _CachedIsFile(ll_path):
# 翻译成功(worker 返回 ok)却没有产物 = 翻译失败:立即终止,
# 不允许跳过该模块继续后处理/编译/链接(否则只会得到链接期
# undefined symbol,真因与现场脱节)。
_vlog().error(f" 翻译未生成 .ll 文件: {module_name}.py")
continue
raise ReportedCompileError(
f"include 翻译未生成 .ll 文件: {module_name}.py,编译立即停止")
ll_content = _ReadTextCached(ll_path)
+4 -2
View File
@@ -11,7 +11,7 @@ from lib.constants.config import mode as _config_mode
from lib.core import TypeSystem
from lib.core.SymbolUtils import (
IsListAnnotation, ParseListAnnotation, RequireDefineConstStorable,
FindAutoAddrMode,
FindAutoAddrMode, MangleTypeArgByRegistry,
)
@@ -973,7 +973,9 @@ class AnnAssignHandle(BaseHandle):
sn2 = sn2.value
r2: tuple[str, str] | None = self.Trans.ExprCallHandle._resolve_generic_slice_type(sn2)
if r2:
resolved_cn = f'{CN}[{r2[0]}]'
# 与特化名同源(int → i32):直接拼 r2[0] 会得到
# 'list[CInt]',方法分派查不到 'list[i32].get'。
resolved_cn = f'{CN}[{MangleTypeArgByRegistry(r2[0])}]'
if resolved_cn != Gen.var_struct_class.get(VarName):
Gen.var_struct_class[VarName] = resolved_cn
Gen.global_struct_class[VarName] = resolved_cn
+21 -4
View File
@@ -10,7 +10,7 @@ import llvmlite.ir as ir
from lib.core.Exportable import EnumMember as ExportEnumMember
from lib.core.Handles.HandlesBase import BaseHandle, CTypeInfo, FuncMeta
from lib.core.SymbolUtils import (
MangleTypeArgByRegistry,
MangleTypeArgByRegistry, NormalizeGenericArgName,
EnumClassIntWidth,
EnumBodyMemberNames, EnumMemberCountMismatchError,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
@@ -90,8 +90,12 @@ class ClassHandle(BaseHandle):
单项 mangling 走 SymbolUtils.MangleTypeArgByRegistry(声明端
DeclarationGenerator 的泛型分支以 ResolveGenericTypeArgStr 调用同一函数,
两侧拼名规则唯一),'][' 分隔与 HandlesTypeMerge / DeclarationGenerator 一致。
源码短名先经 NormalizeGenericArgName 归一(int → CInt → i32):直接对
裸 'int' 调 MangleTypeArgByRegistry 会因注册表无 'int' 而原样保留,拼出
与声明端不一致的 'list[int]'。ResolveGenericTypeArgStr 正是同两步组合。
"""
mangled_args: list[str] = [MangleTypeArgByRegistry(ta) for ta in type_args]
mangled_args: list[str] = [
MangleTypeArgByRegistry(NormalizeGenericArgName(ta)) for ta in type_args]
return class_name + '[' + ']['.join(mangled_args) + ']'
def _HasClassDefinition(self, Name: str, Gen: LlvmGeneratorMixin) -> bool:
@@ -142,6 +146,13 @@ class ClassHandle(BaseHandle):
type_param_names: list[str] = template['type_params']
if len(type_args) != len(type_param_names):
return None
# 实参先归一为**唯一规范形**(int → CInt → i32;与声明端
# ResolveGenericTypeArgStr、定义端 _mangle_generic_class_name 同一两步)。
# spec_key / spec_name / type_map 全部只认这一种写法:否则同一 list[int]
# 会以 'list<CInt>'、'list<int>'、'list<i32>' 三个键各自特化一次
# (struct 重复发射),或缓存键与特化名不同源导致补发方法体时找不到。
type_args = [
MangleTypeArgByRegistry(NormalizeGenericArgName(ta)) for ta in type_args]
spec_key: str = ClassName + '<' + ','.join(type_args) + '>'
# 缓存必须在 Gen 上(每个模块独立),不能在 ClassHandler 上跨模块共享,
# 否则临时 Gen(如共享符号表构建阶段)的特化会污染正式翻译阶段的缓存
@@ -1059,7 +1070,10 @@ class ClassHandle(BaseHandle):
_ec_gc: str | None = None
if _iname_c:
try:
_ec_gc = f'{_iname_c}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_c.slice)}]'
# 实参必须先 mangling(int → i32、CInt → i32):
# _resolve_type_arg_str 只做源码名归一,直接拼会得到
# 'list[CInt]',与特化名/struct 键 'list[i32]' 分裂。
_ec_gc = f'{_iname_c}[{MangleTypeArgByRegistry(self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_c.slice))}]'
except Exception:
_ec_gc = None
if _ec_gc:
@@ -1105,7 +1119,10 @@ class ClassHandle(BaseHandle):
if _elem_slice_c is not None:
_ec_c: str | None = None
try:
_ec_c = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_c)}]'
# 与特化名同源:实参 mangling 后拼(int → i32),
# 否则登记成 'list[CInt]',方法分派查不到
# 'list[i32].get'(Undefined method)。
_ec_c = f'list[{MangleTypeArgByRegistry(self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_c))}]'
except Exception:
_ec_c = None
if _ec_c:
+23 -12
View File
@@ -14,7 +14,7 @@ from lib.core import TypeSystem
from lib.core.TypeSystem import CTypeRegistry
from lib.core.SymbolUtils import (
ExtractTypeNameFromBinOp, IsPtrSubscriptNode,
ResolveTypeArgStr, GenericArgRawName,
ResolveTypeArgStr, GenericArgRawName, MangleTypeArgByRegistry,
ClassHasFuncLevelCVTable, IsVtableSlotMethod)
from lib.core.Handles.HandlesExpr import DefineUseWidthFromLLVMType
from lib.vast.ExprParser import NEW_SRC_MEMGR
@@ -835,6 +835,12 @@ class ExprCallHandle(BaseHandle):
pass
if Node.func.attr == '__sizeof__':
if isinstance(Node.func.value, ast.Subscript):
# 指针下标(`T*` 的脱糖形态 CPtr[T] / rawptr[T],T 替换后可能是
# CPtr[CInt] / CPtr[sha1.AST]):指针尺寸恒为 8。这就是原先把
# 泛型指针实参 T 在 sizeof 上下文替换成裸 CPtr 所表达的唯一语义,
# 现在按真实类型节点判定,不再改写 T 本身。
if IsPtrSubscriptNode(Node.func.value, self.Trans.SymbolTable):
return ir.Constant(ir.IntType(64), 8)
spec_name = self._ResolveGenericSlice(Node.func.value)
if spec_name:
return self._HandleSizeofLlvm(ast.Name(id=spec_name, ctx=ast.Load()))
@@ -3029,6 +3035,10 @@ class ExprCallHandle(BaseHandle):
type_names=_TypeNames, declare_only=False)
except Exception as _spec_err:
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
# 特化发射失败就是翻译失败:记录现场后立即上抛,禁止带着
# 未生成的方法体继续编译(否则只剩链接期 undefined symbol,
# 或调用点按错误签名继续生成 IR)。
raise
return
cached_name: str = cached[0]
# 检查方法是否已有 define(blocks 非空表示已编译方法体)
@@ -3052,13 +3062,10 @@ class ExprCallHandle(BaseHandle):
# cached_declare_only=True 且 declare_only=False,自动补发方法体
# 并更新缓存为 (spec_name, False)。预注册机制(L117)防止递归。
#
# 上下文快照:本函数**吞掉**特化异常、让外层方法继续编译,故必须在失败时把
# 编译器上下文复位——`_EmitFunctionLlvm` 的类名/构建器恢复只在**成功路径**
# 执行(失败路径直接 re-raise),异常穿过多层后 `Gen.builder/func` 与
# `_CurrentCpythonObjectClass` 会停在内层特化方法的中间状态:外层方法随后
# 的 `self.X` 被按内层类解析(ExprGenFstr.vp 的 `self.Gen` 被按
# `list[cfc4ca8da95a30e1.AST*]` 解析 → "属性 'Gen' 在类中找不到"的连带错误),
# 且后续指令会误插进内层函数的块。
# 上下文快照:特化失败时先复位编译器上下文再上抛——`_EmitFunctionLlvm` 的
# 类名/构建器恢复只在**成功路径**执行(失败路径直接 re-raise),异常穿过
# 多层后 `Gen.builder/func` 与 `_CurrentCpythonObjectClass` 会停在内层
# 特化方法的中间状态,导致错误报文/后续阶段按内层类解析。
_saved_builder: object = Gen.builder
_saved_func: object = Gen.func
_saved_cpython_class: str | None = getattr(self.Trans, '_CurrentCpythonObjectClass', None)
@@ -3069,14 +3076,15 @@ class ExprCallHandle(BaseHandle):
type_names=cached_type_names, declare_only=False
)
except Exception as _spec_err:
# 不静默吞特化异常:暴露到 Gen._generic_spec_error(Undefined method /
# len 失败时随报文输出),并恢复 declare_only 标记避免后续调用跳过补发。
# fail-fast:暴露到 Gen._generic_spec_error 后立即上抛(不再吞掉让
# 外层方法继续编译),并恢复 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
self.Trans._CurrentCpythonObjectClass = _saved_cpython_class
Gen._current_class_name = _saved_class_name
raise
def _compute_field_byte_offset(self, Gen: LlvmGeneratorMixin, ClassName: str, field_name: str) -> int | None:
"""计算字段在 struct 中的字节偏移(按 LLVM 布局含对齐)。
@@ -4218,10 +4226,13 @@ class ExprCallHandle(BaseHandle):
"""
Gen = self.Trans.LlvmGen
StructSha1Map: dict[str, str] = getattr(Gen, '_struct_sha1_map', {}) if Gen is not None else {}
return ResolveTypeArgStr(
# 归一 + mangling 两步与声明端 ResolveGenericTypeArgStr 完全相同:
# int → CInt → i32。只做 ResolveTypeArgStr 会返回 'CInt',据此拼出的
# 特化名 'list[CInt]' 与 struct 键/方法名 'list[i32]' 分裂。
return MangleTypeArgByRegistry(ResolveTypeArgStr(
node,
TCAliasMap=getattr(self.Trans, '_from_import_aliases', {}),
StructSha1Map=StructSha1Map)
StructSha1Map=StructSha1Map))
def _get_raw_type_name(self, node: ast.AST) -> str:
"""获取类型节点的原始名称(不带模块前缀),用于泛型 T 替换。
+4
View File
@@ -183,6 +183,8 @@ class ExprUtils:
self.Trans.ExprCallHandle._try_complete_generic_specialization(ClassName, op_name, Gen)
except Exception as _spec_err:
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
# fail-fast:补发特化失败即翻译失败,禁止继续按未生成的方法分派。
raise
FullMethodName = f'{ClassName}.{op_name}'
if not Gen._has_function(FullMethodName):
FullMethodName = f'{ClassName}.{op_name}__'
@@ -226,6 +228,8 @@ class ExprUtils:
self.Trans.ExprCallHandle._try_complete_generic_specialization(best_candidates[0], op_name, Gen)
except Exception as _spec_err:
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
# fail-fast:同上,补发失败立即终止。
raise
_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', []):
+35 -30
View File
@@ -595,10 +595,13 @@ class FunctionHandle(BaseHandle):
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 返回类型)。
# fail-fast:返回类型特化失败 = 翻译失败。转成
# FailFastError 并穿过外层"默认 CInt"回退继续上抛,
# 不允许带着错误/缺失的返回类型继续编译。
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
raise
raise _FFE(
f"泛型返回类型特化失败:{type(_spec_err).__name__}: {_spec_err}") from _spec_err
if spec_name2 and spec_name2 in Gen.structs:
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CStruct(name=spec_name2)
@@ -608,7 +611,12 @@ class FunctionHandle(BaseHandle):
ReturnTypeInfo.BaseType = TypeSystem.CInt()
except TypeError:
raise
except Exception: # 回退:设置默认返回类型为 CInt
except Exception as _ret_exc: # 回退:设置默认返回类型为 CInt
# fail-fast 信号不得被默认返回类型吞掉(否则调用点按 i32
# 继续生成 IR,错误被伪装成运行期崩溃/链接期未定义符号)。
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
if isinstance(_ret_exc, _FFE):
raise
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CInt()
else:
@@ -1002,14 +1010,12 @@ class FunctionHandle(BaseHandle):
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):表达式里 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.Name(id='CPtr', ctx=ast.Load())
else:
node.func.value = self._make_name_node(replacement)
# 用法是 `T.__sizeof__()`。统一替换成**真实类型节点**
# (_make_replacement_node:'sha1.AST*' → CPtr[sha1.AST]),
# 不再替换成裸 CPtr —— 裸 CPtr 把"指针实参"的语义写死进替换
# 规则,标量实参一旦被误判进 cptr_params 就会得到错误的 8。
# __sizeof__ 侧已直接识别指针下标并返回 8。
node.func.value = self._make_replacement_node(replacement)
else:
self._apply_type_map_to_expr(node.func.value, type_map, cptr_params)
elif isinstance(node.func, ast.Subscript):
@@ -1020,12 +1026,9 @@ class FunctionHandle(BaseHandle):
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:
# 同上:指针类型参数的表达式用法(T.__sizeof__ 等)只需
# "指针语义",裸 CPtr 即指针大小 8;不要拼 mangled 串。
node.value = ast.Name(id='CPtr', ctx=ast.Load())
else:
node.value = ast.Name(id=replacement, ctx=ast.Load())
# 同上:按真实类型节点替换(指针实参 → CPtr[<被指类>]),
# 不再退化为裸 CPtr。
node.value = self._make_replacement_node(replacement)
else:
self._apply_type_map_to_expr(node.value, type_map, cptr_params)
elif isinstance(node, ast.BinOp):
@@ -1520,10 +1523,13 @@ class FunctionHandle(BaseHandle):
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 返回类型)。
# fail-fast:返回类型特化失败 = 翻译失败。转成
# FailFastError 并穿过外层"默认 CInt"回退继续上抛,
# 不允许带着错误/缺失的返回类型继续编译。
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
Gen._generic_spec_error = f'{type(_spec_err).__name__}: {_spec_err}'
raise
raise _FFE(
f"泛型返回类型特化失败:{type(_spec_err).__name__}: {_spec_err}") from _spec_err
if spec_name2 and spec_name2 in Gen.structs:
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CStruct(name=spec_name2)
@@ -1533,7 +1539,12 @@ class FunctionHandle(BaseHandle):
ReturnTypeInfo.BaseType = TypeSystem.CInt()
except TypeError:
raise
except Exception: # 回退:设置默认返回类型为 CInt
except Exception as _ret_exc: # 回退:设置默认返回类型为 CInt
# fail-fast 信号不得被默认返回类型吞掉(否则调用点按 i32
# 继续生成 IR,错误被伪装成运行期崩溃/链接期未定义符号)。
from lib.core.LLVMCG.FuncGen import FailFastError as _FFE
if isinstance(_ret_exc, _FFE):
raise
ReturnTypeInfo = CTypeInfo()
ReturnTypeInfo.BaseType = TypeSystem.CInt()
else:
@@ -2037,14 +2048,8 @@ class FunctionHandle(BaseHandle):
try:
self.Trans.BodyHandler.HandleBodyLlvm(func_body)
except Exception as _body_exc:
# 方法体编译失败:确保 block 终止(防止 expected instruction opcode 错误),
# 然后重新抛出异常让编译立即终止(不吞掉错误)
_fn_name: str = Node.name
_cls_name: str = ClassName if ClassName else ''
import sys as _sys
import traceback as _tb
print(f"[FATAL] 方法体编译失败 ({_cls_name}.{_fn_name}): {type(_body_exc).__name__}: {_body_exc}", file=_sys.stderr)
_tb.print_exc(file=_sys.stderr)
# 方法体编译失败:复位上下文后原样上抛,由 Phase2Translator 的
# worker/主进程统一输出一次并终止(此处不打印,避免同一错误重复)。
if Gen.builder is not None:
try:
if not Gen.builder.block.is_terminated:
+11 -6
View File
@@ -15,7 +15,7 @@ from lib.constants.config import mode as _config_mode
from lib.core import TypeSystem
from lib.core.TypeSystem import CTypeRegistry
from lib.core.SymbolUtils import ParseListAnnotation, AnnotationContainsName, FindAutoAddrMode
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords
from lib.core.SymbolUtils import EnumClassIntWidth, ExpandSoftKeywords, MangleTypeArgByRegistry
from lib.core.SymbolUtils import ClassHasFuncLevelCVTable, IsVtableSlotMethod
@@ -2442,7 +2442,9 @@ class ImportHandle(BaseHandle):
_ec_gs: str | None = None
if _iname_s:
try:
_ec_gs = f'{_iname_s}[{self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_s.slice)}]'
# 实参先 mangling(int → i32):与特化名/struct 键
# 'list[i32]' 保持同源,避免 'list[CInt]' 分派失败。
_ec_gs = f'{_iname_s}[{MangleTypeArgByRegistry(self.Trans.ExprCallHandle._resolve_type_arg_str(_inner_sub_s.slice))}]'
except Exception:
_ec_gs = None
if _ec_gs:
@@ -2488,7 +2490,8 @@ class ImportHandle(BaseHandle):
if _elem_slice_s is not None:
_ec_s: str | None = None
try:
_ec_s = f'list[{self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_s)}]'
# 同 HandlesClassDef:实参 mangling 后拼特化名(int → i32)
_ec_s = f'list[{MangleTypeArgByRegistry(self.Trans.ExprCallHandle._resolve_type_arg_str(_elem_slice_s))}]'
except Exception:
_ec_s = None
if _ec_s:
@@ -4257,10 +4260,12 @@ class ImportHandle(BaseHandle):
gc_base, gc_type_args, Gen, type_names=gc_type_names,
declare_only=True)
except Exception as _spec_err:
# 不静默吞特化异常:记录现场,供后续 Undefined method /
# len 失败路径把真因带进报错。
# fail-fast:特化发射失败即翻译失败。记录现场、恢复
# module_sha1 后立即上抛,不带着缺失的方法体继续编译。
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
raise
Gen.module_sha1 = saved_module_sha1
self.Trans._module_sha1 = saved_trans_sha1
if gc_spec and gc_spec in Gen.structs:
+5 -1
View File
@@ -852,7 +852,11 @@ class HandlesTypeMerge(BaseHandle):
_ig_args: list[str] = []
for _ig_elt in _ig_slices:
_ig_res = self.Trans.ExprCallHandle._resolve_generic_slice_type(_ig_elt)
_ig_args.append(_ig_res[0] if _ig_res else self.Trans.ExprCallHandle._resolve_type_arg_str(_ig_elt))
_ig_raw: str = _ig_res[0] if _ig_res else self.Trans.ExprCallHandle._resolve_type_arg_str(_ig_elt)
# 与 _mangle_generic_class_name 同口径(CInt/CInt32T → i32、
# CFloat64T → f64):直接拼原始名会得到 'list[CInt]',
# 与 struct 键/方法名 'list[i32]' 分裂 → Undefined method。
_ig_args.append(MangleTypeArgByRegistry(_ig_raw))
_ig_spec: str = f'{_inner_gsub.value.id}[' + ']['.join(_ig_args) + ']'
ResultInfo.BaseType = TypeSystem.CStruct(name=_ig_spec)
ResultInfo.Name = _ig_spec