<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/138404>138404</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[clang++] Bugs on overloading operator== and operator!=
</td>
</tr>
<tr>
<th>Labels</th>
<td>
clang
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
ConnectionFailedd
</td>
</tr>
</table>
<pre>
I'm writing a generative meta-programming library. Below is the code that triggers the bugs.
``` C++
// test.cpp
#include <concepts>
#include <iostream>
#include <string>
#include <type_traits>
#include <utility>
class CppExpression;
template<typename T1, typename T2>
concept same_after_decay = std::same_as<std::decay_t<T1>, std::decay_t<T2>>;
template<typename T>
concept is_cpp_expression = same_after_decay<T, CppExpression>;
template<typename T>
concept convertible_to_cpp_expression = is_cpp_expression<T>
// below are for generating literals
|| same_after_decay<T, int> || same_after_decay<T, unsigned int> || same_after_decay<T, long> || same_after_decay<T, unsigned long> || same_after_decay<T, long long> || same_after_decay<T, unsigned long long>
|| same_after_decay<T, float> || same_after_decay<T, double> || same_after_decay<T, long double>
|| same_after_decay<T, char> || same_after_decay<T, char *> || same_after_decay<T, const char *> || same_after_decay<T, std::string> || same_after_decay<T, std::string_view>
|| same_after_decay<T, bool> || same_after_decay<T, std::nullptr_t>;
class CppExpression {
public:
enum class Precedence {
LOWEST = 0,
COMMA = 1,
ASSIGN = 2,
LOGICAL_OR = 3,
LOGICAL_AND = 4,
BITWISE_OR = 5,
BITWISE_XOR = 6,
BITWISE_AND = 7,
EQUALITY = 8,
COMPARISON = 9,
SHIFT = 10,
SUM = 11,
PRODUCT = 12,
PREFIX = 13,
SUFFIX = 14,
HIGHEST = 15
};
private:
std::string expression_;
Precedence precedence_;
CppExpression(std::string expression, Precedence precedence = Precedence::LOWEST) : expression_(std::move(expression)), precedence_(precedence) {}
CppExpression(const CppExpression & other) : expression_(other.expression_), precedence_(other.precedence_) {}
CppExpression(CppExpression && other) noexcept : expression_(std::move(other.expression_)), precedence_(other.precedence_) {}
public:
static CppExpression create_expression(const std::string & expression, Precedence precedence = Precedence::LOWEST) { return CppExpression(expression, precedence); }
static CppExpression create_termial(const std::string & expression) { return CppExpression(expression, Precedence::HIGHEST); }
template<convertible_to_cpp_expression T>
static CppExpression create_literal(T && value) {
if constexpr(same_after_decay<T, int>) { return CppExpression(std::to_string(value), Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, unsigned int>) { return CppExpression(std::to_string(value) + 'u', Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, long>) { return CppExpression(std::to_string(value) + 'l', Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, unsigned long>) { return CppExpression(std::to_string(value) + "ul", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, long long>) { return CppExpression(std::to_string(value) + "ll", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, unsigned long long>) { return CppExpression(std::to_string(value) + "ull", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, float>) { return CppExpression(std::to_string(value) + 'f', Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, double>) { return CppExpression(std::to_string(value), Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, long double>) { return CppExpression(std::to_string(value) + 'l', Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, char>) { return CppExpression("'" + std::string(1, value) + "'", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, char *>) { return CppExpression("\"" + std::string(value) + "\"", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, const char *>) { return CppExpression("\"" + std::string(value) + "\"", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, std::string>) { return CppExpression("\"" + value + "\"", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, std::string_view>) { return CppExpression("\"" + std::string(value) + "\"", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, bool>) { return CppExpression(value ? "true" : "false", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, std::nullptr_t>) { return CppExpression("nullptr", Precedence::HIGHEST); }
else if constexpr(same_after_decay<T, CppExpression>) { return CppExpression(value.expression(), value.precedence()); }
else { static_assert(false, "Invalid C++ literal type"); }
}
const std::string & expression() const { return expression_; }
Precedence precedence() const { return precedence_; }
};
template<convertible_to_cpp_expression T1, convertible_to_cpp_expression T2>
requires is_cpp_expression<T1> || is_cpp_expression<T2>
CppExpression operator==(T1 && lhs, T2 && rhs) {
return CppExpression::create_expression(CppExpression::create_literal(lhs).expression() + " == " + CppExpression::create_literal(rhs).expression(), CppExpression::Precedence::EQUALITY);
}
template<convertible_to_cpp_expression T1, convertible_to_cpp_expression T2>
requires is_cpp_expression<T1> || is_cpp_expression<T2>
CppExpression operator!=(T1 && lhs, T2 && rhs) {
return CppExpression::create_expression(CppExpression::create_literal(lhs).expression() + " != " + CppExpression::create_literal(rhs).expression(), CppExpression::Precedence::EQUALITY);
}
template<convertible_to_cpp_expression T1, convertible_to_cpp_expression T2>
requires is_cpp_expression<T1> || is_cpp_expression<T2>
CppExpression operator>(T1 && lhs, T2 && rhs) {
return CppExpression::create_expression(CppExpression::create_literal(lhs).expression() + " > " + CppExpression::create_literal(rhs).expression(), CppExpression::Precedence::EQUALITY);
}
int main() {
auto var_a = CppExpression::create_termial("a");
std::cout << (var_a == 1).expression() << std::endl;
std::cout << (var_a != 1).expression() << std::endl;
std::cout << (var_a > 1).expression() << std::endl;
}
```
**Compiling this code with clang++ takes extremely long time:**
``` shell
$ # clang++ 20.1.2 | Apple M3 | MacOS 15.4.1
$ time clang++ -std=c++23 test.cpp
clang++ -std=c++23 test.cpp 132.19s user 0.29s system 99% cpu 2:12.60 total
```
On another machine:
``` shell
$ # clang++ 18.1.3 | Intel Core i9 14900K | Ubuntu 24.04
$ time clang++ -std=c++23 test.cpp
clang++ -std=c++23 test.cpp 184.87s user 0.07s system 99% cpu 3:04.95 total
```
While g++ performs like:
``` shell
$ # g++ 13.3.0 | Intel Core i9 14900K | Ubuntu 24.04
$ time g++ -std=c++23 test.cpp
g++ -std=c++23 test.cpp 0.15s user 0.04s system 95% cpu 0.202 total
```
And according to my observation:
- Call to `operator!=` is the reason of long compile time here. Removing line `std::cout << (var_a != 1).expression() << std::endl;`, clang++ can compile fastly.
- But if removing the definition of `operator!=`, then call to `operator==` will cause a similarly long compile time.
- Except these two operator, any other binary operator has the same effect.
- The underlying reason may be the concept `convertible_to_cpp_expression`. If we reduce the options of literals, e.g. remove all options after `long double`, the compile time will be short.
Anyway, I think it is a bug because g++ can easily compile such file. However, due to my limited capabilities, I can only hope that you will be able to address it.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWl1v2zrS_jXMzaCCRPnzIhe2E7fG2570bVL07JVBS2ObW0rUkpRT__sFqQ9bitIo3XjPKbCBgNac4fCZh8OPGYlpzXcp4jUZzsnw5orlZi_V9UKmKUaGy3TJuMA4vtrI-Hi9InScwKPihqc7YLDDFBUz_ICQoGHvMiV3iiWJlQq-UUwdPZijkI_ANZg9QiRjBLNnBoziux2qonmT77RH_Jl9Rn7xwILQuX38GaFLQpdgUBsvyjLXEvI0EnmMQMJFJNMIM6NJePtExqU2ClnSJdNG8XTXJTHHDNdGMd5tNDdccHMsRf4sEkxrWGTZ7Y9ModZcpiScFzKDSSaYwdJqyhKEh4DQBZx-0sJS6QdoluCabQ2qdYwROwIJb0CbmIQzEs4KqXb4yyantTYkXDwE1hRdQJfMDWOf-XO4mji4XkdZtsbaqQJIC521bEdsud9_mEimB1SGbwSujewa8gkQN6YzAmVwbFyYMYWwlaqOTBeIBhUT2umOF2S8eNYDnhoS3r6kladuzcQ91YV0Idbbak99q_ZLtqtOPfjYCsn6uBjLfCOwL-hauweCaM9UD7tWDQid9VGVqTav6XBad9Vu8coe6wPHx37ubqQUrxkgzYXIjLKr-7becDo2IyBjK8zyjeCR7erPANM8gUL3s8IIY0wjLBWh_Pt49-32_sEtQZ_QxZlkcffp08wJglIwu79fvf_DNdGm7se796vF7OP67ouTht3S2R83TjwoxfPVw7fV_W3Va9jsVUn_LMWjbnFldFyKb___6-zj6uEfrnHyxKXPsy-r-7vCiWlTev9htSyYCFpU3H_9VLQHzfbPX-5uvi7KPrQtu12u_ixEYdvcshZVXHxYvf9QTUQwLJXJ-Kae80zxg9tkZ6WwFYNw2jrXRafzSc_q_65ri46Rxo5OJ88btVHZadBBPkmK7kVYEToFEs4a0M7GSOQBCZ2cjzF1z6KBl05Ov5zF8dwS48-eoC9Wfmtd0BFIs0fVDcaJvEZbB4BCq9F2jqODyCcgGjhSiT_cwfgyO90AX42xvTe4AGKGRy26IoXM4Pk5XNHaDg3r0BuEx3gOCk2u0icUNo03YoCEcyi5_5kXBlXCmejtwivgtD0q128T29nN6Oc3oOqm88KslBcdQicPVUgdmMjrVXHaYvi2OAbtGDaofnodesHxmjUj1-UJSSfVuL25QKGxL6zW_es_wQeEzoHQcU7o-CJYq7vWG2AUl8LYvnm-AViaC0LpxRh9S6TiYki7b91vQu7FMFc3_rcI2O2lAvaUQPw3tqby79Ux-iYoL774ywTrBYwu3MaEUoennRHRiStntMO06HFJns-yuB4ODBcOznM-PIFf6V_Ug3Y2-pv68TRJfq0jDvZfiblO03_PGShLBy-gL1kOlxaXURYmdWkGoXTLhMZLHSzdJYsXqS61L8zdk8plDxI9bEKd1lugd56MTMqErBufHaS40q-Z1qgMoZNyGhZ2SlbpgQkeV6XwqpbpKseOk5bhOpuzP_plNRZgqXrmcrNccDZCZxb3nJVmXaFCd1a26J8CBeVu-TOdsoqu8F85V6i7q8bBWX2tU6G00kyxZIaKGalIeGMfOnkIqhxL7LXF9kCrBmUb6pSrM4bchHQl08-rnbI7N-D0SfxVuw0UGKHan_qYVN0mO9aG7d5ehlVRrQjHcop_x8mlwd9_ch3G_03uL6zc27_5zFrv_uppJf6MpwYSxmt4deWI5UbCgak1c1W75zGeCmuEUlafU-3KdCRzAyRckNCedZPacvlSoYuqQrm2gGksehsuFs4FDIe3r7Zakl296i7ffFP7LGSScWFParPnunhh_sjNHiLB7O3S3QMM-44a8IdRmKA4Fgmn4YmbXGem_TJd71EIN8gACA0b1qjvBR61SwtmWSYQPoXuxycW3d1DMPQGXlB2tWM0-r5z3t1ExU8anr-k76UHEITUC6Yaco0KfI9ONeijNpjAdEroEKIsB0rCWUC9kQ9GGiaekneXAktdkRsSFu15Wr4L6U1CMPECr_B7lRoUsJAKgU8hGEx9__-c4OsmT00OdOD5g0sSMhl4k3FNiD_uICQk4cwfeNPhc4R823OBUI2WodpKlWgQ_HsfZmpWQi_0_F9jpQ8jPdjwvWB44mJw4mJYceF71KfP8TBLY2BRJFXs1pSE5Ahyo1EdmCk3Ln_2DhZMCCslI791HRj51ccrCpm2J8q2WG-RW6lYOLtHhR58wUQeio8OUrS2LrAnjXx3oJ6FUsTSGsyWaSOOnvNpnhubAakKlPUhxi1PueGFH13eui9T9phC1EFJcf0d-fDIhYCI5RqBgeYJF0xVG9E5MQWS2-J9ltmjRjCP8uzKtQCWHot3X7DhKVPHWgh7VhBv8zbA7RYjU9h72CPkaYxKHK1j5cQk7AgbLL8zKj4tISP_p1cPMvI9WG3h0c5unEdFb5lZgrSb6errEboA9HZewSaC5aZScxmlHeq87lcT2YwTx9sGQe-lMl4VosdHdrTqK7vpp9-BGxtzDDb5DjZY0Hw-28g0F8fass6jPWy5QA8-yEc8oOM1zrGMd8ETbjCGiGVswwU3HHUxnDUmU3GEvczKD7OOMq9Rso1wNlgcW8KAG-8qvg7jaThlV3gdjAcjOqHT8fBqfz0KwvEwHtFxHG5ZMNxiOIziaLuZoE99fzS94tfUp0N_6IfBIBwFQy8cT4ejeDMZbP0BHY6HZOBjwrjwhDgknlS7K651jtdBOBn4gyvBNii0-1KN0jL-KRneXKlr2-HdJt9pMvAF10afTBhuhPu87WzFkOENzPOdBrsKDqiEZG53aIY5sDRu5QZXuRLXe2My7Q7aJaHLHTf7fONFMiF0aUct_3mXKflPjAyhS-eFJnRZOnK4pv8OAAD__3BBJL0">