diff --git a/tccgen.c b/tccgen.c index 4f2e68b..ba79f1f 100644 --- a/tccgen.c +++ b/tccgen.c @@ -2795,8 +2795,6 @@ static int is_compatible_func(CType *type1, CType *type2) /* return true if type1 and type2 are the same. If unqualified is true, qualifiers on the types are ignored. - - - enums are not checked as gcc __builtin_types_compatible_p () */ static int compare_types(CType *type1, CType *type2, int unqualified) { @@ -2828,6 +2826,8 @@ static int compare_types(CType *type1, CType *type2, int unqualified) return (type1->ref == type2->ref); } else if (bt1 == VT_FUNC) { return is_compatible_func(type1, type2); + } else if (IS_ENUM(type1->t) || IS_ENUM(type2->t)) { + return type1->ref == type2->ref; } else { return 1; } @@ -2988,22 +2988,14 @@ static void gen_assign_cast(CType *dt) { CType *st, *type1, *type2; char buf1[256], buf2[256]; - int dbt, sbt; + int dbt, sbt, qualwarn, lvl; st = &vtop->type; /* source type */ dbt = dt->t & VT_BTYPE; sbt = st->t & VT_BTYPE; if (sbt == VT_VOID || dbt == VT_VOID) { if (sbt == VT_VOID && dbt == VT_VOID) - ; /* - It is Ok if both are void - A test program: - void func1() {} - void func2() { - return func1(); - } - gcc accepts this program - */ + ; /* It is Ok if both are void */ else tcc_error("cannot cast from/to void"); } @@ -3014,43 +3006,49 @@ static void gen_assign_cast(CType *dt) /* special cases for pointers */ /* '0' can also be a pointer */ if (is_null_pointer(vtop)) - goto type_ok; + break; /* accept implicit pointer to integer cast with warning */ if (is_integer_btype(sbt)) { tcc_warning("assignment makes pointer from integer without a cast"); - goto type_ok; + break; } type1 = pointed_type(dt); - /* a function is implicitly a function pointer */ - if (sbt == VT_FUNC) { - if ((type1->t & VT_BTYPE) != VT_VOID && - !is_compatible_types(pointed_type(dt), st)) - tcc_warning("assignment from incompatible pointer type"); - goto type_ok; - } - if (sbt != VT_PTR) + if (sbt == VT_PTR) + type2 = pointed_type(st); + else if (sbt == VT_FUNC) + type2 = st; /* a function is implicitly a function pointer */ + else goto error; - type2 = pointed_type(st); - if ((type1->t & VT_BTYPE) == VT_VOID || - (type2->t & VT_BTYPE) == VT_VOID) { - /* void * can match anything */ - } else { - //printf("types %08x %08x\n", type1->t, type2->t); - /* exact type match, except for qualifiers */ - if (!is_compatible_unqualified_types(type1, type2)) { + if (is_compatible_types(type1, type2)) + break; + for (qualwarn = lvl = 0;; ++lvl) { + if (((type2->t & VT_CONSTANT) && !(type1->t & VT_CONSTANT)) || + ((type2->t & VT_VOLATILE) && !(type1->t & VT_VOLATILE))) + qualwarn = 1; + dbt = type1->t & (VT_BTYPE|VT_LONG); + sbt = type2->t & (VT_BTYPE|VT_LONG); + if (dbt != VT_PTR || sbt != VT_PTR) + break; + type1 = pointed_type(type1); + type2 = pointed_type(type2); + } + if (!is_compatible_unqualified_types(type1, type2)) { + if ((dbt == VT_VOID || sbt == VT_VOID) && lvl == 0) { + /* void * can match anything */ + } else if (dbt == sbt + && is_integer_btype(sbt & VT_BTYPE) + && IS_ENUM(type1->t) + IS_ENUM(type2->t) + + !!((type1->t ^ type2->t) & VT_UNSIGNED) < 2) { /* 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 ((type1->t & (VT_BTYPE|VT_LONG)) != (type2->t & (VT_BTYPE|VT_LONG)) - || IS_ENUM(type1->t) || IS_ENUM(type2->t) - ) - tcc_warning("assignment from incompatible pointer type"); - } + } else { + tcc_warning("assignment from incompatible pointer type"); + break; + } } - /* check const and volatile */ - if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || - (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE))) + if (qualwarn) tcc_warning("assignment discards qualifiers from pointer target type"); break; case VT_BYTE: @@ -3074,7 +3072,6 @@ static void gen_assign_cast(CType *dt) } break; } - type_ok: gen_cast(dt); } diff --git a/tests/tests2/60_errors_and_warnings.c b/tests/tests2/60_errors_and_warnings.c index 0028caf..b32a3db 100644 --- a/tests/tests2/60_errors_and_warnings.c +++ b/tests/tests2/60_errors_and_warnings.c @@ -48,4 +48,74 @@ enum rgb3 c = 42; #elif defined test_74_non_const_init int i = i++; +#elif defined test_pointer_assignment + +void (*f1)(void); +void f2(void) {} + +struct s1 *ps1; +struct s2 *ps2; + +void *v1, **v2, ***v3; + +enum e1 { a = 4 } e10, *e11, *e12; +enum e2 { b = -4 } e20, *e21; +enum e3 { c = 5000000000LL } e30; + +int *ip; +unsigned int *up; +long *lp; +long long *llp; + +char **c1; +char const **c2; +unsigned char **u1; + +int no_main () +{ + // function + f1 = f2; + // struct + ps1 = ps2; + // void* + v1 = v3; + v2 = v3; + + // enum + e11 = e12; + e11 = e21; + e11 = &e10; + ip = &e10; + ip = &e20; + up = &e10; + up = &e20; + up = &e30; + + lp = ip; + lp = llp; + + // constness + c1 = c2; + *c1 = *c2; + **c1 = **c2; + + // unsigned = signed + u1 = c2; + *u1 = *c2; + **u1 = **c2; + + c2 = c1; + *c2 = *c1; + **c2 = **c1; + + return 0; +} + + +#elif defined test_enum_compat +enum e4; +enum e5; +void f3(enum e4 e); +void f3(enum e5 e); + #endif diff --git a/tests/tests2/60_errors_and_warnings.expect b/tests/tests2/60_errors_and_warnings.expect index ed6a690..5f2fa1c 100644 --- a/tests/tests2/60_errors_and_warnings.expect +++ b/tests/tests2/60_errors_and_warnings.expect @@ -26,3 +26,21 @@ [test_74_non_const_init] 60_errors_and_warnings.c:49: error: initializer element is not constant + +[test_pointer_assignment] +60_errors_and_warnings.c:79: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:82: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:86: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:88: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:91: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:92: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:94: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:95: warning: assignment from incompatible pointer type +60_errors_and_warnings.c:98: warning: assignment discards qualifiers from pointer target type +60_errors_and_warnings.c:99: warning: assignment discards qualifiers from pointer target type +60_errors_and_warnings.c:103: warning: assignment discards qualifiers from pointer target type +60_errors_and_warnings.c:104: warning: assignment discards qualifiers from pointer target type +60_errors_and_warnings.c:109: warning: assignment of read-only location + +[test_enum_compat] +60_errors_and_warnings.c:119: error: incompatible types for redefinition of 'f3'