fix(tpc): 函数声明创建全部收口到唯一幂等入口,消除 DuplicatedNameError 裸抛

类别 a(编译器缺陷)+ b(机制漏豁免)。报错:翻译 lib\core\Gen\Expr\ExprGenName.vp
失败,报文只有名字 dddc595175f00c66.ExprGenAttr.find_global_in_module
⇒ llvmlite DuplicatedNameError(NameScope.register 对 GlobalValue
deduplicate_name=False,同名 global 再建必抛,__str__ 只回名字)。

根因(已取证,非猜测):
- llvmlite Module 里 `scope`(NameScope,注册/去重)与 `globals`(OrderedDict)
  是两套结构;`add_global` 只在构造时写入 ===> 「名字已在 scope._useset、
  但 globals 查不到」这一状态一旦出现,任何"先查 globals 再建"的守卫都会失效。
- 该状态由**清理不完整**制造:删旧名时只删"对象真名 existing_func.name/old_name",
  而字典键/目标名(func_name / MangledName)仍留在 scope._useset。
  别名注册(Gen.functions[短名] = 名为全名的对象)恰好让二者不同名。
- 另有若干创建点只看 Gen.functions、完全不看 module.globals(裸建 ir.Function),
  只要 module.globals 已有同名声明就裸抛 DuplicatedNameError。

本次收口(函数声明创建的唯一入口 = BaseGen._CreateOrReuseFuncDecl):
1. BaseGen._CreateOrReuseFuncDecl:新增"名字被非函数 global 占住"分支,把冲突
   双方(类型 + repr)报出来,替代原 llvmlite 只给名字的裸抛。
2. 清理不完整处补全为「同时清 真名 + 目标名」:
   - HandlesImports.py 模板 declare 装载(clearing func_name 之外还清 existing_func.name)
   - HandlesFunctions._replace_function_decl(clearing {old_name, MangledName})
   - 其 except 对 FailFastError 改为上抛(不再回退成复用旧声明)
3. 裸建 ir.Function 的创建点改走唯一入口(module.globals 同名则复用、
   签名冲突/非函数冲突则 fail-fast):
   - HandlesExprCall._HandleStaticMethodCallLlvm({sha1}.Class.Method)  ← 本现场形态
   - HandlesExprCall._HandleMethodCallLlvm 的 class_sha1 回退分支
   - HandlesExprCall._try_declare_c_lib_func / HandlesExprBuiltin._try_declare_c_lib_func
   - HandlesAssign 模块级全局赋值处的函数声明
   - HandlesImports.__before_init__ 声明(原查 NewFuncName 却建 MangledName)
   - HandlesFunctions._EmitFunctionLlvm(补登记 module.globals 已有声明,
     使既有的签名修复路径生效,而不是裸建撞名)

行为等价性:上述各点原先"撞名"时必然抛 DuplicatedNameError(即必失败),
收口后要么复用同一符号、要么 fail-fast 报出两侧签名/冲突类型,无静默降级。
This commit is contained in:
TermiNexus committed 2026-09-29 13:50:44 +08:00
1 parent 8c831a38d3
commit f4c8c48748
6 files changed
+68 -29

No files matched your search

+3 -2
View File
@@ -594,8 +594,9 @@ class AssignHandle(BaseHandle):
llvm_param_types.append(lp)
func_type: ir.FunctionType = ir.FunctionType(ret_type, llvm_param_types)
MangledName: str = Gen._mangle_func_name(FuncName)
func_decl: ir.Function = ir.Function(Gen.module, func_type, name=MangledName)
Gen.functions[MangledName] = func_decl
# 走唯一幂等入口(module.globals 已有同名声明复用 /
# 类型冲突 fail-fast),避免裸建抛 DuplicatedNameError。
func_decl: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, func_type)
Gen.functions[FuncName] = func_decl
if not MultiRetTypes and FuncName:
func: ir.Function | None = Gen.functions.get(FuncName)
+3 -3
View File
@@ -576,9 +576,9 @@ class ExprBuiltinHandle(BaseHandle):
param_types: list[ir.Type]
ret_type, param_types = self._C_LIB_FUNCS[func_name]()
func_type: ir.FunctionType = ir.FunctionType(ret_type, param_types)
func: ir.Function = ir.Function(Gen.module, func_type, name=func_name)
Gen.functions[func_name] = func
return func
# C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号:
# 裸建会在第二次抛 llvmlite DuplicatedNameError。
return Gen._CreateOrReuseFuncDecl(func_name, func_type)
def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin,
val: ir.Value | None, reason: str) -> None:
+10 -7
View File
@@ -1220,8 +1220,9 @@ class ExprCallHandle(BaseHandle):
if ClassName in Gen.structs and not is_static:
llvm_param_types.insert(0, ir.PointerType(Gen.structs[ClassName]))
func_type = ir.FunctionType(ret_type, llvm_param_types)
func = ir.Function(Gen.module, func_type, name=MangledName)
Gen.functions[MangledName] = func
# 走唯一幂等入口:module.globals 已有同名函数(stub / 调用点先建)
# 时复用,避免 llvmlite DuplicatedNameError;类型不同则 fail-fast。
func = Gen._CreateOrReuseFuncDecl(MangledName, func_type)
# 方法实际定义类(多继承 this 调整用)
method_anc_st: str | None = None
# 若 MangledName 只有 stub 声明(无函数体),也走 MRO 回退找定义类方法
@@ -3910,8 +3911,10 @@ class ExprCallHandle(BaseHandle):
# 避免 llvmlite DuplicatedNameError(如 gen.__init__ / ImportsHandle.HandleImport)
func = Gen._find_function(FullMethodName)
if func is None:
func = ir.Function(Gen.module, func_type, name=FullMethodName)
Gen.functions[FullMethodName] = func
# 走唯一幂等入口(module.globals 同名函数复用 / 类型冲突 fail-fast),
# 不再裸建 ir.Function —— Guard 只看 Gen.functions 时漏掉
# module.globals 已有声明会抛 llvmlite DuplicatedNameError。
func = Gen._CreateOrReuseFuncDecl(FullMethodName, func_type)
if isinstance(ObjVal.type, ir.PointerType) and ObjVal.type.pointee == struct_type:
call_self = ObjVal
else:
@@ -4010,9 +4013,9 @@ class ExprCallHandle(BaseHandle):
return None
ret_type, param_types = self._C_LIB_FUNCS[func_name]()
func_type = ir.FunctionType(ret_type, param_types)
func = ir.Function(Gen.module, func_type, name=func_name)
Gen.functions[func_name] = func
return func
# C 库声明名固定(malloc/strlen/...),同一模块内多次调用必须复用同一符号:
# 裸建会在第二次抛 llvmlite DuplicatedNameError。
return Gen._CreateOrReuseFuncDecl(func_name, func_type)
def _FailFastLenUnsupported(self, arg_node: ast.AST, Gen: LlvmGeneratorMixin,
val: ir.Value | None, reason: str) -> None:
+23 -9
View File
@@ -1309,19 +1309,29 @@ class FunctionHandle(BaseHandle):
return old_func
try:
old_name: str = old_func.name
if old_name in getattr(Gen.module, 'globals', {}):
del Gen.module.globals[old_name]
# 必须同时清 old_name(对象真名)与 MangledName(字典键 / 目标名):
# 别名场景下二者不同,只清真名会让 MangledName 仍占住 module.scope
# (llvmlite 按名字去重)→ 重建抛 DuplicatedNameError。
module_scope: Any = getattr(Gen.module, 'scope', None)
if getattr(module_scope, '_useset', None):
module_scope._useset.discard(old_name)
for _stale_name in {old_name, MangledName}:
_stale: Any = Gen.module.globals.get(_stale_name)
if isinstance(_stale, ir.Function):
del Gen.module.globals[_stale_name]
if getattr(module_scope, '_useset', None):
module_scope._useset.discard(_stale_name)
if getattr(Gen.module, '_guessed_names', None):
Gen.module._guessed_names.discard(FuncName)
new_func: ir.Function = ir.Function(Gen.module, NewFuncType, name=MangledName)
new_func: ir.Function = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType)
Gen.functions[MangledName] = new_func
if MangledName == FuncName or FuncName not in Gen._export_extern_funcs:
Gen.functions[FuncName] = new_func
return new_func
except Exception as ex:
from lib.core.LLVMCG.FuncGen import FailFastError
if isinstance(ex, FailFastError):
# 同名符号冲突(函数签名分裂 / 名字被非函数 global 占住):必须终止,
# 不能回退成"复用旧声明"——那正是 declare/define 分裂的现场。
raise
traceback.print_exc()
Gen.functions[MangledName] = old_func
if MangledName == FuncName or FuncName not in Gen._export_extern_funcs:
@@ -1737,18 +1747,22 @@ class FunctionHandle(BaseHandle):
if not IsUserMarkedExport and FuncName in Gen._export_funcs:
Gen._export_funcs.discard(FuncName)
MangledName = Gen._mangle_func_name(FuncName)
# 补登记:module.globals 已有同名函数(stub 声明 / 调用点先建)但未登记到
# Gen.functions 时,先登记再走下方判定 —— 否则 1750 分支裸建 ir.Function
# 会抛 llvmlite DuplicatedNameError(报文只有名字,不含冲突双方)。
_g_existing_fn: Any = Gen.module.globals.get(MangledName)
if MangledName not in Gen.functions and isinstance(_g_existing_fn, ir.Function):
Gen.functions[MangledName] = _g_existing_fn
if MangledName in Gen.functions:
func = Gen.functions[MangledName]
if getattr(func, 'name', MangledName) != MangledName:
func = ir.Function(Gen.module, FuncType, name=MangledName)
Gen.functions[MangledName] = func
func = Gen._CreateOrReuseFuncDecl(MangledName, FuncType)
else:
existing_type = getattr(func, 'ftype', None)
if existing_type and existing_type != FuncType:
func = self._replace_function_decl(Gen, FuncName, FuncType)
else:
func = ir.Function(Gen.module, FuncType, name=MangledName)
Gen.functions[MangledName] = func
func = Gen._CreateOrReuseFuncDecl(MangledName, FuncType)
if MangledName == FuncName or FuncName not in Gen._export_extern_funcs:
Gen.functions[FuncName] = func
func.attributes.add('noredzone')
+19 -8
View File
@@ -2578,7 +2578,10 @@ class ImportHandle(BaseHandle):
if StructType is not None:
StructPtrType = ir.PointerType(StructType)
NewFuncType = ir.FunctionType(ir.VoidType(), [StructPtrType])
NewFunc = ir.Function(Gen.module, NewFuncType, name=MangledName)
# 走唯一幂等入口:查的是 NewFuncName 而建的是 MangledName,二者不同名
# ⇒ 此处若裸建,MangledName 已被 stub / 调用点占住时必抛
# llvmlite DuplicatedNameError。
NewFunc = Gen._CreateOrReuseFuncDecl(MangledName, NewFuncType)
Gen.functions[NewFuncName] = NewFunc
bitfields = Gen.class_member_bitfields.get(ClassName, {})
if any(v > 0 for v in bitfields.values()):
@@ -3406,14 +3409,22 @@ class ImportHandle(BaseHandle):
existing_func = Gen.functions[func_name]
existing_ftype = getattr(existing_func, 'ftype', None) or existing_func.type.pointee
if str(existing_ftype) != str(func_type):
# 类型不一致:清理 module.globals + module.scope._useset 后重建,避免 DuplicatedNameError
old_name = existing_func.name
if old_name in Gen.module.globals:
del Gen.module.globals[old_name]
# 类型不一致:清理 module.globals + module.scope._useset 后重建,
# 避免 DuplicatedNameError。必须同时清 existing_func.name(真名)
# 与 func_name(字典键):别名场景下两者不同(如
# Gen.functions['ExprGenAttr.find_global_in_module'] 指向名为
# 'dddc595175f00c66.ExprGenAttr.find_global_in_module' 的对象),
# 只删真名会留下字典键仍占住 module.scope(llvmlite 按名字去重)
# → 重建仍抛 DuplicatedNameError(报文只有名字,不含冲突双方)。
module_scope = getattr(Gen.module, 'scope', None)
if getattr(module_scope, '_useset', None):
module_scope._useset.discard(old_name)
func = ir.Function(Gen.module, func_type, name=func_name)
_useset = getattr(module_scope, '_useset', None)
for _stale_name in {existing_func.name, func_name}:
_stale = Gen.module.globals.get(_stale_name)
if isinstance(_stale, ir.Function):
del Gen.module.globals[_stale_name]
if _useset is not None:
_useset.discard(_stale_name)
func = Gen._CreateOrReuseFuncDecl(func_name, func_type)
Gen.functions[func_name] = func
if '.' in func_name:
short_name = func_name.split('.', 1)[1]
+10
View File
@@ -892,6 +892,16 @@ class BaseGenMixin:
真因,绝不静默复用错签名(会让调用点按错签名传参)。
"""
existing: Any = self.module.globals.get(name)
if existing is not None and not isinstance(existing, ir.Function):
# 名字被非函数 global 占住(全局变量 / vtable / 字符串常量…):llvmlite 的
# NameScope 按名字去重,再建同名 ir.Function 必抛 DuplicatedNameError
# (报文只有名字,不含冲突类型)⇒ 在此把冲突双方摊开,避免裸抛。
from lib.core.LLVMCG.FuncGen import FailFastError
raise FailFastError(
f"同名全局符号冲突:'{name}' 已在 module.globals 中登记,但不是函数"
f"({type(existing).__name__}):{existing!r}"
f"\n 函数声明无法复用该符号(llvmlite NameScope 按名字去重);"
f"请核对该名字是否同时被用作全局变量 / vtable / 字符串常量。")
if isinstance(existing, ir.Function):
_old_type: Any = getattr(existing, 'ftype', None)
if _old_type is None or str(_old_type) == str(func_type):