Bug 61636 - generic lambda: segfault / "cannot call member function without object"
Summary: generic lambda: segfault / "cannot call member function without object"
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 4.9.0
: P3 normal
Target Milestone: 6.4
Assignee: Nathan Sidwell
URL:
Keywords: rejects-valid
: 66769 67050 67952 77376 (view as bug list)
Depends on:
Blocks:
 
Reported: 2014-06-28 08:13 UTC by tower120
Modified: 2018-01-11 12:49 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2014-07-06 00:00:00


Attachments
smaller testcase (97 bytes, text/plain)
2016-12-09 15:43 UTC, Nathan Sidwell
Details

Note You need to log in before you can comment on or make changes to this bug.
Description tower120 2014-06-28 08:13:19 UTC
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
Comment 1 tower120 2014-06-28 08:22:08 UTC
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.
Comment 2 Paolo Carlini 2014-06-28 08:55:01 UTC
Maybe Adam can have a look to this one too.
Comment 3 tower120 2014-06-28 13:17:31 UTC
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
Comment 4 Adam Butcher 2014-07-06 19:58:00 UTC
(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.
Comment 5 Adam Butcher 2014-07-06 20:15:25 UTC
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
Comment 6 Adam Butcher 2014-07-06 20:39:35 UTC
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'.
Comment 7 tower120 2014-07-06 21:55:07 UTC
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.
Comment 8 Adam Butcher 2014-07-07 07:07:23 UTC
(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.
Comment 9 Adam Butcher 2014-07-07 07:15:11 UTC
(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.
Comment 10 Jeaye 2014-07-26 07:09:50 UTC
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.
Comment 11 Adam Butcher 2015-03-09 23:58:28 UTC
*** Bug 64382 has been marked as a duplicate of this bug. ***
Comment 12 Adam Butcher 2015-03-10 00:37:13 UTC
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);
-------------------------------------------------------------------
Comment 13 Markus Trippelsdorf 2015-07-06 12:15:31 UTC
*** Bug 66769 has been marked as a duplicate of this bug. ***
Comment 14 Jonathan Wakely 2015-10-13 11:28:10 UTC
*** Bug 67952 has been marked as a duplicate of this bug. ***
Comment 15 Bernd Schmidt 2015-12-16 16:10:09 UTC
*** Bug 67050 has been marked as a duplicate of this bug. ***
Comment 16 Tatsuyuki Ishi 2016-04-29 08:45:44 UTC
This is really annoying. Is the patch going to fix it or nothing has been done?
Comment 17 Takatoshi Kondo 2016-05-11 06:04:41 UTC
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
Comment 18 Daniel Cooke 2016-08-09 13:52:38 UTC
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?
Comment 19 Markus Trippelsdorf 2016-08-25 05:41:43 UTC
*** Bug 77376 has been marked as a duplicate of this bug. ***
Comment 20 Markus Trippelsdorf 2016-08-25 06:31:39 UTC
Reduced testcase from PR77376:

struct a {
  void b();
  void c(void *, int);
};
void a::b() {
  auto d = [&](auto asdf) { c(asdf, 0); };
  d(nullptr);
}
Comment 21 Nathan Sidwell 2016-12-09 15:43:37 UTC
Created attachment 40288 [details]
smaller testcase

reduced testcase avoiding nullptr_t indeterminacy
Comment 22 Adam Butcher 2016-12-16 02:46:37 UTC
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
}
Comment 23 Nathan Sidwell 2016-12-16 12:13:33 UTC
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
Comment 24 Nathan Sidwell 2017-01-17 18:23:06 UTC
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
Comment 25 Nathan Sidwell 2017-01-17 18:24:39 UTC
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.
Comment 26 Adam Butcher 2017-01-19 21:29:41 UTC
(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.
Comment 27 Marek Polacek 2017-03-14 16:03:41 UTC
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