<div dir="ltr"><div dir="ltr">On Wed, 10 Jun 2020 at 13:06, John McCall via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org">cfe-dev@lists.llvm.org</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 10 Jun 2020, at 15:55, Richard Smith wrote:<br>
> On Wed, 10 Jun 2020 at 12:18, John McCall via cfe-dev <<br>
> <a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>> wrote:<br>
><br>
>> On 10 Jun 2020, at 14:32, Richard Smith wrote:<br>
>>> On Mon, 8 Jun 2020 at 19:52, John McCall via cfe-dev<br>
>>> <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>><br>
>>> wrote:<br>
>>>> It definitely changes observable semantics, but it’s not<br>
>>>> *obviously*<br>
>>>> non-conforming; [expr.call]p7 gives us a lot of flexibility here:<br>
>>>><br>
>>>> It is implementation-defined whether the lifetime of a parameter<br>
>>>> ends when the function in which it is defined returns or at the<br>
>>>> end of the enclosing full-expression.<br>
>>>><br>
>>> This is the non-conformance I'm referring to:<br>
>>> <a href="https://godbolt.org/z/cgf5_2" rel="noreferrer" target="_blank">https://godbolt.org/z/cgf5_2</a><br>
>>><br>
>>> Even given [expr.call]p7, we are still required to destroy<br>
>>> automatic-storage-duration objects in reverse construction order by<br>
>>> [stmt.jump]p2:<br>
>>><br>
>>> "On exit from a scope (however accomplished), objects with automatic<br>
>>> storage duration (6.7.5.3) that have been constructed in that scope<br>
>>> are<br>
>>> destroyed in the reverse order of their construction."<br>
>><br>
>> Don’t temporaries not have automatic storage duration formally?<br>
>><br>
><br>
> The intent is that they don't; we have a longstanding open issue to<br>
> introduce a notion of "full-expression storage duration" to describe<br>
> temporary objects. But in the absence of such a language change, it's<br>
> unclear which utterances about "automatic storage duration" apply to<br>
> temporaries.<br>
><br>
> But in any case, I think that's immaterial here, because function<br>
> parameters are local variables, not temporary objects, and do have<br>
> automatic storage duration even in the hypothetical world where there's a<br>
> different storage duration for temporaries.<br>
><br>
> That’s why [class.temporary]p7 has to spell out the interordering<br>
>> of destruction of lifetime-extended temporaries.<br>
>><br>
>> [expr.call]p7 is the most specific statement about the destruction<br>
>> of parameters. Under normal principles of interpretation, it should<br>
>> take priority.<br>
><br>
> Well, p7 says nothing about the relative ordering of parameter<br>
> destructions, only the points where such destruction may occur.<br>
<br>
Not relative with each other, but I think it has to be understood<br>
as allowing differences relative to temporaries created by the calling<br>
full-expression.<br></blockquote><div><br></div><div>Oh, definitely. Prior to C++17, that's a problem (as described in <a href="http://itanium-cxx-abi.github.io/cxx-abi/argument-destruction.pdf">http://itanium-cxx-abi.github.io/cxx-abi/argument-destruction.pdf</a>). In C++17 and later, though, there is never a case where we elide a copy of a temporary into a parameter, so we never have a function parameter that might need to outlive its call due to the "longer of the two lifetimes" rule. The new model is that a prvalue of class type simply initializes its target in-place, without even notionally creating and copying from a temporary.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
>> Doing the parameters in reverse order seems like a more serious problem,<br>
>> but one that we can address very specifically. On targets where we<br>
>> normally emit left-to-right (everywhere except MS, right?), we can<br>
>> just destroy arguments right-to-left in the caller. I think we<br>
>> probably do this already, because we probably push cleanups<br>
>> left-to-right.<br>
><br>
> We can't destroy [[trivial_abi]] parameters in the caller.<br>
<br>
Argh, sorry, I meant “callee” here.<br>
<br>
>> The one exception about argument order is with<br>
>> assignment operators, where the standard forces us to emit the RHS<br>
>> first. Simple assignment operators can only be declared as non-static<br>
>> member functions with one parameter, so there can only be one by-value<br>
>> parameter in the first place. Compound assignment operators could in<br>
>> theory be overloaded with two by-value parameters, but of course<br>
>> they’ll usually have a reference on the LHS instead. If we really<br>
>> feel strongly about this case, we could destroy left-to-right and<br>
>> thus make this only a problem when someone takes the address of an<br>
>> oddly-defined overloaded compound assignment operator. Or we could<br>
>> call it a standard bug, yeah.<br>
><br>
> I'm arguing on the core reflector right now that this case is a standard<br>
> bug :) I think it would be great if we only got this wrong for an<br>
> address-taken non-member operator$= function (that's the only case where<br>
> right-to-left argument evaluation order is mandated and observable).<br>
<br>
Yeah.<br></blockquote><div><br></div><div>Reflecting on that some more: suppose</div><div><br></div><div>* we define two entry points for a non-member 'operator$=' function where one parameter is callee-cleanup and the other parameter has non-trivial destruction (incredibly rare already)</div><div>* for functions with callee-cleanup parameters, we destroy the first callee-cleanup parameters *and all subsequent parameters* right-to-left in the callee</div><div> - except that for the second entry point for 'operator$=' we instead destroy the last callee-cleanup parameter and all prior parameters left-to-right in the callee</div><div><br></div><div>Then we can get destruction order correct in all cases, and the only cost is that the incredibly rare case of a non-member operator$= taking two parameters of class type (with some additional conditions) gets an extra symbol.</div><div><br></div><div>This (especially the second bullet) would be an ABI change for existing users of [[trivial_abi]]. (Alternative simpler rule: if any parameter is callee-cleanup then all parameter destruction happens in the callee. I'm not sure if that's better or worse, but it seems simpler.)</div></div></div>