Clang fails with "function with deduced return type cannot be used before it is defined", while GCC works

102 Views Asked by At

The following compiles with GCC, but not with Clang:

#include <array>
#include <tuple>

constexpr auto makeNumberGenerator = [](this auto maker, int startingValue)
{
    return [maker, startingValue]<typename Generator>(this Generator generator) -> std::tuple<int, Generator>
    {
        return std::make_tuple(startingValue, maker(startingValue + 1));
    };
};

auto test = []{
    std::array<int, 5> arr;
    
    auto fn = [](this auto self, int* output, const int* end, auto callable) -> void
    {
        auto [value, newCallable] = callable();
        if (output != end)
        {
            *output = value;
            self(output + 1, end, newCallable);
        }
    };
    
    fn(std::begin(arr), std::end(arr), makeNumberGenerator(0));

    return arr;
}();

On compiler explorer: GCC, Clang. Flags used: -O2 -std=c++23.

Outputs:

GCC:

test:
        .long   0
        .long   1
        .long   2
        .long   3
        .long   4

Clang:

<source>:9:47: error: function 'operator()<(lambda at <source>:5:38)>' with deduced return type cannot be used before it is defined
    9 |         return std::make_tuple(startingValue, maker(startingValue + 1));
      |                                               ^
<source>:8:5: note: while substituting into a lambda expression here
    8 |     {
      |     ^
<source>:26:59: note: in instantiation of function template specialization '(anonymous class)::operator()<(lambda at <source>:5:38)>' requested here
   26 |     fn(std::begin(arr), std::end(arr), makeNumberGenerator(0));
      |                                                           ^
<source>:5:38: note: 'operator()<(lambda at <source>:5:38)>' declared here
    5 | constexpr auto makeNumberGenerator = [](this auto maker, int startingValue)
      |                                      ^
1 error generated.
Compiler returned: 1

Which compiler is correct? Should this code compile according to the C++23 standard?


EDIT: This works on both:

constexpr auto makeNumberGenerator = [](this auto self, int startingValue)
{
    return std::bind([startingValue](auto maker)
    {
        return std::make_tuple(startingValue, maker(startingValue + 1));
    }, self);
};

Changing std::bind to a simple lambda gives the same error as before.


EDIT2: Apparently, this problem is unrelated to deducing this, as this code presents the exact same issue (compiles on GCC, not on Clang):

#include <array>
#include <tuple>

constexpr auto makeNumberGenerator = [](auto maker, int startingValue)
{
    return [maker, startingValue]<typename Generator>(Generator generator) -> std::tuple<int, Generator>
    {
        return std::make_tuple(startingValue, maker(maker, startingValue + 1));
    };
};

auto test = []{
    std::array<int, 5> arr;
    
    auto fn = [](auto self, int* output, const int* end, auto callable) -> void
    {
        auto [value, newCallable] = callable(callable);
        if (output != end)
        {
            *output = value;
            self(self, output + 1, end, newCallable);
        }
    };
    
    fn(fn, std::begin(arr), std::end(arr), makeNumberGenerator(makeNumberGenerator, 0));

    return arr;
}();
0

There are 0 best solutions below