`<xutility>`: std::find no longer compiles with structs with defaulted equality comparison with Clang-CL
Nobody has claimed this yet.
- Dominant language
- C++
- Stars
- 11.1k
- Forks
- 1.7k
- Avg merge
- 4d 15h
- Merged PRs (30d)
- 22
Description
Describe the bug
std::find and std::ranges::find (both standardized in <algorithm> but implemented in <xutility>) have long been able to handle structs with defaulted equality comparison (behavior extended in the C++20 standard). However, recent optimizations regarding LLVM usage have resulted in a regression (https://github.com/microsoft/STL/issues/5479 and specifically https://github.com/microsoft/STL/pull/5767/changes#diff-980098f27df5ad089b2a93a8489574cfcab50f4b9d9c55524b0a57444a4c664eR6137-R6138). In particular, types with a sizeof other than 1, 2, 4 or 8, that have a defaulted equality comparison operator, fail to compile.
This was checked using the latest version of the STL shipped with Visual Studio 18.6.0, which seems to be 19.51.36231, and Clang-CL version 20.1.8.
Command-line test case
C:\Temp>type repro.cpp
#include <algorithm>
#include <cstdint>
#include <vector>
namespace {
struct A {
int64_t a{};
int64_t b{};
friend constexpr bool operator==(A x, A y) = default;
};
struct B {
int64_t a{};
int64_t b{};
friend constexpr bool operator==(B x, B y) {
return x.a == y.a && x.b == y.b;
}
};
} // namespace
int main() {
// the following calls fail to compile due to defaulted comparison operator
const auto a = std::vector<A>{};
std::ignore = std::find(a.begin(), a.end(), A{}); // does not compile
std::ignore = std::ranges::find(a, A{}); // does not compile
// user-defined comparison operator works fine
const auto b = std::vector<B>{};
std::ignore = std::find(b.begin(), b.end(), B{}); // OK
std::ignore = std::ranges::find(b, B{}); // OK
return 0;
}
C:\Temp>clang-cl -clang:-std=c++23 -- repro.cpp
In file included from repro.cpp:1:
In file included from C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\algorithm:10:
In file included from C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\__msvc_heap_algorithms.hpp:11:
C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\xutility(320,23): error: static assertion failed: unexpected size
320 | static_assert(false, "unexpected size");
| ^~~~~
C:\Program Files\Microsoft Visual Studio\18\Enterprise\VC\Tools\MSVC\14.51.36231\include\xutility(6445,42): note: in instantiation of function template specialization 'std::_Find_vectorized<const (anonymous namespace)::A,
(anonymous namespace)::A>' requested here
6445 | const auto _Result = _STD _Find_vectorized(_First_ptr, _STD _To_address(_Last), _Val);
| ^
1 error generated
NOTE: the above script compiles just fine with MSVC shipped with the same version of Visual Studio
C:\Temp>cl /EHsc /W4 /WX /std:c++latest .\repro.cpp
Microsoft (R) C/C++ Optimizing Compiler Version 19.51.36243 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
/std:c++latest is provided as a preview of language features from the latest C++
working draft, and we're eager to hear about bugs and suggestions for improvements.
However, note that these features are provided as-is without support, and subject
to changes or removal as the working draft evolves. See
https://go.microsoft.com/fwlink/?linkid=2045807 for details.
repro.cpp
Microsoft (R) Incremental Linker Version 14.51.36243.0
Copyright (C) Microsoft Corporation. All rights reserved.
/out:repro.exe
repro.obj
I found that the vectorization is only implemented on 1-, 2-, 4- or 8-bytes objects, and that all other sizes trigger the above error: static assertion failed: unexpected size.
However, because _Vector_alg_in_find_is_safe_elem under the hood uses __is_trivially_equality_comparable, which evaluates to true, it takes the vectorized branch anyways, which, in turn, triggers the static assertion.
Expected behavior
std::find and std::ranges::find work on types with comparison operator for both Clang-CL and MSVC, regardless of whether it was defaulted or explicitly specified.
Either:
- the vectorization needs to be implemented for types with a size other than 1, 2, 4 or 8 bytes; or
-
_Vector_alg_in_find_is_safeneeds to explicitly exclude those odd-sized and/or large types.
STL version
-
Option 2:
_MSVC_STL_UPDATEvalue (because Clang-CL is used)- Example:
C:\Temp>type meow.cpp #include <iostream> int main() { std::cout << _MSVC_STL_UPDATE << "\n"; } PS C:\Temp> clang-cl -clang:-std=c++23 -MDd -c -- .\meow.cpp PS C:\Temp> lld-link.exe meow.obj /out:meow.exe PS C:\Temp> .\meow.exe 202604
- Example:
-
For completeness:
cl.exeshowsMicrosoft (R) C/C++ Optimizing Compiler Version 19.51.36243 for x64- Example:
C:\Temp>type meow.cpp #include <iostream> int main() { std::cout << _MSVC_STL_UPDATE << "\n"; } C:\Temp>cl /EHsc /nologo /W4 meow.cpp && meow meow.cpp 202604
- Example:
Additional context
- This was found in our Nightly build on GitHub (https://github.com/PowerGridModel/power-grid-model/actions/runs/26138748913/attempts/1) which runs on the
msvc-latestGitHub Actions runner image. Now that the latest Visual Studio is being deployed in the GitHub Actions runner images (https://github.com/actions/runner-images/blob/win25-vs2026/20260518.113/images/windows/Windows2025-VS2026-Readme.md), this will affect more users. - https://en.cppreference.com/cpp/language/default_comparisons contains an example with a struct similar to what was used here, so that suggests that it really is something that is supposed to be supported.
std::pair<int, int>does not suffer from this issue (contrary to what is suggested in https://github.com/microsoft/STL/issues/54790) because it does not have a defaulted equality comparison operator:
This also implies that_EXPORT_STD template <class _Ty1, class _Ty2, class _Uty1, class _Uty2> _NODISCARD constexpr bool operator==(const pair<_Ty1, _Ty2>& _Left, const pair<_Uty1, _Uty2>& _Right) { return _Left.first == _Right.first && _Left.second == _Right.second; }std::pair<int, int>at the moment is not vectorized.- If I understand the libcxx implementation of
findfor types satisfying__is_trivially_equality_comparable_v(https://github.com/llvm/llvm-project/blob/main/libcxx/include/__algorithm/find.h#L137-L139) correctly, then it looks like LLVM opts for the exclusion of vectorization for types with sizes other than1orsizeof(wchar)and that also are not integral types (which effectively excludes more than aforementioned approach_Vector_alg_in_find_is_safe needs to explicitly exclude those odd-sized and/or large types.)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in xutility at _Find_vectorized and the _Vector_alg_in_find_is_safe_elem check, then compare the Clang-CL failure with the supplied repro.cpp and the existing MSVC behavior. The fix is complete when std::find and std::ranges::find compile for the defaulted-equality struct under Clang-CL without regressing the working MSVC case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100