On Tue, Dec 16, 2025 at 4:20 PM Jonathan Wakely <[email protected]> wrote:

> ping
>
> On Mon, 8 Dec 2025 at 11:08, Jonathan Wakely <[email protected]> wrote:
> >
> > Copying narrow characters to a range of bool using std::copy cannot be
> > optimized to use std::memcpy. Assignment of an arbitrary integer to a
> > bool needs to convert all non-zero values to true, so is not a simple
> > memcpy-like or bit_cast-like operation. We currently get this wrong and
> > optimize it to memcpy, producing invalid bool values.
> >
> > By making __memcpyable_integer<bool> false we disable memcpy
> > optimizations for heterogeneous std::copy and std::move calls where
> > either the source or destination type is bool. Copies where both types
> > are bool can still optimize to memcpy, because we don't check
> > __memcpyable_integer in that case.
> >
> > This disables the memcpy optimization for bool as the source type,
> > which isn't actually necessary (the representation of bool in GCC is
> > 0x00 or 0x01 and so copying bool to char is just a bit_cast). We don't
> > currently have a straightforward way to allow memcpy for bool to char
> > but disallow the inverse. This seems acceptable as using std::copy with
> > bool inputs and narrow character outputs is probably not common enough
> > for this to be an important optimization to do in the library code.
>
While I agree that this such inputs are uncommon, I think there is
straightforward way of allowing it, as you can write:
template<typename _Up>
  struct __memcpyable<_Up*, bool*>
  { enum {
      __value = __memcpyable_integer<_Tp>::__width == 1;
 } };

template<typename _Up>
  struct __memcpyable<_Up*, const bool*>
     : __memcpyable<_Up, bool*>
{ };

But as Patrick indicated, the cost of matching partial specializations is
linear,
so we would be at least increasing compile time, for each input, without
that clear benefit. If we ever get a report regarding performance
regression for
this case, we can add above.

>
> > libstdc++-v3/ChangeLog:
> >
> >         PR libstdc++/122907
> >         * include/bits/cpp_type_traits.h (__memcpyable_integer<bool>):
> >         Define as false.
> >         * testsuite/25_algorithms/copy/122907.cc: New test.
> > ---
> >
> > Tested x86_64-linux.
>
LGTM.

> >
> >  libstdc++-v3/include/bits/cpp_type_traits.h   |  7 +++
> >  .../testsuite/25_algorithms/copy/122907.cc    | 43 +++++++++++++++++++
> >  2 files changed, 50 insertions(+)
> >  create mode 100644 libstdc++-v3/testsuite/25_algorithms/copy/122907.cc
> >
> > diff --git a/libstdc++-v3/include/bits/cpp_type_traits.h
> b/libstdc++-v3/include/bits/cpp_type_traits.h
> > index 38cea4c67b76..0a42b7b8d997 100644
> > --- a/libstdc++-v3/include/bits/cpp_type_traits.h
> > +++ b/libstdc++-v3/include/bits/cpp_type_traits.h
> > @@ -518,6 +518,13 @@ __INT_N(__int128)
> >      struct __memcpyable_integer<volatile _Tp>
> >      { enum { __width = 0 }; };
> >
> > +  // Assigning an integer to bool needs to convert all non-zero values
> to true
> > +  // so it is not a memcpyable integer.
> > +  // __memcpyable<bool*, bool*> is still true though.
> > +  template<>
> > +    struct __memcpyable_integer<bool>
> > +    { enum { __width = 0 }; };
> > +
> >    // Specializations for __intNN types with padding bits.
> >  #if defined __GLIBCXX_TYPE_INT_N_0 && __GLIBCXX_BITSIZE_INT_N_0 %
> __CHAR_BIT__
> >    __extension__
> > diff --git a/libstdc++-v3/testsuite/25_algorithms/copy/122907.cc
> b/libstdc++-v3/testsuite/25_algorithms/copy/122907.cc
> > new file mode 100644
> > index 000000000000..02276cea7b9b
> > --- /dev/null
> > +++ b/libstdc++-v3/testsuite/25_algorithms/copy/122907.cc
> > @@ -0,0 +1,43 @@
> > +// { dg-do run }
> > +
> > +// Bug libstdc++/122907
> > +// std::copy incorrectly uses memcpy when copying from signed or
> unsigned char
> > +// buffer to bool buffer
> > +
> > +#include <algorithm>
> > +#include <testsuite_hooks.h>
> > +
> > +template<typename T>
> > +__attribute__((noinline,noipa))
> > +void
> > +test_pr122907(T (&buf)[4])
> > +{
> > +  unsigned char uc[4];
> > +  bool bool_buf[4];
> > +  std::copy(buf, buf+1, bool_buf);
> > +  std::copy(bool_buf, bool_buf+1, uc);
> > +  VERIFY(uc[0] == bool(buf[0]));
> > +  std::copy(buf, buf+4, bool_buf);
> > +  std::copy(bool_buf, bool_buf+4, uc);
> > +  VERIFY(uc[0] == bool(buf[0]));
> > +  VERIFY(uc[1] == bool(buf[1]));
> > +  VERIFY(uc[2] == bool(buf[2]));
> > +  VERIFY(uc[3] == bool(buf[3]));
> > +}
> > +
> > +template<typename T>
> > +void
> > +test_pr122907()
> > +{
> > +  T buf[4] = { (T)3,  (T)2, (T)1, (T)0 };
> > +  test_pr122907(buf);
> > +}
> > +
> > +int main()
> > +{
> > +  test_pr122907<char>();
> > +  test_pr122907<signed char>();
> > +  test_pr122907<unsigned char>();
> > +  test_pr122907<bool>();
> > +  test_pr122907<int>();
> > +}
> > --
> > 2.52.0
> >
>
>

Reply via email to