This is the mail archive of the gcc-bugs@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[Bug c++/71117] New: [6.1 regression] Overeager application of conversion to function pointer during overload resolution of call to function object


https://gcc.gnu.org/bugzilla/show_bug.cgi?id=71117

            Bug ID: 71117
           Summary: [6.1 regression] Overeager application of conversion
                    to function pointer during overload resolution of call
                    to function object
           Product: gcc
           Version: 6.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: eric.niebler at gmail dot com
  Target Milestone: ---

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

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]