From 09833a80ebfa3d4582575ab39da50c4a53048680 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 8 Aug 2026 01:12:40 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20t.Callable=20?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E6=8C=87=E9=92=88=E7=B1=BB=E5=9E=8B=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E3=80=81=E8=B5=8B=E5=80=BC=E4=B8=8E=E9=97=B4=E6=8E=A5?= =?UTF-8?q?=E8=B0=83=E7=94=A8=EF=BC=88=E5=90=AB=E8=BF=94=E5=9B=9E=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E7=AD=BE=E5=90=8D=E7=BC=93=E5=AD=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App/lib/core/Handles/HandlesAnnAssign.vp | 6 + App/lib/core/Handles/HandlesExprCall.vp | 166 ++++++++++++++++++++++- App/lib/core/Handles/HandlesType.vp | 50 ++++++- 3 files changed, 212 insertions(+), 10 deletions(-) diff --git a/App/lib/core/Handles/HandlesAnnAssign.vp b/App/lib/core/Handles/HandlesAnnAssign.vp index 706cfa8..68f0581 100644 --- a/App/lib/core/Handles/HandlesAnnAssign.vp +++ b/App/lib/core/Handles/HandlesAnnAssign.vp @@ -423,6 +423,12 @@ class AnnAssignHandle(HandlesBase.Mixin): "HandlesAnnAssign") var_ty = llvmlite.Int32(pool) + # t.Callable 函数指针:注册返回类型签名(供函数指针间接调用精确返回类型) + callable_ret: llvmlite.LLVMType | t.CPtr = HandlesType.try_extract_callable_ret( + pool, annot, self.Trans._imported_modules, self.Trans._from_imports, self.Trans) + if callable_ret is not None: + HandlesExprCall.register_func_ptr_sig(nm.id, callable_ret) + # global 变量:写入模块作用域中的全局变量 if HT.is_global_name(self.Trans, nm.id) != 0: mod_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_module_var( diff --git a/App/lib/core/Handles/HandlesExprCall.vp b/App/lib/core/Handles/HandlesExprCall.vp index fe3ccbd..264f6a0 100644 --- a/App/lib/core/Handles/HandlesExprCall.vp +++ b/App/lib/core/Handles/HandlesExprCall.vp @@ -5394,11 +5394,16 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr, else: call_ret_ty = _infer_external_func_ret_ty(pool, func_name) - # 如果模块中找不到函数,检查是否是变量(闭包调用) + # 如果模块中找不到函数,检查是否是变量(函数指针 / 闭包调用) if found_func is None: - closure_ptr: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, func_name) - if closure_ptr is not None: - return translate_closure_call(pool, builder, mod, closure_ptr, + fp_alloca: llvmlite.Value | t.CPtr = HandlesVar.lookup_var(trans.SymTab, func_name) + if fp_alloca is not None: + # 函数指针变量(t.Callable 注解 → i8** alloca):按实参构造签名间接调用 + if _is_func_ptr_alloca(fp_alloca) != 0: + return _translate_func_ptr_call(pool, builder, mod, fp_alloca, + func_name, cargs, can, trans) + # 闭包变量(i8* alloca):走闭包调用路径 + return translate_closure_call(pool, builder, mod, fp_alloca, cargs, can) # 翻译参数并链接 @@ -5756,6 +5761,159 @@ def translate_closure_call(pool: memhub.MemBuddy | t.CPtr, env_ptr.Next = None return llvmlite.build_call_indirect(builder, fn_typed, env_ptr, 1, i32_ty) +# ============================================================ +# _is_func_ptr_alloca - 判断变量 alloca 是否为函数指针变量 +# +# 函数指针变量(t.Callable 注解 → i8*)的 alloca 类型是 Ptr(Ptr(i8)) = i8**; +# 闭包变量 alloca 是 i8*(Ptr(i8))。通过类型层级区分。 +# ============================================================ +def _is_func_ptr_alloca(alloca: llvmlite.Value | t.CPtr) -> int: + """判断 alloca 是否为函数指针变量(类型 Ptr(Ptr(i8))),1=是 / 0=否""" + if alloca is None or alloca.Ty is None: + return 0 + if HandlesExpr.is_ptr_type(alloca.Ty) == 0: + return 0 + pointee: llvmlite.LLVMType | t.CPtr = alloca.Ty.Pointee + if pointee is None or HandlesExpr.is_ptr_type(pointee) == 0: + return 0 + inner: llvmlite.LLVMType | t.CPtr = pointee.Pointee + if inner is None or HandlesExpr.is_ptr_type(inner) != 0: + return 0 + return 1 + +# ============================================================ +# 函数指针变量签名表(变量名 → 返回类型) +# +# AnnAssign 处理 t.Callable 注解时注册,函数指针间接调用时 +# 按注册的返回类型构造签名(而非默认 i32)。 +# ============================================================ +MAX_FUNC_PTR_SIGS: t.CDefine = 64 +_g_funcptr_names: t.CChar | t.CPtr = None +_g_funcptr_rets: t.CUInt64T | t.CPtr = None +_g_funcptr_count: int = 0 + + +def _ensure_funcptr_sigs() -> int: + """确保函数指针签名表已分配(stdlib.malloc,懒初始化)""" + global _g_funcptr_names, _g_funcptr_rets + if _g_funcptr_names is not None: + return 1 + _g_funcptr_names = stdlib.malloc(64 * MAX_FUNC_PTR_SIGS) + _g_funcptr_rets = stdlib.malloc(8 * MAX_FUNC_PTR_SIGS) + if _g_funcptr_names is None or _g_funcptr_rets is None: + return 0 + string.memset(_g_funcptr_names, 0, 64 * MAX_FUNC_PTR_SIGS) + return 1 + + +def register_func_ptr_sig(name: str, + ret_ty: llvmlite.LLVMType | t.CPtr) -> int: + """注册函数指针变量的返回类型签名,0 成功""" + global _g_funcptr_count + if name is None or ret_ty is None: + return 1 + if _ensure_funcptr_sigs() == 0: + return 1 + if _g_funcptr_count >= MAX_FUNC_PTR_SIGS: + return 1 + idx: t.CSizeT = t.CSizeT(_g_funcptr_count) * 64 + string.strcpy(_g_funcptr_names + idx, name) + _g_funcptr_rets[_g_funcptr_count] = t.CUInt64T(ret_ty) + _g_funcptr_count += 1 + return 0 + + +def lookup_func_ptr_ret(name: str) -> llvmlite.LLVMType | t.CPtr: + """查函数指针变量的返回类型,None=未注册(调用时默认 i32)""" + if name is None or _g_funcptr_names is None: + return None + for i in range(_g_funcptr_count): + idx: t.CSizeT = t.CSizeT(i) * 64 + if string.strcmp(_g_funcptr_names + idx, name) == 0: + return (llvmlite.LLVMType | t.CPtr)( + t.CVoid(_g_funcptr_rets[i], t.CPtr)) + return None + + +# ============================================================ +# _translate_func_ptr_call - 函数指针变量间接调用 +# +# func_p: t.Callable[[A, B], Ret] 后调用 func_p(a, b): +# 1. load 函数地址 (i8*) +# 2. 翻译实参并链接为 Value 链表 +# 3. 按实参类型 + 注册的返回类型构造签名(未注册时默认 i32) +# 4. bitcast i8* → 函数指针类型,间接调用 +# ============================================================ +def _translate_func_ptr_call(pool: memhub.MemBuddy | t.CPtr, + builder: llvmlite.IRBuilder | t.CPtr, + mod: llvmlite.LLVMModule | t.CPtr, + fp_alloca: llvmlite.Value | t.CPtr, + func_name: str, + cargs: list[ast.AST | t.CPtr] | t.CPtr, + can: t.CSizeT, + trans: HT.Translator | t.CPtr) -> llvmlite.Value | t.CPtr: + """函数指针变量间接调用,返回结果 Value""" + if builder is None or fp_alloca is None: + return None + i8_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int8(pool) + i8_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, i8_ty) + i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool) + + # 1. load 函数地址 (i8*) + fn_i8: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, i8_ptr_ty, fp_alloca) + if fn_i8 is None: + return None + + # 2. 翻译实参并链接为 Value 链表 + head: llvmlite.Value | t.CPtr = None + tail: llvmlite.Value | t.CPtr = None + arg_count: int = 0 + ai: t.CSizeT + for ai in range(can): + arg: ast.AST | t.CPtr = cargs.get(ai) + if arg is not None: + arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value( + builder, pool, mod, arg, None, 0, trans) + if arg_val is not None: + arg_val.Next = None + if head is None: + head = arg_val + else: + tail.Next = arg_val + tail = arg_val + arg_count += 1 + + # 3. 按实参类型 + 注册的返回类型构造签名(未注册返回类型时默认 i32) + ret_ty: llvmlite.LLVMType | t.CPtr = lookup_func_ptr_ret(func_name) + if ret_ty is None: + ret_ty = i32_ty + fp_first: llvmlite.ParamNode | t.CPtr = None + fp_prev: llvmlite.ParamNode | t.CPtr = None + cur_v: llvmlite.Value | t.CPtr = head + for pi in range(arg_count): + if cur_v is None: + break + cur_ty: llvmlite.LLVMType | t.CPtr = llvmlite.value_get_ty(cur_v) + pn2: llvmlite.ParamNode | t.CPtr = llvmlite.new_param_node(pool, cur_ty) + if pn2 is not None: + if fp_first is None: + fp_first = pn2 + if fp_prev is not None: + llvmlite.paramnode_set_next(fp_prev, pn2) + fp_prev = pn2 + cur_v = llvmlite.value_get_next(cur_v) + + func_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Func(pool, ret_ty, fp_first, arg_count) + if func_ty is None: + return None + func_ptr_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Ptr(pool, func_ty) + + # 4. bitcast i8* → 函数指针类型,间接调用 + fn_typed: llvmlite.Value | t.CPtr = llvmlite.build_bitcast(builder, fn_i8, func_ptr_ty) + if fn_typed is None: + return None + return llvmlite.build_call_indirect(builder, fn_typed, head, arg_count, ret_ty) + # ============================================================ # 翻译 printf 调用 — 模块级版本 # ============================================================ diff --git a/App/lib/core/Handles/HandlesType.vp b/App/lib/core/Handles/HandlesType.vp index e25de0c..b557911 100644 --- a/App/lib/core/Handles/HandlesType.vp +++ b/App/lib/core/Handles/HandlesType.vp @@ -885,6 +885,38 @@ def fatal_type_error(node: ast.AST | t.CPtr, msg: str, """以 Python 风格报错并退出(TypeError 版本)""" fatal_error(node, msg, "TypeError", col_start, col_end) + +# ============================================================ +# try_extract_callable_ret - 提取 t.Callable 注解的返回类型 +# +# t.Callable[[A, B], Ret] → Ret 的 LLVMType; +# 非 Callable 注解返回 None。供函数指针变量签名缓存使用。 +# ============================================================ +def try_extract_callable_ret(pool: memhub.MemBuddy | t.CPtr, + annot: ast.AST | t.CPtr, + imported_modules: str, + from_imports: str, + trans: HT.Translator | t.CPtr) -> llvmlite.LLVMType | t.CPtr: + """提取 t.Callable 注解的返回类型 LLVMType;非 Callable 注解返回 None""" + if annot is None or annot.kind() != ast.ASTKind.Subscript: + return None + sub_cr: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot) + if sub_cr.value is None or sub_cr.value.kind() != ast.ASTKind.Attribute: + return None + at_cr: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub_cr.value) + if at_cr.attr is None or string.strcmp(at_cr.attr, "Callable") != 0: + return None + # slice 必须是 Tuple(elts=[args_list, ret_node]) + if sub_cr.slice is None or sub_cr.slice.kind() != ast.ASTKind.Tuple: + return None + tp_cr: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub_cr.slice) + if tp_cr.elts is None or tp_cr.elts.__len__() != 2: + return None + ret_node_cr: ast.AST | t.CPtr = tp_cr.elts.get(1) + if ret_node_cr is None: + return None + return resolve_annotation_type(pool, ret_node_cr, imported_modules, from_imports, trans) + # ============================================================ # 将 t.CXxx 类型名映射到 LLVMType(纯函数) # ============================================================ @@ -1792,19 +1824,25 @@ def resolve_annotation_type(pool: memhub.MemBuddy | t.CPtr, return gen_spec_ty # 未特化:返回 None(不报错,让后续流程处理) return None - # 检查 value 是否是 Attribute(Name('t'), 'CArray') + # 检查 value 是否是 Attribute(Name('t'), 'CArray'/'Callable') if sub.value.kind() != ast.ASTKind.Attribute: - fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray)") + fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)") arr_at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub.value) if arr_at.attr is None: fatal_type_error(node, "Subscript Attribute 缺少 attr") - if string.strcmp(arr_at.attr, "CArray") != 0: - fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray)") if arr_at.value is None or arr_at.value.kind() != ast.ASTKind.Name: - fatal_type_error(node, "t.CArray 必须通过模块名限定") + fatal_type_error(node, "t.CArray / t.Callable 必须通过模块名限定") arr_val_name: ast.Name | t.CPtr = (ast.Name | t.CPtr)(arr_at.value) if arr_val_name.id is None: - fatal_type_error(node, "t.CArray 模块名为 None") + fatal_type_error(node, "Subscript 模块名为 None") + # 函数指针类型: t.Callable[[A, B], Ret] → i8* + # 与 TPV 函数传指针惯例一致(函数值 bitcast 为 i8* 存储,调用时按实参重建签名) + if string.strcmp(arr_at.attr, "Callable") == 0: + if HandlesImports.is_module_imported(imported_modules, arr_val_name.id) == 0: + fatal_type_error(node, "t.Callable 模块未导入") + return llvmlite.Ptr(pool, llvmlite.Int8(pool)) + if string.strcmp(arr_at.attr, "CArray") != 0: + fatal_type_error(node, "不支持的 Subscript 类型注解(仅支持 t.CArray / t.Callable)") if HandlesImports.is_module_imported(imported_modules, arr_val_name.id) == 0: fatal_type_error(node, "t.CArray 模块未导入") # 单参数形式: t.CArray[elem_ty] → Ptr(elem_ty)