Skip to content

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

@mgovers

Description

@mgovers

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions