Q3: Does the effective type come with qualifiers or not? Where in the standard is this stated?
The effective type includes qualifiers (or lack thereof) because the rules about effective type say that a type is used, and types include qualifiers, and the rules about effective type do not say the qualifiers are disregarded.
C 2018 6.5 6 says the effective type of an object for access to its stored value is one of:
- “the declared type of the object” (if any),
- “the type of the lvalue” previously used to store into it (if that is not a character type),
- “the effective type of the object from which the value is copied” (if it was copied by a byte-copy method and the source has an effective type), or
- “the type of the lvalue used for the access.”
The third of these is recursive, so it leads to one of the others. The others all say the effective type is some type, and they do not say the effective type is the unqualified version of that type. It simply is that type; the qualifiers are not removed.
Q2: Does lvalue conversion happen before or after the above quoted rules of effective type/strict aliasing are applied?
Lvalue conversion is immaterial. The aliasing rules in C 2018 6.5 7 make no mention of lvalue conversion, and it might not occur at all, since the rules apply to both reading and modifying values. (The rules in 6.5 7 are for when a stored value is “accessed,” and “access” in the C standard means reading or modifying, per 3.1.) When an object is modified, a new value is written into it; there is no lvalue conversion. When an object is read, the aliasing rules apply to that access, and lvalue conversion happens afterward, as a separate thing.
Q1: What if the effective type is a pointer to qualified-type? Can I lvalue access it as a non-qualified pointer to the same type? Where in the standard is this stated?
The phrasing of these sentences do not make sense in this context. I will consider two meanings for them.
First, I take the first sentence as it stands and the second question as “Can I lvalue access it as a pointer to the unqualified version of the effective type?” Although I suspect my second interpretation below is the one that was intended, this one involves less change to the text. The answer is the C standard does not define the behavior because it does not conform to the rule in 6.5 7.
Given const char *p;
, p
is a pointer to a qualified type. Then, after, char **q = (char **) &p;
, *q
is a pointer to an unqualified type. Using *q
to read or to modify p
would not conform to the rule in 6.5 7. When we consider accessing p
with *q
, then as we see above, the effective type of the object is const char *
, the type of the lvalue is char *
, and none of the cases in 6.5 7 say a const char *
may be accessed as a char *
.
Second, I take the sentences as “What if the effective type is a qualified type? Can I lvalue access it as an unqualified version of the same type?” Again, the answer is the C standard does not define the behavior because it does not conform to the rule in 6.5 7.
Given const int p = 3;
, p
has a qualified type. Then, after int *q = (int *) &p;
, *q
has the unqualified version of the same type. When we consider accessing p
with *q
, the effective type of the object is const int
, and the type of the lvalue is int
, and none of the cases in 6.5 7 say a const int
may be accessed as an int
.
None of these address qualifiers of the effective type itself, only by the lvalue used for access. Which should be quite irrelevant, because of lvalue conversion... right?
No, the qualifiers of the effective type are relevant. lvalue conversion, if it occurs, does not make them irrelevant. 6.5 7 states requirements for the lvalue type with relation to the effective type, and the qualifiers of each are parts of their types and partake in the rule in 6.5 7.