Fix ptr type combining inside the ternary operator

Make it match http://port70.net/~nsz/c/c99/n1256.html#6.5.15p6
(or http://port70.net/~nsz/c/c11/n1570.html#6.5.15p6).
This commit is contained in:
Petr Skocik 2018-11-28 10:22:37 +01:00
parent c81116e29a
commit e0012c2767
2 changed files with 47 additions and 12 deletions

View File

@ -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 =
0<pointed_type(&type1)->ref->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;

View File

@ -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;
}