Clang rejects this demo, while GCC and MSVC accept it. (https://godbolt.org/z/M1Wsxs8fj)
Who is correct? Or is this ill-formed no diagnosis required?
#include <type_traits>
#if 0
#define METHOD balabala // OK
#else
#define METHOD operator= // Clang error
#endif
struct base {};
template<typename T>
concept derived = std::is_base_of_v<base, T>;
template<derived Lhs, derived Rhs> struct assign;
template<typename T>
struct crtp: base {
template<derived Rhs>
constexpr auto METHOD(const Rhs& rhs) -> assign<T, Rhs> {
return {};
}
};
template<typename T>
struct foo: crtp<foo<T>> {
using crtp<foo<T>>::METHOD;
};
template<derived Lhs, derived Rhs>
struct assign: crtp<assign<Lhs, Rhs>> { };
static_assert(std::is_base_of_v<base, foo<int>>);
Clang message:
<source>:12:19: error: substitution into constraint expression resulted in a non-constant expression
concept derived = std::is_base_of_v<base, T>;
^~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:14:10: note: while checking the satisfaction of concept 'derived<foo<int>>' requested here
template<derived Lhs, derived Rhs> struct assign;
^
<source>:14:10: note: while substituting template arguments into constraint expression here
template<derived Lhs, derived Rhs> struct assign;
^~~~~~~
<source>:19:44: note: while checking constraint satisfaction for template 'assign<foo<int>, crtp<foo<int>>>' required here
constexpr auto METHOD(const Rhs& rhs) -> assign<T, Rhs> {
^~~~~~~~~~~~~~
<source>:17:8: note: while substituting deduced template arguments into function template 'operator=' [with Rhs = crtp<foo<int>>]
struct crtp: base {
^
<source>:25:8: note: while declaring the implicit copy assignment operator for 'foo<int>'
struct foo: crtp<foo<T>> {
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/type_traits:3361:68: note: in instantiation of template class 'foo<int>' requested here
inline constexpr bool is_base_of_v = __is_base_of(_Base, _Derived);
^
<source>:32:20: note: in instantiation of variable template specialization 'std::is_base_of_v<base, foo<int>>' requested here
static_assert(std::is_base_of_v<base, foo<int>>);
^
<source>:12:19: note: initializer of 'is_base_of_v<base, foo<int>>' is unknown
concept derived = std::is_base_of_v<base, T>;
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/type_traits:3361:25: note: declared here
inline constexpr bool is_base_of_v = __is_base_of(_Base, _Derived);
^
<source>:12:19: error: substitution into constraint expression resulted in a non-constant expression
concept derived = std::is_base_of_v<base, T>;
^~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:14:10: note: while checking the satisfaction of concept 'derived<foo<int>>' requested here
template<derived Lhs, derived Rhs> struct assign;
^
<source>:14:10: note: while substituting template arguments into constraint expression here
template<derived Lhs, derived Rhs> struct assign;
^~~~~~~
<source>:19:44: note: while checking constraint satisfaction for template 'assign<foo<int>, crtp<foo<int>>>' required here
constexpr auto METHOD(const Rhs& rhs) -> assign<T, Rhs> {
^~~~~~~~~~~~~~
<source>:17:8: note: while substituting deduced template arguments into function template 'operator=' [with Rhs = crtp<foo<int>>]
struct crtp: base {
^
<source>:25:8: note: while declaring the implicit move assignment operator for 'foo<int>'
struct foo: crtp<foo<T>> {
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/type_traits:3361:68: note: in instantiation of template class 'foo<int>' requested here
inline constexpr bool is_base_of_v = __is_base_of(_Base, _Derived);
^
<source>:32:20: note: in instantiation of variable template specialization 'std::is_base_of_v<base, foo<int>>' requested here
static_assert(std::is_base_of_v<base, foo<int>>);
^
<source>:12:19: note: initializer of 'is_base_of_v<base, foo<int>>' is unknown
concept derived = std::is_base_of_v<base, T>;
^
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/13.0.1/../../../../include/c++/13.0.1/type_traits:3361:25: note: declared here
inline constexpr bool is_base_of_v = __is_base_of(_Base, _Derived);
^
2 errors generated.