<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0in;
        margin-bottom:.0001pt;
        font-size:12.0pt;
        font-family:"Times New Roman","serif";}
a:link, span.MsoHyperlink
        {mso-style-priority:99;
        color:blue;
        text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
        {mso-style-priority:99;
        color:purple;
        text-decoration:underline;}
span.EmailStyle17
        {mso-style-type:personal-reply;
        font-family:"Calibri","sans-serif";
        color:#1F497D;}
.MsoChpDefault
        {mso-style-type:export-only;
        font-family:"Calibri","sans-serif";}
@page WordSection1
        {size:8.5in 11.0in;
        margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
        {page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="blue" vlink="purple">
<div class="WordSection1">
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">For the first example: I’d be inclined to view it as type-unsafe because of the cast from void* to B*, not because of the malloc.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">For the rest: At quick glance, the my_malloc/my_free internals look type safe to me, so the issue isn’t wrapping malloc/free per se. Rather, it’s the calling
code that’s unsafe for casting from void* to B*, which is unchanged in the optimized version of the code.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">So IMO the three examples are all the same because they all contain the unsafe cast from void* to B*, just moving it around a little.<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">Moving to a question you didn’t ask: What if my_malloc/my_free returned/took not void*, but my_class* (so a class-specific allocator, implemented using malloc/free)?
That is:<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New"">std::map<size_t, std::stack<my_class*>> size_classes = {{16, {}}, {32, {}}, ...};<br>
<br>
my_class* my_malloc(size_t size) {<br>
auto size_class = size_classes.lower_bound(size);<br>
assert(size_class != size_classes.end());<br>
if (size_class->second.empty())<br>
return (my_class*)malloc(size_class->first);<br>
void* result = size_class->second.top();<br>
size_class->second.pop();<br>
return result;<br>
}<br>
<br>
void my_free(size_t size, my_class* block) {<br>
size_classes.lower_bound(size)->second.push(block);<br>
}</span><br>
<br>
<span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">Then this would require some decoration around the (single) cast to my_class*, perhaps:<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> ...<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> if (size_class->second.empty()) {<br>
my_class* ret = nullptr;<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> extern “c-style” {<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> ret = (my_class*)malloc(size_class->first);<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> }<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> return ret;<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> }<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-family:"Courier New""> …<br>
<br>
</span><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">And this doesn’t surprise me because I would expect the internals of an allocator to be a classic example of code that resorts to the explicit type-unsafe escape
hatch (“extern “C-style” { }” block around or whatever).<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D">Herb<o:p></o:p></span></p>
<p class="MsoNormal"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></p>
<p class="MsoNormal"><a name="_MailEndCompose"><span style="font-size:11.0pt;font-family:"Calibri","sans-serif";color:#1F497D"><o:p> </o:p></span></a></p>
<div style="border:none;border-left:solid blue 1.5pt;padding:0in 0in 0in 4.0pt">
<div>
<div style="border:none;border-top:solid #E1E1E1 1.0pt;padding:3.0pt 0in 0in 0in">
<p class="MsoNormal"><b><span style="font-size:11.0pt;font-family:"Calibri","sans-serif"">From:</span></b><span style="font-size:11.0pt;font-family:"Calibri","sans-serif""> ub-bounces@open-std.org [mailto:ub-bounces@open-std.org]
<b>On Behalf Of </b>Jeffrey Yasskin<br>
<b>Sent:</b> Friday, January 17, 2014 4:21 PM<br>
<b>To:</b> WG21 UB study group<br>
<b>Subject:</b> Re: [ub] type punning through congruent base class?<o:p></o:p></span></p>
</div>
</div>
<p class="MsoNormal"><o:p> </o:p></p>
<div>
<p class="MsoNormal">I'd like to complicate things further, in response to the idea that the snippet involving 'B' and 'short' is type-punning and that we should consider a type-safe mode. :)<br>
<br>
First, consider whether the following code, intended to be completely normal C code:<br>
<br>
<span style="font-family:"Courier New""> B* pb = (B*)malloc(sizeof(B));<br>
pb->i = 0;<br>
free(pb);<br>
short* ps = (short*)malloc(sizeof(short));<br>
*ps = 0;<br>
free(ps);</span><br>
<br>
looks like code that should be valid in this type-safe mode. If you'd like to ban it, the rest of this post won't have anything interesting for you.<br>
<br>
Now imagine I write a library (please forgive compile and logic errors, and typos):<br>
<br>
<span style="font-family:"Courier New"">std::map<size_t, std::stack<void*>> size_classes = {{16, {}}, {32, {}}, ...};<br>
<br>
void* my_malloc(size_t size) {<br>
auto size_class = size_classes.lower_bound(size);<br>
assert(size_class != size_classes.end());<br>
if (size_class->second.empty())<br>
return malloc(size_class->first);<br>
void* result = size_class->second.top();<br>
size_class->second.pop();<br>
return result;<br>
}<br>
<br>
void my_free(size_t size, void* block) {<br>
size_classes.lower_bound(size)->second.push(block);<br>
}</span><br>
<br>
Then I use it like:<br>
<br>
<span style="font-family:"Courier New""> B* pb = (B*)my_malloc(sizeof(B));<br>
pb->i = 0;<br>
my_free(sizeof(B), pb);<br>
short* ps = (short*)my_malloc(sizeof(short));<br>
*ps = 0;<br>
my_free(sizeof(short), ps);</span><br>
<br>
Is this worse than the above malloc/free-based code? That is, can users write wrappers around malloc and free?<br>
<br>
But the compiler can inline the my_malloc use down to:<br>
<br>
<span style="font-family:"Courier New""> void *p = malloc(16); // Probably 16.<br>
B* pb = (B*)p;<br>
pb->i = 0;<br>
short* ps = (short*)pb;<br>
*ps = 0;</span><br>
<br>
using knowledge of the behavior of std::map and std::stack, which is nearly identical to the "type-punning" code. So how do we make the type-punning invalid without breaking standard malloc-based code or user-written libraries?<br>
<br>
On Fri, Jan 17, 2014 at 2:56 PM, Herb Sutter <<a href="mailto:hsutter@microsoft.com">hsutter@microsoft.com</a>> wrote:<br>
>> Note that this post from Herb arrived after<br>
>> <a href="http://www.open-std.org/pipermail/ub/2014-January/000418.html">http://www.open-std.org/pipermail/ub/2014-January/000418.html</a> but was sent<br>
>> before, so the thread got a little mixed up.<br>
><br>
> Yes, I've been trying to reply less on this thread until that sync'ed back<br>
> up. :)<br>
><br>
> From what I've learned in this thread, the (rough) intended C++ model for<br>
> PODs (assuming memory of the right size/alignment) would seem to be "the<br>
> lifetime of a B starts when you write to the memory as a B, and ends when<br>
> you free the memory or write to the memory as different type." [Disclaimer:<br>
> I'm not sure if "read from the memory as a B" also starts lifetime."]<br>
><br>
> I think we can do better, but it seems like that's the (rough) intent of the<br>
> status quo, leaving aside the question of whether the wording actually says<br>
> that.<br>
><br>
> *If* that is the (rough) intent, then in:<br>
><br>
>> void *p = malloc(sizeof(B)); // 1<br>
>><br>
>> B* pb = (B*)p; // 2<br>
>><br>
>> pb->i = 0; // 3<br>
>><br>
>> short* ps = (short*)p; // 4<br>
>> *ps = 0; // 5<br>
>><br>
>> free(p); // 6<br>
><br>
><br>
> I assume that the reasoning would be that:<br>
><br>
> line 3 starts the lifetime of a B (we're writing to the bits of a B member,<br>
> not just any int)<br>
> line 5 ends the lifetime of that B and begins the lifetime of a short<br>
> line 6 ends the lifetime of that short<br>
><br>
><br>
> Again ignoring whether this is desirable, is that (roughly) the intent of<br>
> the current wording?<br>
><br>
><br>
> If yes, does the wording express it (a) accurately and (b) clearly?<br>
><br>
><br>
> Finally, regardless of the above answer, do we want to change anything about<br>
> the legality or semantics of the above type-punning code, such as possibly<br>
> having a "type-safe mode" where such code is somehow not allowed unless in<br>
> an "extern "C-compat"" block or something?<br>
><br>
><br>
> Herb<br>
><br>
><br>
><br>
> ________________________________<br>
> From: <a href="mailto:ub-bounces@open-std.org">ub-bounces@open-std.org</a> <<a href="mailto:ub-bounces@open-std.org">ub-bounces@open-std.org</a>> on behalf of Jeffrey<br>
> Yasskin <<a href="mailto:jyasskin@google.com">jyasskin@google.com</a>><br>
> Sent: Friday, January 17, 2014 1:34 PM<br>
><br>
> To: WG21 UB study group<br>
> Subject: Re: [ub] type punning through congruent base class?<br>
> <br>
> Note that this post from Herb arrived after<br>
> <a href="http://www.open-std.org/pipermail/ub/2014-January/000418.html">http://www.open-std.org/pipermail/ub/2014-January/000418.html</a> but was sent<br>
> before, so the thread got a little mixed up.<br>
><br>
> On Thu, Jan 16, 2014 at 11:38 AM, Herb Sutter <<a href="mailto:hsutter@microsoft.com">hsutter@microsoft.com</a>> wrote:<br>
>><br>
>> Richard, it cannot mean that (or if it does, IMO we have an obvious bug)<br>
>> for at least two specific reasons I can think of (below), besides the<br>
>> general reasons that it would not be sensical and would violate type safety.<br>
><br>
><br>
> We do have an obvious bug in [basic.life]p1, "The lifetime of an object of<br>
> type T begins when storage with the proper alignment and size for type T is<br>
> obtained", if we interpret "obtained" as "obtained from the memory<br>
> allocator". Even with strict uses of placement-new to change the type of<br>
> memory, placement-new doesn't "obtain" any memory. If we interpret<br>
> "obtained" as just "the programmer intends a region of storage to be<br>
> available for a T", as I think Richard is suggesting, the bug is only that<br>
> we need the wording to be clearer.<br>
><br>
>> First, objects must have unique addresses. Consider, still assuming B is<br>
>> trivially constructible:<br>
>><br>
>> void *p = malloc(sizeof(B));<br>
><br>
><br>
> The lifetime of a B starts some time after-or-including the malloc() call in<br>
> the above line and the access of 'pb->i' two lines down. [basic.life]p5<br>
> ("Before the lifetime of an object has started ... The program has undefined<br>
> behavior if ... the pointer is used to access a non-static data member")<br>
><br>
> The assignment to 'i' might start the lifetime of an 'int' subobject, but<br>
> that's not enough to make the use of 'pb->i' defined if no 'B's lifetime has<br>
> started.<br>
> <br>
>><br>
>> B* pb = (B*)p;<br>
>> pb->i = 0;<br>
><br>
><br>
> The lifetime of the B *ends* when its storage is re-used for the 'short'<br>
> ([basic.life]p1 "The lifetime of an object of type T ends when ... the<br>
> storage which the object occupies is reused"), as Daveed said. This happens<br>
> some time after the access in the previous line, and the assignment two<br>
> lines down.<br>
> <br>
>><br>
>> short* ps = (short*)p;<br>
>> *ps = 0;<br>
>><br>
>> This cannot possibly be construed as starting the lifetime of a B object<br>
>> and a short object, else they would have the same address, which is illegal.<br>
>> Am I missing something?<br>
><br>
><br>
> Both a B object and a short object have their lifetimes started in your code<br>
> snippet, but the lifetimes don't overlap.<br>
><br>
> Confusingly, the start of these lifetimes is *not* called out in any<br>
> particular line of code; it's implied by them. In particular, the casts<br>
> don't have any lifetime effects (contra the straw man at<br>
> <a href="http://www.open-std.org/pipermail/ub/2014-January/000406.html">http://www.open-std.org/pipermail/ub/2014-January/000406.html</a>). The code<br>
> would be just as defined (or undefined) written as:<br>
><br>
> void *p = malloc(sizeof(B));<br>
><br>
> B* pb = (B*)p;<br>
> short* ps = (short*)p;<br>
> pb->i = 0;<br>
><br>
> *ps = 0;<br>
><br>
><br>
> As Matt alluded to in<br>
> <a href="http://www.open-std.org/pipermail/ub/2014-January/000456.html">http://www.open-std.org/pipermail/ub/2014-January/000456.html</a>, it might be<br>
> possible to say that all lifetime effects are called out in explicit<br>
> expressions without breaking C compatibility, *if* we instead say that<br>
> accessing the members of objects with trivial constructors can be done<br>
> outside of the lifetime of such objects. I have no idea whether that would<br>
> be better or worse than saying that lifetime effects can be implied.<br>
><br>
><br>
> Jeffrey<br>
><br>
><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">http://www.open-std.org/mailman/listinfo/ub</a><br>
><o:p></o:p></p>
</div>
</div>
</div>
</body>
</html>