diff --git a/App/lib/core/Gen/AssignGenMixin.vp b/App/lib/core/Gen/AssignGenMixin.vp index 5d318d1..78881b4 100644 --- a/App/lib/core/Gen/AssignGenMixin.vp +++ b/App/lib/core/Gen/AssignGenMixin.vp @@ -562,8 +562,17 @@ novirtual class AssignGenMixin: return 1 if string.strcmp(buf_a, buf_b) == 0 else 0 def _is_addrlike_call(self, node: ast.AST*) -> int: - """Return 1 if the rhs is an explicit c.Addr/c.Deref-style pointer call.""" - if node is None or node.kind() != ast.ASTKind.Call: return 0 + """Return 1 if the rhs is an explicit pointer expression (c.Addr/c.Deref/c.Cast/c.Ptr call, or new-syntax `&x` / `*p`).""" + if node is None: return 0 + # New syntax (R3/R4): `&x` and `*p` parse as UnaryOp(AddrOf) / UnaryOp(Deref) and replace the + # c.Addr / c.Deref call forms. A pointer-valued RHS must not take the module-global + # "object with static storage -> copy the object" branch, so both forms count as addrlike. + if node.kind() == ast.ASTKind.UnaryOp: + uo_al: ast.UnaryOp* = ast.UnaryOp*(node) + if uo_al.op == ast.OpKind.AddrOf: return 1 + if uo_al.op == ast.OpKind.Deref: return 1 + return 0 + if node.kind() != ast.ASTKind.Call: return 0 cl: ast.Call* = ast.Call*(node) if cl.func is None or cl.func.kind() != ast.ASTKind.Attribute: return 0 at: ast.Attribute* = ast.Attribute*(cl.func) diff --git a/App/lib/core/Gen/Expr/ExprGenAttr.vp b/App/lib/core/Gen/Expr/ExprGenAttr.vp index 237e66d..472f0cd 100644 --- a/App/lib/core/Gen/Expr/ExprGenAttr.vp +++ b/App/lib/core/Gen/Expr/ExprGenAttr.vp @@ -236,8 +236,19 @@ novirtual class ExprGenAttr(ExprGenBase.ExprGenBase): # _get_cderef_attr_base - base pointer of a c.Deref(ptr).attr access (like *p.a → p->a): return the ptr # itself, not the loaded struct value; None for non-c.Deref forms (_deref_if_ptr_ptr resolves the level). def _get_cderef_attr_base(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*, at_value: ast.AST*, trans: HT.Translator* = None) -> mlirlite.Value*: - """Return the ptr itself if at.value is a c.Deref(ptr) call, else None (no load).""" - if at_value is None or at_value.kind() != ast.ASTKind.Call: return None + """Return the ptr itself for a dereferenced attribute base (`c.Deref(ptr).attr` / `(*ptr).attr`), else None (no load).""" + if at_value is None: return None + # New syntax form: `(*ptr).attr` is AST UnaryOp(OpKind.Deref) — identical semantics to the legacy + # `c.Deref(ptr).attr`. Without this branch the attribute write path sees a *loaded struct value* + # as the base and fails with "base=非指针类型,无法做属性写入"; the read path silently does an + # extra struct load. Both must GEP the pointer itself (R4 migration of `c.Deref` -> `*`). + if at_value.kind() == ast.ASTKind.UnaryOp: + uo_cd: ast.UnaryOp* = ast.UnaryOp*(at_value) + if uo_cd.op != ast.OpKind.Deref or uo_cd.operand is None: return None + inner_uo: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, uo_cd.operand, None, 0, trans) + if inner_uo is not None and self.is_ptr_type(inner_uo.Ty) != 0: return inner_uo + return None + if at_value.kind() != ast.ASTKind.Call: return None cl_cd: ast.Call* = ast.Call*(at_value) if cl_cd.func is None or cl_cd.func.kind() != ast.ASTKind.Attribute: return None f_cd: ast.Attribute* = ast.Attribute*(cl_cd.func)