microsoft / microsoft/STL

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

Open
#6,294 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
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_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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.