3

The gcc documentation says that __builtin_types_compatible_p(t1, t2) "ignores top level qualifiers (e.g. const, volatile)", and indeed it does:

__builtin_types_compatible_p(int, const int)     returns 1
__builtin_types_compatible_p(int, volatile int)  also returns 1

I do not know what it means by "top level qualifiers", but pointers to things-which-are-compatible, are reported as not compatible:

__builtin_types_compatible_p(int *, const int *)     returns 0
__builtin_types_compatible_p(int *, volatile int *)  also returns 0
__builtin_types_compatible_p(void *, void *)         returns 1
__builtin_types_compatible_p(void *, const void *)   returns 0

which is, frankly, a surprise :-(

Can anyone provide a reason for this ? [Is my surprise a symptom of ignorance ?]


FWIW: I can work around this using __typeof__(), thus:

__builtin_types_compatible_p(__typeof__(*(void*)0), __typeof__(*(const void*)0))) returns 1

typedef int*       pi_t ;
typedef const int* pic_t ;
__builtin_types_compatible_p(__typeof__(*(pi_t)0), __typeof__(*(pic_t)0))) returns 1

Since __typeof__() accepts both an expression and a type, I have a macro:

#define Types_Compatible(_a, _b) \
  __builtin_types_compatible_p(__typeof__(_a), __typeof__(_b)) 

to slightly reduce the clutter.

Chris Hall
  • 1,707
  • 1
  • 4
  • 14
  • regarding your observation about `const` and `volatile`, [see this](https://stackoverflow.com/q/4592762/645128) for the reason they are ignored. – ryyker Dec 29 '20 at 18:58
  • Top level. `__builtin_types_compatible_p(int *, int *const )` , `__builtin_types_compatible_p(int *, int *volatile)` , `__builtin_types_compatible_p(int *, int *restrict const volatile)` etc. – KamilCuk Dec 29 '20 at 19:02
  • If you just want to ask «What is «top-level cv qualifiers»?», why do it in a such intricate way? – Language Lawyer Dec 30 '20 at 00:21
  • OK... the question was not direct enough :-( What I meant to ask is: if `int` is "compatible" with `const int`, then why is `int *` *not* "compatible" with `const int *` ? And the answer appears to be that "compatible" is to do with compatibility of actual argument types and formal parameter types. – Chris Hall Dec 30 '20 at 13:09

2 Answers2

2

int and const int are compatible as everywhere an int is expected (eg as a function parameter) a const int can be given. And, importantly, vice versa. The const and volatile only affect the variable, not the value.

That is not the case for int* and const int*. If a function requires an int* you can not supply a const int*. If you try that you will get an 'incompatible types' error. For pointers the const and volatile refer to the value, not the variable. (In most cases, it depends on where you put the const)

And your "workaround" just derefences the pointer so it gets rid of the pointer, so it ultimately answers a different question. It determines if the types pointed to are compatible, which is different from the pointer type itself.

koder
  • 2,038
  • 7
  • 10
  • OK, I see the error in my thinking. I was focusing on the compatibility of the final (pointed to) rvalue (which dereferencing, as you say, gets at). As you say, `const int*` cannot be passed to a function which takes `int*`. But `int*` can be passed to a function which takes a `const int*`. If this is (part of) what "is compatible with" (*icw*) means, then `t1` *icw* `t2` does not imply `t2` *icw* `t1`. And `__builtin_types_compatible_p()` seems to return (`t1` *icw* `t2`) && (`t2` *icw* `t1`). FWIW: gcc does not think `int8_t` *icw* `int`, but clang does ! – Chris Hall Dec 30 '20 at 13:09
0

In my opinion,type T1 is ompatible with type T2 means when you have two variable T1 a and T2 b, then you can assign a = b and at the same time b = a.

kingkong
  • 119
  • 8