Mystifying UB/segfault only on gcc - is the code ill-formed?

Viewed 124
#include <utility>

template <typename P, typename F>
struct foo
{
    P _p;
    int i{0};

    foo(P&& p, F) : _p{std::move(p)} { }

    template <typename... Cs>
    void up(Cs&... cs)
    {
        if constexpr(sizeof...(Cs) == 2) { down(cs...); }
        else { _p.up(*this, cs...); }
    }

    void down() { --i; }

    template <typename C, typename... Cs>
    void down(C& c, Cs&... cs)
    {
        [&] { [&] { c.down(cs...); }(); }();
    }
};

int main()
{
    auto f = foo{foo{foo{0, 0}, 0}, 0};
    f.up();
}

The code snippet above:

  • Always segfaults on execution when compiled with g++ 7.1 and trunk, with -O0.

  • Always reports errors on execution when compiled with g++ 7.1 and trunk, with any optimization level and either -fsanitize=address or -fsanitize=undefined.

  • Never segfaults or report any sanitizer error with clang++ 5 and trunk.

live example on wandbox


Taking any of the following actions:

  • Getting rid of --i; from down().

  • Changing [&] { [&] { c.down(cs...); }(); }(); to [&] { c.down(cs...); }();.

  • Removing typename F and updating the constructor accordingly.

  • Changing foo{foo{foo{0, 0}, 0}, 0} to foo{foo{0, 0}, 0}.

Fixes the segmentation fault and prevents any sanitizer error.

What's going on here? Is my code ill-formed, or is this a weird gcc bug that produces wrong assembly?

0 Answers
Related