Likely nasty bug with enum constants type

$ cat bug.c

enum {
  a = 1U,
  b = a-2
};

int x[b];
$ gcc -S -std=c99 -pedantic -W -Wall bug.c
bug.c:7: error: size of array ‘x’ is negative
$ ~/llvm/Debug/bin/clang -S -std=c99 -pedantic -W -Wall bug.c
$

This typescript show that while gcc (as mandated by c99 standard)
correctly gives int as the type of the enum constant, clang gives it
unsigned type.

Do you confirm the bug?

Yes; see http://llvm.org/bugs/show_bug.cgi?id=4515 .

-Eli

We have found this bug investigating about a failed assertion on clang
that perhaps it's a different bug, I don't know.

The fact is that after the type adapting phases of the enum constants to
the finally decided underlying enum type, the DeclRefExpr referring an
enum constant found inside another enum constant initializer expressions
retains the original type (that is incongruent with newly assigned
EnumConstantDecl type).

This means that if we call e.g. Expr::isIntegerConstantExpr on enum
constant init expression we get an assert violation about incongruent
signedness.

$ cat bug.c
enum {
  a = 1U,
  b = a,
  c = -1
};

$ ~/llvm/Debug/bin/clang -cc1 -ast-dump bug.c
typedef char *__builtin_va_list;
enum {
    a = (ImplicitCastExpr 0x98d65f8 <bug.c:2:7> 'int' <IntegralCast>
  (IntegerLiteral 0x98cfb20 <col:7> 'unsigned int' 1))

$ cat bug.c

enum {
a = 1U,
b = a-2
};

int x[b];
$ gcc -S -std=c99 -pedantic -W -Wall bug.c
bug.c:7: error: size of array ‘x’ is negative
$ ~/llvm/Debug/bin/clang -S -std=c99 -pedantic -W -Wall bug.c
$

This typescript show that while gcc (as mandated by c99 standard)
correctly gives int as the type of the enum constant, clang gives it
unsigned type.

Do you confirm the bug?

Yes; see http://llvm.org/bugs/show_bug.cgi?id=4515 .

We have found this bug investigating about a failed assertion on clang
that perhaps it's a different bug, I don't know.

The fact is that after the type adapting phases of the enum constants to
the finally decided underlying enum type, the DeclRefExpr referring an
enum constant found inside another enum constant initializer expressions
retains the original type (that is incongruent with newly assigned
EnumConstantDecl type).

From the AST perspective, that's not necessarily a problem. In fact, C++ is quite specific that the types of enumerators before and after the closing "}" are different; we don't get that right either:

  http://llvm.org/bugs/show_bug.cgi?id=5854

C is less clear, but the intent appears to be that the type of all enumerator values is int. However, GCC extends C to permit enumerator values which do not have type int when the value is not representable in an int. So, we'll still end up with enumerator values that don't match the enum type.

This means that if we call e.g. Expr::isIntegerConstantExpr on enum
constant init expression we get an assert violation about incongruent
signedness.

That's definitely a bug in the evaluator.

  - Doug

I have to say that the thing is for me a surprise: are you saying that
according to clang design, in the AST the registered type of a
DeclRefExpr node may be different from type of referred Decl (at least
in this case)?

There are also other known cases where we can have such kind of type
incongruences?

I was under the impression that types in AST should always be correct
for things to work (and this is the reason why I was not surprised to
see such assertion in constant expression evaluator).

$ cat bug.c

enum {
a = 1U,
b = a-2
};

int x[b];
$ gcc -S -std=c99 -pedantic -W -Wall bug.c
bug.c:7: error: size of array ‘x’ is negative
$ ~/llvm/Debug/bin/clang -S -std=c99 -pedantic -W -Wall bug.c
$

This typescript show that while gcc (as mandated by c99 standard)
correctly gives int as the type of the enum constant, clang gives it
unsigned type.

Do you confirm the bug?

Yes; see http://llvm.org/bugs/show_bug.cgi?id=4515 .

We have found this bug investigating about a failed assertion on clang
that perhaps it's a different bug, I don't know.

The fact is that after the type adapting phases of the enum constants to
the finally decided underlying enum type, the DeclRefExpr referring an
enum constant found inside another enum constant initializer expressions
retains the original type (that is incongruent with newly assigned
EnumConstantDecl type).

From the AST perspective, that's not necessarily a problem. In fact,
C++ is quite specific that the types of enumerators before and after the
closing "}" are different; we don't get that right either:

  http://llvm.org/bugs/show_bug.cgi?id=5854

C is less clear, but the intent appears to be that the type of all
enumerator values is int. However, GCC extends C to permit enumerator
values which do not have type int when the value is not representable in
an int. So, we'll still end up with enumerator values that don't match
the enum type.

I have to say that the thing is for me a surprise: are you saying that
according to clang design, in the AST the registered type of a
DeclRefExpr node may be different from type of referred Decl (at least
in this case)?

In this case, yes. The C++ language explicitly specifies that the type of enum constants changes after the '}', and it's perfectly valid to have DeclRefExprs to an enum constant both before and after the type changed. We're accurately modeling the language, here.

There are also other known cases where we can have such kind of type
incongruences?

None that I know of.

I was under the impression that types in AST should always be correct
for things to work (and this is the reason why I was not surprised to
see such assertion in constant expression evaluator).

The AST *is* correct; it's just modeling a very weird language. The constant expression evaluator needs to tread lightly here.

  - Doug

Fixed in r134139.