Lundin
Lundin

Reputation: 213892

What rules are there for qualifiers of effective type?

So I was re-reading C17 6.5/6 - 6.5/7 regarding effective type and strict aliasing, but couldn't figure out how to treat qualifiers. Some things confuse me:

Upvotes: 2

Views: 185

Answers (2)

Eric Postpischil
Eric Postpischil

Reputation: 222900

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.

Upvotes: 0

John Bollinger
John Bollinger

Reputation: 180316

"Qualified type" being a defined term, the definition is potentially relevant:

Any type so far mentioned is an unqualified type. Each unqualified type has several qualified versions of its type, corresponding to the combinations of one, two, or all three of the const, volatile, and restrict qualifiers. The qualified or unqualified versions of a type are distinct types that belong to the same type category and have the same representation and alignment requirements. A derived type is not qualified by the qualifiers (if any) of the type from which it is derived.

(C17 6.2.5/26)

I note that the _Atomic keyword is different from the other three categorized as type qualifiers, and I presume that this is related to the fact that atomic types are not required to have the same representation or alignment requirements as their corresponding non-atomic types.

I also note that the specification is explicit that qualified and unqualified versions of a type are different types.

With that background,

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?

I take you to mean this:

const uint32_t *x = &some_uint32;
uint32_t * y = *(uint32_t **) &x;

The effective type of x is const uint32_t * (an unqualified pointer to const-qualified uint32_t), and it is being accessed via an lvalue of type uint32_t * (an unqualified pointer to unqualified uint32_t). This combination is not among the exceptions allowed by the language spec. In particular, uint32_t * is not a qualified version of a const uint32_t *. The resulting behavior is therefore undefined, as specified in C17 6.5, paragraphs 6 and 7.

Although the standard does not discuss this particular application of the SAR, I take it to be justified indirectly. The issue in cases such as this is not so much about accessing the pointer value itself as about producing a pointer whose type discards qualifiers of the pointed-to type.

Note also that the SAR does allow this variation:

const uint32_t *x = &some_uint32;
const uint32_t * const y = *(const uint32_t * const *) &x;

, as const uint32_t * const is a qualified version of const uint32_t *.

Q2: Does lvalue conversion happen before or after the above quoted rules of effective type/strict aliasing are applied?

I don't see how lvalue conversion could be construed to apply before strict aliasing. The strict aliasing rule is expressed in terms of the lvalues used for accessing objects, and the result of lvalue conversion is not an lvalue.

Additionally, as @EricPostpischil observed, the SAR applies to all accesses, which include writes. There is no lvalue conversion in the first place for an lvalue that is being written.

Q3: Does the effective type come with qualifiers or not? Where in the standard is this stated?

Qualified and unqualified versions of a type are different types. I see no justification for interpreting the paragraph 6.5/6's "the declared type of the object" or "the type of the lvalue" as if the type were supposed to be considered stripped of its qualifiers, much less as if all qualifiers in the type(s) from which it is derived were stripped. The words "the type" mean what they say.

Upvotes: 2

Related Questions