Constraints in C++20 are normalized before checked for satisfaction by dividing them on atomic constraints. For example, the constraint E = E1 || E2 has two atomic constrains E1 and E2
And substitution failure in an atomic constraint shall be considered as false value of the atomic constraint.
If we consider a sample program, there concept Complete = sizeof(T)>0 checks for the class T being defined:
template<class T>
concept Complete = sizeof(T)>0;
template<class T, class U>
void f() requires(Complete<T> || Complete<U>) {}
template<class T, class U>
void g() requires(sizeof(T)>0 || sizeof(U)>0) {}
int main() {
f<void,int>(); //ok everywhere
g<void,int>(); //error in Clang
}
then the function f<void,int>() satisfies the requirements, because Complete<void> just evaluates to false due to substitution failure and Complete<int> evaluates to true.
But a similar function g<void,int>() makes the compilers diverge. GCC accepts it, but Clang does not:
error: no matching function for call to 'g'
note: candidate template ignored: substitution failure [with T = void, U = int]: invalid application of 'sizeof' to an incomplete type 'void'
void g() requires(sizeof(T)>0 || sizeof(U)>0) {}
Demo: https://gcc.godbolt.org/z/zedz7dMGx
Are the functions f and g not really identical, or Clang is wrong here?