From 34fc6435ee17e6d2bbfffc367532cf340cf730a2 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Sun, 7 Aug 2016 02:15:34 +0200 Subject: [PATCH] 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). --- tcc.h | 3 ++- tccasm.c | 2 +- tccgen.c | 42 +++++++++++++++++++++++++++++++----------- tests/tcctest.c | 2 +- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/tcc.h b/tcc.h index 470b8dc..f990f8f 100644 --- a/tcc.h +++ b/tcc.h @@ -387,7 +387,8 @@ struct Attribute { mode : 4, weak : 1, 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 */ diff --git a/tccasm.c b/tccasm.c index ea65171..cef3f1c 100644 --- a/tccasm.c +++ b/tccasm.c @@ -612,7 +612,7 @@ static void asm_parse_directive(TCCState *s1) case TOK_ASMDIR_global: case TOK_ASMDIR_weak: case TOK_ASMDIR_hidden: - tok1 = tok; + tok1 = tok; do { Sym *sym; diff --git a/tccgen.c b/tccgen.c index 7c8bc32..eefa1b4 100644 --- a/tccgen.c +++ b/tccgen.c @@ -2565,13 +2565,20 @@ static int compare_types(CType *type1, CType *type2, int unqualified) t1 &= ~VT_DEFSIGN; t2 &= ~VT_DEFSIGN; } + /* An enum is compatible with (unsigned) int. Ideally we would + store the enums signedness in type->ref.a. 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) { - /* An enum is compatible with (unsigned) int. */ - t1 = VT_INT | (t1 & ~VT_BTYPE); + t1 = VT_INT; + if (type1->ref->a.unsigned_enum) + t1 |= VT_UNSIGNED; } if ((t2 & VT_BTYPE) == VT_ENUM) { - /* An enum is compatible with (unsigned) int. */ - t2 = VT_INT | (t2 & ~VT_BTYPE); + t2 = VT_INT; + if (type2->ref->a.unsigned_enum) + t2 |= VT_UNSIGNED; } /* XXX: bitfields ? */ if (t1 != t2) @@ -2766,15 +2773,23 @@ static void gen_assign_cast(CType *dt) (type2->t & VT_BTYPE) == VT_VOID) { /* void * can match anything */ } else { - /* exact type match, except for unsigned */ + /* exact type match, except for qualifiers */ tmp_type1 = *type1; tmp_type2 = *type2; - tmp_type1.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | - VT_VOLATILE); - tmp_type2.t &= ~(VT_DEFSIGN | VT_UNSIGNED | VT_CONSTANT | - VT_VOLATILE); - if (!is_compatible_types(&tmp_type1, &tmp_type2)) - tcc_warning("assignment from incompatible pointer type"); + tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) { + /* Like GCC don't warn by default for merely changes + in pointer target signedness. Do warn for different + 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 */ 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; /* non empty enums are not allowed */ if (a == TOK_ENUM) { + int seen_neg = 0; for(;;) { v = tok; if (v < TOK_UIDENT) @@ -3265,6 +3281,8 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) next(); c = expr_const(); } + if (c < 0) + seen_neg = 1; /* enum symbols have static storage */ ss = sym_push(v, &int_type, VT_CONST, c); ss->type.t |= VT_STATIC; @@ -3276,6 +3294,8 @@ static void struct_decl(CType *type, AttributeDef *ad, int u) if (tok == '}') break; } + if (!seen_neg) + s->a.unsigned_enum = 1; s->c = type_size(&int_type, &align); skip('}'); } else { diff --git a/tests/tcctest.c b/tests/tcctest.c index a604c4a..d8c8bcf 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -591,7 +591,7 @@ void enum_test() { enum test b1; /* The following should give no warning */ - int *p = &b1; + unsigned *p = &b1; printf("enum:\n%d %d %d %d %d %d\n", E0, E1, E2, E3, E4, E5); b1 = 1;