Ill-formed constraint expression
Asked Answered
C

1

8

Regarding function f, is the substituted constraint expression of the non-type template argument of templated variable b a valid constant expression? Which specific wording of the C++20 standard either allows or forbids this?

struct s { static constexpr bool v = true; };
template<auto> inline constexpr bool b = true;
constexpr bool f(auto x) requires b<x.v> { return true; }
static_assert(f(s{})); // clang ok, gcc nope, msvc ok

Live example


The error message from GCC:

<source>:3:36: error: missing template arguments before '<' token
    3 | constexpr bool f(auto x) requires b<x.v> { return true; }
      |                                    ^
<source>:3:36: error: expected initializer before '<' token
<source>:4:15: error: 'f' was not declared in this scope
    4 | static_assert(f(s{}));
      |               ^
Cyathus answered 7/11, 2023 at 13:39 Comment(2)
note that constexpr bool f(auto x) requires b<decltype(x)::v> { return true; } is fine with all of them. And I don't understand why yours is ok for clang / msvc, but thats probably just my ignorance about c++20Dextrosinistral
Related: https://mcmap.net/q/1472172/-invalid-type-conversion-of-non-type-template-argument/567292Biased
B
3

The reason this code is correct is that non of the wording in [expr.const]p5 applies:

An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

  • [...]

And none of the proceeding points apply. Since x.v does not apply an lvalue-to-rvalue conversion on x, and S::v is a constant expression which you can apply an lvalue-to-rvalue conversion on, x.v (when x is of type S and is not a reference) is a constant expression.


It looks like gcc is trying to parse b<x.v> as (b < x.v) > (as two relational operators), and so giving you an error. This is clearly a bug, as b< should be a template no matter what since b is a templated entity.

More examples of this gcc bug happening: https://godbolt.org/z/fP74hW1qs https://godbolt.org/z/19K4nYTjT

If you use the noexcept specifier instead of requires you get an interesting additional error message:

https://godbolt.org/z/8xqre5bbT (https://godbolt.org/z/cPYbb6vsq for trailing return type)

<source>:3:38: error: use of parameter outside function body before '.' token
    3 | constexpr bool f(auto x) noexcept(b<x.v>) { return true; }
      |                                      ^

Which makes me think at some point naming function parameters outside of the function body makes GCC error out but the warnings aren't well written when it's inside a requires.

Baxley answered 7/11, 2023 at 14:24 Comment(1)
GCC issue reported: gcc.gnu.org/bugzilla/show_bug.cgi?id=112448Referee

© 2022 - 2024 — McMap. All rights reserved.