From ad1539d5555a161cf6851de8995641d6dfe792d9 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Mon, 28 Oct 2019 22:46:28 +0000 Subject: [PATCH] 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. From-SVN: r277544 --- gcc/ChangeLog | 6 + gcc/c-family/ChangeLog | 10 + gcc/c-family/c-common.c | 3 +- gcc/c-family/c-common.h | 2 + gcc/c-family/c-lex.c | 53 ++++ gcc/c-family/c-ppoutput.c | 1 + gcc/c/ChangeLog | 5 + gcc/c/c-decl.c | 28 ++ gcc/cp/ChangeLog | 5 + gcc/cp/cp-objcp-common.c | 72 +++++ gcc/doc/cpp.texi | 28 ++ gcc/doc/extend.texi | 2 +- gcc/testsuite/ChangeLog | 7 + .../c-c++-common/cpp/has-builtin-2.c | 297 ++++++++++++++++++ .../c-c++-common/cpp/has-builtin-3.c | 36 +++ gcc/testsuite/c-c++-common/cpp/has-builtin.c | 49 +++ libcpp/include/cpplib.h | 6 +- libcpp/init.c | 4 +- libcpp/macro.c | 4 + libcpp/traditional.c | 6 +- 20 files changed, 616 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/c-c++-common/cpp/has-builtin-2.c create mode 100644 gcc/testsuite/c-c++-common/cpp/has-builtin-3.c create mode 100644 gcc/testsuite/c-c++-common/cpp/has-builtin.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 691128e806c..9f3589022b7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,9 @@ +2019-10-28 Martin Sebor + + PR c/66970 + * doc/cpp.texi (__has_builtin): Document. + * doc/extend.texi (__builtin_frob_return_addr): Correct spelling. + 2019-10-28 Mihailo Stojanovic PR target/82981 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 3b3d5744bac..cfefb8ebf6c 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,13 @@ +2019-10-28 Martin Sebor + + 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. + 2019-10-24 Jakub Jelinek * c-common.h (c_omp_context_selector_matches): Remove. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 483d874bc3a..79c047c4730 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -4468,8 +4468,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 70771834502..42426ef6e1d 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 fb05b5f8af0..42010a762a6 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; @@ -386,6 +387,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/ChangeLog b/gcc/c/ChangeLog index 4b0511888b2..087090f1c85 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,8 @@ +2019-10-28 Martin Sebor + + PR c/66970 + * c-decl.c (names_builtin_p): Define a new function. + 2019-10-28 Richard Biener PR c/92249 diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index c6c4a4d7aeb..e76ed26840f 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -9990,6 +9990,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/ChangeLog b/gcc/cp/ChangeLog index 44c07f148ca..143ddbbfa8f 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,8 @@ +2019-10-28 Martin Sebor + + PR c/66970 + * cp-objcp-common.c (names_builtin_p): Define new function. + 2019-10-28 Nathan Sidwell * parser.h (struct cp_token): Drop {ENUM,BOOL}_BITFIELD C-ism. diff --git a/gcc/cp/cp-objcp-common.c b/gcc/cp/cp-objcp-common.c index f38b4e8cfcb..60dcbe44105 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++. */ @@ -348,6 +349,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 9df4cc2a722..9db4f9b1d29 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -10912,7 +10912,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/ChangeLog b/gcc/testsuite/ChangeLog index 76f14712579..1efdc30e535 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,10 @@ +2019-10-28 Martin Sebor + + 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. + 2019-10-28 Mihailo Stojanovic PR target/82981 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 224369b93ad..c655d3ffc90 100644 --- a/libcpp/include/cpplib.h +++ b/libcpp/include/cpplib.h @@ -676,6 +676,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); @@ -855,7 +858,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 4bcec7be3e5..b0943174f02 100644 --- a/libcpp/init.c +++ b/libcpp/init.c @@ -403,6 +403,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), @@ -483,7 +484,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