diff --git a/tccgen.c b/tccgen.c index 47db9c66..404ed7e1 100644 --- a/tccgen.c +++ b/tccgen.c @@ -2096,9 +2096,11 @@ static inline int is_null_pointer(SValue *p) return ((p->type.t & VT_BTYPE) == VT_INT && (uint32_t)p->c.i == 0) || ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.i == 0) || ((p->type.t & VT_BTYPE) == VT_PTR && - (PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0)); + (PTR_SIZE == 4 ? (uint32_t)p->c.i == 0 : p->c.i == 0) && + ((pointed_type(&p->type)->t&VT_BTYPE)==VT_VOID) && + 0==(pointed_type(&p->type)->t&(VT_CONSTANT|VT_VOLATILE)) + ); } - static inline int is_integer_btype(int bt) { return (bt == VT_BYTE || bt == VT_SHORT || @@ -5588,6 +5590,7 @@ static void expr_cond(void) bt2 = t2 & VT_BTYPE; type.ref = NULL; + /* cast operands to correct type according to ISOC rules */ if (is_float(bt1) || is_float(bt2)) { if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { @@ -5610,16 +5613,30 @@ static void expr_cond(void) (t2 & (VT_BTYPE | VT_UNSIGNED | VT_BITFIELD)) == (VT_LLONG | VT_UNSIGNED)) type.t |= VT_UNSIGNED; } else if (bt1 == VT_PTR || bt2 == VT_PTR) { - /* If one is a null ptr constant the result type - is the other. */ - if (is_null_pointer (vtop)) - type = type1; - else if (is_null_pointer (&sv)) - type = type2; - /* XXX: test pointer compatibility, C99 has more elaborate - rules here. */ - else - type = type1; + /* http://port70.net/~nsz/c/c99/n1256.html#6.5.15p6 */ + /* If one is a null ptr constant the result type + is the other. */ + if (is_null_pointer (vtop)) type = type1; + else if (is_null_pointer (&sv)) type = type2; + else{ + int pbt1 = (pointed_type(&type1)->t&VT_BTYPE); + int pbt2 = (pointed_type(&type2)->t&VT_BTYPE); + /*pointers to void get preferred, otherwise the pointed to types minus qualifs should be compatible*/ + type = (pbt1==VT_VOID) ? type1 : type2; + if (pbt1!=VT_VOID && pbt2!=VT_VOID){ + if(!compare_types(pointed_type(&type1), pointed_type(&type2),1/*unqualif*/)) + tcc_warning("pointer type mismatch in conditional expression\n"); + } + /*qualifs combine*/ + pointed_type(&type)->t |= 0 + |(pointed_type(&type1)->t&(VT_CONSTANT|VT_VOLATILE)) + |(pointed_type(&type2)->t&(VT_CONSTANT|VT_VOLATILE)); + /*pointers to incomplete arrays get converted to pointers to completed ones if possible*/ + if ( (pointed_type(&type1)->t&VT_ARRAY) ) + pointed_type(&type)->ref->c = + 0ref->c ? + pointed_type(&type1)->ref->c : pointed_type(&type2)->ref->c; + } } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { /* XXX: test function pointer compatibility */ type = bt1 == VT_FUNC ? type1 : type2; diff --git a/tests/tests2/94_generic.c b/tests/tests2/94_generic.c index 78ca9129..5f857670 100644 --- a/tests/tests2/94_generic.c +++ b/tests/tests2/94_generic.c @@ -77,5 +77,23 @@ int main() _Generic((__typeof((float const)((float const){42}))*){0}, float*: 0); //casts lose top-level qualifiers int const x = 42; __typeof((__typeof(x))x) *xp = 0; (void)_Generic(xp, int*: 0); //casts lose top-level qualifiers + //TEST TERNARY: + //Same type + _Generic( 0?(long*)0:(long*)0, long*: (void)0); + //combining of qualifiers + _Generic( 0?(long volatile*)0:(long const*)0, long const volatile*: (void)0); + //nul-ptr constant selects other type + _Generic( 0?(long*)0:0, long*: (void)0); + _Generic( 0?(long*)0:(void*)0, long*: (void)0); + + //void ptrs get chosen preferentially; qualifs still combine + _Generic( 0?(int volatile*)0: (void const*)1, void volatile const*: (void)0); + //like gcc but not clang, don't treat (void* const as the null-ptr constant) + _Generic( 0?(int volatile*)0: (void const*)0, void volatile const*: (void)0); + + //ptrs to incomplete types get completed + (void)(sizeof(struct { int x:_Generic( 0?(int (*)[4])0 : (int (*)[])0, int (*)[4]:+1, int (*)[5]:(void)0); })); + (void)(sizeof(struct { int x:_Generic( 0?(int (*)[])0 : (int (*)[4])0, int (*)[4]:+1, int (*)[5]:(void)0); })); + return 0; }