Why are const qualified variables accepted as initializers on gcc?
Asked Answered
P

1

7

When compiling this code in latest verson of gcc (or clang) with -std=c17 -pedantic-errors -Wall -Wextra

static const int y = 1;
static int x = y;

then I get no compiler diagnostic message even though I'm fairly sure that this is not valid C but a constraint violation. We can prove that it is non-conforming by taking look at C17 6.7.9/4:

Constraints
...
All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

Then the definition about constant expressions, in this case an integer constant expression (6.6):

An integer constant expression shall have integer type and shall only have operands that are integer constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, _Alignof expressions, and floating constants that are the immediate operands of casts.

And then finally the definition about integer constants (6.4.4.1/2):

An integer constant begins with a digit, but has no period or exponent part. It may have a prefix that specifies its base and a suffix that specifies its type.

Thus a const int variable is not an integer constant nor is it an integer constant expression. And therefore not a valid initializer. This has been discussed before (for example here) and I think it's already established that this is non-conforming. However, my question is:

Why did gcc chose to be non-compliant even in strict mode?

clang has apparently always been non-compliant, but gcc changed from being compliant in version 7.3 to non-compliant in version 8.0 and above. gcc 7.3 and earlier gives "error: initializer element is not constant" even in default mode without -pedantic-errors.

Some sort of active, conscious decision seems to have been made regarding this message. Why was it removed entirely in gcc and why didn't they leave it as it was when compiling in strict mode -std=c17 -pedantic-errors?

Profiterole answered 5/7, 2021 at 8:0 Comment(7)
If anyone can find the rationale why clang chose to be non-compliant, that would be interesting too. I'm keeping the question to gcc specifically though, to avoid asking two questions at once.Profiterole
Does this answer your question? Why "initializer element is not a constant" is... not working anymore?Heterodyne
@jdm posted a comment in the question asked by KamilCuk that links to this gcc bug: gcc.gnu.org/bugzilla/show_bug.cgi?id=69960 that I think answers your question by documenting the discussion.Dissect
@AlexLop. No, that does not answer the question "Why did gcc chose to be non-compliant even in strict mode?" As mentioned in the question, we already established that this is non-compliant, that's not what I'm asking about.Profiterole
@PaulHankin Not really, that's just various more of less confused random comments. Compilers are not allowed to silently ignore constraint violations.Profiterole
Specifically, -std=c17 -pedantic-errors is supposed to be strict mode. I'm not compiling as -std=gnu17 nor do I want any compiler extensions to be present.Profiterole
An extended, back-and-forth discussion has been archived in chat. Please reserve comments for their intended purpose: to suggest improvements to and/or request clarification on a question.Latinism
T
6

Why did gcc chose to be non-compliant even in strict mode?

Inasmuch as the question as posed is directed to the motivation of the developers, the only information we have to go on as third parties comes from the public development artifacts, such as GCC bugzilla, repository commit messages, and actual code. As was pointed out in comments, the matter is discussed in the Bugzilla comment thread associated with the change.

The Bugzilla discussion appears to show that the developers considered the standard's requirements in this area, albeit in a somewhat perfunctory manner. See in particular comments 9 and 10. They raise paragraph 6.6/10 of the language specification:

An implementation may accept other forms of constant expressions.

They do not subject this to any particular scrutiny, and I read the comments more as seeking a justification for the change than as a thoughtful inquiry into GCC conformance considerations.

Thus, they made the change because they wanted to implement the feature request, and they found sufficient (for them) justification in the language of the standard to consider the altered behavior to be consistent with language constraints, therefore not requiring a diagnostic.


There is also an implied question of whether recent GCC's silent acceptance of the declaration forms presented in fact violates conforming processors' obligation to diagnose constraint violations.

Although it is possible to interpret 6.6/10 as allowing implementations to accept any expressions they choose as conforming to the requirements for any kind of constant expression, that seems fraught. Whether a given piece of code satisfies the language's constraints should not be implementation dependent. Either of these points of interpretation, if accepted, would resolve that problem:

  • 6.6/10 should be interpreted as expressing a specific case of the general rule that a conforming implementation may accept non-conforming code, without implying that doing so entitles the processor to treat the code as conforming.

  • 6.6/10 should be interpreted as permitting processors to interpret more expressions as "constant expressions" than those described in the preceding paragraphs, but that has no bearing on the definitions of the specific kinds of constant expressions defined in those paragraphs ("integer constant expressions" and "arithmetic constant expressions").

Those are not mutually exclusive. I subscribe to the latter, as I have written previously, and I tend to favor the former as well.

Tantalize answered 5/7, 2021 at 14:40 Comment(5)
So we have several competing explanations of that phrase, all plausible to some degree or another. Whatever the actual intent of the standard is, the wording is sloppy and should be fixed.Epigenesis
"Whether a given piece of code satisfies the language's constraints should not be implementation dependent". I beg to differ. int main(void) { int x[sizeof(long)-5]; } cannot be analysed for conformance in an implementation-independent way.Epigenesis
Suppose the standard permits an implementation to accept "improved constant expressions" (which are not integer or arithmetic constant expressions). What does such permission mean without enumerating contexts in which these expressions are allowed and other expressions are not? If an implementation is also allowed to define permissible contexts for them, this is in fact the same as allowing less constrained integer/arithmetic constant expressions, except that the new ones should be named differently.Epigenesis
@n.1.8e9-where's-my-sharem., It is meaningful to accept the hypothetical "improved constant expressions" because there is a variety of contexts that demand a constant expression without specifying an integer constant expression or an arithmetic constant expression. It is not necessary to designate specific contexts where only improved constant expressions may be used.Tantalize
I just went to poke at the linked bugzilla thread since I found out that gcc is also behaving completely differently between ports when casting from a function pointer to an integer inside an initializer list, see #75524933. I'm using the ARM32 port and now I found out it's non-conforming as well...So the current state of affairs with gcc's implementation of constant expressions is best described as "haywire".Profiterole

© 2022 - 2024 — McMap. All rights reserved.