[Bug c++/95349] Using std::launder(p) produces unexpected behavior where (p) produces expected behavior

rguenth at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Mon Jun 15 09:29:25 GMT 2020


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

--- Comment #36 from Richard Biener <rguenth at gcc dot gnu.org> ---
(In reply to Andrew Downing from comment #35)
> I agree that the new implicit object creation rules sound very difficult to
> implement correctly especially because the behavior in C is different. I'm
> curious to see how that will all play out.
> 
> In this situation though, if we use the C rules for what memcpy does C17
> 6.5/6
> https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/
> sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf#section.6.5, the effective
> type shouldn't be changed. The declared type of both objects is known to the
> compiler. In the first memcpy the declared type of the object is unsigned
> char[8], in the second memcpy the declared type of the object is double.

The issue I have with this special handling of objects with a declared type
is that the standard assumes universal knowledge and disregards that
compilers are imperfect.  It's also not clear what it means for

 int *p;
 int x;
 if (<condition>)
   p = &x;
 else
   p = malloc (4);
 memcpy (p, q, 4);

there is a single memcpy call and the standard says that both the dynamic
type transfers (from q) and that it does not (to x).

> Placement new changes the effective type to std::uint64_t, but that doesn't
> change the behavior of memcpy. Footnote 88 says "Allocated objects have no
> declared type.". I believe calling a function defined in another TU that
> returns a pointer also has to be considered to return a pointer to an object
> with no declared type, because the object's declaration isn't visible. In
> this situation though, the declared types are visible, and so a modifying
> access, or memcpy, or memmove shouldn't change the effective type.

Note the C++ standard makes the placement new optional.  Do you say that
your example is incorrect with the placement new elided?  Note you say
the declared types are visible - but at least 'd' is in another function
(compilers are imperfect) and is accessed via a pointer here.  IIRC C++
does not have this "special-casing" of objects with declared types
(it doesn't have this memcpy wording at all I think).  [there's more
"interesting" bits of all this dynamic type frobbing in PR79671 when
partial objects are re-used]

> If gcc is changing the effective type with every memcpy no matter what, that
> would be the wrong thing to do right? Especially since you're saying that
> it's the reason that this example isn't being compiled correctly.

As you say above the C rule that memcpy does not change the dynamic type
of an object with a declared type can only be fulfilled by being conservative.
This is why GCC interprets memcpy as TBAA barrier but it uses the
transfer of dynamic type as means to be able to elide a memcpy roundtrip
as seen in your testcase.  If the memcpy roundtrip has to be considered
a side-effect then I see no easy way to preserve that without preserving
the actual memcpy operation [without changing the intermediate representation
of GCC, that is].  I'd be interested in a C testcase that we get wrong,
even when relying on that special casing of objects with a declared type.
It should then be possible to construct a C++ variant that expects exactly
the opposite behavior.

The main issue I see is that this differing expectations of C and C++ are
impossible to get correct at the same time.


More information about the Gcc-bugs mailing list