Search code examples
c++language-lawyerc++20

Invalid expression in requires clause


Is the expression b<[]{ return true; }()> within the requires-clause in accordance with the C++20 standard? For some reason GCC rejects it and deems it to be an expression error, while MSVC and Clang accept it.

template<bool B>
inline constexpr bool b = B;
static_assert(requires { b<[]{ return true; }()>; });

Demo


GCC's error message:

<source>:3:15: error: static assertion failed
    3 | static_assert(requires { b<[]{ return true; }()>; });
      |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:3:15: note: constraints not satisfied
<source>:3:26: note: the required expression 'b<<expression error> >' is invalid,
because
    3 | static_assert(requires { b<[]{ return true; }()>; });
      |                          ^~~~~~~~~~~~~~~~~~~~~~~

Solution

  • This is a confirmed GCC bug. A requires-expression is a valid expression for a static assertion. The standard is wild in how deep the grammar goes, but here we go.

    Per the grammar:

    The only question after that is whether (per [decl.pre]) your provided requires clause satisfies the following:

    [the expression] E is contextually converted to bool and the converted expression shall be a constant expression

    Well, a requires-expression is definitely convertible to a boolean, per [exp.prim.req.general]

    A requires-expression is a prvalue of type bool [...]

    Your requires-expression is a simple-requirement that just requires an expression that may or may not depend on template substitution. In your case, there is no template substitution, so all that's needed for the requires to pass is a valid expression, any valid expression. Your lambda is quite valid (and constexpr). Note, however, that a lambda returning false should be equally valid and cause static assertion to pass also:

    static_assert(requires { b<([](){ return false; }())>; }); // pass
    

    Therefore it's probably not a good idea to use a requires expression without template substitution.


    A funny thing is that a static_assert may also have a noexcept expression, and wrapping your requires clause in one makes GCC 13.2 pass:

    static_assert(noexcept(requires { b<([]{ return true; }())>; }));