Consider the following code:
template <typename T>
struct Wrapper {
T value;
constexpr auto operator==(const T other) const {
return value == other;
}
};
template <Wrapper W>
void fun() {
static_assert(W != 0); // This line fails on Clang
}
int main() {
fun<Wrapper{1}>();
}
The above compiles successfully on GCC but fails to compile on Clang with the following error message:
<source>:11:21: error: return type 'auto' of selected 'operator==' function for rewritten '!=' comparison is not 'bool'
11 | static_assert(W != 0); // This line fails on Clang
| ~ ^ ~
<source>:15:5: note: in instantiation of function template specialization 'fun<Wrapper<int>{1}>' requested here
15 | fun<Wrapper{1}>();
| ^
<source>:4:20: note: declared here
4 | constexpr auto operator==(const T other) const {
| ^
Note that using bool
instead of auto
makes the program compile on both compilers.
However, interestingly enough, when I change the definition of fun()
to this:
template <Wrapper W>
void fun() {
void(W == 0); // Adding this line makes the error go away
static_assert(W != 0);
}
It compiles fine on both compilers.
Alternatively, if I change the definition of the class Wrapper
to not use a template parameter, it yet again successfully compiles on both compilers.
I believe this might a Clang/LLVM bug but I'm not sure if that's really the case.
This is a known clang bug (LLVM Issue 76649).
Firstly, note that the process of placeholder type deduction is described in Clause 9 of the standard, and it isn't said when that process actually takes place; that is up to the compiler. However, it is said to "replace" the placeholder type, and we we know that this has to happen prior to name lookup:
If a variable or function with an undeduced placeholder type is named by an expression ([basic.def.odr]), the program is ill-formed.
Therefore, even with placeholder return types, the requirement in [over.match.oper] p10 should be satisfied:
If a rewritten
operator==
candidate is selected by overload resolution for an operator@
, its return type shall be cvbool
At the point where this is verified, the placeholder type should have been replaced with bool
.
Therefore, it's a clang bug that the selected operator==
is not considered to have a bool
return type.
As for:
void(W == 0); // Adding this line makes the error go away static_assert(W != 0);
Clang correctly accepts this. I suspect that this works because the return type has been replaced by bool
once W == 0
has run, and the subsequent W != 0
than piggybacks off of this.
Perhaps placeholder type deduction is somehow delayed or inhibited inside of unevaluated operands, which makes it fail when !=
is used in static_assert
first.