fix(assign): 拦截注解为类值类型但右值取地址的 typo

dirp: devfs_directory = &directories[d](注解漏写 *)此前会静默通过,把栈/表元素地址塞进值语义槽位,导致 vptr/父指针悬垂、对象被就地覆写。在 translate_annassign 中新增 fail-fast:注解解析为 struct(类值类型)而右值是取地址表达式(&x 或 c.Addr(x))时报 TypeError 并提示补 *。新增 _is_addr_of_expr 助手,只认取地址,不含 *p 解引用,避免误伤值拷贝。
This commit is contained in:
TermiNexus committed 2026-10-16 20:19:51 +08:00
1 parent 19feb40e8c
commit 201c881b43
1 file changed
+29
+29
View File
@@ -1527,6 +1527,28 @@ novirtual class AssignGenMixin:
if string.strcmp(at.attr, "Ptr") == 0: return 1
return 0
def _is_addr_of_expr(self, node: ast.AST*) -> int:
"""Return 1 only for an explicit address-of expression (`&x` / `c.Addr(x)`).
Distinct from _is_addrlike_call, which also counts deref/cast forms: assigning a *deref*
result (`*p`) to a value-typed variable is a legitimate value copy, so only `&x` indicates
a missing `*` in the annotation.
"""
if node is None: return 0
if node.kind() == ast.ASTKind.UnaryOp:
uo_ao: ast.UnaryOp* = ast.UnaryOp*(node)
if uo_ao.op == ast.OpKind.AddrOf: return 1
return 0
if node.kind() != ast.ASTKind.Call: return 0
cl_ao: ast.Call* = ast.Call*(node)
if cl_ao.func is None or cl_ao.func.kind() != ast.ASTKind.Attribute: return 0
at_ao: ast.Attribute* = ast.Attribute*(cl_ao.func)
if at_ao.value is None or at_ao.value.kind() != ast.ASTKind.Name: return 0
vn_ao: ast.Name* = ast.Name*(at_ao.value)
if vn_ao.id is None or string.strcmp(vn_ao.id, "c") != 0: return 0
if at_ao.attr is not None and string.strcmp(at_ao.attr, "Addr") == 0: return 1
return 0
def _DerefPunBits(self, pool: memhub.MemBuddy*, ty: mlirlite.LLVMType*) -> int:
"""位型 pun(`*a = *b`)的标量位宽:浮点取浮点位宽、整数取整型位宽、指针按 64、其余返回 0。
@@ -2508,6 +2530,13 @@ novirtual class AssignGenMixin:
if var_ty is not None and self.Gen.StmtGenDecl._is_void_type(var_ty) != 0:
self.Gen.TypeGenErr.fatal_error(aa, f"cannot declare a variable of void type '{nm.id}' (void may only be used for void* and typedef aliases)", "TypeError")
# 注解漏 `*` 的 typo:注解解析成"类(值类型)",右值却是取地址表达式(`&x`)。
# 这在类"引用表示"的宽容规则下会被静默接受,随后把栈/元素地址塞进值语义的槽位
# (典型症状:vptr/父指针指向栈区、对象被就地覆写)。明确报错并提示补 `*`。
if var_ty is not None and aa.value is not None and self._is_addr_of_expr(aa.value) == 1:
if var_ty.kind() == mlirlite.LLVMType.Struct:
self.Gen.TypeGenErr.fatal_error(aa, f"type annotation of '{nm.id}' is a class value type but the initializer is an address ('&...'): did you forget '*'? Write the pointer type ('T*') for a pointer target, or initialize with a value (e.g. 'T(...)')", "TypeError")
# t.Callable function pointer: register the return-type signature
callable_ret: mlirlite.LLVMType* = self.Gen.TypeGenErr.try_extract_callable_ret(pool, annot, trans._imported_modules, trans._from_imports, trans)
if callable_ret is not None: