Search code examples
cmacrosclangcompound-literals

Why does Clang complain an "initializer element is not a compile-time constant" for a local compound literal?


With the following code,

#define REINT(T, X) (union {__typeof__(X) x; T t;}){X}.t

int f2i(float x) {
  return REINT(int, x);
}

float i2f(int x) {
  return REINT(float, x);
}

float f2i2f(float x) {
  return REINT(float, REINT(int, x));
}

Clang complains about f2i2f that an "initializer element is not a compile-time constant". This seems odd because compound literals "have automatic storage duration associated with the enclosing block" (6.5.2.5p5), and the elements in the initializer list for such objects don't have to be compile-time constants.


Solution

  • In the comments under the question, it was mentioned that this is a known bug, and "it is quite an old bug still not fixed". So until it's fixed, you'll have to work around it.

    The simplest code I could find that triggers the bug is

    int x = 3;
    struct { __typeof__((int){x}) y; };
    

    The bug is triggered when:

    • a compound literal with a non-constant initializer
    • is used in the argument to __typeof__
    • in the declaration of a member variable in either a struct or a union

    When the REINT macro is used in the f2i and i2f functions, it doesn't trigger the bug because argument X is not a compound literal. X in those functions is a variable with a primitive type.

    But in the f2i2f function, the argument X passed to the outer invocation of REINT contains a compound literal. And that compound literal is part of the argument to a __typeof__ that is used in the definition of the outer union.

    So the workaround is to avoid nesting the REINT macro. For example, f2i2f can be implemented as follows:

    float f2i2f(float x) {
        int temp = REINT(int, x);
        return REINT(float, temp);
    }