diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 948c2c5..d225af1 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -2287,6 +2287,9 @@ novirtual class AssignGenMixin: if mod_alloca.Ty is not None: target_ty = mod_alloca.Ty.Pointee if target_ty is not None: + # 「str/bytes 不是通用指针通配」:目标注解为裸 str/bytes 而右值 + # 是指向聚合类型(结构体/类/数组/函数)的指针 → fail-fast。 + self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node) rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans) # t.CVolatile(global 声明)全局:初始化 store 带 volatile 限定符 vol_gaa: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)) @@ -2329,6 +2332,7 @@ novirtual class AssignGenMixin: trans.CurAssignAnnot = None trans.CurAssignNode = None return 0 + self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node) rhs_coerced: llvmlite.Value* = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, nl_target_ty, node) llvmlite.build_store(builder, rhs_coerced, nl_ptr) # 清除赋值上下文(防残留):与主分支清空时机同款。四处(主分支 / @@ -2395,6 +2399,9 @@ novirtual class AssignGenMixin: if mod_glob.Ty is not None: target_ty_mg = mod_glob.Ty.Pointee if target_ty_mg is not None: + # 「str/bytes 不是通用指针通配」:同上(global 分支), + # 目标注解为裸 str/bytes 而右值是指向聚合类型的指针 → fail-fast。 + self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val_mg, aa.annotation, node) rhs_val_mg = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val_mg, target_ty_mg, node) # t.CVolatile 模块级全局:初始化 store 带 volatile 限定符 vol_mga: int = self.Gen.VarGen.var_is_volatile(self.Gen.VarGen.lookup_var_entry(trans.SymTab, nm.id)) @@ -2637,6 +2644,9 @@ novirtual class AssignGenMixin: if alloca.Ty is not None: target_ty = alloca.Ty.Pointee if target_ty is not None: + # 「str/bytes 不是通用指针通配」:目标注解为裸 str/bytes 而右值 + # 是指向聚合类型(结构体/类/数组/函数)的指针 → fail-fast。 + self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(rhs_val, aa.annotation, node) rhs_val = self.Gen.ExprGenBase.coerce_to_type(builder, rhs_val, target_ty, node, trans) # t.CVolatile 局部变量:标记 VarEntry 并让本次 store 带 volatile vol_laa: int = 0 diff --git a/App/lib/core/Gen/Call/CallGenMixin.vp b/App/lib/core/Gen/Call/CallGenMixin.vp index e5b469c..aff0f03 100644 --- a/App/lib/core/Gen/Call/CallGenMixin.vp +++ b/App/lib/core/Gen/Call/CallGenMixin.vp @@ -1066,6 +1066,17 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): mc_dst2: llvmlite.Value* = None mc_src2: llvmlite.Value* = None mc_num2: llvmlite.Value* = None + # 「str/bytes 不是通用指针通配」前置:同模块**裸名**函数的形参注解来自本 + # 模块的 .vpi(与跨模块路径 CallGenLib._coerce_args_to_def_params 同源同 + # 键式:模块级函数 cls=None、段下标 == 形参下标)。在参数循环前一次性确保 + # 本模块 vpi 参数注解缓存就绪,循环内每参只做一次 O(1) 段等值判定。 + # 仅裸名(cl.func 是 Name):模块限定调用走 _coerce_args_to_def_params 已覆盖。 + same_mod_vpi: int = 0 + if trans is not None and trans.ModuleSha1 is not None \ + and cl.func is not None and cl.func.kind() == ast.ASTKind.Name: + if self.Gen.CallGenBase.TempDir is not None and self.Gen.CallGenBase.TempDirLen > 0: + VpiReader._ensure_vpi_param_annot_loaded(trans.ModuleSha1, self.Gen.CallGenBase.TempDir) + same_mod_vpi = 1 for ai in range(can): arg: ast.AST* = cargs.get(ai) if arg is not None: @@ -1123,6 +1134,18 @@ novirtual class CallGenMixin(CallGenBase.CallGenBase): # 修复: CDefine/i32 常量传给 i64 形参未提升,导致 call(i32) 与 define(i64) 不匹配 if fp is not None: if fpty is not None: + # 「str/bytes 不是通用指针通配」:同模块裸名函数的定义侧 + # 形参注解恰为裸 str/bytes,而实参是指向聚合类型(结构体/ + # 类/数组/函数)的指针 → fail-fast(IR 层同为 i8*,但语义 + # 不同)。与跨模块路径 CallGenLib._coerce_args_to_def_params + # 的同名检查同规则、同判据(段等值判定只做逐字节比较)。 + sm_is_str: int = 0 + if same_mod_vpi != 0: + if VpiReader._vpi_param_annot_seg_is(trans.ModuleSha1, None, func_name, ai, "str") != 0 \ + or VpiReader._vpi_param_annot_seg_is(trans.ModuleSha1, None, func_name, ai, "bytes") != 0: + sm_is_str = 1 + if sm_is_str != 0: + self.Gen.ExprGenBase.fail_if_str_annot_agg_ptr(arg_val, 1, arg) arg_val = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val, fpty, arg, trans) elif found_func.IsVarArg == 1: # Win64 变参 8 字节槽位:<64 位整数按 i64 传(sext 符号扩展), diff --git a/App/lib/core/Gen/Expr/ExprGenBase.vp b/App/lib/core/Gen/Expr/ExprGenBase.vp index 0ede3c9..3afc9a4 100644 --- a/App/lib/core/Gen/Expr/ExprGenBase.vp +++ b/App/lib/core/Gen/Expr/ExprGenBase.vp @@ -429,6 +429,69 @@ novirtual class ExprGenBase: return 1 return 0 # ============================================================ + # 「str / bytes 不是通用指针通配」——规则与统一 fail-fast + # + # 用户规则:`void*` / `t.CPtr` / `rawptr` 允许隐式转为任意指针类型; + # `str` / `bytes` 不允许——**即便它们在 LLVM 层同为 i8***(_is_universal_ptr_ty + # 只看"一级指针 + 8 位被指类型",无法区分 CPtr 与 str),所以 str-ness 只能由 + # 调用方从**注解来源**给出:赋值侧 trans.CurAssignAnnot、返回侧 + # trans.CurFuncRetAnnot、形参侧 vpi/函数签名注解段。 + # + # 只认**裸名**:`str` / `bytes`。`str | t.CPtr` 的语义是 i8**(指针槽, + # 见 AssignGenMixin._is_ptr_element_annotation),不是字符串值本体,不适用。 + # ============================================================ + def _annot_name_is_bare_str(self, txt: str) -> int: + """注解**文本**是否为裸 str / bytes;1=是 / 0=否""" + if txt is None or txt[0] == 0: + return 0 + if string.strcmp(txt, "str") == 0 or string.strcmp(txt, "bytes") == 0: + return 1 + return 0 + def _annot_is_bare_str(self, annot: ast.AST*) -> int: + """注解 AST 是否为裸 str / bytes(Name 形态);1=是 / 0=否""" + if annot is None or annot.kind() != ast.ASTKind.Name: + return 0 + nm: ast.Name* = ast.Name*(annot) + 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) + + 判据:val 是一级指针且被指类型是聚合(Struct/Array/Func)——即"结构体/ + 类/联合/数组/函数指针"。显式转换(如 t.CPtr(p))的产物 pointee 是 i8, + 不触发本检查,故显式转换不受影响。 + """ + if annot_is_str == 0 or val is None or val.Ty is None: + return 0 + if self.is_ptr_type(val.Ty) == 0: + return 0 + pointee: llvmlite.LLVMType* = val.Ty.Pointee + 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: + return 0 + self.Gen.TypeGenErr.fatal_error(self._value_node_of(node), + "str/bytes 不接受指向非基本类型(结构体/类/数组/函数)的指针:两者在 IR 层" + "同为 i8*,但语义不同(str/bytes 不是通用指针通配)。若确需传递指针," + "请改用 t.CPtr/rawptr 形参,或显式转换(如 t.CPtr(p))", "CastError") + return 1 + def fail_if_str_annot_ast_agg_ptr(self, val: llvmlite.Value*, + annot: ast.AST*, + node: ast.AST* = None) -> int: + """注解 AST 形态的入口:注解为裸 str/bytes 且值是指向聚合类型的指针 → fail-fast + + 供注解来源是 AST 的三条路径复用:赋值侧 aa.annotation、返回侧 + trans.CurFuncRetAnnot。形参侧注解来源是文本(vpi 段),直接调 + fail_if_str_annot_agg_ptr 传 1 即可。 + """ + return self.fail_if_str_annot_agg_ptr(val, self._annot_is_bare_str(annot), node) + # ============================================================ # _is_basic_scalar_ty - 判据:类型是否为基本类型(整数/浮点),1=是 / 0=否 # # 非基本类型 = 结构体/类/联合/数组/函数等(LLVM 层 Struct/Array/Func)。 diff --git a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp index ec6ff63..3292296 100644 --- a/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp +++ b/App/lib/core/Gen/Stmt/StmtGenCifMixin.vp @@ -619,6 +619,11 @@ novirtual class StmtGenCifMixin: llvmlite.build_ret_void(builder) return 0 if ret_ty_rt is not None: + # 「str/bytes 不是通用指针通配」:函数返回注解为裸 str/bytes 而返回值是 + # 指向聚合类型(结构体/类/数组/函数)的指针 → fail-fast。放在下面所有 + # 类型转换分支之前:否则 val/ret 两侧同为 i8*(ptr→ptr)会被 + # coerce_to_type 的规则 1(通用指针通配)静默 bitcast 放行。 + self.Gen.ExprGenBase.fail_if_str_annot_ast_agg_ptr(val, trans.CurFuncRetAnnot, node) val_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(val.Ty) ret_bits: int = self.Gen.ExprGenBase.get_llvm_type_bits(ret_ty_rt) val_is_ptr: int = self.Gen.ExprGenBase.is_ptr_type(val.Ty)