fix(类型): str/bytes 聚合指针检查豁免字符数组衰减,并统一判据真源

误报现场:includes/os/_win32.vp:207 `name: str = find_data.cFileName`。WIN32_FIND_DATAA.cFileName 是 [260 x i8],字段访问得到 Ptr(Array(i8,260)),其指针在 C 语义下天然衰减为 char*/str,属合法赋值,原判据把 Ptr(Array) 一律当真而误杀。

新增 ExprGenBase._str_rejects_ptr_kind(单一真源:1=结构体/类/联合指针、2=非字符数组指针、3=函数指针、0=可接受;[N x i8] 豁免)供 fail_if_str_annot_agg_ptr 与 CallGenLib 跨模块实参检查共用,消除两处重复的 kind 判定,避免同规则两处演化。
This commit is contained in:
TermiNexus committed 2026-10-03 15:40:01 +08:00
1 parent bf2f38acd2
commit c035a82394
2 files changed
+41 -27

No files matched your search

+7 -15
View File
@@ -269,21 +269,13 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
# ⇒ 后续把该指针当 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")
# 等值、不受影响),且实参是指向聚合类型的指针。判据与报错统一委托
# ExprGenBase._str_rejects_ptr_kind / fail_if_str_annot_agg_ptr(单一
# 真源,字符数组 [N x i8] 衰减与其在同模块路径同口径豁免)。
if self.Gen.ExprGenBase._str_rejects_ptr_kind(cur) != 0:
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.ExprGenBase.fail_if_str_annot_agg_ptr(cur, 1, arg_node)
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
+34 -12
View File
@@ -455,16 +455,16 @@ novirtual class ExprGenBase:
if nm is None or nm.id is None:
return 0
return self._annot_name_is_bare_str(nm.id)
def fail_if_str_annot_agg_ptr(self, val: llvmlite.Value*,
annot_is_str: int,
node: ast.AST* = None) -> int:
"""注解侧是裸 str/bytes 而值是指向聚合类型的指针 → fail-fast(返回 1)
def _str_rejects_ptr_kind(self, val: llvmlite.Value*) -> int:
"""val 是否为「str/bytes 不应接受」的聚合指针(1/2/3=是,0=否)
判据:val 是一级指针且被指类型是聚合(Struct/Array/Func)——即"结构体/
类/联合/数组/函数指针"。显式转换(如 t.CPtr(p))的产物 pointee 是 i8,
不触发本检查,故显式转换不受影响。
1 = 结构体/类/联合指针;2 = 非字符数组指针;3 = 函数指针。
**字符数组([N x i8])豁免**:它自身就是字符串本体,其指针在 C 语义下
天然衰减为 char*/str(现场:includes/os/_win32.vp:207
`name: str = find_data.cFileName`,WIN32_FIND_DATAA.cFileName 是 [260 x i8])。
多级指针/基本类型指针一律 0(不在本规则范围内)。
"""
if annot_is_str == 0 or val is None or val.Ty is None:
if val is None or val.Ty is None:
return 0
if self.is_ptr_type(val.Ty) == 0:
return 0
@@ -472,12 +472,34 @@ novirtual class ExprGenBase:
if pointee is None:
return 0
pk: int = pointee.kind()
if pk != llvmlite.LLVMType.Struct \
and pk != llvmlite.LLVMType.Array \
and pk != llvmlite.LLVMType.Func:
if pk == llvmlite.LLVMType.Struct:
return 1
if pk == llvmlite.LLVMType.Func:
return 3
if pk == llvmlite.LLVMType.Array:
match pointee:
case llvmlite.LLVMType.Array(elem, _):
if self.get_llvm_type_bits(elem) == 8:
return 0
case _:
pass
return 2
return 0
def fail_if_str_annot_agg_ptr(self, val: llvmlite.Value*,
annot_is_str: int,
node: ast.AST* = None) -> int:
"""注解侧是裸 str/bytes 而值是指向聚合类型的指针 → fail-fast(返回 1)
判据见 _str_rejects_ptr_kind(结构体/类/联合/非字符数组/函数指针拒绝;
字符数组衰减豁免)。显式转换(如 t.CPtr(p))的产物 pointee 是 i8,
不触发本检查,故显式转换不受影响。
"""
if annot_is_str == 0 or val is None or val.Ty is None:
return 0
if self._str_rejects_ptr_kind(val) == 0:
return 0
self.Gen.TypeGenErr.fatal_error(self._value_node_of(node),
"str/bytes 不接受指向非基本类型(结构体/类/数组/函数)的指针:两者在 IR 层"
"str/bytes 不接受指向非基本类型(结构体/类/非字符数组/函数)的指针:两者在 IR 层"
"同为 i8*,但语义不同(str/bytes 不是通用指针通配)。若确需传递指针,"
"请改用 t.CPtr/rawptr 形参,或显式转换(如 t.CPtr(p))", "CastError")
return 1