<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Sat, Jan 9, 2016 at 1:35 AM, David Krauss <span dir="ltr"><<a href="mailto:david_work@me.com" target="_blank">david_work@me.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><span class=""><br><div><blockquote type="cite"><div>On 2016–01–09, at 11:35 AM, Hubert Tong <<a href="mailto:hubert.reinterpretcast@gmail.com" target="_blank">hubert.reinterpretcast@gmail.com</a>> wrote:</div><br><div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">Yes,<span> </span></span><span style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;font-family:monospace,monospace">std::launder</span><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important"><span> </span>requires a static type; however, it does not limit the ability of<span> </span></span><span style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;font-family:monospace,monospace">memcpy</span><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important"><span> </span>to operate without knowing the type of the object being copied. The<span> </span></span><span style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;font-family:monospace,monospace">std::launder</span><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important"><span> </span>call is involved<span> </span></span><i style="font-family:Helvetica;font-size:12px;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px">after</i><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important"><span> </span>the completion of<span> </span></span><span style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;font-family:monospace,monospace">memcpy</span><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important"><span> </span>to access the object that the<span> </span></span><span style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;font-family:monospace,monospace">memcpy</span><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">initialized.</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px"></div></blockquote></div><br></span><div>Right, but in type erasure, the static type must be determined by inspecting the blob somehow. (Stashing a discriminating value elsewhere is one solution, but it’s more common and often more efficient to use an abstract base class or a discriminator inside a union.)</div><div><br></div><div>My library would launder the <font face="Courier">erasure_base</font> subobject to retrieve its dispatch table, but then it’d be stuck. Dispatching to a derived class would lead back to UB.</div></div></blockquote><div>While <span style="font-family:monospace,monospace">std::launder</span> requires a static (as in, bound at compile-time) type, it does not require that the object retrieved is a complete object. Following the <span style="font-family:monospace,monospace">std::launder</span>, using plain-old <span style="font-family:monospace,monospace">static_cast</span> to convert from the base to the derived type is fine (assuming the <span style="font-family:monospace,monospace">static_cast</span> would otherwise be well-formed).<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><br></div><div><div>One workaround could be to launder the same address repeatedly as the type becomes better resolved. For example, the call wrapper could launder a base class address, then perform an indirect call, then the callee could launder again to the derived class. For the common case of virtual dynamic dispatch, this sounds like it would incur UB before first line of the callee. My library doesn’t use <font face="Courier">virtual</font>, but similar ones do. If only complete objects can be laundered, devirtualization could kick in… or launder could refuse to handle an abstract class at all. The workaround would also imply an excessive number of derived-type <font face="Courier">launder</font> calls, which could compromise optimization by suggesting that bitwise manipulations are occurring when none are. Reloading the dispatch table pointer costs cycles.</div><div><br></div><div>Perhaps a second style of laundering could implement a compromise. First, <span style="font-family:Courier">auto &header = *launder(header_ptr)</span> gets a fully-formed <font face="Courier">header</font> object from a blob, and then <font face="Courier">auto &whole = launder_extend<whole_type>(header)</font> revises the object identity to make <font face="Courier">header</font> a subobject sharing its address with another already-fully-formed object of type <font face="Courier">whole</font>. (For example, <font face="Courier">header</font> could be a base, a <font face="Courier">union</font> member, or an initial <font face="Courier">struct</font> member.) The <span style="font-family:Courier">launder_extend</span> function differs in that it acts only if its argument was believed to be a complete object (i.e. fresh from <font face="Courier">launder</font>), and it only launders the remainder of the new complete object. To solve the <font face="Courier">virtual</font> issue, do not let <font face="Courier">launder</font> imply that its result is most-derived. Perhaps, let virtual dispatch implicitly do <font face="Courier">launder_extend</font>.</div><div><br></div><div>This scheme leaves <font face="Courier">launder</font> open-ended so a polymorphic object or union can be used, yet still laundered further. A simple implementation can opt to treat <font face="Courier">launder_extend</font> the same as <font face="Courier">launder</font>.</div><div><br></div><div>Example:</div><div><font face="Courier">struct discriminator { int value; };</font></div><div><font face="Courier">struct foo { discriminator d; int i; };</font></div><div><font face="Courier">struct bar { discriminator d; float f; };</font></div><div><font face="Courier">union foobar { foo a; bar b; };</font></div><div><font face="Courier">struct baz : discriminator { double x; };</font></div><div><font face="Courier">struct bad : discriminator { virtual ~ bad(); };</font></div><div><font face="Courier"><br></font></div><div><font face="Courier">void unpack( discriminator * p ) {</font></div><div><font face="Courier"> std::launder( p ); // OK: now we can access p.</font></div></div></div></blockquote><div>Launder does not cause side-effects. This line does nothing.<br></div><div>Try:<br></div><div><span style="font-family:monospace,monospace">discriminator *pp = std::launder(p); // OK: pp points to a discriminator object if the preconditions of launder are met</span><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> int disc = p->value;</font></div></div></div></blockquote><div>Use <span style="font-family:monospace,monospace">pp</span>, but yes.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> if ( disc == 0 ) {</font></div><div><font face="Courier"> auto & f = std::launder_extend< foo >( * p ); // OK: now we can access a foo.</font></div></div></div></blockquote><div><span style="font-family:monospace,monospace">auto &f = *reinterpret_cast<foo *>(pp); // OK: we had a valid pointer to the first non-static data member of a standard-layout struct (refer to CWG notes from Kona)</span><br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> int q = p->value; // Load may be elided. Value is already in disc, equal to zero.</font></div></div></div></blockquote><div>Use <span style="font-family:monospace,monospace">pp</span>, but yes.<br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> auto & fb = std::launder_extend< foobar >( * f ); // OK: a further extension to a super-object.</font></div></div></div></blockquote><div><span style="font-family:monospace,monospace">auto &fb = reinterpret_cast<foobar &>(f); // OK: we have a valid "pointer" to a non-static data member of a union (refer to CWG notes from Kona)</span><br>or<br><span style="font-family:monospace,monospace">auto &fb = *reinterpret_cast<foobar *>(pp); // OK: the applies transitivity rules</span><br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> auto & f2 = std::launder_extend< foo >( * p ); // OK, no-op: p was already extended, becoming a subobject.</font></div></div></div></blockquote><div>Same as <span style="font-family:monospace,monospace">f</span>.<br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> auto & b = std::launder_extend< bar >( * p ); // UB: there’s already a different object there.</font></div></div></div></blockquote><div>I think the issue of non-active members of a union was still in flux during Kona.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> auto & i = std::launder_extend< int >( * p ); // Library precondition violation: invalid object extension.</font></div></div></div></blockquote><div><span style="font-family:monospace,monospace">auto &i = *reinterpret_cast<int *>(pp); // OK</span><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> } else if ( disc == 1 ) {</font></div><div><font face="Courier"> baz & z = std::launder_extend< baz >( * p ); // OK, but implementation-dependent in theory.</font></div></div></div></blockquote><div><span style="font-family:monospace,monospace">baz &z = *static_cast<baz *>(pp); // OK</span><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> } else if ( disc == 2 ) {</font></div></div></div></blockquote><div>This case is likely to go wrong before it gets here:<span style="font-family:monospace,monospace"><br>bad b;<br></span></div><div><span style="font-family:monospace,monospace">discriminator *pb = reinterpret_cast<discriminator *>(&b); // result unspecified</span><br></div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word"><div><div><font face="Courier"> </font><span style="font-family:Courier">// Library precondition violation: no discriminator subobject shares an address with class bad.</span></div><div><font face="Courier"> bad & x = std::launder_extend< bad >( * p );</font></div><div><font face="Courier"> }</font></div><div><font face="Courier">}</font></div><div><br></div></div></div><br>_______________________________________________<br>
ub mailing list<br>
<a href="mailto:ub@isocpp.open-std.org">ub@isocpp.open-std.org</a><br>
<a href="http://www.open-std.org/mailman/listinfo/ub" rel="noreferrer" target="_blank">http://www.open-std.org/mailman/listinfo/ub</a><br>
<br></blockquote></div><br></div></div>