feat(codegen): 单目 & 支持对构造调用/函数调用结果取址
&ViperKernel() 此前被左值检查拒绝(只在 Name/Attribute/Subscript 放行)。构造调用 _translate_struct_ctor 返回的就是入口 alloca 的栈存储指针,取址即原样返回该指针;其它返回值落到入口临时槽 alloca+store。复用 translate_unaryop 已求值结果,避免构造被求值两遍(__before_init__/__init__ 跑两次)。
This commit is contained in:
1 parent
4255ab9d32
commit
beccb36b66
1 file changed
+39
-1
@@ -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
|
||||
#
|
||||
|
||||
Reference in new issue
Block a user