This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PING] Re: [PATCH] c/66516 - missing diagnostic on taking the address of a builtin function


On 09/02/2015 09:29 AM, Jason Merrill wrote:
On 09/01/2015 06:25 PM, Martin Sebor wrote:
Having now made this change, I don't think the added complexity
of three declarations and two trivial definitions of the new
c_decl_implicit function across five files is an improvement,

Three declarations?  Isn't declaring it in c-common.h enough?
...

Seems like you can do without the check for C++ if you're defining this
function for C++ (to just return false).

Yes on both counts. Thanks.


I agree with Joseph that the function is better.

+         bool diag /* = true */)

Let's call these parameters "reject_builtin" rather than the generic
"diag".

+    function = decay_conversion (function, complain, false);

Please add a comment to "false", e.g. /*reject_builtin*/false

Done.

The latest patch (attached) implements all of requested changes
plus a few tweaks to the tests to make them more strict.

I also noticed and fixed a gotcha in the dg-error directives
that might be interesting to mention: in prior patches tests
were all: /* dg-error "builtin" */
Unfortunately, since both the name of the test file and the
test function have the string "builtin" in them, the directives
would pass even if the error message wasn't the expected:

  builtin function ‘__builtin_trap’ must be directly called

This problem was hiding a few invalid C++ tests. I've fixed
the directives to avoid the problem.

I've tested it on x86_64 this time with no regressions.

Martin

gcc/ChangeLog
2015-09-02  Martin Sebor  <msebor@redhat.com>

	PR c/66516
	* doc/extend.texi (Other Builtins): Document when the address
	of a builtin function can be taken.

gcc/c-family/ChangeLog
2015-09-02  Martin Sebor  <msebor@redhat.com>

	PR c/66516
	* c-common.h (c_decl_implicit, reject_gcc_builtin): Declare new
	functions.
	* c-common.c (reject_gcc_builtin): Define.

gcc/c/ChangeLog
2015-09-02  Martin Sebor  <msebor@redhat.com>

	PR c/66516
	* c/c-typeck.c (convert_arguments, parser_build_unary_op)
	(build_conditional_expr, c_cast_expr, convert_for_assignment)
	(build_binary_op, _objc_common_truthvalue_conversion): Call
	reject_gcc_builtin.
	(c_decl_implicit): Define.

gcc/cp/ChangeLog
2015-09-02  Martin Sebor  <msebor@redhat.com>

	PR c/66516
	* cp/cp-tree.h (mark_rvalue_use, decay_conversion): Add new
	argument(s).
	* cp/expr.c (mark_rvalue_use): Use new argument.
	* cp/call.c (build_addr_func): Call decay_conversion with new
	argument.
	* cp/pt.c (convert_template_argument): Call reject_gcc_builtin.
	* cp/typeck.c (decay_conversion): Use new argument.
	(c_decl_implicit): Define.

gcc/testsuite/ChangeLog
2015-09-02  Martin Sebor  <msebor@redhat.com>

	PR c/66516
	* g++.dg/addr_builtin-1.C: New test.
	* gcc.dg/addr_builtin-1.c: New test.

diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 7691035..4cc6c3e 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -12882,4 +12882,41 @@ pointer_to_zero_sized_aggr_p (tree t)
   return (TYPE_SIZE (t) && integer_zerop (TYPE_SIZE (t)));
 }

+/* For an EXPR of a FUNCTION_TYPE that references a GCC built-in function
+   with no library fallback or for an ADDR_EXPR whose operand is such type
+   issues an error pointing to the location LOC.
+   Returns true when the expression has been diagnosed and false
+   otherwise.  */
+bool
+reject_gcc_builtin (const_tree expr, location_t loc /* = UNKNOWN_LOCATION */)
+{
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    expr = TREE_OPERAND (expr, 0);
+
+  if (TREE_TYPE (expr)
+      && TREE_CODE (TREE_TYPE (expr)) == FUNCTION_TYPE
+      && DECL_P (expr)
+      /* The intersection of DECL_BUILT_IN and DECL_IS_BUILTIN avoids
+	 false positives for user-declared built-ins such as abs or
+	 strlen, and for C++ operators new and delete.
+	 The c_decl_implicit() test avoids false positives for implicitly
+	 declared builtins with library fallbacks (such as abs).  */
+      && DECL_BUILT_IN (expr)
+      && DECL_IS_BUILTIN (expr)
+      && !c_decl_implicit (expr)
+      && !DECL_ASSEMBLER_NAME_SET_P (expr))
+    {
+      if (loc == UNKNOWN_LOCATION)
+	loc = EXPR_LOC_OR_LOC (expr, input_location);
+
+      /* Reject arguments that are builtin functions with
+	 no library fallback.  */
+      error_at (loc, "builtin function %qE must be directly called", expr);
+
+      return true;
+    }
+
+  return false;
+}
+
 #include "gt-c-family-c-common.h"
diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index be63cd2..0d589b5 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -572,6 +572,7 @@ extern int field_decl_cmp (const void *, const void *);
 extern void resort_sorted_fields (void *, void *, gt_pointer_operator,
 				  void *);
 extern bool has_c_linkage (const_tree decl);
+extern bool c_decl_implicit (const_tree);
 
 /* Switches common to the C front ends.  */

@@ -1437,5 +1438,6 @@ extern bool contains_cilk_spawn_stmt (tree);
 extern tree cilk_for_number_of_iterations (tree);
 extern bool check_no_cilk (tree, const char *, const char *,
 		           location_t loc = UNKNOWN_LOCATION);
+extern bool reject_gcc_builtin (const_tree, location_t = UNKNOWN_LOCATION);

 #endif /* ! GCC_C_COMMON_H */
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index e8c8189..22b0d0d 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3339,6 +3339,10 @@ convert_arguments (location_t loc, vec<location_t> arg_loc, tree typelist,
 	  error (invalid_func_diag);
 	  return -1;
 	}
+      else if (TREE_CODE (val) == ADDR_EXPR && reject_gcc_builtin (val))
+	{
+	  return -1;
+	}
       else
 	/* Convert `short' and `char' to full-size `int'.  */
 	parmval = default_conversion (val);
@@ -3376,12 +3380,20 @@ parser_build_unary_op (location_t loc, enum tree_code code, struct c_expr arg)
 {
   struct c_expr result;

-  result.value = build_unary_op (loc, code, arg.value, 0);
   result.original_code = code;
   result.original_type = NULL;

+  if (reject_gcc_builtin (arg.value))
+    {
+      result.value = error_mark_node;
+    }
+  else
+    {
+      result.value = build_unary_op (loc, code, arg.value, 0);
+
   if (TREE_OVERFLOW_P (result.value) && !TREE_OVERFLOW_P (arg.value))
     overflow_warning (loc, result.value);
+    }

   return result;
 }
@@ -4484,6 +4496,12 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
   type2 = TREE_TYPE (op2);
   code2 = TREE_CODE (type2);

+  if (code1 == POINTER_TYPE && reject_gcc_builtin (op1))
+    return error_mark_node;
+
+  if (code2 == POINTER_TYPE && reject_gcc_builtin (op2))
+    return error_mark_node;
+
   /* C90 does not permit non-lvalue arrays in conditional expressions.
      In C99 they will be pointers by now.  */
   if (code1 == ARRAY_TYPE || code2 == ARRAY_TYPE)
@@ -5222,6 +5240,10 @@ c_cast_expr (location_t loc, struct c_type_name *type_name, tree expr)
   type = groktypename (type_name, &type_expr, &type_expr_const);
   warn_strict_prototypes = saved_wsp;

+  if (TREE_CODE (expr) == ADDR_EXPR && !VOID_TYPE_P (type)
+      && reject_gcc_builtin (expr))
+    return error_mark_node;
+
   ret = build_c_cast (loc, type, expr);
   if (type_expr)
     {
@@ -5861,6 +5883,10 @@ convert_for_assignment (location_t location, location_t expr_loc, tree type,
   rhs = require_complete_type (rhs);
   if (rhs == error_mark_node)
     return error_mark_node;
+
+  if (coder == POINTER_TYPE && reject_gcc_builtin (rhs))
+    return error_mark_node;
+
   /* A non-reference type can convert to a reference.  This handles
      va_start, va_copy and possibly port built-ins.  */
   if (codel == REFERENCE_TYPE && coder != REFERENCE_TYPE)
@@ -10350,6 +10376,14 @@ build_binary_op (location_t location, enum tree_code code,
   if (code0 == ERROR_MARK || code1 == ERROR_MARK)
     return error_mark_node;

+  if (code0 == POINTER_TYPE
+      && reject_gcc_builtin (op0, EXPR_LOCATION (orig_op0)))
+    return error_mark_node;
+
+  if (code1 == POINTER_TYPE
+      && reject_gcc_builtin (op1, EXPR_LOCATION (orig_op1)))
+    return error_mark_node;
+
   if ((invalid_op_diag
        = targetm.invalid_binary_op (code, type0, type1)))
     {
@@ -11330,6 +11364,11 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr)
       error_at (location, "void value not ignored as it ought to be");
       return error_mark_node;

+    case POINTER_TYPE:
+      if (reject_gcc_builtin (expr))
+	return error_mark_node;
+      break;
+
     case FUNCTION_TYPE:
       gcc_unreachable ();

@@ -12882,3 +12921,13 @@ cilk_install_body_with_frame_cleanup (tree fndecl, tree body, void *w)
   append_to_statement_list (build_stmt (EXPR_LOCATION (body), TRY_FINALLY_EXPR,
 				       	body_list, dtor), &list);
 }
+
+/* Returns true when the function declaration FNDECL is implicit,
+   introduced as a result of a call to an otherwise undeclared
+   function, and false otherwise.  */
+
+bool
+c_decl_implicit (const_tree fndecl)
+{
+  return C_DECL_IMPLICIT (fndecl);
+}
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 8d4a9e2..c4e69a4 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -288,7 +288,7 @@ build_addr_func (tree function, tsubst_flags_t complain)
       function = build_address (function);
     }
   else
-    function = decay_conversion (function, complain);
+    function = decay_conversion (function, complain, /*reject_builtin=*/false);

   return function;
 }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4dee60c..784a616 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5787,7 +5787,9 @@ extern tree create_try_catch_expr               (tree, tree);

 /* in expr.c */
 extern tree cplus_expand_constant		(tree);
-extern tree mark_rvalue_use			(tree);
+extern tree mark_rvalue_use			(tree,
+                                                 location_t = UNKNOWN_LOCATION,
+                                                 bool = true);
 extern tree mark_lvalue_use			(tree);
 extern tree mark_type_use			(tree);
 extern void mark_exp_read			(tree);
@@ -6461,7 +6463,9 @@ extern tree cxx_alignas_expr                    (tree);
 extern tree cxx_sizeof_nowarn                   (tree);
 extern tree is_bitfield_expr_with_lowered_type  (const_tree);
 extern tree unlowered_expr_type                 (const_tree);
-extern tree decay_conversion			(tree, tsubst_flags_t);
+extern tree decay_conversion			(tree,
+                                                 tsubst_flags_t,
+                                                 bool = true);
 extern tree build_class_member_access_expr      (tree, tree, tree, bool,
 						 tsubst_flags_t);
 extern tree finish_class_member_access_expr     (tree, tree, bool,
diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c
index 6d2d658..2addffe 100644
--- a/gcc/cp/expr.c
+++ b/gcc/cp/expr.c
@@ -91,18 +91,24 @@ cplus_expand_constant (tree cst)
   return cst;
 }

-/* Called whenever an expression is used
-   in a rvalue context.  */
-
+/* Called whenever the expression EXPR is used in an rvalue context.
+   When REJECT_BUILTIN is true the expression is checked to make sure
+   it doesn't make it possible to obtain the address of a GCC built-in
+   function with no library fallback (or any of its bits, such as in
+   a conversion to bool).  */
 tree
-mark_rvalue_use (tree expr)
+mark_rvalue_use (tree expr,
+		 location_t loc /* = UNKNOWN_LOCATION */,
+		 bool reject_builtin /* = true */)
 {
+  if (reject_builtin && reject_gcc_builtin (expr, loc))
+    return error_mark_node;
+
   mark_exp_read (expr);
   return expr;
 }

-/* Called whenever an expression is used
-   in a lvalue context.  */
+/* Called whenever an expression is used in an lvalue context.  */

 tree
 mark_lvalue_use (tree expr)
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index fb7b9d2..ec32c5a 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -7199,6 +7199,18 @@ convert_template_argument (tree parm,
       else if (val == error_mark_node && (complain & tf_error))
 	error ("could not convert template argument %qE to %qT",  orig_arg, t);

+      if (INDIRECT_REF_P (val))
+        {
+          /* Reject template arguments that are references to built-in
+             functions with no library fallbacks.  */
+          const_tree inner = TREE_OPERAND (val, 0);
+          if (TREE_CODE (TREE_TYPE (inner)) == REFERENCE_TYPE
+              && TREE_CODE (TREE_TYPE (TREE_TYPE (inner))) == FUNCTION_TYPE
+              && TREE_CODE (TREE_TYPE (inner)) == REFERENCE_TYPE
+              && reject_gcc_builtin (TREE_OPERAND (inner, 0)))
+              return error_mark_node;
+        }
+
       if (TREE_CODE (val) == SCOPE_REF)
 	{
 	  /* Strip typedefs from the SCOPE_REF.  */
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 83fd34c..fac9c30 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -1911,7 +1911,9 @@ unlowered_expr_type (const_tree exp)
    that the return value is no longer an lvalue.  */

 tree
-decay_conversion (tree exp, tsubst_flags_t complain)
+decay_conversion (tree exp,
+		  tsubst_flags_t complain,
+		  bool reject_builtin /* = true */)
 {
   tree type;
   enum tree_code code;
@@ -1921,7 +1923,7 @@ decay_conversion (tree exp, tsubst_flags_t complain)
   if (type == error_mark_node)
     return error_mark_node;

-  exp = mark_rvalue_use (exp);
+  exp = mark_rvalue_use (exp, loc, reject_builtin);

   exp = resolve_nondeduced_context (exp);
   if (type_unknown_p (exp))
@@ -9397,3 +9399,12 @@ check_literal_operator_args (const_tree decl,
       return true;
     }
 }
+
+/* Always returns false since unlike C90, C++ has no concept of implicit
+   function declarations.  */
+
+bool
+c_decl_implicit (const_tree)
+{
+  return false;
+}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index dba8b43..23e6a76 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10316,14 +10316,22 @@ recommend general use of these functions.

 The remaining functions are provided for optimization purposes.

+With the exception of built-ins that have library equivalents such as
+the standard C library functions discussed below, or that expand to
+library calls, GCC built-in functions are always expanded inline and
+thus do not have corresponding entry points and their address cannot
+be obtained.  Attempting to use them in an expression other than
+a function call results in a compile-time error.
+
 @opindex fno-builtin
 GCC includes built-in versions of many of the functions in the standard
-C library.  The versions prefixed with @code{__builtin_} are always
-treated as having the same meaning as the C library function even if you
-specify the @option{-fno-builtin} option.  (@pxref{C Dialect Options})
-Many of these functions are only optimized in certain cases; if they are
-not optimized in a particular case, a call to the library function is
-emitted.
+C library.  These functions come in two forms: one whose names start with
+the @code{__builtin_} prefix, and the other without.  Both forms have the
+same type (including prototype), the same address (when their address is
+taken), and the same meaning as the C library functions even if you specify
+the @option{-fno-builtin} option @pxref{C Dialect Options}).  Many of these
+functions are only optimized in certain cases; if they are not optimized in
+a particular case, a call to the library function is emitted.

 @opindex ansi
 @opindex std
diff --git a/gcc/testsuite/g++.dg/addr_builtin-1.C b/gcc/testsuite/g++.dg/addr_builtin-1.C
new file mode 100644
index 0000000..09ffc4e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/addr_builtin-1.C
@@ -0,0 +1,276 @@
+// PR66516 - missing diagnostic on taking the address of a builtin function
+// { dg-do compile }
+
+namespace std {
+  // Define type_info type to be able to use typeid in tests without
+  // having to include <typeinfo>.
+  struct type_info {
+    const char *name_;
+
+    explicit type_info (const char *s): name_ (s) { }
+    const char* name() const { return name_; }
+  };
+}
+
+// Extern "C" since builtin functions used in tests have C linkage.
+extern "C" {
+
+typedef void (F)();
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+// Utility function to test passing built-in functions as an ordinary
+// argument and via the ellipsis.
+static void func_arg (F*, ...);
+
+// Utility function with which, along with the built-in function,
+// to instantiate the C98 multi-parameter or C11 variadic tempates
+// below.
+void f () { }
+
+}   // extern "C"
+
+
+// Utility templates to test specializing templates on pointers and
+// references to built-in functions.
+template <F*> struct TestPointer { };
+template <F&> struct TestReference { };
+
+#if 201103 <= __cplusplus
+
+template <F*...> struct TestPointers { };
+template <F&...> struct TestReferences { };
+
+#else
+
+template <F* = &f, F* = &f> struct TestPointers { };
+template <F& = f, F& = f> struct TestReferences { };
+
+#endif
+
+static F* test_taking_address_of_gcc_builtin ()
+{
+  enum UINTPTR_E { e = ~(uintptr_t)0 };
+
+  F *p;
+  void *q;
+  uintptr_t a;
+
+  __builtin_trap ();                           // okay
+  (void)__builtin_trap;                        // okay
+  __builtin_trap;                              // okay (if pointless)
+
+  {
+    typedef __typeof__ (__builtin_trap) F;     // okay
+  }
+
+#if 201103 <= __cplusplus
+  {
+    typedef decltype (__builtin_trap) F;       // okay
+
+    a = noexcept (&__builtin_trap);
+  }
+#endif
+
+  // Address and indirection operators.
+  p = &__builtin_trap;                       // { dg-error "builtin function" }
+  p = *__builtin_trap;                       // { dg-error "builtin function" }
+
+  // Unary NOT.
+  // GCC issues two diagnostics here for some reason, so account for both.
+  a = !__builtin_trap;                   // { dg-error "builtin function|unary" }
+
+  // Casts.
+  p = (F*)__builtin_trap;                    // { dg-error "builtin function" }
+
+  p = &(F&)__builtin_trap;                   // { dg-error "builtin function" }
+
+  p = &reinterpret_cast<F&>(__builtin_trap); // { dg-error "builtin function" }
+  p = &static_cast<F&>(__builtin_trap);      // { dg-error "builtin function" }
+
+  p = reinterpret_cast<F*>(__builtin_trap);  // { dg-error "builtin function" }
+  p = static_cast<F*>(__builtin_trap);       // { dg-error "builtin function" }
+
+  // Expect a diagnostic for an invalid static_cast of a function to
+  // either uintptr_t or enum, rather than one for the argument being
+  // a built-in function, since the former is more relevant than the latter.
+  a = static_cast<uintptr_t>(__builtin_trap);       // { dg-error "invalid" }
+  a = static_cast<UINTPTR_E>(__builtin_trap);       // { dg-error "invalid" }
+
+  // Reinterpret cast can cast a function to uintptr_t or enum,
+  // so verify that a diagnostic is issued for the use of a builtin.
+  a = reinterpret_cast<uintptr_t>(__builtin_trap);  // { dg-error "builtin function" }
+  a = reinterpret_cast<UINTPTR_E>(__builtin_trap);  // { dg-error "builtin function" }
+
+  // Additive operator.  Ill-formed but allowed with -fpermissive.
+  p = __builtin_trap + 0;            // { dg-error "builtin function" }
+  p = __builtin_trap - 0;            // { dg-error "builtin function" }
+  a = __builtin_trap - p;            // { dg-error "builtin function" }
+  a = p - __builtin_trap;            // { dg-error "builtin function" }
+
+  // Relational operators.  Ill-formed but allowed with -fpermissive.
+  a = __builtin_trap < p;            // { dg-error "builtin function" }
+  a = p < __builtin_trap;            // { dg-error "builtin function" }
+
+  a = __builtin_trap <= p;           // { dg-error "builtin function" }
+  a = p <= __builtin_trap;           // { dg-error "builtin function" }
+
+  a = __builtin_trap > p;            // { dg-error "builtin function" }
+  a = p > __builtin_trap;            // { dg-error "builtin function" }
+
+  a = __builtin_trap > p;            // { dg-error "builtin function" }
+  a = p > __builtin_trap;            // { dg-error "builtin function" }
+
+  a = __builtin_trap <= p;           // { dg-error "builtin function" }
+  a = p <= __builtin_trap;           // { dg-error "builtin function" }
+
+  a = __builtin_trap <= p;           // { dg-error "builtin function" }
+  a = p <= __builtin_trap;           // { dg-error "builtin function" }
+
+  // Equality operators.
+  a = __builtin_trap == p;           // { dg-error "builtin function" }
+  a = p == __builtin_trap;           // { dg-error "builtin function" }
+  a = __builtin_trap != p;           // { dg-error "builtin function" }
+  a = p != __builtin_trap;           // { dg-error "builtin function" }
+
+  // Logical AND and OR.
+  a = __builtin_trap && p;           // { dg-error "builtin function" }
+  a = p && __builtin_trap;           // { dg-error "builtin function" }
+
+  a = __builtin_trap || p;           // { dg-error "builtin function" }
+  a = p || __builtin_trap;           // { dg-error "builtin function" }
+
+  // Conditional operator.
+  a = __builtin_trap ? 1 : 0;        // { dg-error "builtin function" }
+  p = a ? __builtin_trap : 0;        // { dg-error "builtin function" }
+  p = a ? 0 : __builtin_trap;        // { dg-error "builtin function" }
+
+  // Assignment operator.
+  p = __builtin_trap;                // { dg-error "builtin function" }
+
+  // Passing as an argument.
+  func_arg (__builtin_trap);         // { dg-error "builtin function" }
+  func_arg (&__builtin_trap);        // { dg-error "builtin function" }
+  func_arg (*__builtin_trap);        // { dg-error "builtin function" }
+
+  // Passing through ellipsis.
+  func_arg (0, __builtin_trap);      // { dg-error "builtin function" }
+  func_arg (0, &__builtin_trap);     // { dg-error "builtin function" }
+  func_arg (0, *__builtin_trap);     // { dg-error "builtin function" }
+
+  {
+    // Template specialization.
+    // GCC issues two diagnostics and we must account for both.
+    TestPointer<__builtin_trap> tp;         // { dg-error "builtin function|could not convert" }
+    TestReference<__builtin_trap> tr;       // { dg-error "builtin function|could not convert" }
+
+    TestPointers<__builtin_trap> tp1;       // { dg-error "builtin function|could not convert" }
+    TestReferences<__builtin_trap> tr1;     // { dg-error "builtin function|could not convert" }
+
+    TestPointers<f, __builtin_trap> tp2;    // { dg-error "builtin function|could not convert" }
+    TestReferences<f, __builtin_trap> tr2;  // { dg-error "builtin function|could not convert" }
+
+    TestPointers<__builtin_trap, f> tp3;    // { dg-error "builtin function|could not convert" }
+    TestReferences<__builtin_trap, f> tr3;  // { dg-error "builtin function|could not convert" }
+  }
+
+  try {
+    throw __builtin_trap;                 // { dg-error "builtin function" }
+  }
+  catch (F) { }
+
+  return __builtin_trap;                    // { dg-error "builtin function" }
+
+  (void)a;
+  (void)p;
+  (void)q;
+}
+
+// Make sure operators new and delete don't trigger false positives
+// (they return true from DECL_IS_BUILTIN(DECL) -- see tree.h).
+void test_taking_address_of_op_new_and_delete ()
+{
+  typedef __SIZE_TYPE__ size_t;
+
+  typedef void* (OpNew) (size_t);
+  typedef void (OpDelete) (void*);
+
+  OpNew &newr = operator new;
+  OpNew &newra = operator new[];
+  OpNew *newp = &operator new;
+  newp = &operator new[];
+
+  OpDelete &delr = operator delete;
+  OpDelete &delra = operator delete[];
+  OpDelete *delp = &operator delete;
+  delp = &operator delete[];
+
+  (void)newr;
+  (void)newra;
+  (void)newp;
+  (void)delr;
+  (void)delra;
+  (void)delp;
+}
+
+// Helper declaration to verify that it's possible to take the address
+// of a user-declared function that's also a GCC built-in.
+extern int abs (int);
+
+typedef __SIZE_TYPE__ size_t;
+extern size_t strlen (const char*);
+
+// Creating a reference to or taking the address of a built-in with
+// a library "fallback" must be allowed.
+void test_taking_address_of_library_builtin ()
+{
+  {
+    typedef int F (int);
+
+    F &r1 = __builtin_abs;
+    F &r2 = *__builtin_abs;
+    F *p = __builtin_abs;
+    p = &__builtin_abs;
+    p = *__builtin_abs;
+    (void)p;
+    (void)r1;
+    (void)r2;
+  }
+
+  {
+    typedef int F (int);
+
+    F &r1 = abs;
+    F &r2 = *abs;
+    F *p = abs;
+    p = &abs;
+    p = *abs;
+    (void)p;
+    (void)r1;
+    (void)r2;
+  }
+
+  {
+    typedef __SIZE_TYPE__ size_t;
+    typedef size_t F (const char*);
+    F &r1 = __builtin_strlen;
+    F &r2 = *__builtin_strlen;
+    F *p = __builtin_strlen;
+    p = &__builtin_strlen;
+    p = *__builtin_strlen;
+    (void)p;
+    (void)r1;
+    (void)r2;
+  }
+
+  {
+    typedef size_t F (const char*);
+    F &r1 = strlen;
+    F &r2 = *strlen;
+    F *p = strlen;
+    p = &strlen;
+    p = *strlen;
+    (void)p;
+    (void)r1;
+    (void)r2;
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/addr_builtin-1.c b/gcc/testsuite/gcc.dg/addr_builtin-1.c
new file mode 100644
index 0000000..f3ce5f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/addr_builtin-1.c
@@ -0,0 +1,198 @@
+/* PR66516 - missing diagnostic on taking the address of a builtin function
+   { dg-do compile }  */
+
+typedef void (F)(void);
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+/* Utility function to test passing built-in functions as an ordinary
+   argument and via the ellipsis.  */
+static void func_arg (F *p, ...) { (void)p; }
+
+static F* test_taking_address_of_gcc_builtin (void)
+{
+  F *p;
+  void *q;
+  uintptr_t a;
+
+  /* Call, cast to void, and id are allowed.  */
+  __builtin_trap ();
+  (void)__builtin_trap;
+  __builtin_trap;
+
+  {
+    typedef __typeof__ (__builtin_trap) F;     /* Okay.  */
+  }
+
+  /* Address and indirection operators.  */
+  p = &__builtin_trap;               /* { dg-error "builtin function" }  */
+  p = *__builtin_trap;               /* { dg-error "builtin function" }  */
+
+  /* Unary NOT.  */
+  a = !__builtin_trap;               /* { dg-error "builtin function" }  */
+
+  /* Sizeof and _Alignof are disallowed by C but allowed by GCC
+     and there's no reason to reject built-ins as operands since
+     doing so doesn't yield their address.  */
+#pragma GCC diagnostic push
+  /* Disable: invalid application of 'sizeof' to a function type.  */
+#pragma GCC diagnostic ignored "-Wpointer-arith"
+  a = sizeof __builtin_trap;
+#pragma GCC diagnostic pop
+
+#ifndef __STDC_VERSION__
+#  pragma GCC diagnostic push
+  /* Disable: ISO C90 does not support '_Alignof'.  */
+#  pragma GCC diagnostic ignored "-Wpedantic"
+#endif
+
+  a = _Alignof __builtin_trap;
+
+#ifndef __STDC_VERSION__
+#  pragma GCC diagnostic pop
+#endif
+
+  /* Casts.  */
+  p = (F*)__builtin_trap;            /* { dg-error "builtin function" }  */
+  a = (uintptr_t)__builtin_trap;     /* { dg-error "builtin function" }  */
+
+  /* Additive operator.  */
+  p = __builtin_trap + 0;            /* { dg-error "builtin function" }  */
+  p = __builtin_trap - 0;            /* { dg-error "builtin function" }  */
+  a = __builtin_trap - p;            /* { dg-error "builtin function" }  */
+  a = p - __builtin_trap;            /* { dg-error "builtin function" }  */
+
+  /* Relational operators.  */
+  a = __builtin_trap < p;            /* { dg-error "builtin function" }  */
+  a = p < __builtin_trap;            /* { dg-error "builtin function" }  */
+
+  a = __builtin_trap <= p;           /* { dg-error "builtin function" }  */
+  a = p <= __builtin_trap;           /* { dg-error "builtin function" }  */
+
+  a = __builtin_trap > p;            /* { dg-error "builtin function" }  */
+  a = p > __builtin_trap;            /* { dg-error "builtin function" }  */
+
+  a = __builtin_trap > p;            /* { dg-error "builtin function" }  */
+  a = p > __builtin_trap;            /* { dg-error "builtin function" }  */
+
+  a = __builtin_trap <= p;           /* { dg-error "builtin function" }  */
+  a = p <= __builtin_trap;           /* { dg-error "builtin function" }  */
+
+  a = __builtin_trap <= p;           /* { dg-error "builtin function" }  */
+  a = p <= __builtin_trap;           /* { dg-error "builtin function" }  */
+
+  /* Equality operators.  */
+  a = __builtin_trap == p;           /* { dg-error "builtin function" }  */
+  a = p == __builtin_trap;           /* { dg-error "builtin function" }  */
+  a = __builtin_trap != p;           /* { dg-error "builtin function" }  */
+  a = p != __builtin_trap;           /* { dg-error "builtin function" }  */
+
+  /* Logical AND and OR.  */
+  a = __builtin_trap && p;           /* { dg-error "builtin function" }  */
+  a = p && __builtin_trap;           /* { dg-error "builtin function" }  */
+
+  a = __builtin_trap || p;           /* { dg-error "builtin function" }  */
+  a = p || __builtin_trap;           /* { dg-error "builtin function" }  */
+
+  /* Conditional operator.  */
+  a = __builtin_trap ? 1 : 0;        /* { dg-error "builtin function" }  */
+  p = a ? __builtin_trap : 0;        /* { dg-error "builtin function" }  */
+  p = a ? 0 : __builtin_trap;        /* { dg-error "builtin function" }  */
+
+  /* Assignment operator.  */
+  p = __builtin_trap;                /* { dg-error "builtin function" }  */
+
+  q = __builtin_trap;                /* { dg-error "builtin function" }  */
+  a = __builtin_trap;                /* { dg-error "builtin function" }  */
+
+  /* Passing as an argument.  */
+  func_arg (__builtin_trap);         /* { dg-error "builtin function" }  */
+
+  /* Passing through the ellipsis.  */
+  func_arg (0, __builtin_trap);      /* { dg-error "builtin function" }  */
+
+  /* Return statement.  */
+  return __builtin_trap;             /* { dg-error "builtin function" }  */
+
+  (void)a;
+  (void)p;
+  (void)q;
+}
+
+/* Helper declarations to verify that it's possible to take the address
+   of a user-declared function that's also a GCC built-in.  */
+extern int abs (int);
+
+extern __SIZE_TYPE__ strlen (const char*);
+
+/* Taking the address of a builtin with a library "fallback" must be
+   allowed, either using the __builtin_xxx form or the xxx form, when
+   the library fallback is declared either explicitly or implicitly
+   by virtue of first calling the function.  */
+void test_taking_address_of_library_builtin (int i)
+{
+  {
+    typedef int F (int);
+
+    /* Compute the address of libc's abs using the implicitly declared
+       __builtin_abs form (all expressions are valid).  */
+    F *p = __builtin_abs;
+    p = &__builtin_abs;
+    p = *__builtin_abs;
+
+    /* Compute the address of libc's abs declared above.  */
+    p = abs;
+    p = &abs;
+    p = *abs;
+    (void)p;
+  }
+
+  {
+    typedef __SIZE_TYPE__ size_t;
+    typedef size_t F (const char*);
+
+    /* Compute the address of libc's strlen using the implicitly
+       declared __builtin_strlen form.  */
+    F *p = __builtin_strlen;
+    p = &__builtin_strlen;
+    p = *__builtin_strlen;
+
+    /* Compute the address of libc's strlen declared above.  */
+    p = strlen;
+    p = &strlen;
+    p = *strlen;
+    (void)p;
+  }
+
+  {
+    typedef int F (int);
+
+    /* Compute the address of libc's isxxx functions using the implicitly
+       declared __builtin_xxx form.  */
+    F *p = __builtin_isalnum;
+    p = &__builtin_isalpha;
+    p = *__builtin_iscntrl;
+
+    /* According to C90 (see also the discussion in c/67386):
+       If the expression that precedes the parenthesized argument list
+       in a function call consists solely of an identifier, and if no
+       declaration is visible for this identifier, the identifier is
+       implicitly declared exactly as if, in the innermost block
+       containing the function call, the declaration
+       extern int identifier();
+       appeared.  */
+
+    /* Call the functions first to have their declarations "injected"
+       into the enclosing block.  Suppress warnings.  */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wimplicit-function-declaration"
+    i = isalnum (i) || isalpha (i) || iscntrl (i);
+#pragma GCC diagnostic pop
+
+    /* Take the address of the functions relying on their declarations
+       having been implicitly provided by the calls above.  */
+    p = isalnum;
+    p = &isalpha;
+    p = *iscntrl;
+    (void)p;
+  }
+}

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]