public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/114817] New: Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type
@ 2024-04-23  4:02 arthur.j.odwyer at gmail dot com
  2024-04-23 18:29 ` [Bug libstdc++/114817] " arthur.j.odwyer at gmail dot com
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: arthur.j.odwyer at gmail dot com @ 2024-04-23  4:02 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114817

            Bug ID: 114817
           Summary: Wrong codegen for std::copy of "trivially copyable but
                    not trivially assignable" type
           Product: gcc
           Version: unknown
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: arthur.j.odwyer at gmail dot com
  Target Milestone: ---

Jiang An raises an interesting issue over at
https://github.com/llvm/llvm-project/pull/89652#discussion_r1575540420
namely:

// https://godbolt.org/z/64KGP1avE
template<class T, class U>
struct EvilPair {
    T& first;
    U& second;
    EvilPair(T& t, U& u) : first(t), second(u) {}
    EvilPair(const EvilPair&) = default;
    void operator=(const volatile EvilPair&) = delete;
    template<class = void> EvilPair& operator=(const EvilPair& rhs) {
        first = rhs.first;
        second = rhs.second;
        return *this;
    }
};

static_assert(std::is_trivially_copyable_v<EvilPair<int&, int&>>);

int main() {
    int a[] = {1,2,3};
    int b[] = {4,5,6};
    int c[] = {0,0,0};
    int d[] = {0,0,0};

    EvilPair<int&, int&> ps[] = {
        {a[0], b[0]},
        {a[1], b[1]},
        {a[2], b[2]},
    };
    EvilPair<int&, int&> qs[] = {
        {c[0], d[0]},
        {c[1], d[1]},
        {c[2], d[2]},
    };
    std::copy(ps, ps+3, qs);
    printf("%d %d %d\n", c[0], c[1], c[2]);
}

Here, EvilPair is trivially copyable, and also copy-assignable, but it is not
trivially copy-assignable. libstdc++'s std::copy assumes that any trivially
copyable type can be... well, trivially copied. So it copies the object
representation of EvilPair, instead of doing overload resolution to discover
that in fact the templated `operator=` should be called instead.

Looks like the std::copy optimization was introduced in the GCC 9 release.

Allegedly Microsoft STL's `std::pair<int&, int&>` is isomorphic to `EvilPair`
these days.

libc++'s `std::copy` avoids this issue (AFAICT) by gating their optimization on
*both* is_trivially_assignable and is_trivially_copyable.

I suspect there will be similar issues with `uninitialized_foo` functions
and/or vector reallocation, for types that are trivially copyable (or trivially
relocatable!) and yet not trivially destructive-movable.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [Bug libstdc++/114817] Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type
  2024-04-23  4:02 [Bug libstdc++/114817] New: Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type arthur.j.odwyer at gmail dot com
@ 2024-04-23 18:29 ` arthur.j.odwyer at gmail dot com
  2024-04-24  3:31 ` de34 at live dot cn
  2024-04-26 18:43 ` arthur.j.odwyer at gmail dot com
  2 siblings, 0 replies; 4+ messages in thread
From: arthur.j.odwyer at gmail dot com @ 2024-04-23 18:29 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114817

--- Comment #1 from Arthur O'Dwyer <arthur.j.odwyer at gmail dot com> ---
Yes, vector reallocation has analogous trouble with types that are "trivial,
but not trivially copy constructible."
https://godbolt.org/z/Psboqf3MP

(libc++ happens to sidestep this pitfall *on Clang 15+,* because libc++ gates
their vector reallocation optimization differently depending on whether
`__has_builtin(__is_trivially_relocatable)`; but libc++ would have the same bug
as libstdc++ when compiled on GCC.)

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [Bug libstdc++/114817] Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type
  2024-04-23  4:02 [Bug libstdc++/114817] New: Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type arthur.j.odwyer at gmail dot com
  2024-04-23 18:29 ` [Bug libstdc++/114817] " arthur.j.odwyer at gmail dot com
@ 2024-04-24  3:31 ` de34 at live dot cn
  2024-04-26 18:43 ` arthur.j.odwyer at gmail dot com
  2 siblings, 0 replies; 4+ messages in thread
From: de34 at live dot cn @ 2024-04-24  3:31 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114817

Jiang An <de34 at live dot cn> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |de34 at live dot cn

--- Comment #2 from Jiang An <de34 at live dot cn> ---
Bug 106547 seems somehow related.

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [Bug libstdc++/114817] Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type
  2024-04-23  4:02 [Bug libstdc++/114817] New: Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type arthur.j.odwyer at gmail dot com
  2024-04-23 18:29 ` [Bug libstdc++/114817] " arthur.j.odwyer at gmail dot com
  2024-04-24  3:31 ` de34 at live dot cn
@ 2024-04-26 18:43 ` arthur.j.odwyer at gmail dot com
  2 siblings, 0 replies; 4+ messages in thread
From: arthur.j.odwyer at gmail dot com @ 2024-04-26 18:43 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114817

--- Comment #3 from Arthur O'Dwyer <arthur.j.odwyer at gmail dot com> ---
https://github.com/boostorg/container/issues/153 , from 2020, is another
similar issue. There, boost::container::vector had assumed that if
__has_trivial_copy(T) and is_copy_constructible_v<T>, then T could be relocated
via memcpy; but in fact __has_trivial_copy(T) is also true for move-only types.
So it was possible to create a type such that (__has_trivial_copy(T) &&
__is_constructible(T, T&)) and yet not __is_trivially_constructible(T, T&).

// https://godbolt.org/z/x5Wda1M6E
struct T {
    template<class U> T(U&&);
    T(T&&);
    ~T();
};
static_assert(__has_trivial_copy(T) && __is_constructible(T, T&));
  // ...and yet...
static_assert(not __is_trivially_constructible(T, T&));

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-04-26 18:43 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-04-23  4:02 [Bug libstdc++/114817] New: Wrong codegen for std::copy of "trivially copyable but not trivially assignable" type arthur.j.odwyer at gmail dot com
2024-04-23 18:29 ` [Bug libstdc++/114817] " arthur.j.odwyer at gmail dot com
2024-04-24  3:31 ` de34 at live dot cn
2024-04-26 18:43 ` arthur.j.odwyer at gmail dot com

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).