Skip to content

Commit

Permalink
[cpp] Capture variables, parameters and this in List.Any/All lambda.
Browse files Browse the repository at this point in the history
  • Loading branch information
pfusik committed Oct 5, 2024
1 parent 98a3cce commit bc28dbd
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 7 deletions.
34 changes: 33 additions & 1 deletion GenCpp.fu
Original file line number Diff line number Diff line change
Expand Up @@ -1482,9 +1482,41 @@ public class GenCpp : GenCCpp
base.VisitBinaryExpr(expr, parent);
}

static bool HasLambdaCapture(FuExpr expr)
{
switch (expr) {
case FuAggregateInitializer init:
return init.Items.Any(item => HasLambdaCapture(item));
case FuLiteral:
case FuLambdaExpr: // TODO: nested lambdas
return false;
case FuInterpolatedString interp:
return interp.Parts.Any(part => HasLambdaCapture(part.Argument));
case FuSymbolReference symbol:
if (symbol.Left != null)
return HasLambdaCapture(symbol.Left);
if (symbol.Symbol is FuMember member)
return !member.IsStatic();
return !(symbol.Symbol.Parent is FuLambdaExpr); // TODO: nested lambdas
case FuUnaryExpr unary:
return unary.Inner != null && HasLambdaCapture(unary.Inner);
case FuBinaryExpr binary:
return HasLambdaCapture(binary.Left) || HasLambdaCapture(binary.Right);
case FuSelectExpr select:
return HasLambdaCapture(select.Cond) || HasLambdaCapture(select.OnTrue) || HasLambdaCapture(select.OnFalse);
case FuCallExpr call:
return HasLambdaCapture(call.Method) || call.Arguments.Any(arg => HasLambdaCapture(arg));
default:
assert false;
}
}

internal override void VisitLambdaExpr!(FuLambdaExpr expr)
{
Write("[](");
WriteChar('[');
if (HasLambdaCapture(expr.Body))
WriteChar('&');
Write("](");
if (expr.First.Type is FuOwningType || expr.First.Type.Id == FuId.StringStorageType) {
Write("const ");
WriteType(expr.First.Type, false);
Expand Down
32 changes: 31 additions & 1 deletion libfut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15084,9 +15084,39 @@ void GenCpp::visitBinaryExpr(const FuBinaryExpr * expr, FuPriority parent)
GenBase::visitBinaryExpr(expr, parent);
}

bool GenCpp::hasLambdaCapture(const FuExpr * expr)
{
if (const FuAggregateInitializer *init = dynamic_cast<const FuAggregateInitializer *>(expr))
return std::any_of(init->items.begin(), init->items.end(), [](const std::shared_ptr<FuExpr> &item) { return hasLambdaCapture(item.get()); });
else if (dynamic_cast<const FuLiteral *>(expr) || dynamic_cast<const FuLambdaExpr *>(expr))
return false;
else if (const FuInterpolatedString *interp = dynamic_cast<const FuInterpolatedString *>(expr))
return std::any_of(interp->parts.begin(), interp->parts.end(), [](const FuInterpolatedPart &part) { return hasLambdaCapture(part.argument.get()); });
else if (const FuSymbolReference *symbol = dynamic_cast<const FuSymbolReference *>(expr)) {
if (symbol->left != nullptr)
return hasLambdaCapture(symbol->left.get());
if (const FuMember *member = dynamic_cast<const FuMember *>(symbol->symbol))
return !member->isStatic();
return !dynamic_cast<const FuLambdaExpr *>(symbol->symbol->parent);
}
else if (const FuUnaryExpr *unary = dynamic_cast<const FuUnaryExpr *>(expr))
return unary->inner != nullptr && hasLambdaCapture(unary->inner.get());
else if (const FuBinaryExpr *binary = dynamic_cast<const FuBinaryExpr *>(expr))
return hasLambdaCapture(binary->left.get()) || hasLambdaCapture(binary->right.get());
else if (const FuSelectExpr *select = dynamic_cast<const FuSelectExpr *>(expr))
return hasLambdaCapture(select->cond.get()) || hasLambdaCapture(select->onTrue.get()) || hasLambdaCapture(select->onFalse.get());
else if (const FuCallExpr *call = dynamic_cast<const FuCallExpr *>(expr))
return hasLambdaCapture(call->method.get()) || std::any_of(call->arguments.begin(), call->arguments.end(), [](const std::shared_ptr<FuExpr> &arg) { return hasLambdaCapture(arg.get()); });
else
std::abort();
}

void GenCpp::visitLambdaExpr(const FuLambdaExpr * expr)
{
write("[](");
writeChar('[');
if (hasLambdaCapture(expr->body.get()))
writeChar('&');
write("](");
if (dynamic_cast<const FuOwningType *>(expr->first->type.get()) || expr->first->type->id == FuId::stringStorageType) {
write("const ");
writeType(expr->first->type.get(), false);
Expand Down
34 changes: 33 additions & 1 deletion libfut.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15431,9 +15431,41 @@ internal override void VisitBinaryExpr(FuBinaryExpr expr, FuPriority parent)
base.VisitBinaryExpr(expr, parent);
}

static bool HasLambdaCapture(FuExpr expr)
{
switch (expr) {
case FuAggregateInitializer init:
return init.Items.Exists(item => HasLambdaCapture(item));
case FuLiteral:
case FuLambdaExpr:
return false;
case FuInterpolatedString interp:
return interp.Parts.Exists(part => HasLambdaCapture(part.Argument));
case FuSymbolReference symbol:
if (symbol.Left != null)
return HasLambdaCapture(symbol.Left);
if (symbol.Symbol is FuMember member)
return !member.IsStatic();
return !(symbol.Symbol.Parent is FuLambdaExpr);
case FuUnaryExpr unary:
return unary.Inner != null && HasLambdaCapture(unary.Inner);
case FuBinaryExpr binary:
return HasLambdaCapture(binary.Left) || HasLambdaCapture(binary.Right);
case FuSelectExpr select:
return HasLambdaCapture(select.Cond) || HasLambdaCapture(select.OnTrue) || HasLambdaCapture(select.OnFalse);
case FuCallExpr call:
return HasLambdaCapture(call.Method) || call.Arguments.Exists(arg => HasLambdaCapture(arg));
default:
throw new NotImplementedException();
}
}

internal override void VisitLambdaExpr(FuLambdaExpr expr)
{
Write("[](");
WriteChar('[');
if (HasLambdaCapture(expr.Body))
WriteChar('&');
Write("](");
if (expr.First.Type is FuOwningType || expr.First.Type.Id == FuId.StringStorageType) {
Write("const ");
WriteType(expr.First.Type, false);
Expand Down
1 change: 1 addition & 0 deletions libfut.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2452,6 +2452,7 @@ class GenCpp : public GenCCpp
void writeMatchProperty(const FuSymbolReference * expr, std::string_view name);
void writeGtRawPtr(const FuExpr * expr);
void writeIsVar(const FuExpr * expr, const FuVar * def, FuPriority parent);
static bool hasLambdaCapture(const FuExpr * expr);
static bool isIsVar(const FuExpr * expr);
bool hasVariables(const FuStatement * statement) const;
void openNamespace();
Expand Down
46 changes: 45 additions & 1 deletion libfut.js
Original file line number Diff line number Diff line change
Expand Up @@ -15907,9 +15907,53 @@ export class GenCpp extends GenCCpp
super.visitBinaryExpr(expr, parent);
}

static #hasLambdaCapture(expr)
{
if (expr instanceof FuAggregateInitializer) {
const init = expr;
return init.items.some(item => GenCpp.#hasLambdaCapture(item));
}
else if (expr instanceof FuLiteral || expr instanceof FuLambdaExpr)
return false;
else if (expr instanceof FuInterpolatedString) {
const interp = expr;
return interp.parts.some(part => GenCpp.#hasLambdaCapture(part.argument));
}
else if (expr instanceof FuSymbolReference) {
const symbol = expr;
if (symbol.left != null)
return GenCpp.#hasLambdaCapture(symbol.left);
let member;
if ((member = symbol.symbol) instanceof FuMember)
return !member.isStatic();
return !(symbol.symbol.parent instanceof FuLambdaExpr);
}
else if (expr instanceof FuUnaryExpr) {
const unary = expr;
return unary.inner != null && GenCpp.#hasLambdaCapture(unary.inner);
}
else if (expr instanceof FuBinaryExpr) {
const binary = expr;
return GenCpp.#hasLambdaCapture(binary.left) || GenCpp.#hasLambdaCapture(binary.right);
}
else if (expr instanceof FuSelectExpr) {
const select = expr;
return GenCpp.#hasLambdaCapture(select.cond) || GenCpp.#hasLambdaCapture(select.onTrue) || GenCpp.#hasLambdaCapture(select.onFalse);
}
else if (expr instanceof FuCallExpr) {
const call = expr;
return GenCpp.#hasLambdaCapture(call.method) || call.arguments_.some(arg => GenCpp.#hasLambdaCapture(arg));
}
else
throw new Error();
}

visitLambdaExpr(expr)
{
this.write("[](");
this.writeChar(91);
if (GenCpp.#hasLambdaCapture(expr.body))
this.writeChar(38);
this.write("](");
if (expr.first.type instanceof FuOwningType || expr.first.type.id == FuId.STRING_STORAGE_TYPE) {
this.write("const ");
this.writeType(expr.first.type, false);
Expand Down
22 changes: 19 additions & 3 deletions test/ListAny.fu
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@ public class Test
{
int Value;

public static bool CaptureParam(int i)
{
List<int>() list; //FAIL: cl
list.Add(42);
return list.Any(it => it == i) && !list.Any(it => it < i); //FAIL: c
}

public bool CaptureThis()
{
List<int>() list;
list.Add(10);
return list.Any(it => it == Value) && !list.Any(it => it < Value);
}

public static bool Run()
{
List<Test#>() list; //FAIL: cl
List<Test#>() list;
Test# t = new Test();
t.Value = 5;
list.Add(t);
Expand All @@ -16,8 +30,10 @@ public class Test
list.Add(t);
List<int>() empty;
List<Test>() listRo;
return list.Any(it => it.Value > 10) //FAIL: c
return list.Any(it => it.Value > 10)
&& !empty.Any(it => true)
&& !listRo.Any(it => true);
&& !listRo.Any(it => true)
&& CaptureParam(42)
&& t.CaptureThis();
}
}

0 comments on commit bc28dbd

Please sign in to comment.