llvm-project icon indicating copy to clipboard operation
llvm-project copied to clipboard

`lambda function` return type deducing seems to be broken in the newest versions of `clang`

Open Life4gal opened this issue 1 year ago • 3 comments

I wrote a piece of code for testing purposes:

#include <algorithm>
#include <functional>
#include <ranges>
#include <vector>

template <typename F>
struct y {
    F f;

    template <typename... Args>
    constexpr auto operator()(Args&&... args) const -> decltype(auto)  //
    {
        #if USE_INVOKE
        return std::invoke(f, *this, std::forward<Args>(args)...);
        #else
        return f(*this, std::forward<Args>(args)...);
        #endif
    }
};

struct list {
    std::vector<list> children;

    int data;
};

struct list_wrapper
{
    list l;
};

int main() {
    struct test {
        int a;
        int b;

        auto operator+(const test& o) const noexcept -> test {
            return {a + o.a, b + o.b};
        }
    };

    auto f1 = y{[](auto self, const list& l) -> test {
        return std::ranges::fold_left(
            l.children, test{.a = l.data % 2, .b = l.data % 4},
            [self](const test& total, const list& nl) -> test {
                return total + self(nl);
            });
    }};

    list_wrapper lw{.l{.data = 42}};
    lw.l.children.emplace_back(std::vector<list>{}, 1337);

#if CALL_F1
    const auto r1 = f1(lw.l);
#endif

    const auto f2 = [f1](const list_wrapper& l) -> test
    {
        return f1(l.l);
    };

    const auto r = f2(lw);

    return 0;
}

The result of the compilation is shown below (you can also check out the godbolt link) image

I found this error with Clang-CL 17.0.3 on VisualStudio, but testing on godbolt I found that Clang 17.0.1 and Clang 18.1.0 don't have these errors.

Confusingly, in y::operator(), I can't use std::invoke unless I call f1(even though this call theoretically doesn't make any sense for our code).

Life4gal avatar Jul 04 '24 05:07 Life4gal

@llvm/issue-subscribers-clang-frontend

Author: Life4gal (Life4gal)

I wrote a piece of code for testing purposes: ```cpp #include <algorithm> #include <functional> #include <ranges> #include <vector>

template <typename F> struct y { F f;

template &lt;typename... Args&gt;
constexpr auto operator()(Args&amp;&amp;... args) const -&gt; decltype(auto)  //
{
    #if USE_INVOKE
    return std::invoke(f, *this, std::forward&lt;Args&gt;(args)...);
    #else
    return f(*this, std::forward&lt;Args&gt;(args)...);
    #endif
}

};

struct list { std::vector<list> children;

int data;

};

struct list_wrapper { list l; };

int main() { struct test { int a; int b;

    auto operator+(const test&amp; o) const noexcept -&gt; test {
        return {a + o.a, b + o.b};
    }
};

auto f1 = y{[](auto self, const list&amp; l) -&gt; test {
    return std::ranges::fold_left(
        l.children, test{.a = l.data % 2, .b = l.data % 4},
        [self](const test&amp; total, const list&amp; nl) -&gt; test {
            return total + self(nl);
        });
}};

list_wrapper lw{.l{.data = 42}};
lw.l.children.emplace_back(std::vector&lt;list&gt;{}, 1337);

#if CALL_F1 const auto r1 = f1(lw.l); #endif

const auto f2 = [f1](const list_wrapper&amp; l) -&gt; test
{
    return f1(l.l);
};

const auto r = f2(lw);

return 0;

}

The result of the compilation is shown below (you can also check out the [godbolt link](https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAMzwBtMA7AQwFtMQByARg9KtQYEAysib0QXACx8BBAKoBnTAAUAHpwAMvAFYTStJg1DIApACYAQuYukl9ZATwDKjdAGFUtAK4sGIAMykrgAyeAyYAHI%2BAEaYxCBmAKykAA6oCoRODB7evgGp6ZkCoeFRLLHxSbaY9o4CQgRMxAQ5Pn6BdpgOWQ1NBCWRMXEJyQqNza15HeP9YYPlw0kAlLaoXsTI7Bzm/mHI3lgA1Cb%2BbmLAJIQILCfYJhoAgjt7B5jHp1ReDN0CYrf3TzMu2%2Br3ebmIhmAmAU/0ezxBXiOJzcADcukRiLCno8CJgWCkDLiwQQAJ4pRisN4AMSxY2IXgchxJxwA7FZHodOYcqYcqCd2diHlzDrj8YS3sjSeTmGwAHTyw4PYjAGH%2BO4crloBhjTCqFLEQ5MLxEQ6ockQjEQJYQJUq8wANgd8tlhuVCiWhy1Y0OAFpbocsPspZgIEaiB7DgB6SMA4UmNmx4Vc55UQ5yITYAD6AEkIgA1ADyAGl1UKk5ziJgCOsGIcxugQCAwijUABrENUUiHABUBAQeAUXfrjf4xAA7k10MjbarsKG3UtnUt%2BYnyzsakpV0nK9XiLWqBBe/3B3WCA2QKOJ8Qp6cZ7d5yrF/Ll/4BeXOeuGPg%2BRqPyyACIAvGgGvkBjx0gyBCHLQA5QfGb5csOIBog4JDIjBYz%2Bsg/a0OglYMCucK/ocYRQegTCNIRTwAVRAIQYyGEEJmY4Qik5pAQmxGMdBtE0aBREPKRhwsEwYRWqyCGcvRUG4t68FbsKQlMFR74kYIhzRLRxHCmGqCmuaFFoZYEBejJ0IEA6poeqZhwMKguqbCkUF%2BmqIrmRJCnvjuNYSUwxyWKaspMF20T%2BRYgWaXxklJsBW6xfxgo6caelUFw7z/ky8mJFYiT/qGyV1jUnaegI3qMZZtAei52BuXJnFluW3l7qe54QkY0Ijp46CZvQVAEBAnnlrQsrYXQeGMF2skWWyQXpdBsrkY0/mJIcZhdrKoUnBlw2LX55grZIwGkINMXZXYfK5SZpVmZhZj2iKqCNLQXY2eVd22ZVvr%2BlNHnaapXJNbWRBPWFhW0AeDCVSp/1/v%2BL7RbFUVgQ10GwcxrHmtBY7wbKtA47tc2SGYiMgdFtBjrjI04eNDCyniBJMJsmbRIzrYQEhKEYuhsH/AmAFdlw/j%2BCy8PIymhxuA8wTBJmVJcFuNm6YcxBpVtvJcBA5O46LcJAq4eA/olmrXYaBVUGYc0mNlqVW3lr1oyxTBsXEFVVd95lxfVqmA%2Brmva9DsNaSjisFQaavm5rY460bFZVj5Gi8f%2BHArLQnCJLwfjcLwqCcG41jWHWawbBKQI8KQBCaMnKytgEiSyv4ACciQNw39qJFwAAcLJcFwDdVKnHCSLwLASBoGikJnWikDnHC8AoIDjxXHBaCscCwDAUAbxASBoPidBxOQlC7yk%2B/xPskKZgQ9IMK2fB0LixDzxA0SV6Q0RhE0JKcGX7/MMQJIFmiNodE39eC7zYIIAskMv7L14FgaIXhgBnFoLQeeWdSBYBEkYcQsCMF4ErN0NEaCp66i6MaLYZdSI1FAfoPA0QIT/w8FgV%2BV88Aj3QWiYg0R0iYH/HiSEMEjCVxWFQAwKo8x4EwGOAs0oaH8EECIMQ7ApAyEEIoFQ6hcG6C4PoSEKB86WFodEeekAVhmjqNqTgPp6xbVMJYawQJs6cOIHgLAJirTVFqFkFwX5Jh%2BB0SEOYZQKh6DSBkCxfjQmFAsQMYJwwdGdB%2BAwXoExPBtD0IkixKTZilCGPEBJMxIkFL6LEvJEgVgKCLpscp%2Bg04Z1fjPQ4qgO72h9PaSQnoDBGEOBAK%2BXxWweggLgQgJB/L%2BC4EsXgS8V4rAQJgJgWB4geJrkLWUZgkiJBZJIbud1JD%2BHtB3Wpg9h4JH8LKLgLIWT%2BA7v4ZuGhW43MSIcye2dOBzwXuXYRpA16IBQKgPe9AyAUCugC4Y58jCX2vrfGgtAH5Pxfrg3%2Bn8aFIv/oA4BDgaHgMYAQKBtAYFT3gYg5BqCaGYIEVsQl%2BD0R4CIa/UhyByE0KoQPMuMF6GfyYZSqZLj2Fl04dwpQfCsHAEEaAWBIixEKAkVImRjA5GyEUeIFR8j5BKDUK/XQ9pdFCLsVYQx7L3FmKclkNB1izy2IMRYIEvp/zpizLmQsJYtpl1QM41xHVTGeJpc4CArgilBC/KUhY%2BSCjhKyAGsJRQGDBpCQkmoPrkmFLSXkeNXj6gzFjfE2wybcj%2BJzSUoJZSJmrHWNUktA904TwaZwJpLS2kdPBcAHpfSb6DOGRiMZEypnCNmfMxZlAU6cCHqQEeZgWSyiuWYDQzchYN38Ns7VLzp5vNsB86Zydvlbx3v8k%2BgLD4gr3WCrpwBIX9LvrCuI8LX6opgT/D%2BaKgEgPQdiyB0DX5EqQWIUl6DyXYO5Xgghjg6W4IZUy9BLKaHsoYSSLlLDeU0IFTw4VAiwjipXnwKVMrpGyPQaqpVyjpCqvURqrRCQdXGCtUYo108TWlSsTY/w/49UOP8LayW0tZbyyY6691bj4AVITUknx7gU35sCbkkNUTw0CEjdErIWbQ2ZJ6Lm9JabE3ZMUxk1TqaC3NC0yWypZblFDo4FW5djTmmtPaZ0yELaoXtvwJ2nY3bPkSr7Qs4YHiB4jrHZIWUHcnn2jMJILu6ym5AmeTW2ea7F5fJ%2BdvP5oKgVH13afFAJ6z03wvXCygCKp63pRQ%2BgBT7MUvv%2BRA3F77cGfpJWgsuf7RUAfwEB2lHVQOqDIbiZlghqHoOg5yjAAHWF8t4EhoV/DsFoa%2BaIpg4jJE4flXhxVohlVEdkCRzRU9dDJBPfo%2BxBq6E0fMaahjFqmMscsDan0/4OMyzli6219qcz5mLHcHjTi4guP4165Tvr/Vib0BJ%2BYcaw3RrkzJmNRapPqaSdkgNf2k2Fsk6DsYfQEeZuh3GipVSTNHPM9Fut1nG0nvs/0xzIyw6l0mW5mZRzfMJH86XedZhe5XI7hznRFnV3zzi%2B50gNd7RnKuSyMwHcG4zskPOxI9oG5HP8PU3BM8e0Sq3fABLO7ksHuPulptncuDjxhbl5%2BN7itFb/iVjFS8y6vqq/ij9mAEFfpQfVuBk2msfupYQ9rJDOuMu6xB3rrLeADcYUN%2BDbDENxEFbw93YqZtYYW3KzOZd8OrcI6otVGjNUgGkHty7NhDUCdoxYs1jHmNWscdPPjnqPGI5E5EswcvgdxMqIcqNETAdN7BzErHixDmI/h13uXg/Mco/73plow/J9abFzj4zNTK2K6npZ%2BtNmm2HE7uc2UGgekdtGS5mnG6lgeYHcsgI9o1nBe7vcyQiQNDtPGfTk59/q1K55%2Bu%2BL26ktHpS4e3XJ6FyGgOiRuV6eWpuFu5un86Kz6NuFWOKeKBKbuTudWZKcezWXuwGPuvAYGAelCQeUGdCMGcGuCI2UeXCyGce02qus282squGqeK2SiEg62ai6qW2Og%2BQ%2BeVGReXqJ29GHA5qN4FeB21qCu1eX2HqNG9efqvigOASQafeoaHeEa8hPeCmShGSQmWSOm%2Bao%2ByOIO2aaOqSea2mBhreNSRmxci%2BdSb%2BK%2BtaVmDatm3SFyO%2B5ye%2BTmB%2B1OKudOcynmSypmDOr%2B3OMWvOtOVcAuF%2BV%2B46BuDcd%2BD%2BeyOiA84hIRPhERNcd%2BAWXA7cGg1ybcTe3ccuA8ZgJyBu48qR4Rq8W83%2BOu%2B6wKtRwwwAUgIB98YBJuiKZu6Ct6MBZWcBLAlWiBDuKB36ruGC6BnurWIGvuXWFCvAkG/WRBg2zCpBCGHC0elBIq8eNBie9BS2jBCi6eLBmem2OeLIFG%2B2%2BqheR2xe/Bligh5eBeVebqkhP2de2h3ishomphCh6ABm6hsmahKhxQmhsOOh6Oah%2Bh%2BmIJk%2BGO5hxa8%2B1hFathIRROThwAyAyAm%2B/maUQynhVO4yR%2BvapAfhZ%2B1cpyk6Hc467cVJeyGg2yz%2Bo6IAwR0W7yfOGGGuP%2Bp82uaWgKIATRT%2BoBj84BHRkBXRxWPR1uYC8Bb69uNWjuxKIxaBmxGBkx2BQQfu4G%2BBuIweRixB4eKxkeaxFBE2mx1BGGtB0qSeDBvAaezBKqG27BOehy3Boh1GNxdGdxQhlqohTxNe0h7x/2ch3xgavx0JQJ2QgJ8mwJ4%2BSmAZSOJham3qcOY%2BhhSmuhZhUJMZlhuONhZmy%2BryHAqJNm6JmJgs7huJlOXahJ/OJJXmgRL%2B5RLJsWlRZJW%2BLIHO4urc2RLcGgHOa0yR%2BZK6MWx%2BpmJRdhBZaRJ%2BpAnCGQzgkgQAA%3D))
![image](https://github.com/llvm/llvm-project/assets/52756109/6dae022a-2747-4fc8-b5b5-8ec95e55c6fd)

I found this error with Clang-CL 17.0.3 on VisualStudio, but testing on godbolt I found that Clang 17.0.1 and Clang 18.1.0 don't have these errors.

Confusingly, in `y::operator()`, I can't use std::invoke unless I call `f1`(even though this call theoretically doesn't make any sense for our code).
</details>

llvmbot avatar Jul 04 '24 14:07 llvmbot

This worked in clang-18: https://godbolt.org/z/9o9Pvznbb

shafik avatar Jul 04 '24 20:07 shafik

This worked in clang-18: https://godbolt.org/z/9o9Pvznbb

If you use std::invoke, it still won't work. :(

Life4gal avatar Jul 05 '24 02:07 Life4gal

Reduced to(https://godbolt.org/z/vrrEbG9MT):

struct invoke_t {
    template <class F, class... Ts>
    constexpr auto operator()(F f, Ts... ts) const {
        return f(ts...);
    }
} constexpr inline static invoke{};

template <typename F>
struct y {
    F f;

    template <class Args>
    constexpr auto operator()(Args args) const {
        return invoke(f, *this, args);
    }
};

constexpr static auto f1 = y{[](auto self, int i) -> bool {
    if (i == 0) {
        return true;
    } else {
        return self(i - 1);
    }
}};

static_assert(f1(1));
  • gcc 14 accepts it, clang 18 and msvc latest reject it.

Also consider(https://godbolt.org/z/h3vGz7xfs):

struct invoke_t {
    template <class F, class... Ts>
    constexpr auto operator()(F f, Ts... ts) const -> decltype(f(ts...)) {
        return f(ts...);
    }
} constexpr inline static invoke{};

template <typename F>
struct y {
    F f;

    template <class Args>
    constexpr auto operator()(Args args) const {
        return invoke(f, *this, args);
    }
};

constexpr static auto f1 = y{[](auto self, int i) -> bool {
    if (i == 0) {
        return true;
    } else {
        return self(i - 1);
    }
}};

static_assert(f1(1));
  • gcc and msvc accept it, only clang rejects it.

And this(https://godbolt.org/z/99Y78vGrM):

template <typename F>
struct y {
    F f;

    template <class Args>
    constexpr auto operator()(Args args) const {
        return f(*this, args);
    }
};

constexpr static auto f1 = y{[](auto self, int i) -> bool {
    if (i == 0) {
        return true;
    } else {
        return self(i - 1);
    }
}};

static_assert(f1(1));

Again, clang 18 is the only compiler that rejects it.

Obvious implementation differences, not clear which compiler is more conforming here and if there's an CWG issue.

BTW, a workaround (maybe not actually a solution) is to explicitly write down the return type to make clang happy(https://godbolt.org/z/EjPG44j4r):

template<class T>
T&& declval() noexcept;

struct invoke_t {
    template <class F, class... Ts>
    constexpr auto operator()(F f, Ts... ts) const -> decltype(f(ts...)) {
        return f(ts...);
    }
} constexpr inline static invoke{};

template <typename F>
struct y {
    F f;

    template <class Args>
    constexpr auto operator()(Args args) const -> decltype(
        declval<F>()(declval<y>(), args)
    ) {
        return invoke(f, *this, args);
    }
};

constexpr static auto f1 = y{[](auto self, int i) -> bool {
    if (i == 0) {
        return true;
    } else {
        return self(i - 1);
    }
}};

static_assert(f1(1));

RungeCC avatar Jul 05 '24 19:07 RungeCC

The offending PR has been reverted, so this issue might be closed now. @RungeCC : Considering that your reduced examples don't compile with 18 and earlier, I presume they're distinct issues. Can you file a separate issue for these? thanks!

zyn0217 avatar Jul 16 '24 11:07 zyn0217