fix(gen): 嵌套 def 的返回类型跟随 -> T 注解(原先写死 i32)

现场:def Inner(p: char*) -> char*: return p 报
CastError: implicit pointer-to-integer conversion is forbidden ...
—— 因为 translate_nested_function_def 把提升后的闭包函数写死成
define i32 @__closure_Inner(i8* %env, i8* %p)(grep 过整个文件,从未读 fd.returns),
return p 于是要把 char* 隐式转 i32。即"嵌套 def 只支持 int 返回",此前被同文件
更早的用例报错掩盖(EscapeMethodTest 的 (A)/(B) 因同命裁剪静音后才露出来)。

改动:
- 提升函数按 fd.returns 解析返回类型(resolve_annotation_type);解析不出 -> fail-fast。
  边界:fd.returns 为 None 或注解是 None / t.CVoid 时**保持 i32**(不改既有 ABI,
  避免打断现有无返回值/无注解的嵌套 def)。
- 隐式收尾的零值改为 ConstZero(ret_ty)(原写死 const_int32(0),签名一变 llc 会拒绝),
  取不到零值则 fail-fast。

已知未覆盖:lambda 的提升路径同样写死 i32(见 FuncGenMixin 中 lambda 分支),
本轮不动(lambda 无返回注解可跟随)。
This commit is contained in:
TermiNexus committed 2026-10-10 10:40:36 +08:00
1 parent 41674d125c
commit 5d1e604aac
1 file changed
+36 -4
+36 -4
View File
@@ -47,8 +47,35 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if promoted_name is not None: viperlib.snprintf(promoted_name, 64, "__closure_%s", func_name)
mangled_promoted: str = self.Gen.FuncGenBase._mangle_name(trans, promoted_name)
# 2. Create the promoted function: define i32 @__closure_{name}(i8* %env, args...)
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled_promoted, i32_ty)
# 2. Create the promoted function: define <ret> @__closure_{name}(i8* %env, args...)
# 返回类型**跟随 `-> T` 注解**。原实现写死 i32:于是 `def Inner(p: char*) -> char*: return p`
# 生成 `define i32 @__closure_Inner(i8* %env, i8* %p)`,而 `return p` 要把 char* 隐式转 i32
# → 撞 coerce_to_type 的 CastError(现场:EscapeMethodTest 的 (C) 用例)。那是"嵌套 def
# 只支持 int 返回"这个预先存在的缺口,此前被同文件更早的用例报错掩盖。
# 约定(最小改动的边界):
# · `fd.returns is None` 或注解是 `None` / `t.CVoid` → **保持 i32**(不改既有 ABI,
# 避免打断现有"无返回值/无注解"的嵌套 def;这是已知缺口,不在本次范围内);
# · 其余注解采用 resolve_annotation_type 的结果;解析不出 → fail-fast,不静默兜底。
ret_ty_n: mlirlite.LLVMType* = i32_ty
if fd.returns is not None:
keep_i32_n: int = 0
if fd.returns.kind() == ast.ASTKind.Constant:
rc_n: ast.Constant* = ast.Constant*(fd.returns)
if rc_n is not None and rc_n.const_kind == ast.CONST_NONE: keep_i32_n = 1
if self.Gen.TypeGenResolve._is_cvoid_marker(fd.returns, trans._imported_modules) != 0: keep_i32_n = 1
if keep_i32_n == 0:
rt_res_n: mlirlite.LLVMType* = self.Gen.TypeGenResolve.resolve_annotation_type(pool, fd.returns, trans._imported_modules, trans._from_imports, trans, 0)
if rt_res_n is None:
self.Gen.TypeGenErr.fatal_error(fd, f"nested function '{fd.name}': cannot resolve the return type annotation for the promoted closure", "TypeError")
else:
rt_is_void_n: int = 0
match rt_res_n:
case mlirlite.LLVMType.Void():
rt_is_void_n = 1
case _:
pass
if rt_is_void_n == 0: ret_ty_n = rt_res_n
func: mlirlite.Function* = mlirlite.create_function(pool, mod, mangled_promoted, ret_ty_n)
if func is None: return 0
mlirlite.add_param(pool, func, i8_ptr_ty, "%env")
@@ -225,8 +252,13 @@ novirtual class FuncGenMixin(CallGenBase.CallGenBase):
if last_is_return == 0:
# M3:隐式收尾也是函数出口,先释放本嵌套函数登记的 slife
self.Gen.VarGen._SlifeEmitFrees(pool, func_builder, mod, trans)
zero_val: mlirlite.Value* = mlirlite.const_int32(pool, 0)
mlirlite.build_ret(func_builder, zero_val)
# 零值必须与**本函数的返回类型**一致(原实现写死 const_int32(0):一旦返回类型改为
# `char*` 之类,`ret i32 0` 与签名不符,llc 会直接拒绝)。ret_ty_n 见第 2 步。
zero_val: mlirlite.Value* = mlirlite.ConstZero(pool, ret_ty_n)
if zero_val is None:
self.Gen.TypeGenErr.fatal_error(fd, f"nested function '{fd.name}': flow falls off the end without an implicit return and the return type has no zero value", "TypeError")
else:
mlirlite.build_ret(func_builder, zero_val)
# M3:弹回外层登记区间(释放已在本嵌套函数各出口完成)
self.Gen.VarGen._SlifeRestore(old_slife_base)