fix(core): print 类型感知格式化,跨模块未导入 fast-fail,报错行号防御
This commit is contained in:
1 parent
557a40c662
commit
adff2f35c4
3 files changed
+159
-5
No files matched your search
@@ -199,9 +199,11 @@ def translate_expr_stmt(trans: HT.Translator | t.CPtr,
|
|||||||
if func_name is None:
|
if func_name is None:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# printf / print 走特殊路径(print 映射到 printf)
|
# printf / print 走特殊路径(print 是类型感知格式化,不映射到 printf)
|
||||||
if string.strcmp(func_name, "printf") == 0 or string.strcmp(func_name, "print") == 0:
|
if string.strcmp(func_name, "printf") == 0:
|
||||||
trans.ExprCallH.HandlePrintfCall(cl)
|
trans.ExprCallH.HandlePrintfCall(cl)
|
||||||
|
elif string.strcmp(func_name, "print") == 0:
|
||||||
|
trans.ExprCallH.HandlePrintCall(cl)
|
||||||
else:
|
else:
|
||||||
# 通用函数调用(返回值丢弃)
|
# 通用函数调用(返回值丢弃)
|
||||||
trans.ExprCallH.HandleCall(call_node)
|
trans.ExprCallH.HandleCall(call_node)
|
||||||
|
|||||||
@@ -5541,6 +5541,22 @@ def translate_call(pool: memhub.MemBuddy | t.CPtr,
|
|||||||
if xmod_nm.id is not None:
|
if xmod_nm.id is not None:
|
||||||
mod_sha1: str = _lookup_module_sha1(xmod_nm.id)
|
mod_sha1: str = _lookup_module_sha1(xmod_nm.id)
|
||||||
if mod_sha1 is not None:
|
if mod_sha1 is not None:
|
||||||
|
# fast-fail: 模块限定调用要求模块已被当前文件 import。
|
||||||
|
# 全局 SHA1 存储包含所有被扫描的 includes,未导入的模块
|
||||||
|
# (如 this.this() 但 import this 被注释)也会命中存储,
|
||||||
|
# 静默生成 @{sha1}.func 调用而非报错——违反"找不到 fast-fail"原则。
|
||||||
|
is_xmod_imported: int = 1
|
||||||
|
if trans is not None and trans._imported_modules is not None:
|
||||||
|
is_xmod_imported = HandlesImports.is_module_imported(
|
||||||
|
trans._imported_modules, xmod_nm.id)
|
||||||
|
if is_xmod_imported == 0:
|
||||||
|
err_ni: t.CChar | t.CPtr = pool.alloc(256)
|
||||||
|
if err_ni is not None:
|
||||||
|
viperlib.snprintf(err_ni, 256,
|
||||||
|
"跨模块调用 '%s.%s':模块 '%s' 未导入(请先 import %s)",
|
||||||
|
xmod_nm.id, func_name, xmod_nm.id, xmod_nm.id)
|
||||||
|
HandlesType.fatal_error(xmod_nm, err_ni)
|
||||||
|
HandlesType.fatal_error(xmod_nm, "跨模块调用:模块未导入")
|
||||||
# 检查是否是 re-export 函数(如 ast._lexer_init 实际定义在 ast.lexer)
|
# 检查是否是 re-export 函数(如 ast._lexer_init 实际定义在 ast.lexer)
|
||||||
reexport_src: str = HandlesImports.lookup_reexport(mod_sha1, func_name)
|
reexport_src: str = HandlesImports.lookup_reexport(mod_sha1, func_name)
|
||||||
if reexport_src is not None:
|
if reexport_src is not None:
|
||||||
@@ -5836,7 +5852,123 @@ def translate_printf_call(pool: memhub.MemBuddy | t.CPtr,
|
|||||||
|
|
||||||
# is_variadic=1: 生成带显式函数类型签名的 call(如 call i32 (i8*, ...) @printf),
|
# is_variadic=1: 生成带显式函数类型签名的 call(如 call i32 (i8*, ...) @printf),
|
||||||
# 让 llc 识别为 variadic 调用,生成正确的 Win64 ABI 代码(浮点参数同时传 XMM 和整数寄存器)
|
# 让 llc 识别为 variadic 调用,生成正确的 Win64 ABI 代码(浮点参数同时传 XMM 和整数寄存器)
|
||||||
llvmlite.build_call(builder, "printf", fmt_ptr, can, i32_ty, 1)
|
llvmlite.build_call(builder, "printf", fmt_ptr, can, i32_ty, 1)
|
||||||
|
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 翻译 print 调用 — 类型感知格式化 + 默认换行
|
||||||
|
#
|
||||||
|
# 与 printf 不同,print 不需要用户提供格式串:每个参数按 LLVM
|
||||||
|
# 类型自动选择格式(%s/%d/%f/0x%llx),结尾追加 "\n"(对齐 Python
|
||||||
|
# print 默认 end="\n",以及 TPC HandlesExprBuiltin._HandlePrintLlvm)。
|
||||||
|
#
|
||||||
|
# 类型分派规则:
|
||||||
|
# i8*(str/bytes) → %s
|
||||||
|
# 其他指针(struct*/int*) → 0x%llx(ptrtoint 到 i64)
|
||||||
|
# i1(bool) → %d(zext 到 i32)
|
||||||
|
# i8/i16/i32 → %d(sext 到 i32,C ABI 整数提升)
|
||||||
|
# i64 → %lld
|
||||||
|
# float/double → %f(float fpext 到 double,C ABI 提升)
|
||||||
|
# 无法识别的类型 → "(unknown)"(不传参)
|
||||||
|
# ============================================================
|
||||||
|
def translate_print_call(pool: memhub.MemBuddy | t.CPtr,
|
||||||
|
builder: llvmlite.IRBuilder | t.CPtr,
|
||||||
|
mod: llvmlite.LLVMModule | t.CPtr,
|
||||||
|
cl: ast.Call | t.CPtr,
|
||||||
|
trans: HT.Translator | t.CPtr = None):
|
||||||
|
"""翻译 print 调用(类型感知格式化 + 默认换行)"""
|
||||||
|
cargs: list[ast.AST | t.CPtr] | t.CPtr = cl.args
|
||||||
|
if cargs is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
FMT_BUF_SIZE: t.CSizeT = 256
|
||||||
|
fmt_buf: bytes = stdlib.malloc(FMT_BUF_SIZE)
|
||||||
|
if fmt_buf is None:
|
||||||
|
return
|
||||||
|
fmt_buf[0] = '\0'
|
||||||
|
|
||||||
|
i32_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int32(pool)
|
||||||
|
i64_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Int64(pool)
|
||||||
|
dbl_ty: llvmlite.LLVMType | t.CPtr = llvmlite.Double(pool)
|
||||||
|
|
||||||
|
can: t.CSizeT = cargs.__len__()
|
||||||
|
arg_head: llvmlite.Value | t.CPtr = None
|
||||||
|
arg_tail: llvmlite.Value | t.CPtr = None
|
||||||
|
arg_count: int = 1 # 格式串本身是第 1 个 printf 参数
|
||||||
|
|
||||||
|
for ai in range(can):
|
||||||
|
arg: ast.AST | t.CPtr = cargs.get(ai)
|
||||||
|
if arg is None:
|
||||||
|
continue
|
||||||
|
arg_val: llvmlite.Value | t.CPtr = HandlesExpr.translate_value(
|
||||||
|
builder, pool, mod, arg, None, 0, trans)
|
||||||
|
if arg_val is None or arg_val.Ty is None:
|
||||||
|
# 参数翻译失败 → "(null)"(与 TPC 一致,不传参)
|
||||||
|
if string.strlen(fmt_buf) < FMT_BUF_SIZE - 16:
|
||||||
|
viperlib.snprintf(fmt_buf + string.strlen(fmt_buf), 16, "(null)")
|
||||||
|
continue
|
||||||
|
|
||||||
|
token: t.CChar | t.CPtr = None
|
||||||
|
chain_arg: int = 1 # 1=该 token 消耗一个参数,0=纯文本
|
||||||
|
arg_bits: int = HandlesExpr.get_llvm_type_bits(arg_val.Ty)
|
||||||
|
arg_fbits: int = HandlesExpr.get_llvm_float_bits(arg_val.Ty)
|
||||||
|
|
||||||
|
if HandlesExpr.is_ptr_type(arg_val.Ty) != 0:
|
||||||
|
# i8*(str/bytes)→ %s;其他指针 → 0x%llx
|
||||||
|
pointee: llvmlite.LLVMType | t.CPtr = arg_val.Ty.Pointee
|
||||||
|
if pointee is not None and HandlesExpr.get_llvm_type_bits(pointee) == 8:
|
||||||
|
token = "%s"
|
||||||
|
else:
|
||||||
|
token = "0x%llx"
|
||||||
|
arg_val = llvmlite.build_ptrtoint(builder, arg_val, i64_ty)
|
||||||
|
elif arg_bits != 0:
|
||||||
|
if arg_bits == 1:
|
||||||
|
token = "%d"
|
||||||
|
arg_val = llvmlite.build_zext(builder, arg_val, i32_ty)
|
||||||
|
elif arg_bits == 8:
|
||||||
|
token = "%d"
|
||||||
|
arg_val = llvmlite.build_sext(builder, arg_val, i32_ty)
|
||||||
|
elif arg_bits == 16:
|
||||||
|
token = "%d"
|
||||||
|
arg_val = llvmlite.build_sext(builder, arg_val, i32_ty)
|
||||||
|
elif arg_bits == 32:
|
||||||
|
token = "%d"
|
||||||
|
else:
|
||||||
|
token = "%lld"
|
||||||
|
elif arg_fbits != 0:
|
||||||
|
token = "%f"
|
||||||
|
if arg_fbits < 64:
|
||||||
|
arg_val = llvmlite.build_fpext(builder, arg_val, dbl_ty)
|
||||||
|
else:
|
||||||
|
# 结构体值/数组/函数等 → "(unknown)",不传参
|
||||||
|
token = "(unknown)"
|
||||||
|
chain_arg = 0
|
||||||
|
|
||||||
|
# 追加格式 token
|
||||||
|
if string.strlen(fmt_buf) < FMT_BUF_SIZE - 16:
|
||||||
|
viperlib.snprintf(fmt_buf + string.strlen(fmt_buf), 16, "%s", token)
|
||||||
|
# 链接参数
|
||||||
|
if chain_arg != 0:
|
||||||
|
arg_val.Next = None
|
||||||
|
if arg_tail is None:
|
||||||
|
arg_head = arg_val
|
||||||
|
else:
|
||||||
|
arg_tail.Next = arg_val
|
||||||
|
arg_tail = arg_val
|
||||||
|
arg_count += 1
|
||||||
|
|
||||||
|
# 追加默认换行(Python print 默认 end="\n")
|
||||||
|
if string.strlen(fmt_buf) < FMT_BUF_SIZE - 2:
|
||||||
|
viperlib.snprintf(fmt_buf + string.strlen(fmt_buf), 2, "\n")
|
||||||
|
|
||||||
|
fmt_ptr: llvmlite.Value | t.CPtr = HandlesExpr.create_global_string(
|
||||||
|
builder, pool, mod, fmt_buf, trans)
|
||||||
|
stdlib.free(fmt_buf)
|
||||||
|
if fmt_ptr is None:
|
||||||
|
return
|
||||||
|
fmt_ptr.Next = arg_head
|
||||||
|
# is_variadic=1: 带显式函数类型签名(C ABI 变参调用)
|
||||||
|
llvmlite.build_call(builder, "printf", fmt_ptr, arg_count, i32_ty, 1)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -6550,6 +6682,15 @@ class ExprCallHandle(HandlesBase.Mixin):
|
|||||||
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
|
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
|
||||||
cl, self.Trans)
|
cl, self.Trans)
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# HandlePrintCall - 翻译 print 调用
|
||||||
|
# ============================================================
|
||||||
|
def HandlePrintCall(self, cl: ast.Call | t.CPtr):
|
||||||
|
"""翻译 print 调用(类型感知格式化 + 默认换行)"""
|
||||||
|
translate_print_call(
|
||||||
|
self.Trans.Pool, self.Trans._cur_builder, self.Trans.Module,
|
||||||
|
cl, self.Trans)
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# NewExprCallHandle - 工厂函数
|
# NewExprCallHandle - 工厂函数
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|||||||
@@ -836,9 +836,20 @@ def fatal_error(node: ast.AST | t.CPtr, msg: str,
|
|||||||
file_name: str = _g_current_file
|
file_name: str = _g_current_file
|
||||||
if file_name is None:
|
if file_name is None:
|
||||||
file_name = "<unknown>"
|
file_name = "<unknown>"
|
||||||
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
# 行号合理性防御:先读取该行源码,验证 node.lineno 是否落在当前文件内。
|
||||||
|
# 若读不到(文件不存在或行号超出范围),说明文件/行号归属不可信
|
||||||
|
# (如 AST 树被内存复用/串扰后跨文件报错),打印警告而非误导性行号。
|
||||||
|
src_line: str = None
|
||||||
|
line_ok: int = 1
|
||||||
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
|
if lineno > 0 and string.strcmp(file_name, "<unknown>") != 0:
|
||||||
src_line: str = _read_source_line(file_name, lineno)
|
src_line = _read_source_line(file_name, lineno)
|
||||||
|
if src_line is None:
|
||||||
|
line_ok = 0
|
||||||
|
if line_ok == 0:
|
||||||
|
stdio.printf(' File "%s", line %d (!! 无法读取该行源码:文件不存在或行号超出范围,错误可能来自被复用的 AST 树,请以错误消息为准)\n',
|
||||||
|
file_name, lineno)
|
||||||
|
else:
|
||||||
|
stdio.printf(' File "%s", line %d\n', file_name, lineno)
|
||||||
if src_line is not None:
|
if src_line is not None:
|
||||||
stdio.printf(" %s\n", src_line)
|
stdio.printf(" %s\n", src_line)
|
||||||
# 列范围标记:在源代码行下方用 ^ 标记出错范围
|
# 列范围标记:在源代码行下方用 ^ 标记出错范围
|
||||||
|
|||||||
Reference in new issue
Block a user