Skip to content

Commit

Permalink
by ref or &this capturing lambdas should have trivial self.x attribut…
Browse files Browse the repository at this point in the history
…e accesses rewritten to this->x like in method bodies
  • Loading branch information
ehren committed Jan 7, 2025
1 parent 77ac9a7 commit 0b1c9e5
Showing 1 changed file with 34 additions and 6 deletions.
40 changes: 34 additions & 6 deletions ceto/codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,10 @@ def is_template_test(expr):
return indt + funcdef + " {\n" + block_str + indt + "}\n\n"


def codegen_function_body(defnode : Call, block, cx):
# methods or functions only (not lambdas!)
assert defnode.func.name == "def"
# Replace self.x = y in a method (but not an inner lambda unless by-ref this-capturing) with this->x = y
def _replace_self_with_this(defnode):
assert defnode.func.name == "def" or isinstance(defnode.func, ArrayAccess) and defnode.func.func.name == "lambda" and any(c.name == "ref" for c in defnode.func.args)

# Replace self.x = y in a method (but not an inner lambda!) with this->x = y
need_self = False
for s in find_all(defnode, test=lambda a: a.name == "self", stop=lambda n: n.func and n.func.name in ["class", "struct"]):

Expand All @@ -385,8 +384,13 @@ def codegen_function_body(defnode : Call, block, cx):
while p is not defnode:
if creates_new_variable_scope(p):
# 'self.foo' inside e.g. a lambda body
replace_self = False
break

if isinstance(p.func, ArrayAccess) and p.func.func.name == "lambda" and p.func.args and p.func.args[0].name == "ref":
# TODO this should also check for &this capture etc
pass
else:
replace_self = False
break
p = p.parent

if replace_self and isinstance(s.parent,
Expand All @@ -409,6 +413,16 @@ def codegen_function_body(defnode : Call, block, cx):
s.parent.parent.func = arrow
else:
need_self = True

return need_self


def codegen_function_body(defnode : Call, block, cx):
# methods or functions only (not lambdas!)
assert defnode.func.name == "def"

need_self = _replace_self_with_this(defnode)

block_cx = cx.enter_scope()
block_cx.in_function_body = True
block_str = codegen_block(block, block_cx)
Expand Down Expand Up @@ -488,6 +502,8 @@ def codegen_lambda(node, cx):
# node.declared_type = declared_type # put type in normal position
# node.func = node.func.lhs # func is now 'lambda' keyword

capture_this_by_ref = False

if isinstance(node.func, ArrayAccess):
# explicit capture list

Expand All @@ -499,8 +515,12 @@ def codegen_capture_list_address_op(u : UnOp):
return "&" + codegen_node(u.args[0], cx)
return None

nonlocal capture_this_by_ref

if isinstance(a, Assign):
if ref_capture := codegen_capture_list_address_op(a.lhs):
if a.lhs.args[0].name == "this":
capture_this_by_ref = True
lhs = ref_capture
elif isinstance(a.lhs, Identifier) and not a.lhs.declared_type:
lhs = codegen_node(a.lhs, cx)
Expand All @@ -509,15 +529,20 @@ def codegen_capture_list_address_op(u : UnOp):
return lhs + " = " + codegen_node(a.rhs, cx)
else:
if ref_capture := codegen_capture_list_address_op(a):
if a.args[0].name == "this":
capture_this_by_ref = True
return ref_capture
if isinstance(a, UnOp) and a.op == "*" and a.args[0].name == "this":
return "*" + codegen_node(a.args[0])
if not isinstance(a, Identifier) or a.declared_type:
raise CodeGenError("Unexpected capture list item", a)
if a.name == "ref":
# special case non-type usage of ref
capture_this_by_ref = True
return "&"
elif a.name == "val":
if a.scope.find_def(a):
raise CodeGenError("no generic 'val' capture allowed because a variable named 'val' has been defined.", a)
return "="
return codegen_node(a, cx)

Expand Down Expand Up @@ -569,6 +594,9 @@ def is_capture(n):
else:
capture_list = ""

if capture_this_by_ref:
_replace_self_with_this(node)

return ("[" + ", ".join(capture_list) + "](" + ", ".join(params) + ")" + type_str + " {\n" +
codegen_block(block, newcx) + newcx.indent_str() + "}" + invocation_str)

Expand Down

0 comments on commit 0b1c9e5

Please sign in to comment.