In the following code: Live http://coliru.stacked-crooked.com/a/b22bb3c9cd603bc7 #include <type_traits> #include <iostream> template<class datatype, class FN1, class FN2> auto if_else(std::true_type,datatype&& data, FN1 &&fn1, FN2 &&) {return fn1(std::forward<datatype>(data));} template<class datatype, class FN1, class FN2> auto if_else(std::false_type,datatype&& data, FN1 &&, FN2 &&fn2) {return fn2(std::forward<datatype>(data));} // --------------------------------------------------------------------- struct A{ int id= 4; }; struct B : A{ int h = 900; }; class Test{ template<class Ba> int fn1(Ba &a){ std::cout << "fn1 " << a.h; return a.h; }; template<class A> int fn2(A &a){ std::cout << "fn2 " << a.id; a.id = 9; return a.id; }; public: template<bool do_it> void go(){ std::integral_constant<bool,do_it> do_first; using datatype = typename std::conditional<do_it,B,A>::type; datatype data; std::cout << std::is_same<decltype(data), A>::value; std::cout << if_else(do_first, data, [&](auto /*B&*/ data){ // comment auto to get rid of error std::cout << std::endl; std::cout << std::is_same<decltype(data), B>::value; std::cout << std::endl; return fn1(/*static_cast<B&>*/(data)); // static cast not work with gcc }, [&](A& data){ return fn2(data); } ); } }; int main(){ Test().template go<false>(); Test().template go<true>(); return 0; } I got the following error: main.cpp:58:61: error: cannot call member function 'int Test::fn1(Ba&) [with Ba = B]' without object If change auto with B - works fine. And it compiles and work as is with clang. P.S. Maybe somehow related to this https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49554
Furthermore, if not pass "data" as a lambda function param, but capture it from current scope, it deduces type wrong. http://coliru.stacked-crooked.com/a/ae022b9d25d93490 Uncomment static_cast to make it work. But may be this is unrelated to described above issue.
Maybe Adam can have a look to this one too.
I found that as only I pass *this as parameter and then call functions from it, it works ok. But it have to be auto: /// This work std::cout << if_else(do_first, *this, [&](auto self){ return self.fn1(data); }, [&](auto self){ return self.fn2(data); } ) << '\n'; /// This not std::cout << if_else(do_first, *this, [&](Test self){ return self.fn1(data); }, [&](Test self){ return self.fn2(data); } ) << '\n'; http://coliru.stacked-crooked.com/a/272a5e0b8089a5c4 And what even more strange, it work if only one lambda is auto: std::cout << if_else(do_first, *this, [&](auto self){ return self.fn1(data); }, [&](Test self){ return self.fn2(data); } // ??? ) << '\n'; http://coliru.stacked-crooked.com/a/ed45a402a6c11d63
(In reply to tower120 from comment #0) > > std::cout << if_else(do_first, data, > [&](auto /*B&*/ data){ > std::cout << std::endl; > std::cout << std::is_same<decltype(data), B>::value; > std::cout << std::endl; > > return fn1(/*static_cast<B&>*/(data)); > }, > [&](A& data){ return fn2(data); } > ); > } > }; > This works if "this->" is used to explicitly specify the object upon which to call 'fn1'. It works whether or not the static_cast is present or whether auto or B is is used. It looks like a binding issue with lambda templates; the name 'fn1' is being seen as a reference to the member function rather than an attempt to bind it to the captured 'this' for a member call. The fact that clang compiles it fine makes me think that this is indeed a genuine bug and that the extra "this->" qualification is not required by the language. In dependent base member access, an explicit "this->" is required, but I don't think it should be necessary here. I'll have a look into it; but I've limited time to do so at the mo.
In an attempt to get a reduced testcase, I've uncovered an ICE. With the extra "this->" qualification on the reference to 'f' below, the code compiles fine. Alternatively, if 'this' is explicitly captured, the unqualified member call works too. There is clearly something a bit odd going on in this area. struct X { template <typename T> void f(T t) {} template <typename T> void g(T t) { [&] (auto x) { f(x); } (t); } }; int main() { X x; x.g(2); } :10:10: internal compiler error: Segmentation fault f(x); ^ 0xb8926f crash_signal ../../gcc/toplev.c:337 0x95afe8 contains_struct_check ../../gcc/tree.h:2840 0x95afe8 size_binop_loc(unsigned int, tree_code, tree_node*, tree_node*) ../../gcc/fold-const.c:1471 0x9d0f49 gimplify_compound_lval ../../gcc/gimplify.c:1998 0x9c89ae gimplify_expr(tree_node**, gimple_statement_base**, gimple_statement_base**, bool (*)(tree_node*), int) ../../gcc/gimplify.c:7618 0x9d2808 gimplify_call_expr ../../gcc/gimplify.c:2432 0x9c8b37 gimplify_expr(tree_node**, gimple_statement_base**, gimple_statement_base**, bool (*)(tree_node*), int) ../../gcc/gimplify.c:7637 0x9cd306 gimplify_stmt(tree_node**, gimple_statement_base**) ../../gcc/gimplify.c:5417 0x9c8bc2 gimplify_cleanup_point_expr ../../gcc/gimplify.c:5193 0x9c8bc2 gimplify_expr(tree_node**, gimple_statement_base**, gimple_statement_base**, bool (*)(tree_node*), int) ../../gcc/gimplify.c:8029 0x9cd306 gimplify_stmt(tree_node**, gimple_statement_base**) ../../gcc/gimplify.c:5417 0x9ce1af gimplify_bind_expr ../../gcc/gimplify.c:1100 0x9c907e gimplify_expr(tree_node**, gimple_statement_base**, gimple_statement_base**, bool (*)(tree_node*), int) ../../gcc/gimplify.c:7863 0x9cd306 gimplify_stmt(tree_node**, gimple_statement_base**) ../../gcc/gimplify.c:5417 0x9cec36 gimplify_body(tree_node*, bool) ../../gcc/gimplify.c:8773 0x9cf236 gimplify_function_tree(tree_node*) ../../gcc/gimplify.c:8926 0x84d117 analyze_function ../../gcc/cgraphunit.c:650 0x84e3bb analyze_functions ../../gcc/cgraphunit.c:1028 0x84fc45 finalize_compilation_unit() ../../gcc/cgraphunit.c:2333 0x63bece cp_write_global_declarations() ../../gcc/cp/decl2.c:4647
Reduced testcase: struct X { template <typename T> void f(T t) {} auto defer_f() { return [&] (auto x) { f(x); }; } }; int main() { X x; x.defer_f()(2); } Compiles if "this->f(x)" is used within 'defer_f'.
I'm not sure what you mean, about adding "this->". But this case is not working : http://coliru.stacked-crooked.com/a/d69de477f9a746cb But to be true, it not work with clang either.
(In reply to tower120 from comment #7) > I'm not sure what you mean, about adding "this->". > I meant spelling "fn1(data)" as "this->fn1(data)" to explicitly specify the subject of the member call. > But this case is not working : > http://coliru.stacked-crooked.com/a/d69de477f9a746cb > > But to be true, it not work with clang either. > In the case you link to, specifically within the member function template 'go', std::cout << if_else<do_it>( [&]{ return this->fn1(data); }, [&]{ return this->fn2(data); } ) << '\n'; you are building up two non-generic (non-template) lambdas that are not dependent on any template parameter. The if_else is, but the two arguments are not. Both have to be evaluated to pass to the if_else. Because they are not templates, the body of both lambdas must be well formed within 'go'. 'fn1' and 'fn2' are both being passed 'data' which is either of type 'A' or of type 'B'. There are no overloads of 'fn1' that can accept a 'B'. That's what both clang and g++ are getting at in their diagnostics. The fact that g++ proceeds to ICE on recovery is another issue.
(In reply to Adam Butcher from comment #8) > There are no overloads of 'fn1' that can accept a 'B'. > Oops, sorry. I meant to say that there are no overloads of 'fn1' that can accept an 'A'. Clearly a 'B' may be used as an 'A' but not vice versa.
I've found this and reduced it to: http://coliru.stacked-crooked.com/a/74930f2d4e963c6a Manually specifying this->work(); allows compilation. Changing auto to int also allows compilation.
*** Bug 64382 has been marked as a duplicate of this bug. ***
If the containing context is made a template (as per pr64382 and pr64466) or if the patch below is made to lambda.c, the synthesized default 'this' looks to get captured OK, but gimplification process crashes. The start looks promising. From gimplify_return_expr: return D.2190 = X::f (__closure->__this, x) through gimplify_call_expr: X::f (__closure->__this, x) but the gimplification of the subject argument __closure->__this crashes due to component_ref_field_offset returning NULL for it. I'm thinking the 'this' reference built with build_x_indirect_ref in maybe_resolve_dummy may need some extra bolstering in the generic lambda case. Another point maybe worthy of note: If a non-static, non-function member is referenced within the generic lambda, it causes 'this' to be default captured correctly (as it does when explicitly specifying "this->") and the compilation completes as expected. ------------------------------------------------------------------- Patch to default capture 'this' in generic lambdas ------------------------------------------------------------------- @@ -781,21 +797,38 @@ maybe_resolve_dummy (tree object, bool add_capture_p) tree type = TYPE_MAIN_VARIANT (TREE_TYPE (object)); gcc_assert (!TYPE_PTR_P (type)); - if (type != current_class_type - && current_class_type - && LAMBDA_TYPE_P (current_class_type) - && lambda_function (current_class_type) - && DERIVED_FROM_P (type, current_nonlambda_class_type ())) + if (type == current_class_type) + return object; + + tree lambda = ((current_class_type && LAMBDA_TYPE_P (current_class_type))? + lambda_function (current_class_type) : 0); + if (lambda && + (DERIVED_FROM_P (type, current_nonlambda_class_type ()) + || (DECL_TEMPLATE_INFO (lambda) + && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (lambda)) == lambda))) { /* In a lambda, need to go through 'this' capture. */ tree lam = CLASSTYPE_LAMBDA_EXPR (current_class_type); tree cap = lambda_expr_this_capture (lam, add_capture_p); -------------------------------------------------------------------
*** Bug 66769 has been marked as a duplicate of this bug. ***
*** Bug 67952 has been marked as a duplicate of this bug. ***
*** Bug 67050 has been marked as a duplicate of this bug. ***
This is really annoying. Is the patch going to fix it or nothing has been done?
x.g([]{}) made the following compile error only if it is called *after* calling f(x) without this->. It seems that a side effect of the problem. If I add this-> to f(x), all errors are disappeared. clang++ doesn't make a compile error. Error message -------------------- ccc_this2.cpp:9:10: note: no known conversion for argument 1 from ‘X::defer_f()::<lambda(auto:1)> [with auto:1 = X]::<lambda()>’ to ‘std::function<void()>’ gcc_this2.cpp:7:10: error: ‘void X::f(T) [with T = X::defer_f()::<lambda(auto:1)> [with auto:1 = X]::<lambda()>]’, declared using local type ‘X::defer_f()::<lambda(auto:1)> [with auto:1 = X]::<lambda()>’, is used but never defined [-fpermissive] void f(T) {} Code ----------------------------- #include <functional> struct X { // function template template <typename T> void f(T) {} // function that has std::function as a parameter void g(std::function<void()> = std::function<void()>()) {} // function (non template) void h() {} auto defer_f() { return [&] (auto x) { x.g([]{}); // Compile passed calling before f(x) f(x); // Compile error *1 (without this->) x.f([]{}); // Compile passed x.h(); // Compile passed x.g(); // Compile passed x.g([]{}); // Compile error (maybe side effect of *1) }; } }; int main() { X x; x.defer_f()(x); } Tested compiler -------------- gcc -v Using built-in specs. COLLECT_GCC=/usr/bin/gcc COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/lto-wrapper Target: x86_64-unknown-linux-gnu Configured with: /build/gcc-multilib/src/gcc-5-20160209/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release Thread model: posix gcc version 5.3.0 (GCC) clang++ -v clang version 3.7.1 (tags/RELEASE_371/final) Target: x86_64-unknown-linux-gnu Thread model: posix Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-unknown-linux-gnu/5.3.0 Found candidate GCC installation: /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/5.3.0 Found candidate GCC installation: /usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0 Found candidate GCC installation: /usr/lib64/gcc/x86_64-unknown-linux-gnu/5.3.0 Selected GCC installation: /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/5.3.0 Candidate multilib: .;@m64 Candidate multilib: 32;@m32 Selected multilib: .;@m64
Duplicate bug report: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=67274 Another example: http://stackoverflow.com/questions/38845720/gcc-fails-to-compile-generic-lambda-with-this-capture Still not fixed in 6.1, is anyone going to look at this in the near future?
*** Bug 77376 has been marked as a duplicate of this bug. ***
Reduced testcase from PR77376: struct a { void b(); void c(void *, int); }; void a::b() { auto d = [&](auto asdf) { c(asdf, 0); }; d(nullptr); }
Created attachment 40288 [details] smaller testcase reduced testcase avoiding nullptr_t indeterminacy
Firstly, apologies for not unassigning myself from this; I hope it hasn't stopped others from looking into it. I had meant to unassign myself after my last attempt to fix this in early 2015. Since then I haven't had any spare time to look into it. The patch I submitted in 2015 (https://gcc.gnu.org/ml/gcc-patches/2015-04/msg00617.html) does indeed solve this. However, it captures 'this' in generic lambdas in some cases where it is not required. This is not ideal (see the follow up list discussion starting from Jason's review https://gcc.gnu.org/ml/gcc-patches/2015-04/msg00934.html). The current implementation, though non-optimal, may be preferable to waiting for a better version as at least code can be written which is correct, minimal and doesn't surprise users (i.e. no 'this->' workarounds required). This bug could be closed in favor of a "'this' is captured in generic lambdas when it is not necessary'" with proofs along the lines as I suggested in https://gcc.gnu.org/ml/gcc-patches/2015-04/msg00959.html and https://gcc.gnu.org/ml/gcc-patches/2015-04/msg00973.html. I've just done some 'this' capture analysis using Clang (3.9.0), the current stock ArchLinux GCC (6.2.1 20160830) and my own build of GCC (7.0.0 20161215) including the patches above. Clang stops short of a fully optimal solution. In fact it is less optimal than GCC even for monomorphic lambdas. It seems to capture 'this' whenever it sees a name within the lambda body that resolves to a member, whether or not that reference requires 'this'. It even captures 'this' when a reference is made to static member of a _different_ class which is surprising. The following show a handful of cases. The comments inline indicate whether 'this' is captured (sizeof lambda == 8) or not captured (sizeof lambda == 1) for Clang (C), GCC (G) and GCC with my 2015 patches (G'). The line tagged with the asterisk is the only case in this set where the patch is non-optimal; "f (2.4)" unambiguously resolves to the static member "A::f", so 'this' need not be captured. Since, with the patch, GCC is correct and more optimal than Clang, I think it would make sense to go with it and raise a separate ticket to address the other issue. struct A { void b (); void f (int); static void f (double); }; struct O { void x (int); static void x (double); }; namespace N { void y (double); } template <int> struct diag; void A::b() { auto l0 = [&](auto z) { f (z); }; diag<sizeof l0> {}; // C:8 G:1 G':8 auto l1 = [&](auto) { f (2.4); }; diag<sizeof l1> {}; // C:8 G:1 G':8 * auto l2 = [&](auto) { O::x (2.4); }; diag<sizeof l2> {}; // C:8 G:1 G':1 auto l3 = [&](auto) { N::y (2.4); }; diag<sizeof l3> {}; // C:1 G:1 G':1 auto l4 = [&](auto) { }; diag<sizeof l4> {}; // C:1 G:1 G':1 auto l5 = [&](int z) { f (z); }; diag<sizeof l5> {}; // C:8 G:8 G':8 auto l6 = [&](int) { f (2.4); }; diag<sizeof l6> {}; // C:8 G:1 G':1 auto l7 = [&](int) { O::x (2.4); }; diag<sizeof l7> {}; // C:8 G:1 G':1 auto l8 = [&](int) { N::y (2.4); }; diag<sizeof l8> {}; // C:1 G:1 G':1 auto l9 = [&](int) { }; diag<sizeof l9> {}; // C:1 G:1 G':1 }
Thanks for the investigation Adam. I was wondering whether the pragmatic solution you describe was acceptable. https://gcc.gnu.org/ml/gcc-patches/2016-12/msg00885.html
Author: nathan Date: Tue Jan 17 18:22:34 2017 New Revision: 244544 URL: https://gcc.gnu.org/viewcvs?rev=244544&root=gcc&view=rev Log: PR c++/61636 * cp-tree.h (maybe_generic_this_capture): Declare. * lambda.c (resolvable_dummy_lambda): New, broken out of ... (maybe_resolve_dummy): ... here. Call it. (maybe_generic_this_capture): New. * parser.c (cp_parser_postfix_expression): Speculatively capture this in generic lambda in unresolved member function call. * pt.c (tsubst_copy_and_build): Force hard error from failed member function lookup in generic lambda. PR c++/61636 * g++.dg/cpp1y/pr61636-1.C: New. * g++.dg/cpp1y/pr61636-2.C: New. * g++.dg/cpp1y/pr61636-3.C: New. Added: trunk/gcc/testsuite/g++.dg/cpp1y/pr61636-1.C trunk/gcc/testsuite/g++.dg/cpp1y/pr61636-2.C trunk/gcc/testsuite/g++.dg/cpp1y/pr61636-3.C Modified: trunk/gcc/cp/ChangeLog trunk/gcc/cp/cp-tree.h trunk/gcc/cp/lambda.c trunk/gcc/cp/parser.c trunk/gcc/cp/pt.c trunk/gcc/testsuite/ChangeLog
Fixed. We capture this if we find at least one non-static member function. This agrees with Clang's implementation, after discussion with Richard Smith. Need to raise DR.
(In reply to Nathan Sidwell from comment #25) > Fixed. We capture this if we find at least one non-static member function. > This agrees with Clang's implementation, after discussion with Richard Smith. > Nice. Your [much cleaner] patch sorts out the starred case above too. With GCC master (7.0.0 20170119) with your patch the results are: auto l0 = [&](auto z) { f (z); }; // C:8 G:1 G':8 G'':8 auto l1 = [&](auto) { f (2.4); }; // C:8 G:1 G':8 G'':1 * fixed :) auto l2 = [&](auto) { O::x (2.4); }; // C:8 G:1 G':1 G'':1 auto l3 = [&](auto) { N::y (2.4); }; // C:1 G:1 G':1 G'':1 auto l4 = [&](auto) { }; // C:1 G:1 G':1 G'':1 auto l5 = [&](int z) { f (z); }; // C:8 G:8 G':8 G'':8 auto l6 = [&](int) { f (2.4); }; // C:8 G:1 G':1 G'':1 auto l7 = [&](int) { O::x (2.4); }; // C:8 G:1 G':1 G'':1 auto l8 = [&](int) { N::y (2.4); }; // C:1 G:1 G':1 G'':1 auto l9 = [&](int) { }; // C:1 G:1 G':1 G'':1 with closure sizes indicated for C = Clang RELEASE_391/final, G = GCC prior to fix, G' = GCC with my old patch, G'' = latest GCC with your patch. It's interesting you reference Clang's implementation though as, judging by the results of the above test, Clang is capturing 'this' far more often than it should be; even in cases where a enclosed class member is not referenced at all (in extreme cases the referenced fn is a static member function of a completely different class). And even stranger, Clang appears to behave the same for monomorphic lambdas too. Not sure what's going on there.
Author: mpolacek Date: Tue Mar 14 16:03:10 2017 New Revision: 246134 URL: https://gcc.gnu.org/viewcvs?rev=246134&root=gcc&view=rev Log: PR c++/79264 * lambda.c (maybe_generic_this_capture): Deal with template-id-exprs. * semantics.c (finish_member_declaration): Assert class is being defined. * g++.dg/cpp1y/pr61636-1.C: Augment. PR c++/61636 * cp-tree.h (maybe_generic_this_capture): Declare. * lambda.c (resolvable_dummy_lambda): New, broken out of ... (maybe_resolve_dummy): ... here. Call it. (maybe_generic_this_capture): New. * parser.c (cp_parser_postfix_expression): Speculatively capture this in generic lambda in unresolved member function call. * pt.c (tsubst_copy_and_build): Force hard error from failed member function lookup in generic lambda. * g++.dg/cpp1y/pr61636-1.C: New. * g++.dg/cpp1y/pr61636-2.C: New. * g++.dg/cpp1y/pr61636-3.C: New. Added: branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/pr61636-1.C branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/pr61636-2.C branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/pr61636-3.C Modified: branches/gcc-6-branch/gcc/cp/ChangeLog branches/gcc-6-branch/gcc/cp/cp-tree.h branches/gcc-6-branch/gcc/cp/lambda.c branches/gcc-6-branch/gcc/cp/parser.c branches/gcc-6-branch/gcc/cp/pt.c branches/gcc-6-branch/gcc/cp/semantics.c branches/gcc-6-branch/gcc/testsuite/ChangeLog