Bug 71117 - [6/7 Regression] Overeager application of conversion to function pointer during overload resolution of call to function object
Summary: [6/7 Regression] Overeager application of conversion to function pointer duri...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.1.0
: P2 normal
Target Milestone: 6.2
Assignee: Jason Merrill
URL:
Keywords: rejects-valid
: 70942 71095 (view as bug list)
Depends on:
Blocks:
 
Reported: 2016-05-14 19:38 UTC by Eric Niebler
Modified: 2025-10-08 19:15 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2016-07-14 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Niebler 2016-05-14 19:38:13 UTC
Casey Carter recently posted this to the committee core- mailing list:

<<<<<<<<<<< BEGIN

It is a commonplace library technique to compose function objects, which are often empty, by inheritance to take advantage of the Empty Base Optimization. E.g., this program patterned on usage in range-v3 turns a function that accepts objects into a function that accepts iterators that denote objects:

template <class T> T&& declval() noexcept;
template <class, class>
constexpr bool is_same = false;
template <class T>
constexpr bool is_same<T, T> = true;

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

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

Unfortunately, when the adapted function object is an underconstrained captureless generic lambda - as is the case in the example - composition by inheritance is extremely fragile. Since the lambda is captureless, its closure type has a member conversion operator that converts to a function pointer type with deduced return type. An attempt to call the derived type's call operator results in overload resolution instantiating the conversion operator's declaration necessitating return type deduction from the base object's call operator. When the base object's call operator is ill-formed for the particular argument types - again as is the case in the example - the program is ill-formed.

GCC 6 and trunk tell me this program is ill-formed:

casey@Semiregular:~/bugs$ ~/gcc/bin/g++ -std=c++14 repro.cpp 
repro.cpp: In instantiation of ‘main()::<lambda(auto:1)> [with auto:1 = int*]’:
repro.cpp:17:25:   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*]’
repro.cpp:22:53:   required from here
repro.cpp:18:9: error: static assertion failed
         static_assert(is_same<decltype(rng), int>, "");
         ^~~~~~~~~~~~~
older versions of GCC and all versions of Clang I've tried compile the program without diagnostics. Since hiding of conversion operators is based on the target types, and determining the target type of the problematic conversion operator results in the aforementioned ill-formed return type deduction, there seems to be no way to hide the problematic conversion operator.

>>>>>>>>>>>>>>>>>>>>>>>> END


To which Richard Smith replied:

<<<<<<<<<<<<<<<<<<<<<<<< BEGIN

template<typename T> using id = T;
struct F {
  template<typename T> operator id<T(*)(T)>();
} f;
int n = f(0);

GCC accepts this and calls the conversion function template with T=int. Clang, EDG, MSVC reject.

Per [over.call.object]/2, I think GCC is wrong per the current language wording. A conversion function template does not qualify as a "non-explicit conversion function declared in [F or base class thereof]" (because it is not a conversion function), and nor does a specialization of one (because it is not itself declared in F).

I also don't see any way the current wording would take us to [temp.deduct.conv] for this case, nor how that wording would apply (since we don't have a function type that's required as the result of the conversion). Perhaps GCC is following the rules of [temp.deduct.call] in this case, treating the (pointee type of the) result type of the conversion function as if it were the function type of the callee?

In the abstract, GCC's approach to this situation seems superior -- it's able to use a conversion to function pointer in many cases where other compilers can't -- but I'm a little hesitant to suggest we adopt that approach since it breaks examples like yours.

>>>>>>>>>>>>>>>>>>>>>>>>> END
Comment 1 Casey Carter 2016-05-14 21:15:13 UTC
Very closely related, but not an exact duplicate of, PR 71105.
Comment 2 Jakub Jelinek 2016-05-18 08:31:53 UTC
The second testcase started to be accepted with r229210 change and similarly r229209 accepted the first testcase, while r229210 rejects it.
Comment 3 Jakub Jelinek 2016-05-18 08:35:39 UTC
BTW, the first testcase has been rejected at gcc-5-branch branch point, only got accepted later on in r226642 (and that change has been later backported).
Comment 4 Jason Merrill 2016-07-15 18:49:56 UTC
Author: jason
Date: Fri Jul 15 18:49:25 2016
New Revision: 238394

URL: https://gcc.gnu.org/viewcvs?rev=238394&root=gcc&view=rev
Log:
	PR c++/71117 - core 2189 and generic lambda

	* call.c (add_template_conv_candidate): Disable if there are
	viable candidates.

Added:
    trunk/gcc/testsuite/g++.dg/cpp1y/lambda-generic-conv2.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/call.c
    trunk/gcc/testsuite/g++.dg/cpp0x/conv-tmpl1.C
Comment 5 Jason Merrill 2016-07-20 20:37:12 UTC
*** Bug 70942 has been marked as a duplicate of this bug. ***
Comment 6 Jason Merrill 2016-07-21 06:16:50 UTC
Author: jason
Date: Thu Jul 21 06:16:11 2016
New Revision: 238573

URL: https://gcc.gnu.org/viewcvs?rev=238573&root=gcc&view=rev
Log:
	PR c++/71117 - core 2189 and generic lambda

	* call.c (add_template_conv_candidate): Disable if there are
	viable candidates.

Added:
    branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/lambda-generic-conv2.C
    branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp1y/lambda-generic-conv3.C
Modified:
    branches/gcc-6-branch/gcc/cp/ChangeLog
    branches/gcc-6-branch/gcc/cp/call.c
    branches/gcc-6-branch/gcc/testsuite/g++.dg/cpp0x/conv-tmpl1.C
Comment 7 Jason Merrill 2016-07-21 06:23:59 UTC
Fixed.
Comment 8 Paolo Carlini 2017-10-04 09:20:43 UTC
*** Bug 71095 has been marked as a duplicate of this bug. ***