Skip to content

<xutility>: std::find no longer compiles with structs with defaulted equality comparison with Clang-CL #6294

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 (#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_safe needs to explicitly exclude those odd-sized and/or large types.

STL version

  • Option 2: _MSVC_STL_UPDATE value (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
      
  • For completeness: cl.exe shows Microsoft (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
      

Additional context

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions