fix(gen): 裸名 CArray 类字段解析 + 跨模块函数返回类型 vpi 解析

- TypeGenMixin/HandlesAnnAssign: 支持 from t import * 后裸名 CArray[...]
  (单参→Ptr(elem),双参→Array),修复 T4K3 类字段 CArray[str]=[...]
  退化为 list[str] 动态构造 → list[str] 特化/append/_mbuddy 链接 undefined
- CallGenMixin: 跨模块函数调用返回类型按 vpi stub 注解解析
  (tuple[T1,T2]→{T1,T2}),修复 b2a_hqx tuple 解包失效
This commit is contained in:
TermiNexus committed 2026-08-12 19:12:45 +08:00
1 parent 304bece6fa
commit b232c9266c
3 files changed
+91 -12

No files matched your search

+32 -1
View File
@@ -6102,7 +6102,38 @@ class CallGenMixin:
if found_ret_ty is not None:
call_ret_ty: llvmlite.LLVMType | t.CPtr = found_ret_ty
else:
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name)
# found_func=None(跨模块 stub 未注入):先从 vpi stub 返回注解
# 缓存精确获取返回类型(含 tuple[T1,T2] → 匿名结构体,如
# b2a_hqx -> tuple[str, t.CUnsignedShort] → {i8*, i16}),
# 未命中才回退 _infer_external_func_ret_ty 白名单推断。
# 根因:_infer 白名单无 tuple 概念,未知函数默认 i32 会截断
# 指针/i64;b2a_hqx 被推断为单值 i8*/i32 后调用侧返回值类型
# 与定义侧 {i8*, i16} 不匹配 → HandlesAssign 的 tuple 解包
# (依赖 rhs_val.Ty 是 Struct)静默跳过 → 目标变量从未赋值 →
# free 未初始化栈垃圾指针崩溃(HashLibTest hqx 段)。
vpi_ret_ty: llvmlite.LLVMType | t.CPtr = None
if cl.func is not None and cl.func.kind() == ast.ASTKind.Attribute:
at_ret: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func)
if at_ret.value is not None and at_ret.value.kind() == ast.ASTKind.Name:
mod_ret_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_ret.value)
if mod_ret_nm.id is not None:
mod_ret_sha1: str = self._lookup_module_sha1(mod_ret_nm.id)
if mod_ret_sha1 is not None:
# 函数内懒 import:VpiReader imports HandlesExprCall,
# 顶层 import 会成环(与 _lookup_method_ret_ty_via_vpi 一致)
import lib.StubGen.VpiReader as VpiReader
VpiReader._ensure_vpi_ret_annot_loaded(mod_ret_sha1, self.TempDir)
ret_annot_fc: str = VpiReader._lookup_vpi_ret_annot(
mod_ret_sha1, None, func_name)
if ret_annot_fc is not None:
vpi_ret_ty = VpiReader._resolve_vpi_ret_annot_str(
pool, ret_annot_fc,
llvmlite.Int32(pool),
llvmlite.Ptr(pool, llvmlite.Int8(pool)))
if vpi_ret_ty is not None:
call_ret_ty = vpi_ret_ty
else:
call_ret_ty = self._infer_external_func_ret_ty(pool, func_name)
# 如果模块中找不到函数,检查是否是变量(函数指针 / 闭包调用)
if found_func is None:
+36
View File
@@ -1955,6 +1955,42 @@ class TypeGenMixin:
if sub.value.kind() == ast.ASTKind.Name:
gen_nm: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if gen_nm.id is not None:
# 裸名 CArray(from t import * 场景,如 core.vp 的
# `__python_suffix__: CArray[str] = ["py", ...]`):与 t.CArray
# 语义等价。否则走泛型模板查找失败(CArray 不是泛型类模板)→
# 字段类型退化 Ptr(i8) → __before_init__ 默认值 List 字面量误走
# 动态 list 构造(list[str].__new__/append 符号未生成 → 链接
# undefined reference,Termination4K3 链接失败根因)。
if string.strcmp(gen_nm.id, "CArray") == 0:
# 单参数: CArray[elem_ty] → Ptr(elem_ty)(静态推导长度数组)
if sub.slice.kind() != ast.ASTKind.Tuple:
elem_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, sub.slice, imported_modules, from_imports, trans)
if elem_n is None:
return None
return llvmlite.Ptr(pool, elem_n)
# 双参数: CArray[elem_ty, count] → [count x elem_ty]
tup_n: ast.Tuple | t.CPtr = (ast.Tuple | t.CPtr)(sub.slice)
if tup_n.elts is None or tup_n.elts.__len__() < 2:
return None
elem_node_n: ast.AST | t.CPtr = tup_n.elts.get(0)
count_node_n: ast.AST | t.CPtr = tup_n.elts.get(1)
if elem_node_n is None or count_node_n is None:
return None
elem_ty_n: llvmlite.LLVMType | t.CPtr = self.resolve_annotation_type(
pool, elem_node_n, imported_modules, from_imports, trans)
if elem_ty_n is None:
return None
# count=None → Ptr(elem)(等价单参)
if count_node_n.kind() == ast.ASTKind.Constant:
cnt_n: ast.Constant | t.CPtr = (ast.Constant | t.CPtr)(count_node_n)
if cnt_n.const_kind == ast.CONST_NONE:
return llvmlite.Ptr(pool, elem_ty_n)
count_n: int = self._eval_const_int_expr(
count_node_n, pool, from_imports, imported_modules)
if self.ConstEvalOk == 0 or count_n <= 0:
return None
return llvmlite.Array(pool, elem_ty_n, count_n)
return self._resolve_generic_subscript_ty(pool, sub, gen_nm.id, trans,
imported_modules, from_imports)
# 检查 value 是否是 Attribute(t.CArray / t.Callable / 模块限定泛型类如 numpy.ndarray[T])
+23 -11
View File
@@ -171,23 +171,35 @@ def _is_ptr_element_annotation(annot: ast.AST | t.CPtr) -> int:
# "单参数 list[type] → 指针,但如果有列表字面量初始化值则按数组 list[type, N] 处理")。
# ============================================================
def _is_single_param_carray_annot(annot: ast.AST | t.CPtr) -> int:
"""检查注解是否为单参/None 长度 t.CArray[...],返回 1=是 0=否"""
"""检查注解是否为单参/None 长度 t.CArray[...],返回 1=是 0=否
支持两种书写形式:
1. 模块限定 t.CArray[...](Attribute value)
2. 裸名 CArray[...](from t import * 后使用,如 Termination4K3 core.vp
的 `__python_suffix__: CArray[str] = ["py", ...]`)
"""
if annot is None or annot.kind() != ast.ASTKind.Subscript:
return 0
sub: ast.Subscript | t.CPtr = (ast.Subscript | t.CPtr)(annot)
if sub.value is None or sub.slice is None:
return 0
if sub.value.kind() != ast.ASTKind.Attribute:
if sub.value.kind() == ast.ASTKind.Attribute:
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub.value)
if at.attr is None or string.strcmp(at.attr, "CArray") != 0:
return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
mv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mv.id is None or string.strcmp(mv.id, "t") != 0:
return 0
elif sub.value.kind() == ast.ASTKind.Name:
# 裸名 CArray(from t import *):与 t.CArray 等价
bn: ast.Name | t.CPtr = (ast.Name | t.CPtr)(sub.value)
if bn.id is None or string.strcmp(bn.id, "CArray") != 0:
return 0
else:
return 0
at: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(sub.value)
if at.attr is None or string.strcmp(at.attr, "CArray") != 0:
return 0
if at.value is None or at.value.kind() != ast.ASTKind.Name:
return 0
mv: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at.value)
if mv.id is None or string.strcmp(mv.id, "t") != 0:
return 0
# 单参形式:slice 非 Tuple(如 t.CArray[u64])
# 单参形式:slice 非 Tuple(如 t.CArray[u64] / CArray[str])
if sub.slice.kind() != ast.ASTKind.Tuple:
return 1
# 双参形式:count=None(t.CArray[u64, None])→ 等价单参