PR c/66970 - Add __has_builtin() macro
authorMartin Sebor <msebor@redhat.com>
Mon, 28 Oct 2019 22:46:28 +0000 (22:46 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Mon, 28 Oct 2019 22:46:28 +0000 (16:46 -0600)
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

20 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c-family/c-lex.c
gcc/c-family/c-ppoutput.c
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/cp/ChangeLog
gcc/cp/cp-objcp-common.c
gcc/doc/cpp.texi
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/cpp/has-builtin-2.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/cpp/has-builtin-3.c [new file with mode: 0644]
gcc/testsuite/c-c++-common/cpp/has-builtin.c [new file with mode: 0644]
libcpp/include/cpplib.h
libcpp/init.c
libcpp/macro.c
libcpp/traditional.c

index 691128e806c80a187340df66c6a6ec2be490612f..9f3589022b7f97d342ba195c650794437924044b 100644 (file)
@@ -1,3 +1,9 @@
+2019-10-28  Martin Sebor  <msebor@redhat.com>
+
+       PR c/66970
+       * doc/cpp.texi (__has_builtin): Document.
+       * doc/extend.texi (__builtin_frob_return_addr): Correct spelling.
+
 2019-10-28  Mihailo Stojanovic  <mistojanovic@wavecomp.com>
 
        PR target/82981
index 3b3d5744bac70cfc1e3c74183468c3698b6aa105..cfefb8ebf6c678b1392a37bbaeca566c51b04d86 100644 (file)
@@ -1,3 +1,13 @@
+2019-10-28  Martin Sebor  <msebor@redhat.com>
+
+       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  <jakub@redhat.com>
 
        * c-common.h (c_omp_context_selector_matches): Remove.
index 483d874bc3a2148830bbd835c03b2b7608885b21..79c047c4730d6710e7ea638fa0ef913e18881cde 100644 (file)
@@ -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");
 
index 70771834502f9e72a8abb5b8ce57b7d5d43220a8..42426ef6e1d58e382e58d401dbbab4fd24910b00 100644 (file)
@@ -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);
 
index fb05b5f8af05f9720c760fb1134547daab8071b5..42010a762a62e8d8d06578a0f32aeeed27db135e 100644 (file)
@@ -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);
+}
+
 \f
 /* 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
index 5f391988939e4099acf2986829e96218bd20aa90..91fabc7c1b89e79c7db758b93dc6158dae153503 100644 (file)
@@ -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;
 
index 4b0511888b2d2056d5fd78219063e79ec104a62e..087090f1c85e649952d758ee4a20c87d25620dc0 100644 (file)
@@ -1,3 +1,8 @@
+2019-10-28  Martin Sebor  <msebor@redhat.com>
+
+       PR c/66970
+       * c-decl.c (names_builtin_p): Define a new function.
+
 2019-10-28  Richard Biener  <rguenther@suse.de>
 
        PR c/92249
index c6c4a4d7aebc5c57f30d6a8d747ffe4c21b7ea3d..e76ed26840f7c4883b380c2a4ca03b4c64030051 100644 (file)
@@ -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
index 44c07f148cadc34a35c33568f894ecb7da420371..143ddbbfa8fda8eda69f0c91ce173321a31c5b86 100644 (file)
@@ -1,3 +1,8 @@
+2019-10-28  Martin Sebor  <msebor@redhat.com>
+
+       PR c/66970
+       * cp-objcp-common.c (names_builtin_p): Define new function.
+
 2019-10-28  Nathan Sidwell  <nathan@acm.org>
 
        * parser.h (struct cp_token): Drop {ENUM,BOOL}_BITFIELD C-ism.
index f38b4e8cfcbce32b3d1d244e00731042a30de912..60dcbe441056e5fb3860a1018d30b74a5abe8951 100644 (file)
@@ -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
index f2de39a270c4a033e4aa0e8efbf79d80fdbf18c9..bd741ce78ee1409087d8f321742705dc1d262c74 100644 (file)
@@ -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}
index 9df4cc2a7223b1f80f4a0e7659346807f5f5704f..9db4f9b1d2991000223e00f155e28817d76efde4 100644 (file)
@@ -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
 
index 76f147125793e9fca8ad8e1e4e4c7f2d498ad956..1efdc30e5353be778c05763464e60590ac2281cc 100644 (file)
@@ -1,3 +1,10 @@
+2019-10-28  Martin Sebor  <msebor@redhat.com>
+
+       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  <mistojanovic@wavecomp.com>
 
        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 (file)
index 0000000..92b944f
--- /dev/null
@@ -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 (file)
index 0000000..40018cc
--- /dev/null
@@ -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 (file)
index 0000000..9351651
--- /dev/null
@@ -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
index 224369b93ad9bdc60b5941e7c78dd3dde7ec2ceb..c655d3ffc903f4270edc6a613a00b3f5af7e2b47 100644 (file)
@@ -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))
index 4bcec7be3e555f2ba8969abca0142a660cd0c15c..b0943174f02111604c97ac0a53dff4b2e9f47820 100644 (file)
@@ -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;
index 30d3686451cd3a0bc01d4212fd4bea26c7e5f5e8..eb8321f8ceeece687b0c1aaa96491db85fac1e2d 100644 (file)
@@ -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)
index f1e72796cf221a0bea02a18f94e2af7b31026304..54738e8a169e243e40a7b99cd946525aa7d5dcdc 100644 (file)
@@ -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