__builtin_constant_p(), __builtin_expect() and __builtin_types_compatible_p() and __has_builtin()

I find that __builtin_constant_p() works as expected, but __has_builtin(constant_p) denies it !

Similarly __builtin_expect() and __builtin_types_compatible_p() !

Can I just assume these are all supported by all version of clang ?

Chris

I find that __builtin_constant_p() works as expected, but __has_builtin(constant_p) denies it !

I believe you need __has_builtin(__builtin_constant_p).

I find that __builtin_constant_p() works as expected, but __has_builtin(constant_p) denies it !

I believe you need __has_builtin(__builtin_constant_p).

Ah :frowning: So you do... sorry... I have no idea why I thought otherwise :frowning:

Similarly __builtin_expect() and __builtin_types_compatible_p() !

Except that __has_builtin(__builtin_types_compatible_p) also denies it.

   #include <stdio.h>

   int
   main(int argc, char* argv[])
   {
   printf("__has_builtin(__builtin_types_compatible_p)=%d\n"
   "__builtin_types_compatible_p(int, int)=%d\n"
   "__builtin_types_compatible_p(int, long)=%d\n",
   __has_builtin(__builtin_types_compatible_p),
   __builtin_types_compatible_p(int, int),
   __builtin_types_compatible_p(int, long)) ;
   }

outputs:

   __has_builtin(__builtin_types_compatible_p)=0
   __builtin_types_compatible_p(int, int)=1
   __builtin_types_compatible_p(int, long)=0

I hope I haven't missed something blindingly obvious this time.

Thanks,

Chris

This is a historical accident.

__has_builtin detects builtin functions. We also have a bunch of things that start with __builtin and look somewhat like functions, but that take types as arguments so can’t actually be functions – instead, they’re keywords with custom parsing rules. __has_builtin doesn’t detect those (because they’re not builtin functions).

We’ve fixed this for such things we added recently, such as __builtin_bit_cast and __builtin_FILE, but the older ones like __builtin_types_compatible_p and __builtin_choose_expr and __builtin_offsetof are unfortunately not detectable by __has_builtin.

There is another way to detect these: use !__is_identifier(__builtin_types_compatible_p). That will evaluate to 1 whenever __builtin_types_compatible_p is not a valid identifier (because it’s a keyword) – that is, whenever the feature is available. However, __is_identifier was added in April 2014, and nearly all the builtins that __has_builtin can’t detect are older than that. So this technique isn’t really useful.

In practice, every version of Clang from the past 10 years supports __builtin_types_compatible_p (in C mode). So you can just assume it exists.

I find that __builtin_constant_p() works as expected, but
__has_builtin(constant_p) denies it !

I believe you need __has_builtin(__builtin_constant_p).

Ah :frowning: So you do… sorry… I have no idea why I thought otherwise :frowning:

Similarly __builtin_expect() and __builtin_types_compatible_p() !

Except that __has_builtin(__builtin_types_compatible_p) also denies it.

#include <stdio.h>

int
main(int argc, char* argv[])
{
printf("__has_builtin(__builtin_types_compatible_p)=%d\n"
“__builtin_types_compatible_p(int, int)=%d\n”
“__builtin_types_compatible_p(int, long)=%d\n”,
__has_builtin(__builtin_types_compatible_p),
__builtin_types_compatible_p(int, int),
__builtin_types_compatible_p(int, long)) ;
}

outputs:

__has_builtin(__builtin_types_compatible_p)=0
__builtin_types_compatible_p(int, int)=1
__builtin_types_compatible_p(int, long)=0

I hope I haven’t missed something blindingly obvious this time.

This is a historical accident.

__has_builtin detects builtin functions. We also have a bunch of things that start with __builtin and look somewhat like functions, but that take types as arguments so can’t actually be functions – instead, they’re keywords with custom parsing rules. __has_builtin doesn’t detect those (because they’re not builtin functions).

We’ve fixed this for such things we added recently, such as __builtin_bit_cast and __builtin_FILE, but the older ones like __builtin_types_compatible_p and __builtin_choose_expr and __builtin_offsetof are unfortunately not detectable by __has_builtin.

There is another way to detect these: use !__is_identifier(__builtin_types_compatible_p). That will evaluate to 1 whenever __builtin_types_compatible_p is not a valid identifier (because it’s a keyword) – that is, whenever the feature is available. However, __is_identifier was added in April 2014, and nearly all the builtins that __has_builtin can’t detect are older than that. So this technique isn’t really useful.

This thread taught me something too, thanks :slight_smile: The __is_identifier trick seems even more tricky to use than this implies. E.g. it returns true for __builtin_constant_p because that one is implemented as a function, as you describe. Does this mean to fully detect support for __builtin_foo I need something like __has_builtin(foo) || __has_builtin(__builtin_foo) || !__is_identifier(__builtin_foo)?

While we’re on this topic, are the semantics of __is_identifier just “is not a keyword”? I ask because even when it returns true for a __-prefixed symbol, it’s not an indication you can use it in user code because it’s still reserved for use by the implementation. In fact __is_identifier even returns true for itself, which I guess is technically correct.

In practice, every version of Clang from the past 10 years supports __builtin_types_compatible_p (in C mode). So you can just assume it exists.

This is what I do too, as it’s widely supported back to every reasonable version of GCC as well.

I find that __builtin_constant_p() works as expected, but
__has_builtin(constant_p) denies it !

I believe you need __has_builtin(__builtin_constant_p).

Ah :frowning: So you do… sorry… I have no idea why I thought otherwise :frowning:

Similarly __builtin_expect() and __builtin_types_compatible_p() !

Except that __has_builtin(__builtin_types_compatible_p) also denies it.

#include <stdio.h>

int
main(int argc, char* argv[])
{
printf("__has_builtin(__builtin_types_compatible_p)=%d\n"
“__builtin_types_compatible_p(int, int)=%d\n”
“__builtin_types_compatible_p(int, long)=%d\n”,
__has_builtin(__builtin_types_compatible_p),
__builtin_types_compatible_p(int, int),
__builtin_types_compatible_p(int, long)) ;
}

outputs:

__has_builtin(__builtin_types_compatible_p)=0
__builtin_types_compatible_p(int, int)=1
__builtin_types_compatible_p(int, long)=0

I hope I haven’t missed something blindingly obvious this time.

This is a historical accident.

__has_builtin detects builtin functions. We also have a bunch of things that start with __builtin and look somewhat like functions, but that take types as arguments so can’t actually be functions – instead, they’re keywords with custom parsing rules. __has_builtin doesn’t detect those (because they’re not builtin functions).

We’ve fixed this for such things we added recently, such as __builtin_bit_cast and __builtin_FILE, but the older ones like __builtin_types_compatible_p and __builtin_choose_expr and __builtin_offsetof are unfortunately not detectable by __has_builtin.

There is another way to detect these: use !__is_identifier(__builtin_types_compatible_p). That will evaluate to 1 whenever __builtin_types_compatible_p is not a valid identifier (because it’s a keyword) – that is, whenever the feature is available. However, __is_identifier was added in April 2014, and nearly all the builtins that __has_builtin can’t detect are older than that. So this technique isn’t really useful.

This thread taught me something too, thanks :slight_smile: The __is_identifier trick seems even more tricky to use than this implies. E.g. it returns true for __builtin_constant_p because that one is implemented as a function, as you describe. Does this mean to fully detect support for __builtin_foo I need something like __has_builtin(foo) || __has_builtin(__builtin_foo) || !__is_identifier(__builtin_foo)?

Approximately all the function-like things that could be functions (that is, where a use can be parsed as a function call, and in particular where none of the arguments are types) are functions, and can be detected with __has_builtin. Things that must have custom parsing logic are modeled as keywords and can be detected by __is_identifier. You should never need both.

(In https://reviews.llvm.org/D66100 I’m proposing that we extend __has_builtin to also be able to identify the builtins that we model as keywords, but sadly that does nothing for versions of Clang we’ve already released, so you still need to use the old techniques there.)

While we’re on this topic, are the semantics of __is_identifier just “is not a keyword”? I ask because even when it returns true for a __-prefixed symbol, it’s not an indication you can use it in user code because it’s still reserved for use by the implementation. In fact __is_identifier even returns true for itself, which I guess is technically correct.

It’s “is identifier”, which (for identifier-shaped tokens) is almost but not quite “is not a keyword”, due to things like “and” which in C++ is neither an identifier nor a keyword.