public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Martin Sebor <msebor@gmail.com>
To: Joseph Myers <joseph@codesourcery.com>
Cc: Jakub Jelinek <jakub@redhat.com>,
	Marek Polacek <polacek@redhat.com>,
	 Gcc Patch List <gcc-patches@gcc.gnu.org>
Subject: Re: [PATCH] c/66516 - missing diagnostic on taking the address of a builtin function
Date: Sat, 04 Jul 2015 22:32:00 -0000	[thread overview]
Message-ID: <55985EE0.3060802@gmail.com> (raw)
In-Reply-To: <alpine.DEB.2.10.1507021411440.29415@digraph.polyomino.org.uk>

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

> I don't think c_validate_addressable is a good name - given that it's
> called for lots of things that aren't addressable, in contexts in which
> there is no need for them to be addressable, and doesn't do checks of
> addressability in contexts where they are actually needed and done
> elsewhere (e.g. checks for not taking the address of a register variable).
> The question seems to be something more like: is the expression used as an
> operand something it's OK to use as an operand at all?

Thank you for the review.

I've changed the name to c_reject_gcc_builtin. If you would prefer
a different name still please suggest one. I'm not partial to any
one in particular.

> What is the logic for the list of functions above being a complete list of
> the places that need changes?

The logic by which I arrived at the changes was by constructing
test cases exercising expressions where a function is a valid
operand and where its address might need to be obtained when
it's not available, and stepping through the code and modifying
it until I found a suitable place to change to reject it.

> How can ifexp be of pointer type?  It's undergone truthvalue conversion
> and should always be of type int at this point (but in any case, you can't
> refer to TREE_OPERAND (ifexp, 0) without knowing what sort of expression
> it is).

I've removed the redundant test from this function.

>
>> +/* 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.  */
>
> Apart from the naming issue, the comment says nothing about the semantics
> of the function for an argument that's not of that form.

I've reworded the comment to hopefully make the semantics of
the function more clear.

Attached is an updated patch with these changes.

Martin

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

gcc/ChangeLog

2015-07-04  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 built-in function can be taken.

gcc/c/ChangeLog

2015-07-04  Martin Sebor  <msebor@redhat.com>

	pr c/66516
	* c-tree.h (c_reject_gcc_builtin): 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_reject_gcc_builtin): Define function.

gcc/cp/ChangeLog

2015-07-04  Martin Sebor  <msebor@redhat.com>

	pr c/66516
	* cp-tree.h (cp_reject_gcc_builtin): New function.
	* call.c (build_conditional_expr_1): Call it.
	(convert_arg_to_ellipsis, convert_for_arg_passing): Same.
	* pt.c (convert_template_argument): Same.
	* 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_reject_gcc_builtin): Define function.

gcc/testsuite/ChangeLog

2015-07-04  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/c-tree.h b/gcc/c/c-tree.h
index 28b58c6..6a492c8 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_reject_gcc_builtin (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 636e0bb..d27ace2 100644
--- a/gcc/c/c-typeck.c
+++ b/gcc/c/c-typeck.c
@@ -3343,6 +3343,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_reject_gcc_builtin (val))
+	{
+	  return -1;
+	}
       else
 	/* Convert `short' and `char' to full-size `int'.  */
 	parmval = default_conversion (val);
@@ -3380,12 +3384,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 (c_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;
 }
@@ -4486,6 +4498,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 && c_reject_gcc_builtin (op1))
+    return error_mark_node;
+
+  if (code2 == POINTER_TYPE && c_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)
@@ -5224,6 +5242,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_reject_gcc_builtin (expr))
+    return error_mark_node;
+
   ret = build_c_cast (loc, type, expr);
   if (type_expr)
     {
@@ -5863,6 +5885,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_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)
@@ -10341,6 +10367,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_reject_gcc_builtin (op0, EXPR_LOCATION (orig_op0)))
+    return error_mark_node;
+
+  if (code1 == POINTER_TYPE
+      && c_reject_gcc_builtin (op1, EXPR_LOCATION (orig_op1)))
+    return error_mark_node;
+
   if ((invalid_op_diag
        = targetm.invalid_binary_op (code, type0, type1)))
     {
@@ -11318,6 +11352,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_reject_gcc_builtin (expr))
+	return error_mark_node;
+      break;
+
     case FUNCTION_TYPE:
       gcc_unreachable ();

@@ -12870,3 +12909,30 @@ 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 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
+c_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) && 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 true;
+    }
+
+  return false;
+}
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 2d90ed9..75a1892 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -4689,6 +4689,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_reject_gcc_builtin (arg1))
+    return error_mark_node;
+
+  if (TREE_CODE (arg2) == FUNCTION_DECL && cp_reject_gcc_builtin (arg2))
+    return error_mark_node;
+
+  if (TREE_CODE (arg3) == FUNCTION_DECL && cp_reject_gcc_builtin (arg3))
+    return error_mark_node;
+
   /* [expr.cond]

      The first expression is implicitly converted to bool (clause
@@ -6628,12 +6637,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_reject_gcc_builtin (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
@@ -6909,6 +6923,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_reject_gcc_builtin (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 1eac636..d32ba06 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -6293,6 +6293,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_reject_gcc_builtin             (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 7f04fe6..d657eb1 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -1053,7 +1053,7 @@ retrieve_specialization (tree tmpl, tree args, hashval_t hash)
 		  ? TMPL_PARMS_DEPTH (DECL_TEMPLATE_PARMS (tmpl))
 		  : template_class_depth (DECL_CONTEXT (tmpl))));

-#ifdef ENABLE_CHECKING
+#if 0 // def ENABLE_CHECKING
   /* We should have gone through coerce_template_parms by now.  */
   ++processing_template_decl;
   if (!any_dependent_template_arguments_p (args))
@@ -6805,6 +6805,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_reject_gcc_builtin (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_reject_gcc_builtin (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 5b09b73..5cba479 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -4025,6 +4025,14 @@ cp_build_binary_op (location_t location,
   if (code0 == ERROR_MARK || code1 == ERROR_MARK)
     return error_mark_node;

+  if (code0 == POINTER_TYPE
+      && cp_reject_gcc_builtin (op0, complain, EXPR_LOCATION (orig_op0)))
+    return error_mark_node;
+
+  if (code1 == POINTER_TYPE
+      && cp_reject_gcc_builtin (op1, complain, EXPR_LOCATION (orig_op1)))
+    return error_mark_node;
+
   if ((invalid_op_diag
        = targetm.invalid_binary_op (code, type0, type1)))
     {
@@ -5621,6 +5629,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_reject_gcc_builtin (arg, complain))
+      return error_mark_node;
+
   return cp_build_addr_expr_1 (arg, 1, complain);
 }

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

+  if (cp_reject_gcc_builtin (arg, complain))
+    return error_mark_node;
+
   switch (code)
     {
     case UNARY_PLUS_EXPR:
@@ -6566,6 +6580,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_reject_gcc_builtin (expr, complain))
+      return error_mark_node;
+
   /* [expr.static.cast]

      An expression e can be explicitly converted to a type T using a
@@ -6833,6 +6851,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_reject_gcc_builtin (expr, complain))
+	  return error_mark_node;
+
       expr = cp_build_addr_expr (expr, complain);

       if (warn_strict_aliasing > 2)
@@ -6860,6 +6881,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_reject_gcc_builtin (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.  */
@@ -7241,6 +7266,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_reject_gcc_builtin (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);
@@ -8105,6 +8134,9 @@ convert_for_assignment (tree type, tree rhs,
       return error_mark_node;
     }

+  if (coder == FUNCTION_TYPE && cp_reject_gcc_builtin (rhs, complain))
+      return error_mark_node;
+
   if (c_dialect_objc ())
     {
       int parmno;
@@ -8307,6 +8339,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_reject_gcc_builtin (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
@@ -9328,3 +9364,37 @@ check_literal_operator_args (const_tree decl,
       return true;
     }
 }
+
+/* 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
+   and, when COMPLAIN & tf_error is non-zero,  issues an error pointing to
+   the location LOC.
+   Returns true when the expression would have been diagnosed if COMPLAIN
+   & tf_error had been non-zero and false otherwise.  */
+bool
+cp_reject_gcc_builtin (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 true;
+    }
+
+  return false;
+}
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 9ad2b68..8d0c2b1 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 built-in function */
+/* { dg-do compile } */
+/* { dg-options "-Wno-error=pedantic" } */
+
+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 templates to test specializing templates on pointers and
+// references to built-in functions.
+template <F*> struct TestPointer { };
+template <F&> struct TestReference { };
+
+// Utility function with which, along with the built-in 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 builti-ns 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 built-in 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..42346ef
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/addr_builtin-1.c
@@ -0,0 +1,117 @@
+/* PR66516 - missing diagnostic on taking the address of a built-in function */
+/* { dg-do compile } */
+/* { dg-options "-Wno-error=pedantic" } */
+
+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)
+{
+    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" }
+    (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" }
+
+    // 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 built-in 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 size_t F (const char*);
+    F *p = &__builtin_strlen;           // { dg-bogus "builtin" }
+    (void)p;
+  }
+}
diff --git a/gcc/tree.h b/gcc/tree.h
index ca5e681..17663d9 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 built-ins 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-07-04 22:32 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
2015-07-02 14:20             ` Joseph Myers
2015-07-04 22:32               ` Martin Sebor [this message]
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=55985EE0.3060802@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).