feat(gen): 值引用自动取址接入虚分派/构造/属性/静态等全部方法调用路径
CallGenBase 新增 _vpi_auto_addr_mode(把原先埋在 _coerce_args_to_def_params 里的 vpi 注解解析抽成唯一实现,两处共用)与 _apply_auto_addr_to_call_args(同模块先查 AutoAddrModes,跨模块回退 vpi 注解)。接入原先完全不查表的路径:虚分派(原在 vpi 取址前 return)、_call_method_on_ptr 覆盖的构造 __new__/__init__(含泛型)与 dunder 分派、属性 getter/setter/deleter、静态/类方法、super()、FakeDuck;属性 setter 由 AssignGenMixin 传入 rhs AST 以支持 &T 取左值地址。
This commit is contained in:
1 parent
5c1b882169
commit
c20bd4734b
7 files changed
+143
-19
No files matched your search
@@ -2082,7 +2082,8 @@ novirtual class AssignGenMixin:
|
||||
# Narrow target to ast.Attribute* explicitly (required under the strict pointer rules)
|
||||
at_tg: ast.Attribute* = ast.Attribute*(target)
|
||||
# property setter: when attr is a setter, emit the setter call and skip the field store
|
||||
if self.Gen.CallGenProp._try_property_setter(pool, builder, mod, at_tg, rhs_val, trans) == 1: continue
|
||||
# (asgn.value gives the setter value param's AST for value-reference auto-address).
|
||||
if self.Gen.CallGenProp._try_property_setter(pool, builder, mod, at_tg, rhs_val, trans, asgn.value) == 1: continue
|
||||
# FakeDuck: write per-instance str fields through the {var}__meta__ shadow struct
|
||||
fd_an: ast.Attribute* = at_tg
|
||||
if fd_an is not None and fd_an.value is not None and fd_an.attr is not None:
|
||||
|
||||
@@ -176,6 +176,90 @@ novirtual class CallGenBase:
|
||||
return slot
|
||||
return av
|
||||
|
||||
# _vpi_auto_addr_mode - value-reference mode of parameter idx for a cross-module ({sha1}-mangled)
|
||||
# callee, read from its vpi definition-side param annotations. The parsing (split / ensure-load /
|
||||
# lookup / segment decode) lives here only; _coerce_args_to_def_params and _apply_auto_addr_to_call_args
|
||||
# both call it, so there is a single implementation. cur_sha1 is the module being compiled: its own
|
||||
# functions are covered by AutoAddrModes, so the (guaranteed-miss) vpi load is skipped for them.
|
||||
# Returns 0=none / 1=ref / 2=temp.
|
||||
def _vpi_auto_addr_mode(self, call_name: str, cur_sha1: str, idx: int) -> int:
|
||||
"""Cross-module ({sha1}.cls.method / {sha1}.func) value-reference mode from the vpi cache; 0 if none/unknown."""
|
||||
if call_name is None or idx < 0: return 0
|
||||
if GenApi.sha1_prefix_len(call_name) == 0: return 0
|
||||
# Same-module callee: AutoAddrModes (registration during this compilation) is authoritative.
|
||||
if cur_sha1 is not None and string.strncmp(call_name, cur_sha1, 16) == 0: return 0
|
||||
# split {sha1}.{rest}: if rest has '.', it is {cls}.{method}, else {func}
|
||||
rest: str = call_name + 17
|
||||
cls_nm: str = None
|
||||
meth_nm: str = rest
|
||||
dot_r: str = string.strrchr(rest, 46)
|
||||
if dot_r is not None:
|
||||
cls_len: sizet = uint64(dot_r) - uint64(rest)
|
||||
if cls_len > 0:
|
||||
cls_buf: bytes = stdlib.malloc(cls_len + 1)
|
||||
if cls_buf is not None:
|
||||
string.strncpy(cls_buf, rest, cls_len)
|
||||
cls_buf[cls_len] = '\0'
|
||||
cls_nm = cls_buf
|
||||
meth_nm = dot_r + 1
|
||||
if meth_nm is None or meth_nm[0] == '\0':
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return 0
|
||||
sha1_buf: bytes = stdlib.malloc(17)
|
||||
if sha1_buf is None:
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return 0
|
||||
string.strncpy(sha1_buf, call_name, 16)
|
||||
sha1_buf[16] = '\0'
|
||||
if self.TempDir is not None and self.TempDirLen > 0: VpiReader._ensure_vpi_param_annot_loaded(sha1_buf, self.TempDir)
|
||||
prm_annots: str = VpiReader._lookup_vpi_param_annots(sha1_buf, cls_nm, meth_nm)
|
||||
mode: int = 0
|
||||
if prm_annots is not None: mode = VpiReader._vpi_param_annot_seg_mode(prm_annots, idx)
|
||||
stdlib.free(sha1_buf)
|
||||
if cls_nm is not None: stdlib.free(cls_nm)
|
||||
return mode
|
||||
|
||||
# _apply_auto_addr_to_call_args - auto-address the user arguments of a call whose definition params
|
||||
# are value references (&T -> take the lvalue address / &temp T -> copy into a temp slot). The
|
||||
# per-parameter mode is the same-module AutoAddrModes table first, then (for a {sha1}-mangled callee)
|
||||
# its vpi annotations. head/count is the emitted argument chain; the first start_idx slots are
|
||||
# non-user (self/obj/cls); arg_nodes holds the source ASTs of the user arguments, indexed by
|
||||
# (param index - start_idx). Returns the rebuilt chain head; nodes beyond count (e.g. defaults
|
||||
# appended by the caller) are preserved. Idempotent: _apply_auto_addr_arg passes an already-pointer
|
||||
# argument through, so a caller that also ran translate_call_arg does not double-address.
|
||||
def _apply_auto_addr_to_call_args(self, builder: mlirlite.IRBuilder*, pool: memhub.MemBuddy*, mod: mlirlite.LLVMModule*,
|
||||
trans: HT.Translator*, callee_name: str, arg_nodes: list[ast.AST*]*,
|
||||
head: mlirlite.Value*, count: int, start_idx: int) -> mlirlite.Value*:
|
||||
"""Apply value-reference auto-address (&T / &temp T) to a call's user args; returns the chain head."""
|
||||
if head is None or count <= 0: return head
|
||||
if callee_name is None or callee_name[0] == '\0': return head
|
||||
if trans is None: return head
|
||||
new_head: mlirlite.Value* = None
|
||||
new_tail: mlirlite.Value* = None
|
||||
cur: mlirlite.Value* = head
|
||||
idx: int = 0
|
||||
while cur is not None and idx < count:
|
||||
node_v: mlirlite.Value* = cur
|
||||
if idx >= start_idx:
|
||||
# Redirect to the canonical CallGenBase sub-object: AutoAddrModes / TempDir live there
|
||||
# (registration targets self.Gen.CallGenBase), not on the sibling Call* sub-object.
|
||||
mode: int = self.Gen.CallGenBase._auto_addr_mode(callee_name, idx)
|
||||
if mode == 0: mode = self.Gen.CallGenBase._vpi_auto_addr_mode(callee_name, trans.ModuleSha1, idx)
|
||||
if mode != 0:
|
||||
arg_i: int = idx - start_idx
|
||||
arg_node: ast.AST* = None
|
||||
if arg_nodes is not None and arg_i < len(arg_nodes): arg_node = arg_nodes.get(arg_i)
|
||||
node_v = self.Gen.CallGenBase._apply_auto_addr_arg(builder, pool, mod, trans, mode, arg_node, cur)
|
||||
if node_v is None: node_v = cur
|
||||
if new_head is None: new_head = node_v
|
||||
else: mlirlite.value_set_next(new_tail, node_v)
|
||||
new_tail = node_v
|
||||
cur = mlirlite.value_get_next(cur)
|
||||
idx += 1
|
||||
if new_head is None: return head
|
||||
mlirlite.value_set_next(new_tail, cur)
|
||||
return new_head
|
||||
|
||||
def _lookup_module_sha1(self, mod_name: str) -> str:
|
||||
"""Look up the SHA1 for a module name via StubMerger.LookupSha1ByModName (exact match + package fallback); None if absent."""
|
||||
return StubMergerStore.LookupSha1ByModName(mod_name)
|
||||
|
||||
@@ -141,7 +141,7 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
# first argument (goes through virtual dispatch if the method is in the vtable).
|
||||
|
||||
# ============================================================
|
||||
def _call_method_on_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, self_ptr: mlirlite.Value*, extra_args: sizet*, extra_count: int, trans: HT.Translator*, node: ast.AST* = None, is_volatile: int = 0) -> mlirlite.Value*:
|
||||
def _call_method_on_ptr(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, self_ptr: mlirlite.Value*, extra_args: sizet*, extra_count: int, trans: HT.Translator*, node: ast.AST* = None, is_volatile: int = 0, arg_nodes: list[ast.AST*]* = None) -> mlirlite.Value*:
|
||||
"""Call a method on a struct pointer; adds a volatile barrier for constructors."""
|
||||
if pool is None or builder is None or class_name is None or method_name is None: return None
|
||||
if self_ptr is None: return None
|
||||
@@ -295,6 +295,13 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
|
||||
total_count: int = 1 + extra_count
|
||||
|
||||
# Value-reference (&T / &temp T) params: auto-address the user args before default-filling and
|
||||
# the param-type coercion below (a temp copy must be taken from the source-typed value). call_name
|
||||
# is the resolved call symbol ({sha}.Class.method / Class.method), matching the registration key.
|
||||
head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, head, total_count, 1)
|
||||
# Recover the tail after the (possibly rebuilt) prefix so default args append to the end.
|
||||
tail = head
|
||||
while tail is not None and mlirlite.value_get_next(tail) is not None: tail = mlirlite.value_get_next(tail)
|
||||
# Default-argument filling: if the call site supplies fewer args than the
|
||||
# definition, fill the missing ones (defined default, else zero).
|
||||
mdf_defaults: t.CPtr = None
|
||||
@@ -1202,6 +1209,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
if mc_has_vt == 1 and mc_method_idx >= 0:
|
||||
# Virtual dispatch must pass the adjusted self_arg (multi-inheritance
|
||||
# this-adjust) but read the vtable from the object head obj_ptr.
|
||||
# Virtual dispatch returns before the cross-module vpi coercion below, so the value-reference
|
||||
# (&T / &temp T) auto-address must be applied here (same-module table first, then vpi).
|
||||
head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, cargs, head, total_count, 1)
|
||||
return self._do_virtual_call(pool, builder, mod, class_name, method_name, self_arg, head, total_count, found_func, call_ret_ty, obj_ptr)
|
||||
|
||||
# Cross-module call: coerce args to the vpi definition-side params (width
|
||||
@@ -1510,7 +1520,9 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
pp_sp = mlirlite.param_get_next(pp_sp)
|
||||
pi_sp += 1
|
||||
if pp_sp is not None: pty_sp = mlirlite.param_get_ty(pp_sp)
|
||||
arg_val_sp: mlirlite.Value* = self.Gen.ExprGen.translate_value(builder, pool, mod, arg_node_sp, None, 0, trans, pty_sp)
|
||||
# Value-reference params (&T / &temp T): translate_call_arg applies the same-module auto-address
|
||||
# before the coercion below (cross-module vpi addressing is applied in bulk for the whole chain).
|
||||
arg_val_sp: mlirlite.Value* = self.Gen.CallGenBase.translate_call_arg(builder, pool, mod, trans, call_name, int(ai_sp) + 1, arg_node_sp, pty_sp, None)
|
||||
if arg_val_sp is None: continue
|
||||
if pty_sp is not None: arg_val_sp = self.Gen.ExprGenBase.coerce_to_type(builder, arg_val_sp, pty_sp, arg_node_sp)
|
||||
else:
|
||||
@@ -1549,6 +1561,8 @@ novirtual class CallGenCall(CallGenBase.CallGenBase):
|
||||
if anc_sha1_sp is None or self._dep_text_ll_exists(pool, anc_sha1_sp) == 0: ret_ty_sp = mlirlite.Int32(pool)
|
||||
else: self._fail_method_ret_ty(method_name, cl)
|
||||
# 7. Cross-module: create the declare; emit the call.
|
||||
# Value-reference (&T / &temp T) params: auto-address the parent-method args (self is slot 0).
|
||||
head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, cargs_sp, head, arg_count_sp, 1)
|
||||
if found_func is None: self._ensure_method_declare(pool, mod, call_name, ret_ty_sp, head, arg_count_sp)
|
||||
is_variadic_sp: int = self.Gen.CallGenVa._check_and_promote_variadic_call(pool, builder, mod, call_name, head, arg_count_sp, cargs_sp, 1, trans)
|
||||
return mlirlite.build_call(builder, call_name, head, arg_count_sp, ret_ty_sp, is_variadic_sp)
|
||||
|
||||
@@ -127,7 +127,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
new_arg_vals[new_arg_count] = sizet(new_av)
|
||||
new_arg_count += 1
|
||||
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile)
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile, cargs)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
|
||||
@@ -159,7 +159,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
arg_vals[real_arg_count] = sizet(av)
|
||||
real_arg_count += 1
|
||||
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__init__", storage_ptr, arg_vals, real_arg_count, trans, diag_node_ct, is_volatile)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__init__", storage_ptr, arg_vals, real_arg_count, trans, diag_node_ct, is_volatile, cargs)
|
||||
elif has_new_flag == 0:
|
||||
# No __init__ and no __new__: store args field by field
|
||||
# (with __new__, args were already consumed and must not be stored).
|
||||
@@ -394,7 +394,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
if new_arg_count < 32:
|
||||
new_arg_vals[new_arg_count] = sizet(nkw_av)
|
||||
new_arg_count += 1
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile)
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__new__", tmp, new_arg_vals, new_arg_count, trans, diag_node_ct, is_volatile, cargs)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
|
||||
@@ -429,7 +429,7 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
init_arg_count += 1
|
||||
|
||||
# call __init__(storage_ptr, *args)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__init__", storage_ptr, init_arg_vals, init_arg_count, trans, diag_node_ct, is_volatile)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, class_name, "__init__", storage_ptr, init_arg_vals, init_arg_count, trans, diag_node_ct, is_volatile, cargs)
|
||||
|
||||
# return the pointer
|
||||
return storage_ptr
|
||||
@@ -850,13 +850,19 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
# volatile prefix: `a: T = volatile list[int](mb)` -- the ctor writes are volatile
|
||||
gc_volatile: int = cl.IsVolatile
|
||||
|
||||
# AST nodes matching extra_args (positional slots) for the value-reference (&T / &temp T)
|
||||
# auto-address inside _call_method_on_ptr. With keyword args the slot mapping needs the
|
||||
# __init__ param table, so it is skipped there (temp copies still work; a ref lvalue is skipped).
|
||||
gc_arg_nodes: list[ast.AST*]* = cargs
|
||||
if kw_count > 0: gc_arg_nodes = None
|
||||
|
||||
# Storage pointer: alloca by default, or __new__'s return when present (__new__ is
|
||||
# optional; matches _translate_struct_ctor to avoid over-declaring for classes without it).
|
||||
storage_ptr: mlirlite.Value* = tmp
|
||||
has_new_flag: int = 0
|
||||
if struct_entry is not None: has_new_flag = struct_entry.HasNew
|
||||
if has_new_flag != 0:
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, extra_args, arg_count, trans, None, gc_volatile)
|
||||
new_ptr: mlirlite.Value* = self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__new__", tmp, extra_args, arg_count, trans, None, gc_volatile, gc_arg_nodes)
|
||||
if new_ptr is not None:
|
||||
storage_ptr = new_ptr
|
||||
# __new__ declares `-> t.CPtr` (i8*) but the class type is Ptr(struct_ty).
|
||||
@@ -871,6 +877,6 @@ novirtual class CallGenCtor(CallGenBase.CallGenBase):
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__before_init__", storage_ptr, None, 0, trans, None, gc_volatile)
|
||||
|
||||
# call __init__(storage_ptr, *args) (arg_count = slots 0..highest filled)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, extra_args, arg_count, trans, None, gc_volatile)
|
||||
self.Gen.CallGenCall._call_method_on_ptr(pool, builder, mod, spec_name, "__init__", storage_ptr, extra_args, arg_count, trans, None, gc_volatile, gc_arg_nodes)
|
||||
|
||||
return storage_ptr
|
||||
@@ -124,6 +124,9 @@ novirtual class CallGenFd(CallGenBase.CallGenBase):
|
||||
if fd_exact is None: fd_exact = self._LookupMethodRetTyFromTextLL(pool, fd_sha1, fd_cls, method_name)
|
||||
if fd_exact is not None: fd_ret_ty = fd_exact
|
||||
fd_total: int = 1 + fd_can
|
||||
# Value-reference (&T / &temp T) params of the resolved extension method: auto-address the
|
||||
# user args (the shadow self pointer occupies slot 0).
|
||||
fd_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, fd_fname, fd_args, fd_head, fd_total, 1)
|
||||
return mlirlite.build_call(builder, fd_fname, fd_head, fd_total, fd_ret_ty, 0)
|
||||
# _inject_requires_fields - FakeDuck: inject requires fields from the with context.
|
||||
# For each class __requires__ field (registered by fd1), call _find_provider(field) at runtime; if non-NULL bitcast and store it (conditional inject); skip when _find_provider is unregistered.
|
||||
|
||||
@@ -209,9 +209,11 @@ novirtual class CallGenLib(CallGenBase.CallGenBase):
|
||||
arg_node: ast.AST* = None
|
||||
if cargs is not None and arg_idx >= 0 and arg_idx < len(cargs): arg_node = cargs.get(arg_idx)
|
||||
# Value-reference params (&T / &temp T): auto-address the argument (cross-module callee).
|
||||
# The mode comes from the definition-side vpi annotation segment; if the arg is not a
|
||||
# pointer it is addressed (ref: lvalue) or copied into a temp slot (temp).
|
||||
mode_cm: int = VpiReader._vpi_param_annot_seg_mode(prm_annots, idx)
|
||||
# The mode comes from the definition-side vpi annotation segment (shared resolver); if the
|
||||
# arg is not a pointer it is addressed (ref: lvalue) or copied into a temp slot (temp).
|
||||
vpi_cur_sha: str = None
|
||||
if trans is not None: vpi_cur_sha = trans.ModuleSha1
|
||||
mode_cm: int = self.Gen.CallGenBase._vpi_auto_addr_mode(call_name, vpi_cur_sha, idx)
|
||||
if mode_cm != 0 and trans is not None and trans.Module is not None:
|
||||
node_v = self.Gen.CallGenBase._apply_auto_addr_arg(builder, pool, trans.Module, trans, mode_cm, arg_node, node_v)
|
||||
# "str / bytes are not generic pointer wildcards": void* / t.CPtr / rawptr may
|
||||
|
||||
@@ -23,7 +23,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
|
||||
# _translate_property_call - translate a property/static method call.
|
||||
# Resolve the call name like _translate_method_call; call_head/call_count is the prebuilt arg list.
|
||||
def _translate_property_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, call_head: mlirlite.Value*, call_count: int, trans: HT.Translator*) -> mlirlite.Value*:
|
||||
def _translate_property_call(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, class_name: str, method_name: str, call_head: mlirlite.Value*, call_count: int, trans: HT.Translator*, arg_nodes: list[ast.AST*]* = None, start_idx: int = 0) -> mlirlite.Value*:
|
||||
"""Translate a property/static method call, return the call result Value"""
|
||||
if pool is None or builder is None or class_name is None or method_name is None: return None
|
||||
|
||||
@@ -74,6 +74,10 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if cls_sha1_rt is None or self._dep_text_ll_exists(pool, cls_sha1_rt) == 0: call_ret_ty = mlirlite.Int32(pool)
|
||||
else: call_ret_ty = self._fail_method_ret_ty(method_name)
|
||||
|
||||
# Value-reference (&T / &temp T) params: auto-address the user args before param-type
|
||||
# coercion (self/obj or the classmethod cls occupies index 0, skipped via start_idx).
|
||||
call_head = self._apply_auto_addr_to_call_args(builder, pool, mod, trans, call_name, arg_nodes, call_head, call_count, start_idx)
|
||||
|
||||
# Coerce args to the declared param types (same as _translate_method_call); target func: found_func,
|
||||
# else the mangled module declare. Otherwise call(i32) vs declare(i64) mismatches and llc errors.
|
||||
tgt_func_pc: mlirlite.Function* = found_func
|
||||
@@ -135,13 +139,13 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
if class_name is None: return None
|
||||
meta: int = self.Gen.ClassGenBase.lookup_method_meta(class_name, at_node.attr)
|
||||
if meta & GenTypes.META_PROP_GETTER == 0: return None
|
||||
# Single-arg call: obj -> getter
|
||||
# Single-arg call: obj -> getter (no user args to auto-address; obj is slot 0)
|
||||
mlirlite.value_set_next(obj_ptr, None)
|
||||
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans)
|
||||
return self._translate_property_call(pool, builder, mod, class_name, at_node.attr, obj_ptr, 1, trans, None, 1)
|
||||
# _try_property_setter - try to translate obj.attr = v into a property setter call.
|
||||
# If the class has an @x.setter for attr, emit call Class.attr$set(obj, v) and return 1; else 0.
|
||||
|
||||
def _try_property_setter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, rhs_val: mlirlite.Value*, trans: HT.Translator*) -> int:
|
||||
def _try_property_setter(self, pool: memhub.MemBuddy*, builder: mlirlite.IRBuilder*, mod: mlirlite.LLVMModule*, target_attr: ast.Attribute*, rhs_val: mlirlite.Value*, trans: HT.Translator*, rhs_node: ast.AST* = None) -> int:
|
||||
"""Try a property setter assignment, return 1=handled (call emitted)"""
|
||||
if pool is None or builder is None or mod is None or trans is None: return 0
|
||||
if target_attr is None or target_attr.attr is None or rhs_val is None: return 0
|
||||
@@ -160,7 +164,13 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
setter_name_buf: str = pool.alloc(128)
|
||||
if setter_name_buf is None: return 0
|
||||
viperlib.snprintf(setter_name_buf, 128, "%s$set", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name, setter_name_buf, obj_val, 2, trans)
|
||||
# Value-reference (&T / &temp T) setter value param: expose the rhs AST as user arg 0
|
||||
# (obj is slot 0) so the helper can take the lvalue address / build a temp copy.
|
||||
set_arg_nodes: list[ast.AST*]* = None
|
||||
if rhs_node is not None:
|
||||
set_arg_nodes = list[ast.AST*](pool, 1)
|
||||
if set_arg_nodes is not None: set_arg_nodes.append(rhs_node)
|
||||
self._translate_property_call(pool, builder, mod, class_name, setter_name_buf, obj_val, 2, trans, set_arg_nodes, 1)
|
||||
return 1
|
||||
# _try_property_deleter - try to translate del obj.attr into a property deleter call.
|
||||
# If the class has an @x.deleter for attr, emit call Class.attr$del(obj) and return 1; else 0.
|
||||
@@ -181,7 +191,7 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
del_name_buf: str = pool.alloc(128)
|
||||
if del_name_buf is None: return 0
|
||||
viperlib.snprintf(del_name_buf, 128, "%s$del", target_attr.attr)
|
||||
self._translate_property_call(pool, builder, mod, class_name, del_name_buf, obj_val, 1, trans)
|
||||
self._translate_property_call(pool, builder, mod, class_name, del_name_buf, obj_val, 1, trans, None, 1)
|
||||
return 1
|
||||
# _translate_static_class_call - translate ClassName.method(args) for static/class methods.
|
||||
# @staticmethod: call Class.method(args...). @classmethod: prepend a stack-allocated class pointer.
|
||||
@@ -232,7 +242,11 @@ novirtual class CallGenProp(CallGenBase.CallGenBase):
|
||||
|
||||
# Zero-arg @staticmethod (e.g. PyListObj.new()): head=None is a valid no-arg call, so still route
|
||||
# through _translate_property_call; returning None would fall back to bare-name lookup and fail.
|
||||
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans)
|
||||
# Value-reference (&T / &temp T) user args: static methods have no self slot (start_idx=0),
|
||||
# classmethods carry the cls pointer at slot 0 (start_idx=1).
|
||||
sc_start_idx: int = 0
|
||||
if is_class != 0: sc_start_idx = 1
|
||||
return self._translate_property_call(pool, builder, mod, class_name, method_name, head, total_count, trans, cargs, sc_start_idx)
|
||||
# _is_list_builtin_method - test whether (class_name, method_name) is a list generic builtin.
|
||||
# list[T] methods are inlined by translate_call's list branch, not registered as functions.
|
||||
|
||||
|
||||
Reference in new issue
Block a user