diff --git a/App/lib/core/Gen/Expr/ExprGenOps.vp b/App/lib/core/Gen/Expr/ExprGenOps.vp index 053ed0d..4c849e0 100644 --- a/App/lib/core/Gen/Expr/ExprGenOps.vp +++ b/App/lib/core/Gen/Expr/ExprGenOps.vp @@ -896,9 +896,47 @@ novirtual class ExprGenOps(ExprGenBase.ExprGenBase): sptr_ao: llvmlite.Value* = self.Gen.ExprGenName.get_subscript_ptr(builder, pool, mod, uo.operand, trans) if sptr_ao is not None: return sptr_ao - self.Gen.TypeGenErr.fatal_error(node, "单目 '&' 取地址的操作数必须是左值(Name/Attribute/Subscript)", "TypeError") + elif uo.operand.kind() == ast.ASTKind.Call: + # &() → rvalue 取址。复用上面已求值的 operand(绝不能二次求值: + # 构造调用会再跑一遍 __before_init__/__init__)。判定见 _addrof_of_call。 + ac_ao: llvmlite.Value* = self._addrof_of_call(builder, pool, operand) + if ac_ao is not None: + return ac_ao + self.Gen.TypeGenErr.fatal_error(node, "单目 '&' 取地址的操作数必须是左值(Name/Attribute/Subscript)或构造调用/函数调用结果", "TypeError") return None return operand + + def _addrof_of_call(self, builder: llvmlite.IRBuilder*, + pool: memhub.MemBuddy*, + call_val: llvmlite.Value*) -> llvmlite.Value*: + """`&()` 的取址结果(call_val 是调用结果,已由 translate_unaryop 求值一次) + + 两种形态: + · 结构体/类构造调用(`&ViperKernel()`):CallGenCtor._translate_struct_ctor + 末尾明写"返回指针(而非结构体值)"——结果**就是**入口 alloca 的栈存储 + 指针 Ptr(Struct),即"栈构造结构体的地址"本身,直接原样返回。 + 不能在这里再套 alloca+store:那会得到 Ptr(Ptr(Struct))(存放地址的槽 + 地址),赋值端还得 load 一次才回落,语义也从"对象地址"变成"槽地址"。 + · 其它返回值(标量、按值返回的结构体值等):rvalue 的地址只能靠临时槽 + 承载——入口 alloca 同类型槽 → store → 返回槽地址(与 c.Addr(expr) 的 + Call 形式同口径,见 CallGenCBuiltin.translate_c_addr)。 + """ + if call_val is None or call_val.Ty is None: + return None + if self.Gen.ExprGenBase.is_ptr_type(call_val.Ty) != 0 and call_val.Ty.Pointee is not None: + pointee_is_struct: int = 0 + match call_val.Ty.Pointee: + case llvmlite.LLVMType.Struct(_, _, _): + pointee_is_struct = 1 + case _: + pointee_is_struct = 0 + if pointee_is_struct != 0: + return call_val + tmp_slot_ao: llvmlite.Value* = llvmlite.build_alloca_at_entry(builder, call_val.Ty) + if tmp_slot_ao is None: + return None + llvmlite.build_store(builder, call_val, tmp_slot_ao) + return tmp_slot_ao # ============================================================ # 翻译布尔运算 BoolOp(op, values) — 短路求值,返回 i1 #