Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why "initializer element is not a constant" is... not working anymore?

static const int a = 42;
static const int b = a;

I would expect a compilation error in such code. The initializer must be a constant expression or a string literal. A value stored in an object with the type int with const type qualifier is not a constant expression.

I compile with -Wall -Wextra -pedantic, even with -ansi. Then:

  • the code compiles fine on gcc8.2 and gcc 8.1
  • the code fails to compile on gcc lower then 7.4 with error: initializer element is not constant
  • the code compiles fine on all clang versions

Surprisingly, the following:

static const char * const a = "a";
static const char * const b = a;
  • works on gcc 8.2
  • fails on gcc below 7.4 with error: initializer element is not constant
  • works on clang greater then 4.0
  • fails on clang lower then 3.9.1 with error: initializer element is not a compile-time constant

For the snipped below, I think I was 100% sure that it should not compile:

static const int a[] = { 1, 2, 3 };
static const int b = a[1];

, but:

  • it works on gcc 8.2
  • it fails on gcc lower then 7.4 with error: initializer element is not constant
  • and it fails on any version of clang

I tried browsing the net for an explanation, which mostly resulted in copying the code out of old stackoverflow questions about not constant intializers and finding out if they work now. I couldn't find anything relevant in gcc 8 changes.

I am lost. Is it expected behavior and such code should compile? Why/Why not? What changed between gcc7.4 and gcc8.1? Is this a compiler bug? Is this a compiler extension?

like image 260
KamilCuk Avatar asked Jan 10 '19 19:01

KamilCuk


People also ask

What is the meaning of initializer element is not constant?

What is the error initializer element is not constant? Solution 1 It's not inside a function, which means it has to be an initializer – which is assigned only when the item is declared – which means it must be a constant value at compile time, which malloc cannot be.22-Jul-2021.

Can constant be initialized?

To initialize the const value using constructor, we have to use the initialize list. This initializer list is used to initialize the data member of a class. The list of members, that will be initialized, will be present after the constructor after colon. members will be separated using comma.

Is it necessary to initialize const variables?

The const variable cannot be left un-initialized at the time of the assignment. It cannot be assigned value anywhere in the program. Explicit value needed to be provided to the constant variable at the time of declaration of the constant variable.

What does it mean to initialize a constant?

"constant initialization" means the initializer is a constant expression. Neither the expression nor the variable need to be const-qualified. int x = 6; at file scope is constant initialization.


1 Answers

Initializers for objects with static storage duration are required to be composed of constant expressions. As @EugenSh. observed in comments, "constant expression" is a defined term. Specifically, in C2011 it is the subject of section 6.6. The description is simply

A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.

But the devil is in the details. The semantic details of constant expressions contain specific rules for particular kinds and uses of constant expressions.

For example, the expression a is not an "integer constant expression" under any circumstance, regardless of the type or constness of a, and therefore may not be used where the standard requires that specific kind of constant expression, such as in bitfield widths.

Although the standard does not give a name to it, it presents slightly more relaxed rules for constant expressions in initializers, which is the case we're considering here:

Such a constant expression shall be, or evaluate to, one of the following:

  • an arithmetic constant expression,
  • a null pointer constant,
  • an address constant, or
  • an address constant for a complete object type plus or minus an integer constant expression.

The terms "arithmetic constant expression" and "address constant" are also defined:

An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, sizeof expressions whose results are integer constants, and _Alignof expressions. [...]

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type. [...]

None of the initializers for your various b variables conform to those rules. An lvalue expression designating an object having const-qualified type is not among the elements that are permitted to appear in any of the varieties of constant expression that the standard requires for initializers.

The standard does allow generally that

An implementation may accept other forms of constant expressions.

, but that does not override its specific requirements for constant expressions appearing in initializers.

Each of the given declarations of a variable b violates a "shall" requirement of the standard appearing outside a constraint. The resulting behavior is therefore undefined, but the standard does not require a diagnostic. Implementations may accept such forms as an extension, as GCC 8.2 evidently does, and GCC's -pedantic option ensures diagnostics only where required by the standard, which does not include these cases.

Since the behavior is undefined, none of the observed behavior of various implementations is non-conforming. You cannot rely on a conforming implementation to reject non-conforming code. Under some circumstances (but not these) it must diagnose non-conformance, but even in such cases it is permitted to translate successfully anyway.

I am lost. Is it expected behavior and such code should compile?

No, but neither is it safe to expect that it should fail to compile.

Why/Why not?

I explain above why the various codes fail to conform, and therefore may by rejected by conforming compilers. But on the other side, conforming compilers are not required to reject non-conforming code.

What changed between gcc7.4 and gcc8.1? Is this a compiler bug? Is this a compiler extension?

GCC evidently implemented an extension. I'm uncertain whether that was intentional, but it's certainly the result. As long as the behavior is what one would naively expect, it seems pretty natural and benign, except from the point of view of GCC (not) helping you to write conforming code.

like image 135
John Bollinger Avatar answered Sep 21 '22 05:09

John Bollinger