enums and ints are compatible

But like GCC do warn about changes in signedness.  The latter
leads to some changes in gen_assign_cast to not also warn about
  unsigned* = int*
(where GCC warns, but only with extra warnings).
master
Michael Matz 2016-08-07 02:15:34 +02:00
parent b1a906b970
commit 34fc6435ee
4 changed files with 35 additions and 14 deletions

3
tcc.h
View File

@ -387,7 +387,8 @@ struct Attribute {
mode : 4, mode : 4,
weak : 1, weak : 1,
visibility : 2, visibility : 2,
fill : 8; // 8 bits left to fit well in union below unsigned_enum : 1,
fill : 7; // 7 bits left to fit well in union below
}; };
/* GNUC attribute definition */ /* GNUC attribute definition */

View File

@ -612,7 +612,7 @@ static void asm_parse_directive(TCCState *s1)
case TOK_ASMDIR_global: case TOK_ASMDIR_global:
case TOK_ASMDIR_weak: case TOK_ASMDIR_weak:
case TOK_ASMDIR_hidden: case TOK_ASMDIR_hidden:
tok1 = tok; tok1 = tok;
do { do {
Sym *sym; Sym *sym;

View File

@ -2565,13 +2565,20 @@ static int compare_types(CType *type1, CType *type2, int unqualified)
t1 &= ~VT_DEFSIGN; t1 &= ~VT_DEFSIGN;
t2 &= ~VT_DEFSIGN; t2 &= ~VT_DEFSIGN;
} }
/* An enum is compatible with (unsigned) int. Ideally we would
store the enums signedness in type->ref.a.<some_bit> and
only accept unsigned enums with unsigned int and vice versa.
But one of our callers (gen_assign_cast) always strips VT_UNSIGNED
from pointer target types, so we can't add it here either. */
if ((t1 & VT_BTYPE) == VT_ENUM) { if ((t1 & VT_BTYPE) == VT_ENUM) {
/* An enum is compatible with (unsigned) int. */ t1 = VT_INT;
t1 = VT_INT | (t1 & ~VT_BTYPE); if (type1->ref->a.unsigned_enum)
t1 |= VT_UNSIGNED;
} }
if ((t2 & VT_BTYPE) == VT_ENUM) { if ((t2 & VT_BTYPE) == VT_ENUM) {
/* An enum is compatible with (unsigned) int. */ t2 = VT_INT;
t2 = VT_INT | (t2 & ~VT_BTYPE); if (type2->ref->a.unsigned_enum)
t2 |= VT_UNSIGNED;
} }
/* XXX: bitfields ? */ /* XXX: bitfields ? */
if (t1 != t2) if (t1 != t2)
@ -2766,15 +2773,23 @@ static void gen_assign_cast(CType *dt)
(type2->t & VT_BTYPE) == VT_VOID) { (type2->t & VT_BTYPE) == VT_VOID) {
/* void * can match anything */ /* void * can match anything */
} else { } else {
/* exact type match, except for unsigned */ /* exact type match, except for qualifiers */
tmp_type1 = *type1; tmp_type1 = *type1;
tmp_type2 = *type2; tmp_type2 = *type2;
tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE);
VT_VOLATILE); tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE);
tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | if (!is_compatible_types(&tmp_type1, &tmp_type2)) {
VT_VOLATILE); /* Like GCC don't warn by default for merely changes
if (!is_compatible_types(&tmp_type1, &tmp_type2)) in pointer target signedness. Do warn for different
tcc_warning("assignment from incompatible pointer type"); base types, though, in particular for unsigned enums
and signed int targets. */
if ((tmp_type1.t & (VT_DEFSIGN | VT_UNSIGNED)) !=
(tmp_type2.t & (VT_DEFSIGN | VT_UNSIGNED)) &&
(tmp_type1.t & VT_BTYPE) == (tmp_type2.t & VT_BTYPE))
;
else
tcc_warning("assignment from incompatible pointer type");
}
} }
/* check const and volatile */ /* check const and volatile */
if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) ||
@ -3252,6 +3267,7 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
c = 0; c = 0;
/* non empty enums are not allowed */ /* non empty enums are not allowed */
if (a == TOK_ENUM) { if (a == TOK_ENUM) {
int seen_neg = 0;
for(;;) { for(;;) {
v = tok; v = tok;
if (v < TOK_UIDENT) if (v < TOK_UIDENT)
@ -3265,6 +3281,8 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
next(); next();
c = expr_const(); c = expr_const();
} }
if (c < 0)
seen_neg = 1;
/* enum symbols have static storage */ /* enum symbols have static storage */
ss = sym_push(v, &int_type, VT_CONST, c); ss = sym_push(v, &int_type, VT_CONST, c);
ss->type.t |= VT_STATIC; ss->type.t |= VT_STATIC;
@ -3276,6 +3294,8 @@ static void struct_decl(CType *type, AttributeDef *ad, int u)
if (tok == '}') if (tok == '}')
break; break;
} }
if (!seen_neg)
s->a.unsigned_enum = 1;
s->c = type_size(&int_type, &align); s->c = type_size(&int_type, &align);
skip('}'); skip('}');
} else { } else {

View File

@ -591,7 +591,7 @@ void enum_test()
{ {
enum test b1; enum test b1;
/* The following should give no warning */ /* The following should give no warning */
int *p = &b1; unsigned *p = &b1;
printf("enum:\n%d %d %d %d %d %d\n", printf("enum:\n%d %d %d %d %d %d\n",
E0, E1, E2, E3, E4, E5); E0, E1, E2, E3, E4, E5);
b1 = 1; b1 = 1;