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

--- Comment #34 from Richard Biener <rguenth at gcc dot gnu.org> ---
(In reply to Andrew Downing from comment #33)
> Those are all perfectly good arguments, but the problem ended up not having
> anything to do with std::launder or new implicit object creation rules or
> anything else introduced in the most recent standards right? This should be
> well defined in c++11 and on https://godbolt.org/z/w5FoZN. It compiles
> correctly until gcc 5.1, and in all versions of the other major compilers
> I've tried that will actually compile on godbolt.org. As far as I can tell,
> it should also be well defined in c++98 and on if you use different types
> and check the size and alignment some other way.

GCC assumes that memcpy transfers the dynamic type as it does in C which makes
the testcase invalid without the new implicit object creation rules (which
then must rely on that memcpy behavior).  Citing your new example here:

#include <new>
#include <cstring>
#include <cstdlib>
#include <cstdint>

static_assert(sizeof(double) == sizeof(std::uint64_t), "");
static_assert(alignof(double) == alignof(std::uint64_t), "");

std::uint64_t *pun(void *p) {
    char storage[sizeof(double)];
    std::memcpy(storage, p, sizeof(storage));
    std::uint64_t *u = new(p) std::uint64_t;
    std::memcpy(u,storage, sizeof(storage));
    return u;
}

std::uint64_t f1(std::uint64_t *maybe) {
    double d = 3.14159;
    std::uint64_t *u = pun(&d);

    if(rand() == 0) {
        u = maybe;
    }

    return *u;
}

In particular in C++14 3.9/4 refers to footnote 44 which says "The intent is
that the memory model of C++ is compatible with that of ISO/IEC 9899
Programming Language C" which is a laudable goal.

But I see that information about the memory model and the impact of language
features on it is scattered across many places in the C++ standard and some
wordings sound contradictory.  And I always fail to remember where all the
relevant points were.  But I did a thorough research of C and C++ standards
when implementing what GCC does in the GCC 5 timeframe (where it was C++11
and C++14 draft state IIRC).

IMHO you cannot elide memcpy if it implicitely creates an object of a type
that is only determined later by (the first?) access.  It would also be
an extremely bad choice of semantics.  Making it so that memcpy does not
alter the type of the destination object is bad as well if you consider
re-use of allocated storage where the last access to the old object is
visible which determines the dynamic type.  So what C specifies is the
only viable semantics for memcpy.  C has the additional restriction
of declared objects which does not apply to C++ and thus GCC does not use
that for C either.

Reply via email to