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.
>
> 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.
>
> 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
>