Attached is an implementation of the __has_builtin special preprocessor operator/macro analogous to __has_attribute and (hopefully) compatible with the synonymous Clang feature (I couldn't actually find tests for it in the Clang test suite but if someone points me at them I'll verify it).
Tested on x86_64-linux. Martin PS I couldn't find an existing API to test whether a reserved symbol like __builtin_offsetof is a function-like built-in so I hardwired the tests for C and C++ into the new names_builtin_p functions. I don't like this very much because the next time such an operator is added there is nothing to remind us to update the functions. Adding a flag to the c_common_reswords array would solve the problem but at the expense of a linear search through it. Does anyone have a suggestion for how to do this better?
PR c/66970 - Add __has_builtin() macro gcc/ChangeLog: PR c/66970 * doc/cpp.texi (__has_builtin): Document. * doc/extend.texi (__builtin_frob_return_addr): Correct spelling. gcc/c/ChangeLog: PR c/66970 * c-decl.c (names_builtin_p): Define a new function. gcc/c-family/ChangeLog: PR c/66970 * c-common.c (c_common_nodes_and_builtins): Call c_define_builtins even when only preprocessing. * c-common.h (names_builtin_p): Declare new function. * c-lex.c (init_c_lex): Set has_builtin. (c_common_has_builtin): Define a new function. * c-ppoutput.c (init_pp_output): Set has_builtin. gcc/cp/ChangeLog: PR c/66970 * cp-objcp-common.c (names_builtin_p): Define new function. gcc/testsuite/ChangeLog: PR c/66970 * c-c++-common/cpp/has-builtin-2.c: New test. * c-c++-common/cpp/has-builtin-3.c: New test. * c-c++-common/cpp/has-builtin.c: New test. libcpp/ChangeLog: PR c/66970 * include/cpplib.h (cpp_builtin_type): Add BT_HAS_BUILTIN. (cpp_callbacks::has_builtin): Declare new member. * init.c (builtin_array): Add an element for BT_HAS_BUILTIN. (cpp_init_special_builtins): Handle BT_HAS_BUILTIN. * macro.c (_cpp_builtin_macro_text): Same. * traditional.c: Same. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 7169813d0f2..cd664566249 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -4467,8 +4467,7 @@ c_common_nodes_and_builtins (void) va_list_ref_type_node = build_reference_type (va_list_type_node); } - if (!flag_preprocess_only) - c_define_builtins (va_list_ref_type_node, va_list_arg_type_node); + c_define_builtins (va_list_ref_type_node, va_list_arg_type_node); main_identifier_node = get_identifier ("main"); diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 1e13aaa16fc..ad1bc6c3628 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -801,6 +801,7 @@ extern void c_register_addr_space (const char *str, addr_space_t as); extern bool in_late_binary_op; extern const char *c_addr_space_name (addr_space_t as); extern tree identifier_global_value (tree); +extern bool names_builtin_p (const char *); extern tree c_linkage_bindings (tree); extern void record_builtin_type (enum rid, const char *, tree); extern tree build_void_list_node (void); @@ -1022,6 +1023,7 @@ extern bool c_cpp_diagnostic (cpp_reader *, enum cpp_diagnostic_level, const char *, va_list *) ATTRIBUTE_GCC_DIAG(5,0); extern int c_common_has_attribute (cpp_reader *); +extern int c_common_has_builtin (cpp_reader *); extern bool parse_optimize_options (tree, bool); diff --git a/gcc/c-family/c-lex.c b/gcc/c-family/c-lex.c index e3c602fbb8d..61ecbb23569 100644 --- a/gcc/c-family/c-lex.c +++ b/gcc/c-family/c-lex.c @@ -81,6 +81,7 @@ init_c_lex (void) cb->valid_pch = c_common_valid_pch; cb->read_pch = c_common_read_pch; cb->has_attribute = c_common_has_attribute; + cb->has_builtin = c_common_has_builtin; cb->get_source_date_epoch = cb_get_source_date_epoch; cb->get_suggestion = cb_get_suggestion; cb->remap_filename = remap_macro_filename; @@ -385,6 +386,58 @@ c_common_has_attribute (cpp_reader *pfile) return result; } + +/* Callback for has_builtin. */ + +int +c_common_has_builtin (cpp_reader *pfile) +{ + const cpp_token *token = get_token_no_padding (pfile); + if (token->type != CPP_OPEN_PAREN) + { + cpp_error (pfile, CPP_DL_ERROR, + "missing '(' after \"__has_builtin\""); + return 0; + } + + const char *name = ""; + token = get_token_no_padding (pfile); + if (token->type == CPP_NAME) + { + name = (const char *) cpp_token_as_text (pfile, token); + token = get_token_no_padding (pfile); + if (token->type != CPP_CLOSE_PAREN) + { + cpp_error (pfile, CPP_DL_ERROR, + "expected ')' after \"%s\"", name); + name = ""; + } + } + else + { + cpp_error (pfile, CPP_DL_ERROR, + "macro \"__has_builtin\" requires an identifier"); + if (token->type == CPP_CLOSE_PAREN) + return 0; + } + + /* Consume tokens up to the closing parenthesis, including any nested + pairs of parentheses, to avoid confusing redundant errors. */ + for (unsigned nparen = 1; ; token = get_token_no_padding (pfile)) + { + if (token->type == CPP_OPEN_PAREN) + ++nparen; + else if (token->type == CPP_CLOSE_PAREN) + --nparen; + else if (token->type == CPP_EOF) + break; + if (!nparen) + break; + } + + return names_builtin_p (name); +} + /* Read a token and return its type. Fill *VALUE with its value, if applicable. Fill *CPP_FLAGS with the token's flags, if it is diff --git a/gcc/c-family/c-ppoutput.c b/gcc/c-family/c-ppoutput.c index 5f391988939..91fabc7c1b8 100644 --- a/gcc/c-family/c-ppoutput.c +++ b/gcc/c-family/c-ppoutput.c @@ -151,6 +151,7 @@ init_pp_output (FILE *out_stream) } cb->has_attribute = c_common_has_attribute; + cb->has_builtin = c_common_has_builtin; cb->get_source_date_epoch = cb_get_source_date_epoch; cb->remap_filename = remap_macro_filename; diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index 132fa3edb27..17f54892b1d 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -9986,6 +9986,34 @@ identifier_global_value (tree t) return NULL_TREE; } +/* Returns true if NAME refers to a built-in function or function-like + operator. */ + +bool +names_builtin_p (const char *name) +{ + tree id = get_identifier (name); + if (tree decl = identifier_global_value (id)) + return TREE_CODE (decl) == FUNCTION_DECL && DECL_IS_BUILTIN (decl); + + /* Also detect common reserved C words that aren't strictly built-in + functions. */ + switch (C_RID_CODE (id)) + { + case RID_BUILTIN_CONVERTVECTOR: + case RID_BUILTIN_HAS_ATTRIBUTE: + case RID_BUILTIN_SHUFFLE: + case RID_CHOOSE_EXPR: + case RID_OFFSETOF: + case RID_TYPES_COMPATIBLE_P: + return true; + default: + break; + } + + return false; +} + /* In C, the only C-linkage public declaration is at file scope. */ tree diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index 4369a5b5570..340808e36b6 100644 --- a/gcc/cp/cp-objcp-common.c +++ b/gcc/cp/cp-objcp-common.c @@ -24,6 +24,7 @@ along with GCC; see the file COPYING3. If not see #include "cp-tree.h" #include "cp-objcp-common.h" #include "dwarf2.h" +#include "stringpool.h" /* Special routine to get the alias set for C++. */ @@ -350,6 +351,77 @@ identifier_global_value (tree name) return get_global_binding (name); } +/* Returns true if NAME refers to a built-in function or function-like + operator. */ + +bool +names_builtin_p (const char *name) +{ + tree id = get_identifier (name); + if (tree binding = get_global_binding (id)) + { + if (TREE_CODE (binding) == FUNCTION_DECL && DECL_IS_BUILTIN (binding)) + return true; + + /* Handle the case when an overload for a built-in name exists. */ + if (TREE_CODE (binding) != OVERLOAD) + return false; + + for (ovl_iterator it (binding); it; ++it) + { + tree decl = *it; + if (DECL_IS_BUILTIN (decl)) + return true; + } + } + + /* Also detect common reserved C++ words that aren't strictly built-in + functions. */ + switch (C_RID_CODE (id)) + { + case RID_ADDRESSOF: + case RID_BUILTIN_CONVERTVECTOR: + case RID_BUILTIN_HAS_ATTRIBUTE: + case RID_BUILTIN_SHUFFLE: + case RID_BUILTIN_LAUNDER: + case RID_OFFSETOF: + case RID_HAS_NOTHROW_ASSIGN: + case RID_HAS_NOTHROW_CONSTRUCTOR: + case RID_HAS_NOTHROW_COPY: + case RID_HAS_TRIVIAL_ASSIGN: + case RID_HAS_TRIVIAL_CONSTRUCTOR: + case RID_HAS_TRIVIAL_COPY: + case RID_HAS_TRIVIAL_DESTRUCTOR: + case RID_HAS_UNIQUE_OBJ_REPRESENTATIONS: + case RID_HAS_VIRTUAL_DESTRUCTOR: + case RID_IS_ABSTRACT: + case RID_IS_AGGREGATE: + case RID_IS_BASE_OF: + case RID_IS_CLASS: + case RID_IS_EMPTY: + case RID_IS_ENUM: + case RID_IS_FINAL: + case RID_IS_LITERAL_TYPE: + case RID_IS_POD: + case RID_IS_POLYMORPHIC: + case RID_IS_SAME_AS: + case RID_IS_STD_LAYOUT: + case RID_IS_TRIVIAL: + case RID_IS_TRIVIALLY_ASSIGNABLE: + case RID_IS_TRIVIALLY_CONSTRUCTIBLE: + case RID_IS_TRIVIALLY_COPYABLE: + case RID_IS_UNION: + case RID_IS_ASSIGNABLE: + case RID_IS_CONSTRUCTIBLE: + case RID_UNDERLYING_TYPE: + return true; + default: + break; + } + + return false; +} + /* Register c++-specific dumps. */ void diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi index f2de39a270c..bd741ce78ee 100644 --- a/gcc/doc/cpp.texi +++ b/gcc/doc/cpp.texi @@ -3159,6 +3159,7 @@ directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}. * Elif:: * @code{__has_attribute}:: * @code{__has_cpp_attribute}:: +* @code{__has_builtin}:: * @code{__has_include}:: @end menu @@ -3478,6 +3479,33 @@ information including the dates of the introduction of current standard attributes, see @w{@uref{https://isocpp.org/std/standing-documents/sd-6-sg10-feature-test-recommendations/, SD-6: SG10 Feature Test Recommendations}}. +@node @code{__has_builtin} +@subsection @code{__has_builtin} +@cindex @code{__has_builtin} + +The special operator @code{__has_builtin (@var{operand})} may be used in +constant integer contexts and in preprocessor @samp{#if} and @samp{#elif} +expressions to test whether the symbol named by its @var{operand} is +recognized as a built-in function by GCC in the current language and +conformance mode. It evaluates to a constant integer with a nonzero +value if the argument refers to such a function, and to zero otherwise. +The operator may also be used in preprocessor @samp{#if} and @samp{#elif} +expressions. The @code{__has_builtin} operator by itself, without any +@var{operand} or parentheses, acts as a predefined macro so that support +for it can be tested in portable code. Thus, the recommended use of +the operator is as follows: + +@smallexample +#if defined __has_builtin +# if __has_builtin (__builtin_object_size) +# define builtin_object_size(ptr) __builtin_object_size (ptr, 2) +# endif +#endif +#ifndef builtin_object_size +# define builtin_object_size(ptr) ((size_t)-1) +#endif +@end smallexample + @node @code{__has_include} @subsection @code{__has_include} @cindex @code{__has_include} diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index 64fccfe9b87..96fef615c76 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -10897,7 +10897,7 @@ executed. If no fixup is needed, this function simply passes through @var{addr}. @end deftypefn -@deftypefn {Built-in Function} {void *} __builtin_frob_return_address (void *@var{addr}) +@deftypefn {Built-in Function} {void *} __builtin_frob_return_addr (void *@var{addr}) This function does the reverse of @code{__builtin_extract_return_addr}. @end deftypefn diff --git a/gcc/testsuite/c-c++-common/cpp/has-builtin-2.c b/gcc/testsuite/c-c++-common/cpp/has-builtin-2.c new file mode 100644 index 00000000000..92b944f352f --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/has-builtin-2.c @@ -0,0 +1,297 @@ +/* PR c/66970 - Add __has_builtin() macro + Verify __has_builtin evaluation for common built-ins and other identifiers. + { dg-do compile } */ + +// Verify a few library built-ins. +#if !__has_builtin (__builtin_abs) +# error "__has_builtin (__builtin_abs) failed" +#endif + +#if !__has_builtin (abs) + // abs is also a built-in unless disabled by -fno-builtin. +# error "__has_builtin (abs) failed" +#endif + +#if __cplusplus +// Declare an overload and verify that __has_builtin (isalpha) still +// evaluates to true. +int isalpha (const char*); +#endif + +#if !__has_builtin (__builtin_isalpha) +# error "__has_builtin (__builtin_isalpha) failed" +#endif + +#if !__has_builtin (isalpha) + // isalpha is still a built-in despite the overload above. +# error "__has_builtin (isalpha) failed" +#endif + + +#if !__has_builtin (__builtin__Exit) +# error "__has_builtin (__builtin__Exit) failed" +#endif + + +#if !__has_builtin (__builtin_alloca) +# error "__has_builtin (__builtin_alloca) failed" +#endif + +#if !__has_builtin (__builtin_is_constant_evaluated) + // __builtin_is_constant_evaluated is a C++-only built. +# ifdef __cplusplus +# error "__has_builtin (__builtin_is_constant_evaluated) failed" +# endif +#else +# ifndef __cplusplus +# error "__has_builtin (__builtin_is_constant_evaluated) failed" +# endif +#endif + +#if !__has_builtin (__builtin_expect) +# error "__has_builtin (__builtin_expect) failed" +#endif + +#if !__has_builtin (__builtin_trap) +# error "__has_builtin (__builtin_trap) failed" +#endif + +#if !__has_builtin (__builtin_unreachable) +# error "__has_builtin (__builtin_unreachable) failed" +#endif + +#if !__has_builtin (__builtin_LINE) +# error "__has_builtin (__builtin_LINE) failed" +#endif + +#if !__has_builtin (__builtin_object_size) +# error "__has_builtin (__builtin_object_size) failed" +#endif + +#if !__has_builtin (__builtin_inf) +# error "__has_builtin (__builtin_inf) failed" +#endif + +#if !__has_builtin (__builtin_nan) +# error "__has_builtin (__builtin_nan) failed" +#endif + +#if !__has_builtin (__builtin_bswap16) +# error "__has_builtin (__builtin_bswap16) failed" +#endif + +#if !__has_builtin (__builtin_bswap32) +# error "__has_builtin (__builtin_bswap32) failed" +#endif + + +// Verify a few integer overflow built-ins. +#if !__has_builtin (__builtin_add_overflow) +# error "__has_builtin (__builtin_add_overflow) failed" +#endif + +#if !__has_builtin (__builtin_sadd_overflow) +# error "__has_builtin (__builtin_sadd_overflow) failed" +#endif + +#if !__has_builtin (__builtin_add_overflow_p) +# error "__has_builtin (__builtin_add_overflow_p) failed" +#endif + + +// Verify a few atomic built-ins. +#if !__has_builtin (__atomic_load) +# error "__has_builtin (__atomic_load) failed" +#endif + +#if !__has_builtin (__atomic_load_n) +# error "__has_builtin (__atomic_load_n) failed" +#endif + +#if !__has_builtin (__atomic_store) +# error "__has_builtin (__atomic_store) failed" +#endif + +#if !__has_builtin (__atomic_store_n) +# error "__has_builtin (__atomic_store_n) failed" +#endif + +#if !__has_builtin (__atomic_exchange) +# error "__has_builtin (__atomic_echange) failed" +#endif + +#if !__has_builtin (__atomic_exchange_n) +# error "__has_builtin (__atomic_exchange_n) failed" +#endif + + +// Verify a few sync built-ins. +#if !__has_builtin (__sync_fetch_and_add) +# error "__has_builtin (__sync_fetch_and_add) failed" +#endif + +#if !__has_builtin (__sync_add_and_fetch) +# error "__has_builtin (__sync_add_and_fetch) failed" +#endif + +#if !__has_builtin (__sync_bool_compare_and_swap) +# error "__has_builtin (__sync_bool_compare_and_swap) failed" +#endif + +#if !__has_builtin (__sync_val_compare_and_swap) +# error "__has_builtin (__sync_val_compare_and_swap) failed" +#endif + +#if !__has_builtin (__sync_synchronize) +# error "__has_builtin (__sync_synchronize) failed" +#endif + +// Verify nonlocal goto builtins. +#if !__has_builtin (__builtin_setjmp) +# error "__has_builtin (__builtin_setjmp) failed" +#endif + +#if !__has_builtin (__builtin_longjmp) +# error "__has_builtin (__builtin_longjmp) failed" +#endif + + +// Verify a few built-ins for constructing function calls. + +#if !__has_builtin (__builtin_apply) +# error "__has_builtin (__builtin_apply) failed" +#endif + +#if !__has_builtin (__builtin_return) +# error "__has_builtin (__builtin_return) failed" +#endif + +// Verify built-ins for function return address. +#if !__has_builtin (__builtin_return_address) +# error "__has_builtin (__builtin_return_address) failed" +#endif + +#if !__has_builtin (__builtin_extract_return_addr) +# error "__has_builtin (__builtin_extract_return_addr) failed" +#endif + +#if !__has_builtin (__builtin_frob_return_addr) +# error "__has_builtin (__builtin_frob_return_addr) failed" +#endif + +#if !__has_builtin (__builtin_frame_address) +# error "__has_builtin (__builtin_frame_address) failed" +#endif + +// Verify keywords that aren't declared built-in functions. + +#if !__has_builtin (__builtin_has_attribute) +# error "__has_builtin (__builtin_has_attribute) failed" +#endif + +#if !__has_builtin (__builtin_offsetof) +# error "__has_builtin (__builtin_offsetof) failed" +#endif + +// Verify some C-only built-ins. + +#if !__has_builtin (__builtin_types_compatible_p) +# if !__cplusplus +# error "__has_builtin (__builtin_types_compatible_p) failed" +# endif +#else +# if __cplusplus +# error "__has_builtin (__builtin_types_compatible_p) failed" +# endif +#endif + +// Verify a few C++ traits built-ins. + +#if !__has_builtin (__builtin_addressof) +# if __cplusplus +# error "__has_builtin (__builtin_addressof) failed" +# endif +#else +# if !__cplusplus +# error "__has_builtin (__builtin_addressof) failed" +# endif +#endif + +#if !__has_builtin (__builtin_launder) +# if __cplusplus +# error "__has_builtin (__builtin_launder) failed" +# endif +#else +# if !__cplusplus +# error "__has_builtin (__builtin_launder) failed" +# endif +#endif + +#if !__has_builtin (__has_nothrow_assign) +# if __cplusplus +# error "__has_builtin (__has_nothrow_assign) failed" +# endif +#else +# if !__cplusplus +# error "__has_builtin (__has_nothrow_assign) failed" +# endif +#endif + +#if !__has_builtin (__has_trivial_assign) +# if __cplusplus +# error "__has_builtin (__has_trivial_assign) failed" +# endif +#else +# if !__cplusplus +# error "__has_builtin (__has_trivial_assign) failed" +# endif +#endif + +#if !__has_builtin (__has_virtual_destructor) +# if __cplusplus +# error "__has_builtin (__has_virtual_destructor) failed" +# endif +#else +# if !__cplusplus +# error "__has_builtin (__has_virtual_destructor) failed" +# endif +#endif + + +// Verify an Intel built-in that's not implemented by any other target. +#if !__has_builtin (__builtin_ia32_pause) +# if defined (__i386__) || defined (__x86_64__) +# error "__has_builtin (__builtin_ia32_pause) failed" +# endif +#else +# if !defined (__i386__) && !defined (__x86_64__) +# error "__has_builtin (__builtin_ia32_pause) failed" +# endif +#endif + + +// Verify non-functions. + +#if __has_builtin (__alignof__) +# error "__has_builtin (__alignof__) failed" +#endif + +#if __has_builtin (asm) +# error "__has_builtin (asm) failed" +#endif + +#if __has_builtin (__asm__) +# error "__has_builtin (__asm__) failed" +#endif + +#if __has_builtin (__attribute__) +# error "__has_builtin (__attribute__) failed" +#endif + +#if __has_builtin (__inline__) +# error "__has_builtin (__inline__) failed" +#endif + +#if __has_builtin (__typeof__) +# error "__has_builtin (__typeof__) failed" +#endif diff --git a/gcc/testsuite/c-c++-common/cpp/has-builtin-3.c b/gcc/testsuite/c-c++-common/cpp/has-builtin-3.c new file mode 100644 index 00000000000..40018ccffa2 --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/has-builtin-3.c @@ -0,0 +1,36 @@ +/* PR c/66970 - Add __has_builtin() macro + Verify __has_builtin evaluation for disabled library built-ins. + { dg-do compile } + { dg-options "-fno-builtin-abs" } + { dg-additional-options "-std=c90" { target c } } */ + +#if !__has_builtin (__builtin_abs) +// __builtin_xxx is always available regardless of -fno-builtin. +# error "__has_builtin (__builtin_abs) failed" +#endif + +#if __has_builtin (abs) +# error "__has_builtin (abs) failed" +#endif + +#if __has_builtin (abs) +# error "__has_builtin (abs) failed" +#endif + + +#if !__has_builtin (__builtin_vsnprintf) +// __builtin_vsnprintf is available in all language modes. +# error "__has_builtin (__builtin_vsnprintf) failed" +#endif + +#if !__has_builtin (vsnprintf) +# if __cplusplus +// vsnprintf is always available in C++. +# error "__has_builtin (vsnprintf) failed" +# endif +#else +# if !__cplusplus +// vsnprintf is a C99 function not available in C90. +# error "__has_builtin (vsnprintf) failed" +# endif +#endif diff --git a/gcc/testsuite/c-c++-common/cpp/has-builtin.c b/gcc/testsuite/c-c++-common/cpp/has-builtin.c new file mode 100644 index 00000000000..93516519ace --- /dev/null +++ b/gcc/testsuite/c-c++-common/cpp/has-builtin.c @@ -0,0 +1,49 @@ +/* PR c/66970 - Add __has_builtin() macro + Verify that errors are detected and handled gracefully. + { dg-do compile } */ + +#ifndef __has_builtin +# error "__has_builtin is not defined" +#endif + +#if __has_builtin // { dg-error "missing '\\\(' after \"__has_builtin\"" } +#endif + +#if __has_builtin ( // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin () // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin (1) // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin (1, 2) // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin (1 + 2) // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin (x, y) // { dg-error "expected '\\\)' after \"x\"" } */ +#endif + +#if __has_builtin (x + 1) // { dg-error "expected '\\\)' after \"x\"" } */ +#endif + +#if __has_builtin (p->i) // { dg-error "expected '\\\)' after \"p\"" } */ +#endif + +#if __has_builtin ((x)) // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin ((y) // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin ((((z) // { dg-error "macro \"__has_builtin\" requires an identifier" } +#endif + +#if __has_builtin (x))) // { dg-error "missing '\\\('" }" +#endif + +#if __has_builtin (f ()) // { dg-error "expected '\\\)' after \"f\"" }" +#endif diff --git a/libcpp/include/cpplib.h b/libcpp/include/cpplib.h index a645f8136a6..4da24ff9f33 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -666,6 +666,9 @@ struct cpp_callbacks /* Callback to identify whether an attribute exists. */ int (*has_attribute) (cpp_reader *); + /* Callback to determine whether a built-in function is recognized. */ + int (*has_builtin) (cpp_reader *); + /* Callback that can change a user lazy into normal macro. */ void (*user_lazy_macro) (cpp_reader *, cpp_macro *, unsigned); @@ -845,7 +848,8 @@ enum cpp_builtin_type BT_PRAGMA, /* `_Pragma' operator */ BT_TIMESTAMP, /* `__TIMESTAMP__' */ BT_COUNTER, /* `__COUNTER__' */ - BT_HAS_ATTRIBUTE /* `__has_attribute__(x)' */ + BT_HAS_ATTRIBUTE, /* `__has_attribute__(x)' */ + BT_HAS_BUILTIN /* `__has_builtin(x)' */ }; #define CPP_HASHNODE(HNODE) ((cpp_hashnode *) (HNODE)) diff --git a/libcpp/init.c b/libcpp/init.c index ccbfc96489d..9d009e3b304 100644 --- a/libcpp/init.c +++ b/libcpp/init.c @@ -398,6 +398,7 @@ static const struct builtin_macro builtin_array[] = B("__COUNTER__", BT_COUNTER, true), B("__has_attribute", BT_HAS_ATTRIBUTE, true), B("__has_cpp_attribute", BT_HAS_ATTRIBUTE, true), + B("__has_builtin", BT_HAS_BUILTIN, true), /* Keep builtins not used for -traditional-cpp at the end, and update init_builtins() if any more are added. */ B("_Pragma", BT_PRAGMA, true), @@ -478,7 +479,8 @@ cpp_init_special_builtins (cpp_reader *pfile) for (b = builtin_array; b < builtin_array + n; b++) { - if (b->value == BT_HAS_ATTRIBUTE + if ((b->value == BT_HAS_ATTRIBUTE + || b->value == BT_HAS_BUILTIN) && (CPP_OPTION (pfile, lang) == CLK_ASM || pfile->cb.has_attribute == NULL)) continue; diff --git a/libcpp/macro.c b/libcpp/macro.c index 30d3686451c..eb8321f8cee 100644 --- a/libcpp/macro.c +++ b/libcpp/macro.c @@ -568,6 +568,10 @@ _cpp_builtin_macro_text (cpp_reader *pfile, cpp_hashnode *node, case BT_HAS_ATTRIBUTE: number = pfile->cb.has_attribute (pfile); break; + + case BT_HAS_BUILTIN: + number = pfile->cb.has_builtin (pfile); + break; } if (result == NULL) diff --git a/libcpp/traditional.c b/libcpp/traditional.c index f1e72796cf2..54738e8a169 100644 --- a/libcpp/traditional.c +++ b/libcpp/traditional.c @@ -326,9 +326,9 @@ static inline bool fun_like_macro (cpp_hashnode *node) { if (cpp_builtin_macro_p (node)) - return node->value.builtin == BT_HAS_ATTRIBUTE; - else - return node->value.macro->fun_like; + return (node->value.builtin == BT_HAS_ATTRIBUTE + || node->value.builtin == BT_HAS_BUILTIN); + return node->value.macro->fun_like; } /* Set up state for finding the opening '(' of a function-like