fix: 泛型返回类型短路/数组迭代/非工作区崩溃
- CallGenMixin: 本模块 mangled 调用从当前 IR 取返回类型, 不查自己的 text.ll(未生成误告警) - HandlesFor: Ptr([N x T]) 数组退化指针 load 后走数组迭代 - Config: 配置字段默认 None/0, 避免垃圾指针通过 is not None 检查 - main: 无 project 配置且无 --src 时 fail-fast 退出(非工作区崩溃)
This commit is contained in:
1 parent
e7b0fecb99
commit
a649e4807d
4 files changed
+87
-53
No files matched your search
@@ -19,23 +19,26 @@ import w32.fileio as fileio
|
||||
_mbuddy: memhub.MemManager | t.CPtr
|
||||
|
||||
# 配置值(从 project.vpj 加载)
|
||||
SourceDir: str
|
||||
BuildDir: str
|
||||
TempDir: str # = {BuildDir}/temp(自动计算)
|
||||
OutputDir: str # = {BuildDir}/output(自动计算)
|
||||
ProjectName: str
|
||||
ProjectVersion: str
|
||||
CompilerCmd: str
|
||||
CompilerFlags: str # 编译器参数(空格分隔,从 compiler.flags 数组拼接)
|
||||
LinkerCmd: str
|
||||
LinkerFlags: str # 链接器参数(空格分隔,从 linker.flags 数组拼接)
|
||||
LinkerOutput: str
|
||||
TargetTriple: str
|
||||
TargetDataLayout: str
|
||||
Sha1SliceLevel: t.CInt # SHA1 切片层数(目录分散等级)
|
||||
StrictMode: t.CInt
|
||||
IncludesDir: str # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
|
||||
IncludesDirs: str # 全部 includes 目录(空格分隔,多 includes 目录支持)
|
||||
# 默认 None/0:未加载 project.vpj 时字段为 null,调用方 is not None
|
||||
# 检查才能正确兜底(否则垃圾指针通过检查 → 用非法路径 → 崩溃,
|
||||
# 尤其非工作区执行无配置时)。
|
||||
SourceDir: str = None
|
||||
BuildDir: str = None
|
||||
TempDir: str = None # = {BuildDir}/temp(自动计算)
|
||||
OutputDir: str = None # = {BuildDir}/output(自动计算)
|
||||
ProjectName: str = None
|
||||
ProjectVersion: str = None
|
||||
CompilerCmd: str = None
|
||||
CompilerFlags: str = None # 编译器参数(空格分隔,从 compiler.flags 数组拼接)
|
||||
LinkerCmd: str = None
|
||||
LinkerFlags: str = None # 链接器参数(空格分隔,从 linker.flags 数组拼接)
|
||||
LinkerOutput: str = None
|
||||
TargetTriple: str = None
|
||||
TargetDataLayout: str = None
|
||||
Sha1SliceLevel: t.CInt = 0 # SHA1 切片层数(目录分散等级)
|
||||
StrictMode: t.CInt = 0
|
||||
IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景)
|
||||
IncludesDirs: str = None # 全部 includes 目录(空格分隔,多 includes 目录支持)
|
||||
|
||||
|
||||
def Load_project_config(path: str) -> int:
|
||||
|
||||
@@ -6291,45 +6291,59 @@ class CallGenMixin:
|
||||
string.strncpy(cf_sha1, call_name, 16)
|
||||
cf_sha1[16] = '\0'
|
||||
string.strcpy(cf_bare, cf_last + 1)
|
||||
cf_ret: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
|
||||
pool, cf_sha1, None, cf_bare, 1)
|
||||
if cf_ret is not None:
|
||||
call_ret_ty = cf_ret
|
||||
# 本模块自身函数(泛型特化等):从当前 IR 已声明的函数
|
||||
# 取返回类型;不走跨模块 text.ll 查找——自己的 text.ll
|
||||
# 在 Phase A 翻译中尚未生成,直接查会误报"依赖未生成"
|
||||
# 并保持默认类型(GenericTest 泛型 add/combine 等警告
|
||||
# 根因)。未命中(特化尚未声明)保持默认推断。
|
||||
is_local_mod: int = 0
|
||||
if trans is not None and trans.ModuleSha1 is not None:
|
||||
if string.strncmp(cf_sha1, trans.ModuleSha1, 16) == 0:
|
||||
is_local_mod = 1
|
||||
if is_local_mod != 0:
|
||||
local_fn: llvmlite.Function | t.CPtr = self.find_func_in_module(mod, call_name)
|
||||
if local_fn is not None and local_fn.RetTy is not None:
|
||||
call_ret_ty = local_fn.RetTy
|
||||
else:
|
||||
cf_vpi: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi(
|
||||
pool, cf_sha1, None, cf_bare)
|
||||
if cf_vpi is not None:
|
||||
call_ret_ty = cf_vpi
|
||||
cf_ret: llvmlite.LLVMType | t.CPtr = self._LookupMethodRetTyFromTextLL(
|
||||
pool, cf_sha1, None, cf_bare, 1)
|
||||
if cf_ret is not None:
|
||||
call_ret_ty = cf_ret
|
||||
else:
|
||||
# 区分两种失败场景:
|
||||
# 1. 依赖模块 text.ll 存在但函数未命中 → 真错误,fail-fast
|
||||
# 2. 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按
|
||||
# 文件顺序翻译,main.vp 先于其 import 的 mbuddytest.vp
|
||||
# (同项目多源文件)。此时标记 trans._deps_pending=1,
|
||||
# 保持白名单类型继续,Phase A 多遍循环下遍重译——
|
||||
# 届时依赖已生成 text.ll,可精确解析(治本:依赖先翻译)。
|
||||
if self._dep_text_ll_exists(pool, cf_sha1) != 0:
|
||||
cf_err: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if cf_err is not None:
|
||||
viperlib.snprintf(
|
||||
cf_err, 1024,
|
||||
"跨模块函数 '%s' 返回类型无法精确获取(text.ll/vpi 均未命中),"
|
||||
"默认 i32 会截断指针/i64,请检查依赖模块是否已编译/注册", call_name)
|
||||
VLogger.error(cf_err, "HandlesExprCall")
|
||||
HandlesType.fatal_error(
|
||||
node,
|
||||
f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}",
|
||||
"TypeError")
|
||||
cf_vpi: llvmlite.LLVMType | t.CPtr = self._lookup_method_ret_ty_via_vpi(
|
||||
pool, cf_sha1, None, cf_bare)
|
||||
if cf_vpi is not None:
|
||||
call_ret_ty = cf_vpi
|
||||
else:
|
||||
if trans is not None:
|
||||
trans._deps_pending = 1
|
||||
cf_dbg: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if cf_dbg is not None:
|
||||
viperlib.snprintf(
|
||||
cf_dbg, 1024,
|
||||
"跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序),"
|
||||
"标记重试,本遍保持默认类型", call_name)
|
||||
VLogger.warning(cf_dbg, "HandlesExprCall")
|
||||
# 区分两种失败场景:
|
||||
# 1. 依赖模块 text.ll 存在但函数未命中 → 真错误,fail-fast
|
||||
# 2. 依赖模块 text.ll 不存在(模块尚未翻译)→ Phase A 按
|
||||
# 文件顺序翻译,main.vp 先于其 import 的 mbuddytest.vp
|
||||
# (同项目多源文件)。此时标记 trans._deps_pending=1,
|
||||
# 保持白名单类型继续,Phase A 多遍循环下遍重译——
|
||||
# 届时依赖已生成 text.ll,可精确解析(治本:依赖先翻译)。
|
||||
if self._dep_text_ll_exists(pool, cf_sha1) != 0:
|
||||
cf_err: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if cf_err is not None:
|
||||
viperlib.snprintf(
|
||||
cf_err, 1024,
|
||||
"跨模块函数 '%s' 返回类型无法精确获取(text.ll/vpi 均未命中),"
|
||||
"默认 i32 会截断指针/i64,请检查依赖模块是否已编译/注册", call_name)
|
||||
VLogger.error(cf_err, "HandlesExprCall")
|
||||
HandlesType.fatal_error(
|
||||
node,
|
||||
f"跨模块函数返回类型无法精确获取(text.ll/vpi 均未命中),默认 i32 会截断指针: {call_name}",
|
||||
"TypeError")
|
||||
else:
|
||||
if trans is not None:
|
||||
trans._deps_pending = 1
|
||||
cf_dbg: t.CChar | t.CPtr = VLogger.fmt_buf()
|
||||
if cf_dbg is not None:
|
||||
viperlib.snprintf(
|
||||
cf_dbg, 1024,
|
||||
"跨模块函数 '%s' 依赖模块 text.ll 尚未生成(Phase A 顺序),"
|
||||
"标记重试,本遍保持默认类型", call_name)
|
||||
VLogger.warning(cf_dbg, "HandlesExprCall")
|
||||
stdlib.free(cf_sha1)
|
||||
stdlib.free(cf_bare)
|
||||
# 跨模块调用:创建 declare 声明,避免 llc 报 undefined value
|
||||
|
||||
@@ -396,6 +396,13 @@ class ForHandle(HandlesBase.Mixin):
|
||||
if elem_ty is None:
|
||||
HandlesType.fatal_error(for_node, "指针迭代: 无法获取元素类型")
|
||||
|
||||
# 元素类型是数组(Ptr([N x T]),如 t.CArray 变量退化指针):
|
||||
# load 出数组值后走数组迭代(按长度,非 null 终止)
|
||||
if HandlesExpr.is_array_type(elem_ty) != 0:
|
||||
arr_v2: llvmlite.Value | t.CPtr = llvmlite.build_load(builder, elem_ty, ptr_val)
|
||||
if arr_v2 is not None:
|
||||
return self._handle_array_iter(for_node, var_name, arr_v2)
|
||||
|
||||
# 元素类型必须是整数(用于 icmp eq 0 检查 null 终止符)
|
||||
elem_bits: int = HandlesExpr.get_llvm_type_bits(elem_ty)
|
||||
if elem_bits == 0:
|
||||
|
||||
+10
@@ -369,6 +369,16 @@ def main() -> int:
|
||||
# AST 解析:如果指定了 --src 或 --project,读取文件并解析为 AST 树
|
||||
src_path: str = args.get_str("src")
|
||||
|
||||
# 无 project 配置且未指定 --src:无法构建,fail-fast 退出。
|
||||
# (非工作区执行时 CWD 无 project.vpj,proj_loaded=0;若继续执行,
|
||||
# 未初始化的 Config 字段可能为垃圾指针 → is not None 检查通过 →
|
||||
# 用非法路径 → 崩溃且 ASan 堆栈来不及输出)
|
||||
if proj_loaded == 0 and src_path is None:
|
||||
if log is not None:
|
||||
log.error("未找到 project.vpj(当前目录/上级目录)且未指定 --src,无法构建", "config")
|
||||
argparse.release(args)
|
||||
return 1
|
||||
|
||||
# === 多文件项目模式 ===
|
||||
# 当 --project 指定但 --src 未指定时,扫描 source_dir 下所有 .vp/.py 文件编译
|
||||
if src_path is None and project is not None and Config.SourceDir is not None:
|
||||
|
||||
Reference in new issue
Block a user