From c035a8239474096c5820f32e7659158037fb4666 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 3 Oct 2026 15:40:01 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E7=B1=BB=E5=9E=8B):=20str/bytes=20?= =?UTF-8?q?=E8=81=9A=E5=90=88=E6=8C=87=E9=92=88=E6=A3=80=E6=9F=A5=E8=B1=81?= =?UTF-8?q?=E5=85=8D=E5=AD=97=E7=AC=A6=E6=95=B0=E7=BB=84=E8=A1=B0=E5=87=8F?= =?UTF-8?q?=EF=BC=8C=E5=B9=B6=E7=BB=9F=E4=B8=80=E5=88=A4=E6=8D=AE=E7=9C=9F?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 误报现场: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 判定,避免同规则两处演化。 --- App/lib/core/Gen/Call/CallGenLib.vp | 22 +++++-------- App/lib/core/Gen/Expr/ExprGenBase.vp | 46 ++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/App/lib/core/Gen/Call/CallGenLib.vp b/App/lib/core/Gen/Call/CallGenLib.vp index 33b7ae8..718fe67 100644 --- a/App/lib/core/Gen/Call/CallGenLib.vp +++ b/App/lib/core/Gen/Call/CallGenLib.vp @@ -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 diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index 3afc9a4..a01d050 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -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