feat(call): 跨模块实参加 str/bytes 形参的 fail-fast(str 不作通用指针通配),并加 vpi 注解段等值判定
This commit is contained in:
1 parent
feb29fa7a3
commit
98ee53340f
2 files changed
+63
No files matched your search
@@ -1056,6 +1056,47 @@ def _ensure_vpi_ret_annot_loaded(src_sha1: str, temp_dir: str) -> int:
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _vpi_param_annot_seg_is - 形参注解段等值判定(供"str/bytes 不是通用指针通配"规则)
|
||||
# ============================================================
|
||||
def _vpi_param_annot_seg_is(src_sha1: str, cls_name: str, method_name: str,
|
||||
seg_idx: int, want: str) -> int:
|
||||
"""vpi 定义侧第 seg_idx 个形参注解段是否**恰等于** want(1=是 / 0=否)
|
||||
|
||||
注解串格式(见 _lookup_vpi_param_annots):以 '@' 分隔的段序列,段 0 是
|
||||
self 占位(空串)。本函数只做逐字节等值比较,不解析、不做模板类型形参替换
|
||||
——调用方是跨模块调用的**热路径**(每个实参一次),不能在此分配/解析。
|
||||
典型用途:判定定义侧形参注解是否为**裸** `str` / `bytes`(用于"str/bytes
|
||||
不是通用指针通配"这条规则;`str | t.CPtr` 这类组合段不等值,不受影响)。
|
||||
"""
|
||||
if want is None or seg_idx < 0:
|
||||
return 0
|
||||
prm: str = _lookup_vpi_param_annots(src_sha1, cls_name, method_name)
|
||||
if prm is None or prm[0] == '\0':
|
||||
return 0
|
||||
cur: sizet = 0
|
||||
seg: int = 0
|
||||
# 定位段起点:跳过 seg_idx 个 '@' 段
|
||||
while seg < seg_idx:
|
||||
while prm[cur] != '\0' and prm[cur] != '@':
|
||||
cur += 1
|
||||
if prm[cur] == '\0':
|
||||
return 0
|
||||
cur += 1
|
||||
seg += 1
|
||||
slen: sizet = 0
|
||||
while prm[cur + slen] != '\0' and prm[cur + slen] != '@':
|
||||
slen += 1
|
||||
wlen: sizet = string.strlen(want)
|
||||
if slen != wlen:
|
||||
return 0
|
||||
i: sizet
|
||||
for i in range(slen):
|
||||
if prm[cur + i] != want[i]:
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
# ============================================================
|
||||
# _ensure_vpi_param_annot_loaded - 按需加载模块的 vpi stub 参数注解缓存
|
||||
#
|
||||
|
||||
@@ -262,6 +262,28 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
arg_node: ast.AST* = None
|
||||
if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs):
|
||||
arg_node = cargs.get(arg_idx)
|
||||
# 「str / bytes 不是通用指针通配」:`void*` / `t.CPtr` / `rawptr` 允许
|
||||
# 隐式转为任意指针类型,但 `str` / `bytes` 不允许——即便它们在 LLVM
|
||||
# 层同为 i8*。跨模块实参在这条路径上原本只做 bitcast、完全不过指针
|
||||
# 转换校验,于是"把结构体/类/数组/函数指针传给 str 形参"会被静默放行
|
||||
# ⇒ 后续把该指针当 C 字符串用时生成非法 IR(现场:把 Function* 传给
|
||||
# `callee: str`,发射出 `call i32 @(i32 7)` 空符号名,直到 llc 才炸)。
|
||||
# 判据:定义侧形参注解**恰为**裸 str/bytes(组合段如 `str|t.CPtr` 不
|
||||
# 等值、不受影响),且实参是指向聚合类型(Struct/Array/Func)的指针。
|
||||
if self.Gen.ExprGenBase.is_ptr_type(cur.Ty) != 0:
|
||||
pointee_a: llvmlite.LLVMType* = cur.Ty.Pointee
|
||||
if pointee_a is not None:
|
||||
pk_a: int = pointee_a.kind()
|
||||
if pk_a == llvmlite.LLVMType.Struct \
|
||||
or pk_a == llvmlite.LLVMType.Array \
|
||||
or pk_a == llvmlite.LLVMType.Func:
|
||||
if VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "str") != 0 \
|
||||
or VpiReader._vpi_param_annot_seg_is(sha1_buf, cls_nm, meth_nm, idx, "bytes") != 0:
|
||||
self.Gen.TypeGenErr.fatal_error(arg_node,
|
||||
"str/bytes 形参不接受指向非基本类型(结构体/类/数组/函数)的指针:"
|
||||
"两者在 IR 层同为 i8*,但语义不同(str/bytes 不是通用指针通配)。"
|
||||
"若确需传递指针,请改用 t.CPtr/rawptr 形参或显式转换(如 t.CPtr(p))",
|
||||
"CastError")
|
||||
node_v = self._coerce_arg_value_to_ty(pool, builder, cur, tgt_ty, arg_node, trans, tpl_src)
|
||||
if new_head is None:
|
||||
new_head = node_v
|
||||
|
||||
Reference in new issue
Block a user