clanguage-lawyerc99compound-literalsc23

Initialize block-scope static const variable with pointer to compound literal?


The following code is rejected by GCC and Clang (godbolt link):

struct thing;

typedef enum {
  THING_TYPE_A,
  THING_TYPE_B,
} thing_type_t;

typedef struct thing_a {
  int i;
} thing_a_t;

typedef struct thing_b {
  struct thing const *t;
} thing_b_t;

typedef struct thing {
  thing_type_t type;
  union {
    thing_a_t a;
    thing_b_t b;
  } t;
} thing_t;

thing_t const *get_thing(void) {
  static const thing_t s_thing = {
    .type = THING_TYPE_B, 
    .t = {
      .b = { 
        .t = &(thing_t) { .type = THING_TYPE_A, .t = { .a = { .i = 234 } } } 
      }
    },   
  };

  return &s_thing;
}

cppreference's page on Compound Literals says:

The unnamed object to which the compound literal evaluates has static storage duration if the compound literal occurs at file scope and automatic storage duration if the compound literal occurs at block scope (in which case the object's lifetime ends at the end of the enclosing block).

I believe that explains the compile error; the anonymous thing_t whose address is used to initialize s_thing.t.b.t has automatic storage duration and is therefore not a compile-time constant. If s_thing is moved to file-scope, both Clang and GCC accept it. (There is more discussion at this SO question)

It looks like C23 will expand this by allowing constexpr to be specified inside the compound literal parentheses, which is a welcome improvement!

In the meantime, is there any way to achieve a declaration like s_thing (that is, the initialization of a static const struct that contains a pointer to another constant variable) in pre-C23 at block scope, without having to explicitly declare the anonymous thing_t as its own separate variable?


Solution

  • In the meantime, is there any way to achieve a declaration like s_thing (that is, the initialization of a static const struct that contains a pointer to another constant variable) in pre-C23 at block scope, without having to explicitly declare the anonymous thing_t as its own separate variable?

    No, you have effectively ruled out all the possibilities.

    And I don't think it's different in C23 constexpr helps. Yes, you can use constexpr in the declaration of a compound literal to get a "compound literal constant" of structure type, but as far as I can tell, that does not confer static storage duration on said object. And if the object doesn't have static storage duration then its address is not an address constant.

    However, C23 does allow you to specify storage class static for a compound literal appearing at block scope, and that has the effect one would expect: the compound literal has static storage duration. In that case, its address is an address constant, and can be used in the initializer of another object with static storage duration.