Could we access member of a non-existing class type object?
Asked Answered
B

1

12

In the c++ standard, in [basic.lval]/11.6 says:

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:[...]

  • an aggregate or union type that includes one of the aforementioned types among its elements or non-static data members (including, recursively, an element or non-static data member of a subaggregate or contained union),[...]

This sentence is part of the strict-aliasing rule.

Does it allow us to alias through class member access?

class A{ //"casted to" aggregate
  int i;
  float g;
  };

int j=10;
int l = reinterpret_cast<A*>(&j)->i;

According to the standard, an object value is only accessed if the object is subject to lvalue-to-rvalue-conversion [conv.lval]/2.

[expr.ref] does not stipulate that the object-expression must refer to an object of that type, only that the glvalue must have class type (the object-expression is the expression at the left of the dot "."). Nevertheless there is a word that recurrently appears in sentence related to member access which may imply constraint on the program that I overlooked. For example [expr.ref]/4.1:

If E2 is a static data member and the type of E2 is T, then E1.E2 is an lvalue; the expression designates the named member of the class.

Could "designates" means that this member is within its lifetime and that I cannot use class member access to perfom aliasing? Or is it allowed by [basic.lval]/11.6?

Bozen answered 5/11, 2018 at 9:25 Comment(31)
I think that you need to analyze pointer-interconvertibility as well (&j is pointer-interconvertible to A*).Rabbinical
Btw, you say "class" in the title, and give union in the question. Is this a mistake?Rabbinical
@Rabbinical Yes it was I mistake. &j is not pointer interconvertible with A*. *reinterpret_cast<A*>(&j) still refer to j, this expression is a glvalue of type A* that refers to j. This is the case described in [basic.lval]/11.6Bozen
@Rabbinical Unions are classes.Aquamarine
I made this mistake while splitting a large question in two: this other question is about union:#53151544Bozen
@LightnessRacesinOrbit: yes, but it could matter whether it is a union or a class. It seemed a mistake, and indeed it was :)Rabbinical
There is no "union or a class", since unions are classes. :)Aquamarine
@Oliv: are you sure about pointer-interconvertibility? i is an int member, A has standard-layout, so...Rabbinical
@Rabbinical I have few doubts. pointer-interconvertibility is about two objects, the standard says: Two objects a and b are pointer-interconvertible but there are no two objects here. This rule would apply if j where a subobject of an existing object of type A. On the other hand [basic.lval]/11.6 does not requires two objects to exist, it just about the type of the glvalue through which is performed the access to the value of an existing object.Bozen
@LightnessRacesinOrbit: in the example, it could matter whether A is a class, or a union.Rabbinical
@Oliv: okay then, let's wait for a high-level expert to answer this question :)Rabbinical
@Rabbinical Better ;)Aquamarine
@LightnessRacesinOrbit: yes. For someone who don't read the question carefully.Rabbinical
@Rabbinical Soon will be the sun rise in America. I hope to get an answer in few hours.Bozen
@geza: Quite the contrary - since the question is tagged language-lawyer, a careful reading is mandated, and a careful reader will note that "class" includes unions unless otherwise qualified.Aquamarine
I have to think about it some more but I believe the case it was meant to support was the example I give in my strict aliasing answer here for that paragraph. Also see the paper I reference hereEmergent
@ShafikYaghmour In C this [basic.lval]/11.6 rule makes much more sense. In C++, I'am troubled by the example you give for 11.6. Even if this rule (11.6) was not there, fp.x could be an alias for ip no? And even if fp were not an aggregate, fp.x could be an alias for ip?Bozen
@Oliv: In C, N1570 5.6p7 gives does not give general permission to access a struct or union object with an lvalue of member type; nor does it grant any special permission for access via member-access expression. This would not be a problem if the footnote were recognized as indicating that compilers are only meant to use the rule to indicate when they must allow for the possibility that seemingly-unrelated lvalues might alias. Rather than interpreting the rule that way, however, gcc and clang behave as though they must give blanket permission to access structs via member-type lvalues.Scorecard
@Scorecard I understand the intent of this rule now, my reading of the standard was two much literal.Bozen
@Oliv: You may understand the intent of the rule, but the authors of gcc and clang seem deliberately oblivious to it, and also to the Spirit of C which is discussed in the Rationale, and includes the key principle "Don't prevent the programmer from doing what needs to be done". Among implementations intended for different purposes, "what needs to be done" will vary, and the authors of the Standard wanted to avoid mandating "one size fits all". Unfortunately, gcc/clang interpret as saying that programmers have no right to expect compilers to recognize any needs that aren't universal.Scorecard
@Scorecard I don't know C, and the C standard, I can't find 5.6p7 in N1570. Where can I find the Rationale? I'd like to be able to follow you. I am really interested in understanding how deviation from the spirit to some literal interpretation (like I did) happen.Bozen
@Oliv: Sorry. Typo. I meant port70.net/~nsz/c/c11/n1570.html#6.5p7 [6.5p7]. Type C Rationale into google to find open-std.org/jtc1/sc22/wg14/www/C99RationaleV5.10.pdf to receive a document that should be required reading for people intending or claiming to write quality C implementations.Scorecard
@ShafikYaghmour This answer from the associated question site core issue 2051, which says that 11.6 is subsumed by other rules in c++.Bozen
@Bozen that is very different view point then is expressed in this paper which still seems relevant and conflicts with conversations I have had with other committee members as well. The issue has been open since 2014 :-(Emergent
[basic.lval]/11.6 is a negative rule. "If you do X, the behavior is undefined" doesn't mean "if you don't do X, the behavior is defined". That's denying the antecedent.Alake
@Alake This is why I am trying to find what is allowed!Bozen
@Alake And the only thing that I thought could match was to perform an access to non static data member. (Calling a member function is not allowed).Bozen
@Alake Could your comment be an example of "Petitio principi"?Bozen
@Oliv: Are you still interrested in an answer? What do you want to achieve? In particular, why do you want to cast an int value to a class, and then access member of the class? If you would use a union than it would make a bit more sense. However, there may be an easier way, anyway.Sentimental
@Jörg'Wuwei'Brüggmann Actually I was wondering why there is this paragraph in the standard. Finally it is a relics of C, or depending on the sensibility, a rule that allow "access" to an aggregate member without strict aliasing rule violation. There is an open core language issue for that but it will probably stay in the standard because all committee members don't have the same "sensibility" on the subject.Bozen
@Oliv: All that should have been necessary for the C Standard would have been to apply the footnote and the Spirit of C "Don't prevent the programmer from doing what needs to be done" as implying that quality implementations should use the rule for the purpose of identifying when seemingly-unrelated lvalues may or may not alias, and not for the purpose of ignoring the fact that an access to an lvalue which is visibly derived from some object is an access to that object.Scorecard
C
1

For non-static data member, the word is:

If E2 is a non-static data member and the type of E1 is “cq1 vq1 X”, and the type of E2 is “cq2 vq2 T”, the expression designates the named member of the object designated by the first expression.

Clearly the object designated by *reinterpret_cast<A*>(&j), i.e. j, has no member, so the behavior is undefined by omission.

Cate answered 1/1, 2019 at 4:41 Comment(1)
There are many situations where parts of the Standard would imply the behavior of some actions in the absence of other parts saying that an overlapping class of actions invoke UB. Transitively applying the equivalence of &structPtr->firstMember and (*firstMemberType)structPtr, as well as *&intLvalue and intLvalue would be sufficient to imply how ((structType*)&intObject)->firstMember should work, so I wouldn't regard such constructs as "undefined by omission".Scorecard

© 2022 - 2024 — McMap. All rights reserved.