Search code examples
c++gcclambdacompiler-errorslanguage-lawyer

Non-constant condition error only shown on some compilers with lambda


I have the following snippet which has an object with a compile-time value:

template <int V>
struct Value
{
  constexpr Value() = default;
  constexpr int value() const  { return V; }
};

int main()
{
  Value<1> v;
  static_assert(v.value(), ""); // OK

  auto l = [=]()
  {
    static_assert(v.value(), ""); // Error
  };

  (void)l;
}

The problem is in the static_assert within the lambda. It passes on gcc 6.4, 10.1 but fails on 7.1 and 8.1. The first assert (outside of the lamda) passes on all of them. If changed to constexpr Value<1> v;, it starts passing on 7.1 but not 8.1. I was wondering why this is happening.

Demo: https://godbolt.org/z/4z7vdKGMq


Solution

  • gcc 8.1 contained a compiler bug that prevented lambda captures from being constexpr:
    Bug 86429 - [8 Regression] lambda capture breaks constexpr-ness

    It was fixed in 8.4, but not backported to previous versions. So gcc versions 8.1 - 8.3 still contain this bug, preventing the captured v from being treated as constexpr.


    For 7.1 i'm not 100% sure since i didn't manage to find a bug for it; but given that it rejects some constructs even with constexpr my guess would be that it is a precursor-bug to the 8.1 one that just happend to remain unnoticed.

    It fails with exactly the same error message - '__closure' is not a constant expression - if an explicit capture clause is used:

    godbolt

    constexpr Value<1> v;
    static_assert(v.value(),""); // OK
    
    auto l = [v]() {
        static_assert(v.value(),""); // OK (Error in 7.1)
    };
    
    auto l2 = [&v]() {
        static_assert(v.value(),""); // OK (Error in 7.1)
    };
    
    auto l3 = [=]() {
        static_assert(v.value(),""); // OK
    };
    
    auto l4 = [&]() {
        static_assert(v.value(),""); // OK
    };