Order of Variable

A

Alf P. Steinbach /Usenet

* Joshua Maurice, on 13.09.2010 21:37:
[snip]
I know it's rather pedantic, but it specifically allows reading and
writing to the common leading part of layout-compatible POD-structs
but only when they're both members of the same union. By specifically
restricting it to only in a union, it is also saying that it does not
work outside of a union.

No, it does not restrict, and it does not restrict specifically.

Granted, the language indicates that the authors didn't think beyond the union case.

However, there is no inconsistency in the logical consequences of that rule.
This is just how it is with much else in the standard.


Cheers & hth.,

- Alf
 
J

Johannes Schaub (litb)

Joshua said:
Hopefully we agree that the intent of 3.10 / 15 is to allow compilers
to optimize assuming that certain pointers do not alias. The note
there explicitly mentions as much. The rules given there effectively
say which pointers can and cannot alias. These rules are logically
consistent with the rest of the standard in large part. When obeying
the rest of the rules of the standard (excepting unions), you cannot
reach a state where two differently typed pointers (as defined by
3.10 / 15) can alias.

The paragraph 3.10/15, as Gabriel mentions, places restrictions regarding
the type of an *object*. The type of an object is a runtime property and a
compiler that wishes to do such optimizations first has to determine what
"dynamic" type an object would have at certain points of the code in order
to conclude that an object can't possibly be aliased by an lvalue of a
certain type.

Now, like Gabriel mentions, that concept of runtime knowledge ("dynamic
type" - with which Gabriel does not mean the most derived class type that
typeid would figure out, but the type of an object which the lifetime-
starting/stopping sequences would yield at runtime) is inherent in 3.10/15.
And this is what causes problems in the union example. That is, 3.10/15 is
*not* based on "int*" and "float*" not allowed to be aliased.

But really, I can't see the connection to 3.10/15 of the "read A::x as B::x"
issue. If "a.x" is an lvalue of type "int" and B::x is an int too, and a.x
happens to refer to that int because the members have the same offset, then
I don't see how we would violate 3.10/15 at all. Would we not have an object
of type "int"?

It just has to do with 3.8/6 first bullet: If "a"'s type would be a non-POD
class then that bullet renders this attempt undefined because the object we
try to get a member from is of type "B" so lifetime of an "A" object would
not have started yet there. But since we handle with POD types, 3.8/6 does
not forbid it.
 
J

Johannes Schaub (litb)

Alf said:
* Joshua Maurice, on 13.09.2010 21:45:

No, they don't: 3.10/15 has explicit language about aggregates and unions,
quoted above.

That bullet that reads "an aggregate or union type that includes one of the
aforementioned types among its members..." does not grant the "A::x by B::x"
because what you get in the end is not an lvalue of that aggregate type, but
an lvalue of the member. It just grants the following

struct A { int a, b; };

A a;

// note: We read two integers by using an lvalue of type "A"
B b = a;
 
A

Alf P. Steinbach /Usenet

* Johannes Schaub (litb), on 13.09.2010 23:32:
That bullet that reads "an aggregate or union type that includes one of the
aforementioned types among its members..." does not grant the "A::x by B::x"
because what you get in the end is not an lvalue of that aggregate type, but
an lvalue of the member. It just grants the following

struct A { int a, b; };

A a;

// note: We read two integers by using an lvalue of type "A"
B b = a;

Asuming B is a typedef for A, the only aliasing here is that &a is the same
address as &a.a.

Now 3.10/15 (or was it 3.10/16? no matter) doesn't say that the aggregate or
union must be the original object type, like type A for accessing a.a.

On the contrary, it talks about an aggreate or union that /includes/ the
original object type or one of the earlier mentioned sufficiently similar types.



Cheers,

- Alf
 
J

Johannes Schaub (litb)

Alf said:
* Johannes Schaub (litb), on 13.09.2010 23:32:

Asuming B is a typedef for A, the only aliasing here is that &a is the
same address as &a.a.

Now 3.10/15 (or was it 3.10/16? no matter) doesn't say that the aggregate
or union must be the original object type, like type A for accessing a.a.

On the contrary, it talks about an aggreate or union that /includes/ the
original object type or one of the earlier mentioned sufficiently similar
types.

All I wanted to say is that I think that the "an aggregate or union type..."
bullet of 3.10/15 is not needed for unions to work, and neiter is applicable
nor needed to the "read A::x as B::x" issue. It's just needed to allow
copying members of aggregates over when copying the aggregate.

An union works because a write into one member changes the dynamic type of
the contained object to the written-to type and ends the lifetime of the
object that previously was there by reusing its memory according to 3.8/1.
Reading afterwards from that written-to member then goes fine with all
aliasing rules and doesn't need any precautions. This is why there is a
close relation-ship with placement new, because it does the same thing.

I remember having read in a DR where the C committee argued (IIRC) that this
bullet applies in the following case

union A { int x; float y; };
int main() {
A a;
a.x = 1;
// y accessed by "a", which is an lvalue of an aggregate or [...]
a.y = 2;
}

I just wanted to note that I don't agree with such an interpretation and
maybe I even misinterpreted the C committee and they didn't want to say such
things. 3.10/15 does not say anything about the object expression used in a
class member access. It just talks about the lvalue used for accessing the
value.
 
J

Joshua Maurice

* Joshua Maurice, on 13.09.2010 21:45:

No, we don't.

For C++98 the intent is explicitly stated in footnote 48.

Non-normative, but intents are non-normative: "The intent of this list is to
specify the circumstances in which an object may or may not be aliased".

There is not a word about reordering of statements or about /compiler's/
optimization.

James' failure (so far) to come up with a reference about reordering adds to that

Furthermore, 3.10/15 explicitly supports the C hack (it says aliasing can occur
through "an aggregeate or union that includes one of the aforementioned types"),
it does not exclude it  --  so even if the argument about intent was not
directly countered by an explicit note (as it is), it would be pretty moot.


No, it does not. I quoted it above for your benefit. Anything you see there
about optimization or reordering is only in your /imagination/; it's not there.


That is incorrect.

Several examples of such aliasing have been given up-thread.

Gabriel's DR is another example.

I'm been looking for this for a while now. I've even reread this
entire thread, and I can't seem to locate exactly it, "Gabriel's DR".
Can anyone provide a link or a concise example demonstrating what
current standard behavior is, and a quick explanation of what the new
standard behavior should be?

No, they don't: 3.10/15 has explicit language about aggregates and unions,
quoted above.

Please try to add some logical inferences.

Sheep are black (they aren't), therefore Nelson Mandela is king of France (he
isn't, and there's currently no such thing as king of France), doesn't carry
much weight, you see.


No, it was not.

Requoting the intent, note 48, for your benefit (non-normative, but intents are
non-normative): "The intent of this list is to specify the circumstances in
which an object may or may not be aliased".

Where you read "compiler" and "optimization", that's just your /imagination/.


No, we don't.

So, the intent of 3.10 / 15 is to list when an object may or may not
be aliased. What other deeper reason could there be to list such a
thing besides allowed compiler optimizations?

This reminds me back to my high school president policy debate days,
and arguments over topicality, aka arguments over definition. We would
get into long (relative) drawn out arguments over the best way to
define words and interpret meaning. There was the laymen definition,
the dictionary definition, the expert definition, and a large variety
of sub-kinds on those arguments. We would then argue which is the
preferred "standard" of definition and interpretation, which in the
end was mostly useless pedantic words lawyering.

To bring this back, basically the C and C++ standards committees, the
compiler writers, and a majority of experts have a particular
interpretation of that section, and that interpretation is consistent
with mine, not yours. You're welcome to argue your point all you want,
including trotting out experts in etymology and the English language,
all the English dictionaries in the world, etc., but I will go with
the interpretation which is much more widely accepted and
implemented.

To wit, my argument was:
1- The standard has the strict aliasing rule, which allows compilers
to optimize assuming that sufficiently differently typed pointers do
not alias.
2- The standard also has unions, which allow sufficiently differently
typed pointers to alias.
3- Doing optimizations based on (1) in the presence of (2) results in
broken translations of source code to assembly.
4- Thus the standard allows broken translations from source code to
assembly.
5- Thus the standard has a "bug".

"Please try to add some logical inferences."
Also, I find that your suggestion that I did not provide inferences to
be insulting and distracting. Simply because you disagree with my
premises does not render my argument invalid (using the technical
logic definition) - it does not relate to my inferences. From your
point of view of the facts, that merely renders it not sound, not
invalid.
 
A

Alf P. Steinbach /Usenet

* Joshua Maurice, on 14.09.2010 00:42:
First, I should mention that that last sentence was incorrect, and I addressed
my error in a more-or-less immediate self-correction follow-up.

In this article you address precisely the two points where I had to correct myself.

Again, sorry for posting two incorrect statements (the rest stands).

I'm been looking for this for a while now. I've even reread this
entire thread, and I can't seem to locate exactly it, "Gabriel's DR".

<url: http://www.open-
std.org/jtc1/sc22/wg21/docs/cwg_active.html#636>

Can anyone provide a link or a concise example demonstrating what
current standard behavior is, and a quick explanation of what the new
standard behavior should be?

Regarding the first, there's AFAIK no clear consensus.

And I think that's because the parties discussing it have vested interests,
because except for Gabriel's DR (an inconsistency) the standard is clear, and
g++ is violating the standard when used with default options. But then default
options are generally not enough to make any C++ compiler standard-compliant. So
I'd put this in that same box, that one needs to tell the compiler to not apply
optimizations such as assuming no RTTI (msvc), assuming no exceptions (msvc), or
assuming no static-type-incompatible aliasing (g++) -- in short, the compiler
needs to be customized for standard C++.

Regarding your second question, ditto. ;-)

So, the intent of 3.10 / 15 is to list when an object may or may not
be aliased. What other deeper reason could there be to list such a
thing besides allowed compiler optimizations?

To define the language -- which is what the standard is about.

It tells you, for example, that "to access the stored value" of an object with
dynamic type 'double' through an lvalue of type 'int', is Undefined Behavior.

That's great, yes?

A nice constraint that ensures that you don't try to interpret a bitpattern in a
way that it was never intended to be interpreted, or, for that matter, exceeding
the object bounds, or invoking a trap (ouch).

Knowing the *dynamic type* is the problem, though. An optimization that looks
only at the static referent type of a pointer, does not in all cases know the
dynamic referent type (as exemplified by pointing into a union). The problem
with g++'s optimization, its so called "strict aliasing" which isn't part of the
standard, is that it incorrectly assumes that it knows the dynamic referent type
by checking only the static referent type.

The programmer can know this.

The optimizer may be able to prove it in certain cases.

But g++'s optimizer incorrectly takes it as granted that the static referent
type tells the full story (which it doesn't).

Almost any rule in the standard allows some optimization of some kind, and so
does §3.10/15.

The problem is that the concrete optimization that we're discussing, g++'s
"strict aliasing" assumption, goes beyond what the standard allows for.

Note that there is a similar presumed exhaustive list of well-defined
reinterpret_cast conversions, in C++98 §5.2.10, and that list is even explicitly
stated to be exhaustive, and then broken by the rules of the same
about-POD-members section that breaks the aliasing rules wrt. union.

So we have two separate issues:

A) Broken optimizer in g++.

B) Inconsistencies introduced by the about-POD-members section §9.2; it breaks
both the exhaustive list of valid reinterpret_cast's, and aliasing rules.

And it seems to me that some folks have attempted to hide (A) under the carpet
by focusing on (B), using the uncertainty (FUD) created by the inconsistencies
to create the impression that g++'s rule is in the standard somewhere.

It isn't.


[snip]
"Please try to add some logical inferences."
Also, I find that your suggestion that I did not provide inferences to
be insulting and distracting. Simply because you disagree with my
premises does not render my argument invalid (using the technical
logic definition) - it does not relate to my inferences. From your
point of view of the facts, that merely renders it not sound, not
invalid.

I'm sorry, but I really missed the inferences.

I could not connect your premises to your conclusions (and still can't).

As you know ;-), I speak my mind. "Think straight, talk straight." However,
while I apologize for my wording, I also thank you for the compliment that you
take my arguments seriously even though you've stated that you think that I'm
the sole holder of the opinion that I've advocated (and no, I'm not insulted by
that -- sh*t happens, as they say :), and I trust everything's OK after ).

Cheers & hth.,

- Alf
 
J

Joshua Maurice

* Joshua Maurice, on 14.09.2010 00:42:

I'm sorry, but I really missed the inferences.

I could not connect your premises to your conclusions (and still can't).

As you know ;-), I speak my mind. "Think straight, talk straight." However,
while I apologize for my wording, I also thank you for the compliment that you
take my arguments seriously even though you've stated that you think that I'm
the sole holder of the opinion that I've advocated (and no, I'm not insulted by
that  --  sh*t happens, as they say :), and I trust everything's OK after ).

Uhh. I came off strongly. I'm sorry. I still think you're pretty wrong
though.

Really though, which step(s) do you disagree with, or not understand
how they follow?
1- The standard has the strict aliasing rule, which allows compilers
to optimize assuming that sufficiently differently typed pointers do
not alias.
2- The standard also has unions, which allow sufficiently differently
typed pointers to alias.
3- Doing optimizations based on (1) in the presence of (2) results in
broken translations of source code to assembly.
4- Thus the standard allows broken translations from source code to
assembly.
5- Thus the standard has a "bug".

1-3 are premises. 4 and 5 are conclusions. 4 builds on 1, 2, and 3,
and 5 just a small thing to allow us to argue over when a standard has
a "bug" separate from the other issues. I know that you disagree with
1, but the inferences and logical deductions should follow pretty
nicely and be clear. If not, I'm more than willing to clarify.
 
J

Joshua Maurice

* Joshua Maurice, on 14.09.2010 00:42:

Regarding the first, there's AFAIK no clear consensus.

And I think that's because the parties discussing it have vested interests,
because except for Gabriel's DR (an inconsistency) the standard is clear, and
g++ is violating the standard when used with default options. But then default
options are generally not enough to make any C++ compiler standard-compliant. So
I'd put this in that same box, that one needs to tell the compiler to not apply
optimizations such as assuming no RTTI (msvc), assuming no exceptions (msvc), or
assuming no static-type-incompatible aliasing (g++)  --  in short, the compiler
needs to be customized for standard C++.

Regarding your second question, ditto. ;-)

Ah. This defect is exactly what I've been talking about for several
posts now. This is the defect in the standard. When you combine 1- the
desire for separate compilation, 2- the desire for optimizing assuming
sufficiently differently typed pointers do not alias, and 3- unions
which allow sufficiently differently typed pointers to alias, then you
get the bug described in that DR.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,901
Latest member
Noble71S45

Latest Threads

Top