feat(codegen): 单目 & 支持对构造调用/函数调用结果取址

&ViperKernel() 此前被左值检查拒绝(只在 Name/Attribute/Subscript 放行)。构造调用 _translate_struct_ctor 返回的就是入口 alloca 的栈存储指针,取址即原样返回该指针;其它返回值落到入口临时槽 alloca+store。复用 translate_unaryop 已求值结果,避免构造被求值两遍(__before_init__/__init__ 跑两次)。
This commit is contained in:
TermiNexus committed 2026-09-27 15:15:59 +08:00
1 parent 4255ab9d32
commit beccb36b66
1 file changed
+39 -1
+39 -1
View File
@@ -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:
# &<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>()` 的取址结果(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
#