Compound Literal semantic analysis...

Folks,

Eli and I have been on improving clang’s support for Compound Literals.

GCC and EDG produce different diagnostics for the following program.

void a(void) {
int tmp;
static int *t = &tmp; // Both GCC and EDG issue an error diagnostic (“initializer element is not constant”).
static int *a = (int){1}; // GCC issues an error diagnostic for this as well (but EDG allows it).
}

C99 6.5.2.5p6 says the following:

The value of the compound literal is that of an unnamed object initialized by the
initializer list. If the compound literal occurs outside the body of a function, the object
has static storage duration; otherwise, it has automatic storage duration associated with
the enclosing block.

This seems pretty clear to me…GCC is right and EDG is wrong.

Anyone disagree?

snaroff

Steve Naroff wrote:-

Folks,

Eli and I have been on improving clang's support for Compound Literals.

GCC and EDG produce different diagnostics for the following program.

void a(void) {
int tmp;
static int *t = &tmp; // Both GCC and EDG issue an error
diagnostic ("initializer element is not constant").
static int *a = (int){1}; // GCC issues an error diagnostic for this
as well (but EDG allows it).
}

C99 6.5.2.5p6 says the following:

The value of the compound literal is that of an unnamed object
initialized by the
initializer list. If the compound literal occurs outside the body of a
function, the object
has static storage duration; otherwise, it has automatic storage
duration associated with
the enclosing block.

This seems pretty clear to me...GCC is right and EDG is wrong.

Anyone disagree?

I'm curious about your reasoning. My front end, which is pretty
good about most things but a tad weak on compuond literals emits:

neil@duron:~/src/c$ ../cfe/cfe /tmp/bug.c
"/tmp/bug.c", line 3: error: expression must be a constant
static int *t = &tmp; // Both GCC and EDG issue an error
                ^
"/tmp/bug.c", line 4: warning: declaration hides function "a" declared
at line 1
static int *a = (int){1}; // GCC issues an error diagnostic for this
            ^
"/tmp/bug.c", line 3: warning: variable "t" declared but not used
static int *t = &tmp; // Both GCC and EDG issue an error
            ^
"/tmp/bug.c", line 4: warning: variable "a" declared but not used
static int *a = (int){1}; // GCC issues an error diagnostic for this
            ^

1 error found compiling "/tmp/bug.c".

Which agrees with GCC and seems pretty reasonable to me :slight_smile:

Neil.

Neil Booth wrote:-

Which agrees with GCC and seems pretty reasonable to me :slight_smile:

I meant EDG, not GCC. In other words, why do you agree with
GCC?

Neil.

C99 6.5.2.5p6 says the following:

The value of the compound literal is that of an unnamed object
initialized by the
initializer list. If the compound literal occurs outside the body of a
function, the object
has static storage duration; otherwise, it has automatic storage
duration associated with
the enclosing block.

This seems pretty clear to me...GCC is right and EDG is wrong.

Anyone disagree?

I'm curious about your reasoning. My front end, which is pretty
good about most things but a tad weak on compuond literals emits:

neil@duron:~/src/c$ ../cfe/cfe /tmp/bug.c
"/tmp/bug.c", line 3: error: expression must be a constant
static int *t = &tmp; // Both GCC and EDG issue an error
               ^
"/tmp/bug.c", line 4: warning: declaration hides function "a" declared
at line 1
static int *a = (int){1}; // GCC issues an error diagnostic for this
           ^
"/tmp/bug.c", line 3: warning: variable "t" declared but not used
static int *t = &tmp; // Both GCC and EDG issue an error
           ^
"/tmp/bug.c", line 4: warning: variable "a" declared but not used
static int *a = (int){1}; // GCC issues an error diagnostic for this
           ^

1 error found compiling "/tmp/bug.c".

Which agrees with GCC and seems pretty reasonable to me :slight_smile:

Compatibility with GCC isn't as important as compatibility with the spec (i.e. we should accept this, but as an explicit extension). The spec says:

"If the compound literal occurs outside the body of a
function, the object
has static storage duration; otherwise, it has automatic storage
duration associated with
the enclosing block."

Doesn't that preclude this example from being accepted, or does "automatic storage" still mean it can be a constant somehow?

-Chris

Neil Booth wrote:-

Which agrees with GCC and seems pretty reasonable to me :slight_smile:

I meant EDG, not GCC. In other words, why do you agree with
GCC?

Agreeing with GCC (on a topic like this) isn't my goal. I'm simply trying to understand what the C99 spec requires.

I was surprised that EDG didn't conform to my interpretation of the spec. Since your front-end agrees with EDG, I'm glad I raised the issue...

My interpretation is simple. If an explicit variable with automatic storage duration is an error, then an implicit variable with automatic storage duration should be an error. Here is the example I provided earlier...

void a(void) {
int explicit;
int anylocal;
static int *t = &explicit; // error for explicit automatic variables.
static int *a = (int){anylocal}; // implicit automatic variable. EDG and NEILCC permit this, while GCC error.
}

Is my analogy flawed? Since the compound literal can refer to any local variable, I don't see how it could ever be constant...

snaroff

Comeau Computing - Tech Magazine 2022 over the following:

void a(void) {
int anylocal;
static int *a = (int){anylocal};
}

gives the following (in C99 mode):

"ComeauTest.c", line 3: error: expression must have a constant value
  static int *a = (int){anylocal};
                          ^

"ComeauTest.c", line 3: warning: variable "a" was declared but never referenced
  static int *a = (int){anylocal};

What were you testing with?

-Eli

Take the following program:

#include "stdio.h"
void a(void) {
static int *a = (int){1};
printf("Part a: %d", *a);
*a = 2;

int *b = (int){1};
printf("Part b: %d", *b);
*b = 2;
}

int main(void) {
a();
a();
return 0;
}

What does your frontend print out? (i.e. what is the storage
duration of each of the compound literals?)

-Eli

void a(void) {
int explicit;
int anylocal;
static int *t = &explicit; // error for explicit automatic variables.
static int *a = (int){anylocal}; // implicit automatic variable.
EDG and NEILCC permit this, while GCC error.
}

Is my analogy flawed? Since the compound literal can refer to any
local variable, I don't see how it could ever be constant...

Comeau Computing - Tech Magazine 2022 over the following:

void a(void) {
int anylocal;
static int *a = (int){anylocal};
}

gives the following (in C99 mode):

"ComeauTest.c", line 3: error: expression must have a constant value
static int *a = (int){anylocal};
                         ^

"ComeauTest.c", line 3: warning: variable "a" was declared but never referenced
static int *a = (int){anylocal};

What were you testing with?

I never tested the example above. I just tested my original example (which had a constant, not a variable reference).

In any event, now I understand why EDG/NEILCC allow my original example (but not the revised example).

I guess clang should only complain if the expression isn't constant.

It appears my analogy was flawed and GCC is incorrect...

snaroff

I'm not sure I'm following... are EDG/NEILCC assuming that compound
literals in initializers for static local variables have static
storage duration? It doesn't appear to be a legal extension per the
spec, because the spec explicitly states that because they are within
a function body, such compound literals have automatic storage
duration. clang could do the same thing as EDG/NEILCC's as an
extension, since it doesn't break any compliant C99 programs, but it
doesn't seem like an especially useful extension...

-Eli

I agree with Eli here. I don't understand why Neil and EDG are accepting the constant case.

Do they accept this?

void a(void) {
static int *a = &(int){1}; // implicit automatic variable.
}

GCC says "initializer element is not constant"

GCC also rejects this:

void a(void) {
static int *a = (int){1};
}

-chris

In any event, now I understand why EDG/NEILCC allow my original
example (but not the revised example).

I'm not sure I'm following... are EDG/NEILCC assuming that compound
literals in initializers for static local variables have static
storage duration?
It doesn't appear to be a legal extension per the
spec, because the spec explicitly states that because they are within
a function body, such compound literals have automatic storage
duration. clang could do the same thing as EDG/NEILCC's as an
extension, since it doesn't break any compliant C99 programs, but it
doesn't seem like an especially useful extension...

Agreed. The spec is clear...the storage duration is determined by the location of the CompoundLiteral. If a compound literal within a function happens to be a constant expression, it still has automatic storage duration.

snaroff

Chris Lattner wrote:-

I agree with Eli here. I don't understand why Neil and EDG are
accepting the constant case.

Do they accept this?

void a(void) {
static int *a = &(int){1}; // implicit automatic variable.
}

GCC says "initializer element is not constant"

GCC also rejects this:

void a(void) {
static int *a = (int){1};
}

You're all correct; there was a bug in my front end not saving
state on recursion (recursively parsing a top level initializer:
one for the = and one for the compound literal). Now I've fixed
that I emit the expected two errors:

$ ./cfe /tmp/bug.c
"/tmp/bug.c", line 3: error: expression must be a constant
static int *t = &tmp; // Both GCC and EDG issue an error
                ^
"/tmp/bug.c", line 4: warning: declaration hides function "a" declared
       at line 1
static int *a = (int){1}; // GCC issues an error diagnostic for this
            ^
"/tmp/bug.c", line 4: error: expression must be a constant
static int *a = (int){1}; // GCC issues an error diagnostic for this
                ^
"/tmp/bug.c", line 3: warning: variable "t" declared but not used
static int *t = &tmp; // Both GCC and EDG issue an error
            ^
"/tmp/bug.c", line 4: warning: variable "a" declared but not used
static int *a = (int){1}; // GCC issues an error diagnostic for this
            ^

2 errors found compiling "/tmp/bug.c".

Thanks for the test case; a nasty bug indeed! I don't think clang
needs an extension to accept it.

Neil.