Path: csiph.com!eternal-september.org!reader02.eternal-september.org!.POSTED!not-for-mail From: Tim Rentsch Newsgroups: comp.std.c Subject: Re: Assignment between union object members of incompatible types Date: Tue, 01 Dec 2020 03:39:38 -0800 Organization: A noiseless patient Spider Lines: 96 Message-ID: <865z5lpxfp.fsf@linuxsc.com> References: <272ec269-16e7-4e06-b8a8-e5f6534e66a8n@googlegroups.com> <87czzuoavh.fsf@nosuchdomain.example.com> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Injection-Info: reader02.eternal-september.org; posting-host="af7d4b2a5c3e9b4ea4551b168bc8c616"; logging-data="22294"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX19GlwPFCBiHcC57K1pjS0/mkMcvoNwB9l8=" User-Agent: Gnus/5.11 (Gnus v5.11) Emacs/22.4 (gnu/linux) Cancel-Lock: sha1:9x9G/RpiUFiYuxogsrKLK0l4q3g= sha1:ms8LRx+d1Q+Lc31ibzOIC1J7KRc= Xref: csiph.com comp.std.c:6164 Keith Thompson writes: > Ian Abbott writes: > >> A question (not posted by myself) from >> https://stackoverflow.com/questions/65077630 : >> >> Consider the following: >> >> union { int i; char c; } x = {0}; >> x.c = x.i; >> >> Does the assignment x.c = x.i result in undefined behavior? >> >> C18 6.15.16.1/3 says: >> >> | If the value being stored in an object is read from another >> | object that overlaps in any way the storage of the first object, >> | then the overlap shall be exact and the two objects shall have >> | qualified or unqualified versions of a compatible type; >> | otherwise, the behavior is undefined. >> >> The objects x.c and x.i overlap, but have incompatible types, so on >> first glance it appears to be UB. > > On first glance, yes, but I think this passage needs to be updated > to reflect its intent. > > The RHS of an assignment is not an lvalue. If it starts out as an > lvalue, then lvalue conversion is applied. Logically the value is > retrieved *and then* copied into the destination object. > > A simple case like this is not likely to cause problems (in the > absence of agressive optimization), but we can construct more > problematic cases where the object being copied is arbitrarily large > and the overlap might not be detectable at compile time. > > I've written an answer: > https://stackoverflow.com/a/65080498/827263 Let me start with where we agree. I agree that 6.5.16.1 p3 deserves some clarification. After that however my reading and yours reach different conclusions. First I think the passage as written clearly conveys the meaning intended in this case, that this assignment is undefined behavior. The value being stored was read out of x.i, and is being stored into x.c. The types of those two objects don't mesh, and so the assignment is undefined behavior. This assignment is about the clearest possible case that would violate 6.5.16.1 p3, and the passage as written conveys that, IMO with no room for argument. Second I think whether the RHS is an lvalue has no bearing on the question. The cited paragraph does not mention anything about lvalues; it speaks only of "the value being stored". Consider a slight variation: x.c = (printf( "hello world\n" ), x.i); The RHS of this assignment is not an lvalue. But the /value/ being stored was read from x.i. As I read the Standard this assignment too is undefined behavior, and IMO the Standard does convey that intention. Here is another variation: x.c = 0 ? printf( "hello world\n" ) : x.i; Once again the value being stored was read from x.i, and again as I read the Standard this assignment too is undefined behavior, and IMO the Standard does convey that intention. Now let's look at an example you give in your stackoverflow answer. Quoting a whole paragraph: The passage says that the value "is read from another object". That's ambiguous. Must name of the object be the entire RHS expression, or can it be just a subexpression? If the latter, then x.c = x.i + 1; would have undefined behavior, which in my opinion would be absurd. You left out an important part: "the value /being stored/". Here the value being stored is x.i + 1. That /value/ was not read from x.i; it was formed by an addition operation after reading x.i. Ostensibly this assignment would not be undefined behavior. I say "ostensibly" because actually I think this case is not clear, and may have been intended to be undefined behavior along with the others. And I don't see anything absurd about having it be undefined behavior, or even surprising if it were discovered that this case had been meant to be undefined behavior all along. Disclaimer: I haven't yet done any research into Defect Reports or committee meeting notes to see if this question has been addressed there. I suspect it has, but I just haven't looked yet.