Repository navigation
[QUESTION] How to properly capture this pointer in lambda #281
Copy link
Copy link
Closed
Description
In the current implementation of cppfront (4c52d2d) I am trying to capture this pointer to lambda:
html: namespace = {
element: type = {
children: html::children = ();
parent: *element;
set_parent: (inout this, p : *element) = parent = p;
operator=: (out this, forward cs : html::children) = {
children = cs;
parent = nullptr;
for children do :(inout v) = {
std::visit(:(inout c) = {
c.set_parent(this&$); // <-- trying to capture a pointer to this
}, v);
}
}
}
}that produces (skipping boilerplate_:
for ( auto&& cpp2_range = children; auto& v : cpp2_range ) {
std::visit([_0 = (&(*this))](auto& c) -> void{
CPP2_UFCS(set_parent, c, _0);
}, v);
}and fail to compile with an error:
[build] html/html.h2:115:31: error: 'this' cannot be implicitly captured in this context
[build] CPP2_UFCS(set_parent, c, _0);
[build] ^
[build] html/html.h2:115:21: note: explicitly capture 'this'
[build] CPP2_UFCS(set_parent, c, _0);
[build] ^
When capturing value before lambda creation like the following:
self := this&;
for children do :(inout v) = {
std::visit(:(inout c) = {
c.set_parent(self$);
}, v);
}Generates:
auto self {&(*this)};
for ( auto&& cpp2_range = children; auto& v : cpp2_range ) {
std::visit([_0 = self](auto& c) -> void{
CPP2_UFCS(set_parent, c, _0);
}, v);
}That also ends with an error:
[build] html/html.h2:115:31: error: 'this' cannot be implicitly captured in this context
[build] CPP2_UFCS(set_parent, c, _0);
[build] ^
[build] html/html.h2:115:21: note: explicitly capture 'this'
[build] CPP2_UFCS(set_parent, c, _0);
[build] ^
When trying another syntax:
for children do :(inout v) = {
std::visit(:(inout c) = {
c.set_parent(this$&);
}, v);
}That worked and produced:
for ( auto&& cpp2_range = children; auto& v : cpp2_range ) {
std::visit([_0 = (*this)](auto& c) -> void{
CPP2_UFCS(set_parent, c, &_0);
}, v);
}Unfortunately, this solution makes a copy of *this and calls set_parent with the address to the copy.
Question
Could you tell me how to capture a pointer to this in lambda?
Reactions are currently unavailable
Activity
hsutter commentedon Mar 20, 2023
Thanks, I'll take a look. I thought the
thiscapture was only deprecated, not banned, as of C++20, but even so I should make sure we capture it the right way.Also: This example found a bug in the code I just pushed, because it has a lambda inside an
operator=... I'll take a look at what's failing there too.hsutter commentedon Mar 21, 2023
Interim update: I can't repro your full example because I don't have all the types, but here's what I know so far...
Capturing a pointer to
thisactually works fine, just like you did it. Here's a simpler example that works and does what you'd expect:The problem seems to be when a member function has a lambda that does a UFCS call to a member function (that's a mouthful), because that causes the member function name to appear unadorned. In this example the lambda contains the expression
c.set_parent(this&$);which turns intoCPP2_UFCS(set_parent, c, _0);. I suspect that if you change the code toc.set_parent(blah);you might find the same problem (i.e., it's not actually about capturingthis), because that would generateCPP2_UFCS(set_parent, c, blah);which is a non-this-qualified use ofset_parent.Here's a distilled repro:
That's what I have so far...
hsutter commentedon Mar 22, 2023
(Again, the answer to your original question "how to capture
thispointer" is: Yes, the syntax you triedthis&$works fine. See above.)I think I've found the problem that your code hit, and it seems to be a bug in Clang and MSVC. What's happening is that Clang and MSVC are not properly discarding the not-taken
if constexprbranch in the UFCS macro's lambda -- here is a minimal distilled case: https://godbolt.org/z/M47zzMsoTI don't have access to your entire test case, but I'm about to push a commit that I've verified does fix a slightly-distilled version of your test case. @filipsajdak please check to see whether it fixes your whole test case.
Thanks again for reporting, this was a subtle issue where (if I'm not mistaken) two of the three major commercial C++ compilers had the same bug! That doesn't happen every day.
Fix generation when `operator=` nests a lambda, and work around Clang…
filipsajdak commentedon Mar 22, 2023
Hi @hsutter,
Thank you for working on that issue, and I am sorry I did not help with it so far - I am stuck with other work.
I have been investigating this issue. I have found at least one issue (that still manifests in the current main (de25e0e). Also, I have simplified the code not to contain unknown types:
element: type = { children: std::vector<std::variant<std::unique_ptr<element>>> = (); parent: *element; get_children: (this) -> forward _ = children; get_children: (inout this) -> forward _ = children; get_parent: (this) -> forward _ = parent; get_parent: (inout this) -> forward _ = parent; set_parent: (inout this, p : *element) = parent = p; operator=: (out this, forward t : _ ) = { parent = nullptr; } operator=: (out this, forward cs : std::vector<std::unique_ptr<element>>) = { children = cs; parent = nullptr; for children do :(inout c) = { c*.set_parent(this&); // case 1: cpp2 works, cpp1 fail } } operator=: (out this, forward cs : std::vector<std::variant<std::unique_ptr<element>>>) = { children = cs; parent = nullptr; for children do :(inout v) = { std::visit(:(inout c) = { c*.set_parent(this&$); // case 2: cpp2 fail (ICE) }, v); } } }There are two cases here.
Case 1 is the original one. It fails even without using
std::variantandstd::visit.Case 2 causes ICE in cppfront - it looks like
build_capture_lambda_intro_for()function is called two times that causecap.strandcap.str_suppressed_movecontains duplicated symbol inside - the dirty fix is below patch:Case 2 still fail on cpp1 side.
filipsajdak commentedon Mar 22, 2023
@hsutter I am checking your latest change.
filipsajdak commentedon Mar 22, 2023
@hsutter what I can quickly check is that the following code:
element: type = { children: std::vector<std::variant<std::unique_ptr<element>>> = (); parent: *element; get_children: (this) -> forward _ = children; get_children: (inout this) -> forward _ = children; get_parent: (this) -> forward _ = parent; get_parent: (inout this) -> forward _ = parent; set_parent: (inout this, p : *element) = parent = p; operator=: (out this, forward t : _ ) = { parent = nullptr; } operator=: (out this, forward cs : std::vector<std::variant<std::unique_ptr<element>>>) = { children = cs; parent = nullptr; for children do :(inout v) = { std::visit(:(inout c) = { c*.set_parent(this&$); // cpp2 fail (ICE) }, v); } } }with the patch:
Generates:
It contains an issue in the implementation of
auto operator=(auto&& t) -> element&- nothing is assigned to thechildrenmember:After correcting it manually, it compiles fine.
That is all I can do now - running original test cases requires me to do the rebase of the branch with raw string literals, which I will do later.
Thank you again for investigating it, and sorry for not helping more with it.
filipsajdak commentedon Mar 22, 2023
I have rebased my other branch, and I am not getting the previous errors, but I have plenty of new ones:
I will investigate and get back to you with more content - it may be in the new issue.
I see that there is an issue with generating:
auto operator=(cpp2::in<std::string> n) -> spec&- cppfront generates something that looks like a constructors initialization list, but in assignment operator:filipsajdak commentedon Mar 24, 2023
@hsutter I have distilled three issues that I have reported as separate issues: #290, #291, and #292
filipsajdak commentedon Mar 27, 2023
@hsutter as
thisis captured properly and I have created three other issues for tracking. We can close this one.Emit anonymous functions as `mutable` lambdas
Fix generation when `operator=` nests a lambda, and work around Clang…
JohelEGP commentedon Dec 6, 2023
Is there an MSVC bug report for #281 (comment)?
I suppose the LLVM issue linked at #555 (comment) works for Clang.
It's too bad that a function expression in a
thisfunction doesn't convert to a function pointer.