Bug 71105 - [6/7 regression] lambdas with default captures improperly have function pointer conversions
Summary: [6/7 regression] lambdas with default captures improperly have function point...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.1.0
: P3 normal
Target Milestone: 6.2
Assignee: Not yet assigned to anyone
URL:
Keywords: c++-lambda
Depends on:
Blocks: lambdas
  Show dependency treegraph
 
Reported: 2016-05-13 15:36 UTC by Casey Carter
Modified: 2022-03-11 00:32 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2016-05-28 00:00:00


Attachments
Minimal test case (252 bytes, text/plain)
2016-05-13 15:36 UTC, Casey Carter
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Casey Carter 2016-05-13 15:36:43 UTC
Created attachment 38483 [details]
Minimal test case

N4582 [expr.prim.lambda]/7 says:

The closure type for a non-generic lambda-expression with no lambda-capture has a conversion function to pointer to function with C++ language linkage (7.5) having the same parameter and return types as the closure type’s function call operator. ... For a generic lambda with no lambda-capture, the closure type has a conversion function template to pointer to function. The conversion function template has the same invented template-parameter-list, and the pointer to function has the same parameter types, as the function call operator template. ...

So all of:

    static_cast<void(*)()>([]{});
    static_cast<void(*)(int)>([](auto){});
    static_cast<float(*)(float)>([](auto x){ return x; });

should compile. These should all be ill-formed since the lambdas have lambda-captures:

    static_cast<void(*)()>([i]{});
    static_cast<void(*)()>([=]{});
    static_cast<void(*)()>([&]{});
    static_cast<void(*)(int)>([i](auto){});
    static_cast<void(*)(int)>([=](auto){});
    static_cast<void(*)(int)>([&](auto){});
    static_cast<float(*)(float)>([i](auto x){ return x; });
    static_cast<float(*)(float)>([=](auto x){ return x; });
    static_cast<float(*)(float)>([&](auto x){ return x; });

clang (tested 3.4 - 3.9, all versions that implement generic lambdas) diagnoses all of these conversions as ill-formed; gcc (4.9.0 - 6.1.0, again all versions that implement generic lambdas) diagnoses only those with explicit captures.

This issue is significant when a library composes function objects via private inheritance to take advantage of the empty base optimization and it adapts a (possibly) underconstrained generic lambda to a different/larger valid space of parameter types. For example, from range-v3:

    #include <type_traits>

    template <class F>
    struct indirected : F {
        indirected(F f) : F(f) {}
        template <class I>
        auto operator()(I i) -> decltype(std::declval<F&>()(*i)) {
            return static_cast<F&>(*this)(*i);
        }
    };

    int main() {
        auto f = [=](auto i) { return i + i; };
        auto i = indirected<decltype(f)>{f};
        static_assert(std::is_same<decltype(i(std::declval<int*>())), int>(), "");
    }

This program compiles correctly with the GCC 4.9 and 5 series compilers, 6.1 diagnoses the static_assert line (See http://melpon.org/wandbox/permlink/yy61pk0iJRfUVBya):

prog.cc: In instantiation of 'main()::<lambda(auto:1)> [with auto:1 = int*]':
prog.cc:13:24:   required by substitution of 'template<class auto:1> main()::<lambda(auto:1)>::operator decltype (((main()::<lambda(auto:1)>)0u).operator()(static_cast<auto:1>(<anonymous>))) (*)(auto:1)() const [with auto:1 = int*]'
prog.cc:15:63:   required from here
prog.cc:13:37: error: invalid operands of types 'int*' and 'int*' to binary 'operator+'
     auto f = [=](auto i) { return i + i; };
                                   ~~^~~
Comment 1 Paolo Carlini 2016-05-28 19:12:30 UTC
Mine, seems easy.
Comment 2 paolo@gcc.gnu.org 2016-05-29 08:06:03 UTC
Author: paolo
Date: Sun May 29 08:05:30 2016
New Revision: 236859

URL: https://gcc.gnu.org/viewcvs?rev=236859&root=gcc&view=rev
Log:
/cp
2016-05-29  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/71105
	* lambda.c (maybe_add_lambda_conv_op): Early return also when
	LAMBDA_EXPR_DEFAULT_CAPTURE_MODE != CPLD_NONE.

/testsuite
2016-05-29  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/71105
	* g++.dg/cpp0x/lambda/lambda-conv11.C: New.
	* g++.dg/cpp1y/lambda-conv1.C: Likewise.
	* g++.dg/cpp1y/lambda-conv2.C: Likewise.

Added:
    trunk/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv11.C
    trunk/gcc/testsuite/g++.dg/cpp1y/lambda-conv1.C
    trunk/gcc/testsuite/g++.dg/cpp1y/lambda-conv2.C
Modified:
    trunk/gcc/testsuite/ChangeLog
Comment 3 paolo@gcc.gnu.org 2016-05-29 08:27:38 UTC
Author: paolo
Date: Sun May 29 08:27:07 2016
New Revision: 236860

URL: https://gcc.gnu.org/viewcvs?rev=236860&root=gcc&view=rev
Log:
/cp
2016-05-29  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/71105
	* lambda.c (maybe_add_lambda_conv_op): Early return also when
	LAMBDA_EXPR_DEFAULT_CAPTURE_MODE != CPLD_NONE.

/testsuite
2016-05-29  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/71105
	* g++.dg/cpp0x/lambda/lambda-conv11.C: New.
	* g++.dg/cpp1y/lambda-conv1.C: Likewise.
	* g++.dg/cpp1y/lambda-conv2.C: Likewise.

Added:
    branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-conv11.C
    branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/lambda-conv1.C
    branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/lambda-conv2.C
Modified:
    branches/gcc-6-branch/gcc/cp/ChangeLog
    branches/gcc-6-branch/gcc/cp/lambda.c
    branches/gcc-6-branch/gcc/testsuite/ChangeLog
Comment 4 paolo@gcc.gnu.org 2016-05-29 08:30:23 UTC
Author: paolo
Date: Sun May 29 08:29:46 2016
New Revision: 236861

URL: https://gcc.gnu.org/viewcvs?rev=236861&root=gcc&view=rev
Log:
/cp
2016-05-29  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/71105
	* lambda.c (maybe_add_lambda_conv_op): Early return also when
	LAMBDA_EXPR_DEFAULT_CAPTURE_MODE != CPLD_NONE.

/testsuite
2016-05-29  Paolo Carlini  <paolo.carlini@oracle.com>

	PR c++/71105
	* g++.dg/cpp0x/lambda/lambda-conv11.C: New.
	* g++.dg/cpp1y/lambda-conv1.C: Likewise.
	* g++.dg/cpp1y/lambda-conv2.C: Likewise.

Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/lambda.c
Comment 5 Paolo Carlini 2016-05-29 08:31:20 UTC
Fixed trunk and 6.2.