feat(gen): 重复声明改为无条件失败(不再比较类型),global 后再注解同样报错
- 判据改为:模块级已有同名变量 + 函数内再次用注解声明同名变量 ⇒ TypeError 重复声明,不再比较注解类型是否与全局一致(此前仅在类型不一致时报错) - 有 global/nonlocal 声明后再带注解声明同名变量也报错(注解与既有全局声明重复) - 检查点前移到注解解析之前(紧跟 REPL 分支),保证优先于 coerce_to_type 报错 - 合法写法只剩两种:global + 无注解赋值修改全局;或换不重名的局部变量
This commit is contained in:
1 parent
78fc7313fb
commit
f084da7c01
1 file changed
+31
-30
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user