From f084da7c013fa18c0486e4c3af92cb85f0817e2a Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sun, 27 Sep 2026 08:55:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(gen):=20=E9=87=8D=E5=A4=8D=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E6=94=B9=E4=B8=BA=E6=97=A0=E6=9D=A1=E4=BB=B6=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=EF=BC=88=E4=B8=8D=E5=86=8D=E6=AF=94=E8=BE=83=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=EF=BC=89=EF=BC=8Cglobal=20=E5=90=8E=E5=86=8D=E6=B3=A8?= =?UTF-8?q?=E8=A7=A3=E5=90=8C=E6=A0=B7=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 判据改为:模块级已有同名变量 + 函数内再次用注解声明同名变量 ⇒ TypeError 重复声明,不再比较注解类型是否与全局一致(此前仅在类型不一致时报错) - 有 global/nonlocal 声明后再带注解声明同名变量也报错(注解与既有全局声明重复) - 检查点前移到注解解析之前(紧跟 REPL 分支),保证优先于 coerce_to_type 报错 - 合法写法只剩两种:global + 无注解赋值修改全局;或换不重名的局部变量 --- App/lib/core/Gen/AssignGenMixin.vp | 61 +++++++++++++++--------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index a00f13b..ae87137 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -2101,6 +2101,37 @@ novirtual class AssignGenMixin: self.Gen.VarGen.repl_try_annassign(trans, nm.id, aa.annotation, aa.value, aa) return 0 + # 重复声明检测(fail-fast,先于注解解析与 RHS 翻译): + # 模块级已有同名变量时,函数内**再次用注解声明**同名变量一律判为重复声明: + # ① 无 global/nonlocal 声明 → 会把全局遮蔽成局部,属重复声明 + # ② 有 global/nonlocal 声明 → 注解与既有全局声明重复,属重复声明 + # 判据**不含**"类型是否一致":注解类型即便与全局完全相同也是重复声明 + # (无条件失败)。同口径的合法写法只有两种: + # - 修改全局:函数内 `global x` + **无注解**赋值 `x = v` + # - 独立局部:换一个不与全局重名的变量名 + # 目的:不让错误延后到 RHS 的 coerce_to_type,暴露成与根因无关的 + # 「指针隐式转整数」(如 PointerTest/App/main.vp:46 `_g_ptr: int = None`: + # 全局已是 t.CUInt32T | t.CPtr)。范围仅函数内——模块级自身的重复注解 + # 不归本检查(模块级由 handle_module_level_var 登记,本就不构成"遮蔽")。 + if nm.id is not None and trans is not None \ + and self.Gen.VarGen.is_module_scope(trans.SymTab) == 0 \ + and self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id) is not None: + dup_decl: int = 0 + if GenApi.is_global_name(trans, nm.id) != 0 \ + or GenApi.is_nonlocal_name(trans, nm.id) != 0: + dup_decl = 1 + if dup_decl != 0: + self.Gen.TypeGenErr.fatal_error(aa, + f"重复声明:'{nm.id}' 已是模块级变量,此处声明 global/nonlocal 之后" + f"又用类型注解重新声明。请删除本行的类型注解,直接赋值即可修改全局", + "TypeError") + else: + self.Gen.TypeGenErr.fatal_error(aa, + f"重复声明:'{nm.id}' 已是模块级变量,函数内未声明 global 时不得再用" + f"类型注解声明同名变量(会把全局遮蔽为局部)。如需独立局部变量请换名;" + f"如需修改全局请在函数内加 global 声明并删除本行注解", + "TypeError") + pool: memhub.MemBuddy* = trans.Pool builder: llvmlite.IRBuilder* = trans._cur_builder mod: llvmlite.LLVMModule* = trans.Module @@ -2127,36 +2158,6 @@ novirtual class AssignGenMixin: f"AnnAssign 缺少类型注解,变量名: {nm.id}", "SyntaxError") - # 重复定义检测(优先于一切下游转换,fail-fast): - # 模块级已有同名变量,而当前作用域既无 global/nonlocal 声明(有则被下方 - # global/nonlocal 分支接管)、又不在模块级(模块级由 handle_module_level_var - # 登记,本就不构成"重复"),此处却用**与全局不一致的注解**再次声明同名变量 - # ⇒ 报重复定义。若无此检查,会先建局部 alloca 再由 RHS 走 coerce_to_type, - # 把根因暴露成无关的 CastError(如 PointerTest/App/main.vp:46 - # `_g_ptr: int = None`:全局已是 t.CUInt32T | t.CPtr,报"指针隐式转整数")。 - # 注解与全局**一致**时不报错:保持 Python 语义,遮蔽为独立局部变量 - # (如全局 `_g_val: t.CInt` 与局部 `_g_val: int` 解析后同为 i32)。 - if nm.id is not None and trans is not None and var_ty is not None \ - and GenApi.is_global_name(trans, nm.id) == 0 \ - and GenApi.is_nonlocal_name(trans, nm.id) == 0 \ - and self.Gen.VarGen.is_module_scope(trans.SymTab) == 0: - dup_alloca: llvmlite.Value* = self.Gen.VarGen.lookup_module_var(trans.SymTab, nm.id) - if dup_alloca is not None and dup_alloca.Ty is not None: - dup_g_ty: llvmlite.LLVMType* = dup_alloca.Ty.Pointee - if dup_g_ty is not None and self._stg_ty_equal(pool, dup_g_ty, var_ty) == 0: - # 类型内容不等(_stg_ty_equal 走 TypePrint 内容比较,非地址比较: - # llvmlite 的 Int32()/Ptr() 每次构造都是新实例)。 - dup_g_buf: str = pool.alloc(256) - dup_l_buf: str = pool.alloc(256) - if dup_g_buf is not None and dup_l_buf is not None: - llvmlite.TypePrint(dup_g_buf, 256, dup_g_ty, pool) - llvmlite.TypePrint(dup_l_buf, 256, var_ty, pool) - self.Gen.TypeGenErr.fatal_error(aa, - f"重复定义:'{nm.id}' 已是模块级变量(类型 {dup_g_buf})," - f"此处又注解为 {dup_l_buf}。函数内同名注解必须与全局一致;" - f"如需独立局部变量请换名,如需修改全局请在函数内加 global 声明", - "TypeError") - # t.Callable 函数指针:注册返回类型签名(供函数指针间接调用精确返回类型) callable_ret: llvmlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, annot, trans._imported_modules, trans._from_imports, trans) if callable_ret is not None: