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

--- Comment #30 from Arthur O'Dwyer <arthur.j.odwyer at gmail dot com> ---
I think I understand jwakely's argument at this point, and it's consistent and
teachable. https://eel.is/c++draft/class.temporary#3.sentence-1 says:

> When an object of class type X is passed to or returned from a function, if X 
> has at least one eligible copy or move constructor, each such constructor is 
> trivial, and the destructor of X is either trivial or deleted, 
> implementations are permitted to create a temporary object to hold the 
> function parameter or result object.

It says "implementations are permitted"; this means that an implementation
(GCC) can create such a temporary *whenever* permitted, and then re-optimize
the codegen under the As-If Rule to eliminate the trivial move. That still ends
the lifetime of the original object. `global` points to that original object,
which is out-of-lifetime, so dereferencing `global` is UB. (Yes, even if its
value happens to _compare_ equal to some other pointer that's still
in-lifetime: this is "pointer provenance" as I see and understand it.)

So, in order for me to call this `Widget` example a "bug," I'd have to find a
`Widget` that doesn't meet [class.temporary]/3's conditions. That is, it would
have to be a type that does NOT have eligible copy or move constructors; or has
at least one NON-trivial eligible copy or move constructor; or has a
NON-deleted NON-trivial destructor. Then the implementation (GCC) would NOT be
permitted to create a temporary and the argument wouldn't hold anymore.

And indeed, I cannot find such a `Widget`! Every example I've tried so far
matches jwakely's explanation. For example: https://godbolt.org/z/rT6Mv537e

struct X {
    X();
    X(X&, int=0);
    X(const X&) = default;
    int i = 1;
    int a[4];
};

This `X` has a non-trivial eligible copy constructor, so it doesn't meet
[class.temporary]/3's conditions, and GCC treats it the same at -O1 and -O2.
If you delete the characters `=0`, then that constructor is no longer a copy
constructor, so `X` DOES meet [class.temporary]/3 and GCC treats it differently
at -O1 and -O2 (which is fine because now the program has UB, as jwakely says).
GCC is not misbehaving in this example.

Here's another example (still matching jwakely's argument perfectly: GCC is not
misbehaving) -- this one exploits [special]/6 to play with whether the
non-trivial copy ctor is "eligible" -- https://godbolt.org/z/PWT85n5xb

I'm satisfied with the explanation -- and GCC seems to be implementing it
correctly AFAICT -- so I think it would be reasonable to close this bug as
INVALID. On the other hand, if anyone wants to argue that the current behavior
is technically correct but super confusing to working programmers, I wouldn't
argue against them, either. ;)

Reply via email to