public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Martin Sebor <msebor@gmail.com>
To: Jakub Jelinek <jakub@redhat.com>, Marek Polacek <polacek@redhat.com>
Cc: Gcc Patch List <gcc-patches@gcc.gnu.org>,
	 "Joseph S. Myers" <joseph@codesourcery.com>
Subject: Re: [PATCH] c/66516 - missing diagnostic on taking the address of a builtin function
Date: Mon, 29 Jun 2015 07:37:00 -0000	[thread overview]
Message-ID: <5590965B.6030103@gmail.com> (raw)
In-Reply-To: <55897735.1040509@redhat.com>

[-- Attachment #1: Type: text/plain, Size: 359 bytes --]

Attached is a rewrite of the patch to enforce that GCC builtin
functions with no library equivalents are only used to make
calls or cast to void (or in sizeof and _Alignof expressions
as a GCC extension). This version of the patch also addresses
the requests made in responses to the first patch.

Bootstrapped and tested on x86_64-unknown-linux-gnu.

Martin

[-- Attachment #2: gcc-66516.patch --]
[-- Type: text/x-patch, Size: 30553 bytes --]

2015-06-28  Martin Sebor  <msebor@redhat.com>

	pr c/66516
	* tree.h (DECL_IS_GCC_BUILTIN): New macro.
	* doc/extend.texi (Other Builtins): Document when the address of
	a builtin function can be taken.

2015-06-28  Martin Sebor  <msebor@redhat.com>

	pr c/66516
	* c-tree.h (c_validate_addressable): New function.
	* c-typeck.c (convert_arguments, parser_build_unary_op): Call it.
	(build_conditional_expr, c_cast_expr, convert_for_assignment): Same.
	(build_binary_op, c_objc_common_truthvalue_conversion): Same.
	(c_validate_addressable): Define function.

2015-06-28  Martin Sebor  <msebor@redhat.com>

	pr c/66516
	* call.c (build_conditional_expr_1): Call c_validate_addressable.
	(convert_arg_to_ellipsis, convert_for_arg_passing): Same.
	* cp-tree.h (cp_validate_addressable): New function.
	* pt.c (convert_template_argument): Call it.
	* typeck.c (cp_build_binary_op, cp_build_addr_expr_strict): Same.
	(cp_build_unary_op, build_static_cast_1, build_reinterpret_cast_1):
	Same.
	(cp_build_c_cast, convert_for_assignment, convert_for_initialization):
	Same.
	(cp_validate_addressable): Define function.

2015-06-28  Martin Sebor  <msebor@redhat.com>

	pr c/66516
	* g++.dg/addr_builtin-1.C: New test.
	* gcc.dg/addr_builtin-1.c: New test.
	* gcc.dg/lto/pr54702_1.c: Add a missing include directive.

diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 28b58c6..4219129 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -655,6 +655,7 @@ extern tree c_finish_transaction (location_t, tree, int);
 extern bool c_tree_equal (tree, tree);
 extern tree c_build_function_call_vec (location_t, vec<location_t>, tree,
 				       vec<tree, va_gc> *, vec<tree, va_gc> *);
+extern bool c_validate_addressable (const_tree, location_t = UNKNOWN_LOCATION);

 /* Set to 0 at beginning of a function definition, set to 1 if
    a return statement that specifies a return value is seen.  */
diff --git a/gcc/c/c-typeck.c b/gcc/c/c-typeck.c
index 8e2696a..5fd3669 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3340,6 +3340,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 && !c_validate_addressable (val))
+	{
+	  return -1;
+	}
       else
 	/* Convert `short' and `char' to full-size `int'.  */
 	parmval = default_conversion (val);
@@ -3376,13 +3380,18 @@ struct c_expr
 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 (c_validate_addressable (arg.value))
+    {
+      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);
+    }
+  else
+      result.value = error_mark_node;

   return result;
 }
@@ -4477,11 +4486,22 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp,
       || TREE_CODE (TREE_TYPE (op2)) == ERROR_MARK)
     return error_mark_node;

+  if (TREE_CODE (TREE_TYPE (ifexp)) == POINTER_TYPE
+      && !c_validate_addressable (ifexp,
+				  EXPR_LOCATION (TREE_OPERAND (ifexp, 0))))
+    return error_mark_node;
+
   type1 = TREE_TYPE (op1);
   code1 = TREE_CODE (type1);
   type2 = TREE_TYPE (op2);
   code2 = TREE_CODE (type2);

+  if (code1 == POINTER_TYPE && !c_validate_addressable (op1))
+    return error_mark_node;
+
+  if (code2 == POINTER_TYPE && !c_validate_addressable (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)
@@ -5220,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)
+      && !c_validate_addressable (expr))
+    return error_mark_node;
+
   ret = build_c_cast (loc, type, expr);
   if (type_expr)
     {
@@ -5859,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 && !c_validate_addressable (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)
@@ -10336,6 +10364,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
+      && !c_validate_addressable (op0, EXPR_LOCATION (orig_op0)))
+    return error_mark_node;
+
+  if (code1 == POINTER_TYPE
+      && !c_validate_addressable (op1, EXPR_LOCATION (orig_op1)))
+    return error_mark_node;
+
   if ((invalid_op_diag
        = targetm.invalid_binary_op (code, type0, type1)))
     {
@@ -11313,6 +11349,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 (!c_validate_addressable (expr))
+	return error_mark_node;
+      break;
+
     case FUNCTION_TYPE:
       gcc_unreachable ();

@@ -12865,3 +12906,33 @@ 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);
 }
+
+/* For EXPR that is an ADDR_EXPR or whose type is a FUNCTION_TYPE,
+   determines whether its operand can have its address taken issues
+   an error pointing to the location LOC.
+   Operands that cannot have their address taken are builtin functions
+   that have no library fallback (no other kinds of expressions are
+   considered).
+   Returns true when the expression can have its address taken and
+   false otherwise.  */
+bool
+c_validate_addressable (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) && DECL_IS_GCC_BUILTIN (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 functions must be directly called");
+
+      return false;
+    }
+
+  return true;
+}
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index b846919..9bbde27 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4662,6 +4662,15 @@ build_conditional_expr_1 (location_t loc, tree arg1, tree arg2, tree arg3,
       return fold_build3 (VEC_COND_EXPR, arg2_type, arg1, arg2, arg3);
     }

+  if (TREE_CODE (arg1) == FUNCTION_DECL && !cp_validate_addressable (arg1))
+    return error_mark_node;
+
+  if (TREE_CODE (arg2) == FUNCTION_DECL && !cp_validate_addressable (arg2))
+    return error_mark_node;
+
+  if (TREE_CODE (arg3) == FUNCTION_DECL && !cp_validate_addressable (arg3))
+    return error_mark_node;
+
   /* [expr.cond]

      The first expression is implicitly converted to bool (clause
@@ -6603,12 +6612,17 @@ convert_arg_to_ellipsis (tree arg, tsubst_flags_t complain)
   tree arg_type;
   location_t loc = EXPR_LOC_OR_LOC (arg, input_location);

+  if (TREE_CODE (TREE_TYPE (arg)) == FUNCTION_TYPE
+      && !cp_validate_addressable (arg))
+      return error_mark_node;
+
   /* [expr.call]

      The lvalue-to-rvalue, array-to-pointer, and function-to-pointer
      standard conversions are performed.  */
   arg = decay_conversion (arg, complain);
   arg_type = TREE_TYPE (arg);
+
   /* [expr.call]

      If the argument has integral or enumeration type that is subject
@@ -6884,6 +6898,10 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain)
 	   && COMPLETE_TYPE_P (type)
 	   && tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node)))
     val = cp_perform_integral_promotions (val, complain);
+  else if (TREE_CODE (val) == ADDR_EXPR
+	   && !cp_validate_addressable (val, complain))
+    return error_mark_node;
+
   if ((complain & tf_warning)
       && warn_suggest_attribute_format)
     {
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index e8cc38f..98e9a63 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6298,6 +6298,9 @@ extern bool check_raw_literal_operator		(const_tree decl);
 extern bool check_literal_operator_args		(const_tree, bool *, bool *);
 extern void maybe_warn_about_useless_cast       (tree, tree, tsubst_flags_t);
 extern tree cp_perform_integral_promotions      (tree, tsubst_flags_t);
+extern bool cp_validate_addressable             (const_tree,
+                                                 tsubst_flags_t = tf_error,
+                                                 location_t = UNKNOWN_LOCATION);

 /* in typeck2.c */
 extern void require_complete_eh_spec_types	(tree, tree);
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 874f29f..3f9f49f 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -6827,6 +6827,26 @@ 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 (TREE_CODE (val) == ADDR_EXPR
+          && !cp_validate_addressable (TREE_OPERAND (val, 0), complain))
+        {
+          /* Reject template arguments that are pointers to builtin
+             functions with no library fallbacks.  */
+          return error_mark_node;
+        }
+
+      if (INDIRECT_REF_P (val))
+        {
+          /* Reject template arguments that are references to builtin
+             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
+              && !cp_validate_addressable (TREE_OPERAND (inner, 0), complain))
+              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 2d03d75..02aab3d 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -4021,6 +4021,14 @@ cp_build_binary_op (location_t location,
   if (code0 == ERROR_MARK || code1 == ERROR_MARK)
     return error_mark_node;

+  if (code0 == POINTER_TYPE
+      && !cp_validate_addressable (op0, complain, EXPR_LOCATION (orig_op0)))
+    return error_mark_node;
+
+  if (code1 == POINTER_TYPE
+      && !cp_validate_addressable (op1, complain, EXPR_LOCATION (orig_op1)))
+    return error_mark_node;
+
   if ((invalid_op_diag
        = targetm.invalid_binary_op (code, type0, type1)))
     {
@@ -5651,6 +5659,9 @@ cp_build_addr_expr (tree arg, tsubst_flags_t complain)
 static tree
 cp_build_addr_expr_strict (tree arg, tsubst_flags_t complain)
 {
+  if (!cp_validate_addressable (arg, complain))
+      return error_mark_node;
+
   return cp_build_addr_expr_1 (arg, 1, complain);
 }

@@ -5688,6 +5699,9 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
       return error_mark_node;
     }

+  if (!cp_validate_addressable (arg, complain))
+    return error_mark_node;
+
   switch (code)
     {
     case UNARY_PLUS_EXPR:
@@ -6596,6 +6610,10 @@ build_static_cast_1 (tree type, tree expr, bool c_cast_p,
   if (abstract_virtuals_error_sfinae (ACU_CAST, type, complain))
     return error_mark_node;

+  if (TREE_CODE (intype) == FUNCTION_TYPE
+      && !cp_validate_addressable (expr, complain))
+      return error_mark_node;
+
   /* [expr.static.cast]

      An expression e can be explicitly converted to a type T using a
@@ -6863,6 +6881,9 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
 	warning (0, "casting %qT to %qT does not dereference pointer",
 		 intype, type);

+      if (!cp_validate_addressable (expr, complain))
+	  return error_mark_node;
+
       expr = cp_build_addr_expr (expr, complain);

       if (warn_strict_aliasing > 2)
@@ -6890,6 +6911,10 @@ build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
 	  || VOID_TYPE_P (TREE_TYPE (type))))
     return convert_member_func_to_ptr (type, expr, complain);

+  if (TREE_CODE (intype) == FUNCTION_TYPE
+      && !cp_validate_addressable (expr, complain, input_location))
+      return error_mark_node;
+
   /* If the cast is not to a reference type, the lvalue-to-rvalue,
      array-to-pointer, and function-to-pointer conversions are
      performed.  */
@@ -7271,6 +7296,10 @@ cp_build_c_cast (tree type, tree expr, tsubst_flags_t complain)
     warning_at (input_location, OPT_Wint_to_pointer_cast,
 		"cast to pointer from integer of different size");

+  if (FUNCTION_POINTER_TYPE_P (type)
+      && !cp_validate_addressable (value, complain))
+    return error_mark_node;
+
   /* A C-style cast can be a const_cast.  */
   result = build_const_cast_1 (type, value, complain & tf_warning,
 			       &valid_p);
@@ -8135,6 +8164,9 @@ convert_for_assignment (tree type, tree rhs,
       return error_mark_node;
     }

+  if (coder == FUNCTION_TYPE && !cp_validate_addressable (rhs, complain))
+      return error_mark_node;
+
   if (c_dialect_objc ())
     {
       int parmno;
@@ -8337,6 +8369,10 @@ convert_for_initialization (tree exp, tree type, tree rhs, int flags,
       || (TREE_CODE (rhs) == TREE_LIST && TREE_VALUE (rhs) == error_mark_node))
     return error_mark_node;

+  if (TREE_CODE (TREE_TYPE (rhs)) == FUNCTION_TYPE
+      && !cp_validate_addressable (rhs, complain, input_location))
+      return error_mark_node;
+
   if ((TREE_CODE (TREE_TYPE (rhs)) == ARRAY_TYPE
        && TREE_CODE (type) != ARRAY_TYPE
        && (TREE_CODE (type) != REFERENCE_TYPE
@@ -9358,3 +9394,40 @@ check_literal_operator_args (const_tree decl,
       return true;
     }
 }
+
+/* For EXPR that is an ADDR_EXPR or whose type is a FUNCTION_TYPE,
+   determines whether its operand can have its address taken and,
+   when COMPLAIN & tf_error is non-zero, issues an error pointing
+   to the location LOC.
+   Operands that cannot have their address taken are builtin functions
+   that have no library fallback (no other kinds of expressions are
+   considered).
+   Returns true when the expression can have its address taken and
+   false otherwise.  */
+bool
+cp_validate_addressable (const_tree expr, tsubst_flags_t complain,
+			 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) && DECL_IS_GCC_BUILTIN (expr)
+      && !DECL_IS_OPERATOR_NEW (expr)
+      && (!DECL_NAME (expr)
+	  || !NEW_DELETE_OPNAME_P (DECL_NAME (expr))))
+    {
+      if (complain & tf_error) {
+	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 functions must be directly called");
+      }
+
+      return false;
+    }
+
+  return true;
+}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 8258000..0473797 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -10080,14 +10080,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 are prefixed
+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
+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..5e78b35
--- /dev/null
+++ b/gcc/testsuite/g++.dg/addr_builtin-1.C
@@ -0,0 +1,188 @@
+/* PR66516 - missing diagnostic on taking the address of a builtin function */
+/* { dg-do compile } */
+/* { dg-options "-Wno-error=pedantic" } */
+
+typedef void (F)();
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+// Utility function to test passing builtin functions as an ordinary
+// argument and via the ellipsis.
+static void func_arg (F*, ...);
+
+// Utility templates to test specializing templates on pointers and
+// references to builtin functions.
+template <F*> struct TestPointer { };
+template <F&> struct TestReference { };
+
+// Utility function with which, along with the builtin function,
+// to instantiate the C98 multi-parameter or C11 variadic tempates
+// below.
+void f () { }
+
+#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 ();                 // { dg-bogus "builtin" }
+  (void)__builtin_trap;              // { dg-bogus "builtin" }
+  __builtin_trap;                    // { dg-bogus "builtin" }
+
+  // Address operator.
+  p = &__builtin_trap;               // { dg-error "builtin" }
+
+  // Unary NOT.
+  a = !__builtin_trap;               // { dg-error "builtin" }
+
+  // Casts.
+  p = (F*)__builtin_trap;            // { dg-error "builtin" }
+
+  p = &(F&)__builtin_trap;            // { dg-error "builtin" }
+
+  p = &reinterpret_cast<F&>(__builtin_trap);   // { dg-error "builtin" }
+  p = &static_cast<F&>(__builtin_trap);        // { dg-error "builtin" }
+
+  p = reinterpret_cast<F*>(__builtin_trap);    // { dg-error "builtin" }
+  p = static_cast<F*>(__builtin_trap);         // { dg-error "builtin" }
+
+  a = reinterpret_cast<uintptr_t>(__builtin_trap);  // { dg-error "builtin" }
+  a = static_cast<uintptr_t>(__builtin_trap);       // { dg-error "builtin" }
+
+  a = reinterpret_cast<UINTPTR_E>(__builtin_trap);  // { dg-error "builtin" }
+  a = static_cast<UINTPTR_E>(__builtin_trap);       // { dg-error "builtin" }
+
+  // Additive operator.  Ill-formed but allowed with -fpermissive.
+  p = __builtin_trap + 0;            // { dg-error "builtin" }
+  p = __builtin_trap - 0;            // { dg-error "builtin" }
+  a = __builtin_trap - p;            // { dg-error "builtin" }
+  a = p - __builtin_trap;            // { dg-error "builtin" }
+
+  // Relational operators.  Ill-formed but allowed with -fpermissive.
+  a = __builtin_trap < p;            // { dg-error "builtin" }
+  a = p < __builtin_trap;            // { dg-error "builtin" }
+
+  a = __builtin_trap <= p;           // { dg-error "builtin" }
+  a = p <= __builtin_trap;           // { dg-error "builtin" }
+
+  a = __builtin_trap > p;            // { dg-error "builtin" }
+  a = p > __builtin_trap;            // { dg-error "builtin" }
+
+  a = __builtin_trap > p;            // { dg-error "builtin" }
+  a = p > __builtin_trap;            // { dg-error "builtin" }
+
+  a = __builtin_trap <= p;           // { dg-error "builtin" }
+  a = p <= __builtin_trap;           // { dg-error "builtin" }
+
+  a = __builtin_trap <= p;           // { dg-error "builtin" }
+  a = p <= __builtin_trap;           // { dg-error "builtin" }
+
+  // Equality operators.
+  a = __builtin_trap == p;           // { dg-error "builtin" }
+  a = p == __builtin_trap;           // { dg-error "builtin" }
+  a = __builtin_trap != p;           // { dg-error "builtin" }
+  a = p != __builtin_trap;           // { dg-error "builtin" }
+
+  // Logical AND and OR.
+  a = __builtin_trap && p;           // { dg-error "builtin" }
+  a = p && __builtin_trap;           // { dg-error "builtin" }
+
+  a = __builtin_trap || p;           // { dg-error "builtin" }
+  a = p || __builtin_trap;           // { dg-error "builtin" }
+
+  // Conditional operator.
+  a = __builtin_trap ? 1 : 0;        // { dg-error "builtin" }
+  p = a ? __builtin_trap : 0;        // { dg-error "builtin" }
+  p = a ? 0 : __builtin_trap;        // { dg-error "builtin" }
+
+  // Assignment operator.
+  p = __builtin_trap;                // { dg-error "builtin" }
+
+  // Passing as an argument.
+  func_arg (__builtin_trap);         // { dg-error "builtin" }
+
+  // Passing through ellipsis.
+  func_arg (0, __builtin_trap);      // { dg-error "builtin" }
+
+  {
+    // Template specialization.
+    TestPointer<__builtin_trap> tp;         // { dg-error "builtin" }
+    TestReference<__builtin_trap> tr;       // { dg-error "builtin" }
+
+    TestPointers<__builtin_trap> tp1;       // { dg-error "builtin" }
+    TestReferences<__builtin_trap> tr1;     // { dg-error "builtin" }
+
+    TestPointers<f, __builtin_trap> tp2;    // { dg-error "builtin" }
+    TestReferences<f, __builtin_trap> tr2;  // { dg-error "builtin" }
+
+    TestPointers<__builtin_trap, f> tp3;    // { dg-error "builtin" }
+    TestReferences<__builtin_trap, f> tr3;  // { dg-error "builtin" }
+  }
+
+  return __builtin_trap;                    // { dg-error "builtin" }
+
+  (void)a;
+  (void)p;
+  (void)q;
+}
+
+// Operators new and delete are treated as GCC builtins with no library
+// fallbacks yet they exist in the runtime library and programs must be
+// able to take their address.
+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;           // { dg-bogus "builtin" }
+  OpNew &newra = operator new[];        // { dg-bogus "builtin" }
+  OpNew *newp = &operator new;          // { dg-bogus "builtin" }
+  newp = &operator new[];               // { dg-bogus "builtin" }
+
+  OpDelete &delr = operator delete;     // { dg-bogus "builtin" }
+  OpDelete &delra = operator delete[];  // { dg-bogus "builtin" }
+  OpDelete *delp = &operator delete;    // { dg-bogus "builtin" }
+  delp = &operator delete[];            // { dg-bogus "builtin" }
+
+  (void)newr;
+  (void)newp;
+  (void)delr;
+  (void)delp;
+}
+
+// Creating a reference to or taking the address of a builtin with
+// a library "fallback" must be allowed.
+void test_taking_address_of_library_builtin ()
+{
+  {
+    typedef int F (int);
+    F &r = __builtin_abs;               // { dg-bogus "builtin" }
+    F *p = &__builtin_abs;              // { dg-bogus "builtin" }
+    (void)p;
+    (void)r;
+  }
+  {
+    typedef __SIZE_TYPE__ size_t;
+    typedef size_t F (const char*);
+    F &r = __builtin_strlen;            // { dg-bogus "builtin" }
+    F *p = &__builtin_strlen;           // { dg-bogus "builtin" }
+    (void)p;
+    (void)p;
+  }
+}
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..1dbd871
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/addr_builtin-1.c
@@ -0,0 +1,127 @@
+/* PR66516 - missing diagnostic on taking the address of a builtin function */
+/* { dg-do compile } */
+/* { dg-options "-Wno-error=pedantic" } */
+
+typedef void (F)(void);
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+// Utility function to test passing builtin 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)
+{
+    enum UINTPTR_E { e = ~(uintptr_t)0 };
+
+    F *p;
+    void *q;
+    uintptr_t a;
+
+    // Call, cast to void, and id are allowed.
+    __builtin_trap ();                 // { dg-bogus "builtin" }
+    (*__builtin_trap)();               // { dg-bogus "builtin" }
+    (void)__builtin_trap;              // { dg-bogus "builtin" }
+    __builtin_trap;                    // { dg-bogus "builtin" }
+
+    // Address and indirection operators.
+    p = &__builtin_trap;               // { dg-error "builtin" }
+    p = *__builtin_trap;               // { dg-error "builtin" }
+    p = &*__builtin_trap;              // { dg-error "builtin" }
+
+    // Unary NOT.
+    a = !__builtin_trap;               // { dg-error "builtin" }
+
+    // Sizeof and _Alignof are disallowed by C but allowed by GCC.
+    a = sizeof __builtin_trap;         // { dg-bogus "builtin" }
+    a = _Alignof __builtin_trap;       // { dg-bogus "builtin" }
+
+    // Casts.
+    p = (F*)__builtin_trap;            // { dg-error "builtin" }
+    a = (uintptr_t)__builtin_trap;     // { dg-error "builtin" }
+
+    // Additive operator.
+    p = __builtin_trap + 0;            // { dg-error "builtin" }
+    p = __builtin_trap - 0;            // { dg-error "builtin" }
+    a = __builtin_trap - p;            // { dg-error "builtin" }
+    a = p - __builtin_trap;            // { dg-error "builtin" }
+
+    // Relational operators.
+    a = __builtin_trap < p;            // { dg-error "builtin" }
+    a = p < __builtin_trap;            // { dg-error "builtin" }
+
+    a = __builtin_trap <= p;           // { dg-error "builtin" }
+    a = p <= __builtin_trap;           // { dg-error "builtin" }
+
+    a = __builtin_trap > p;            // { dg-error "builtin" }
+    a = p > __builtin_trap;            // { dg-error "builtin" }
+
+    a = __builtin_trap > p;            // { dg-error "builtin" }
+    a = p > __builtin_trap;            // { dg-error "builtin" }
+
+    a = __builtin_trap <= p;           // { dg-error "builtin" }
+    a = p <= __builtin_trap;           // { dg-error "builtin" }
+
+    a = __builtin_trap <= p;           // { dg-error "builtin" }
+    a = p <= __builtin_trap;           // { dg-error "builtin" }
+
+    // Equality operators.
+    a = __builtin_trap == p;           // { dg-error "builtin" }
+    a = p == __builtin_trap;           // { dg-error "builtin" }
+    a = __builtin_trap != p;           // { dg-error "builtin" }
+    a = p != __builtin_trap;           // { dg-error "builtin" }
+
+    // Logical AND and OR.
+    a = __builtin_trap && p;           // { dg-error "builtin" }
+    a = p && __builtin_trap;           // { dg-error "builtin" }
+
+    a = __builtin_trap || p;           // { dg-error "builtin" }
+    a = p || __builtin_trap;           // { dg-error "builtin" }
+
+    // Conditional operator.
+    a = __builtin_trap ? 1 : 0;        // { dg-error "builtin" }
+    p = a ? __builtin_trap : 0;        // { dg-error "builtin" }
+    p = a ? 0 : __builtin_trap;        // { dg-error "builtin" }
+
+    // Assignment operator.
+    p = __builtin_trap;                // { dg-error "builtin" }
+
+    q = __builtin_trap;                // { dg-error "builtin" }
+    a = __builtin_trap;                // { dg-error "builtin" }
+
+    // Passing as an argument.
+    func_arg (__builtin_trap);         // { dg-error "builtin" }
+
+    // Passing through the ellipsis.
+    func_arg (0, __builtin_trap);      // { dg-error "builtin" }
+
+    // Return statement.
+    return __builtin_trap;             // { dg-error "builtin" }
+
+    (void)a;
+    (void)p;
+    (void)q;
+}
+
+// Taking the address of a builtin with a library "fallback" must be
+// allowed.
+void test_taking_address_of_library_builtin (void)
+{
+  {
+    typedef int F (int);
+    F *p = &__builtin_abs;              // { dg-bogus "builtin" }
+    (void)p;
+  }
+  {
+    typedef __SIZE_TYPE__ size_t;
+    typedef void* F (void*, const void*, size_t);
+    F *p = &*__builtin_memcpy;          // { dg-bogus "builtin" }
+    (void)p;
+  }
+
+  {
+    typedef __SIZE_TYPE__ size_t;
+    typedef size_t F (const char*);
+    F *p = &__builtin_strlen;           // { dg-bogus "builtin" }
+    (void)p;
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/lto/pr54702_1.c b/gcc/testsuite/gcc.dg/lto/pr54702_1.c
index 2afb0fb..73c5693 100644
--- a/gcc/testsuite/gcc.dg/lto/pr54702_1.c
+++ b/gcc/testsuite/gcc.dg/lto/pr54702_1.c
@@ -1,3 +1,5 @@
+#include <stdlib.h>
+
 int *b;
 void *d;
 int c;
diff --git a/gcc/tree.h b/gcc/tree.h
index 250f99d..e8c846a 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -2086,12 +2086,19 @@ extern machine_mode element_mode (const_tree t);
 #define DECL_SOURCE_FILE(NODE) LOCATION_FILE (DECL_SOURCE_LOCATION (NODE))
 #define DECL_SOURCE_LINE(NODE) LOCATION_LINE (DECL_SOURCE_LOCATION (NODE))
 #define DECL_SOURCE_COLUMN(NODE) LOCATION_COLUMN (DECL_SOURCE_LOCATION (NODE))
-/* This accessor returns TRUE if the decl it operates on was created
+
+/* This accessor returns TRUE if the DECL it operates on was created
    by a front-end or back-end rather than by user code.  In this case
    builtin-ness is indicated by source location.  */
 #define DECL_IS_BUILTIN(DECL) \
   (LOCATION_LOCUS (DECL_SOURCE_LOCATION (DECL)) <= BUILTINS_LOCATION)

+/* Returns TRUE if the DECL it operates on is a GCC builtin that has
+   no library fallback.  This includes builtins defined using the
+   DEF_SYNC_BUILTIN macro (see builtins.def).  */
+#define DECL_IS_GCC_BUILTIN(DECL) \
+  (DECL_IS_BUILTIN (DECL) && !DECL_ASSEMBLER_NAME_SET_P (DECL))
+
 /*  For FIELD_DECLs, this is the RECORD_TYPE, UNION_TYPE, or
     QUAL_UNION_TYPE node that the field is a member of.  For VAR_DECL,
     PARM_DECL, FUNCTION_DECL, LABEL_DECL, RESULT_DECL, and CONST_DECL

  reply	other threads:[~2015-06-29  0:50 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-06-22  0:36 Martin Sebor
2015-06-22 14:11 ` Joseph Myers
2015-06-22 14:41 ` Marek Polacek
2015-06-23  4:38   ` Martin Sebor
2015-06-23 10:29     ` Marek Polacek
2015-06-23 10:39       ` Jakub Jelinek
2015-06-23 15:12         ` Martin Sebor
2015-06-29  7:37           ` Martin Sebor [this message]
2015-07-02 14:20             ` Joseph Myers
2015-07-04 22:32               ` Martin Sebor
2015-07-14  3:37                 ` [PING] " Martin Sebor
2015-07-14 15:01                   ` Jason Merrill
2015-07-14 15:07                     ` Jason Merrill
2015-07-14 18:04                     ` Martin Sebor
2015-07-29  7:27                       ` Jason Merrill
2015-07-29 19:07                         ` Martin Sebor
2015-08-03 23:02                           ` Martin Sebor
2015-08-04 15:04                             ` Jason Merrill
2015-08-04 15:58                               ` Jeff Law
2015-08-04 15:19                             ` Joseph Myers
2015-08-28 20:10                         ` Martin Sebor
2015-08-28 20:49                           ` Joseph Myers
2015-08-29  0:57                             ` Martin Sebor
2015-09-01 15:01                               ` Martin Sebor
2015-09-01 17:29                                 ` Joseph Myers
2015-09-01 22:25                                   ` Martin Sebor
2015-09-02  0:20                                     ` Joseph Myers
2015-09-02 15:29                                     ` Jason Merrill
2015-09-02 22:13                                       ` Martin Sebor
2015-09-03 14:29                                         ` Jason Merrill
2015-09-03 14:53                                           ` Joseph Myers
2015-09-03 17:30                                         ` Jakub Jelinek
2015-09-03 18:28                                           ` Martin Sebor
2015-06-22 19:45 ` Marek Polacek

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5590965B.6030103@gmail.com \
    --to=msebor@gmail.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=joseph@codesourcery.com \
    --cc=polacek@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).