PR c/66516 - missing diagnostic on taking the address of a builtin function
authorMartin Sebor <msebor@redhat.com>
Wed, 25 Nov 2015 23:29:57 +0000 (23:29 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 25 Nov 2015 23:29:57 +0000 (16:29 -0700)
PR c/66516 - missing diagnostic on taking the address of a builtin function
* g++.dg/addr_builtin-1.C: New test (accidentally omitted from
initial commit).
* gcc.dg/addr_builtin-1.c: Same.

From-SVN: r230916

gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/addr_builtin-1.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/addr_builtin-1.c [new file with mode: 0644]

index eb52769540d14cb39b3517088eef6791cb3d89fb..387a71f7dbcc500a8ecf238d5c5efcf50816c15e 100644 (file)
@@ -1,3 +1,9 @@
+2015-11-25  Martin Sebor  <msebor@redhat.com>
+
+       PR c/66516
+       * g++.dg/addr_builtin-1.C: New test (left out of initial commit).
+       * gcc.dg/addr_builtin-1.c: Same.
+
 2015-11-25  Michael Meissner  <meissner@linux.vnet.ibm.com>
 
        * gcc.target/powerpc/dform-1.c: New test.
        check the assembly.
        * gcc.target/aarch64/arm_align_max_stack_pwr.c: Likewise.
 
-2015-09-03  Martin Sebor  <msebor@redhat.com>
-
-       PR c/66516
-       * g++.dg/addr_builtin-1.C: New test.
-       * gcc.dg/addr_builtin-1.c: New test.
-
 2015-09-03  Bill Schmidt  <wschmidt@linux.vnet.ibm.com>
 
        * gcc.target/powerpc/vec-shift.c: New test.
diff --git a/gcc/testsuite/g++.dg/addr_builtin-1.C b/gcc/testsuite/g++.dg/addr_builtin-1.C
new file mode 100644 (file)
index 0000000..e8ba31f
--- /dev/null
@@ -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 "built-in" }
+  p = *__builtin_trap;                       // { dg-error "built-in" }
+
+  // Unary NOT.
+  // GCC issues two diagnostics here for some reason, so account for both.
+  a = !__builtin_trap;                   // { dg-error "built-in|unary" }
+
+  // Casts.
+  p = (F*)__builtin_trap;                    // { dg-error "built-in" }
+
+  p = &(F&)__builtin_trap;                   // { dg-error "built-in" }
+
+  p = &reinterpret_cast<F&>(__builtin_trap); // { dg-error "built-in" }
+  p = &static_cast<F&>(__builtin_trap);      // { dg-error "built-in" }
+
+  p = reinterpret_cast<F*>(__builtin_trap);  // { dg-error "built-in" }
+  p = static_cast<F*>(__builtin_trap);       // { dg-error "built-in" }
+
+  // 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 "built-in" }
+  a = reinterpret_cast<UINTPTR_E>(__builtin_trap);  // { dg-error "built-in" }
+
+  // Additive operator.  Ill-formed but allowed with -fpermissive.
+  p = __builtin_trap + 0;            // { dg-error "built-in" }
+  p = __builtin_trap - 0;            // { dg-error "built-in" }
+  a = __builtin_trap - p;            // { dg-error "built-in" }
+  a = p - __builtin_trap;            // { dg-error "built-in" }
+
+  // Relational operators.  Ill-formed but allowed with -fpermissive.
+  a = __builtin_trap < p;            // { dg-error "built-in" }
+  a = p < __builtin_trap;            // { dg-error "built-in" }
+
+  a = __builtin_trap <= p;           // { dg-error "built-in" }
+  a = p <= __builtin_trap;           // { dg-error "built-in" }
+
+  a = __builtin_trap > p;            // { dg-error "built-in" }
+  a = p > __builtin_trap;            // { dg-error "built-in" }
+
+  a = __builtin_trap > p;            // { dg-error "built-in" }
+  a = p > __builtin_trap;            // { dg-error "built-in" }
+
+  a = __builtin_trap <= p;           // { dg-error "built-in" }
+  a = p <= __builtin_trap;           // { dg-error "built-in" }
+
+  a = __builtin_trap <= p;           // { dg-error "built-in" }
+  a = p <= __builtin_trap;           // { dg-error "built-in" }
+
+  // Equality operators.
+  a = __builtin_trap == p;           // { dg-error "built-in" }
+  a = p == __builtin_trap;           // { dg-error "built-in" }
+  a = __builtin_trap != p;           // { dg-error "built-in" }
+  a = p != __builtin_trap;           // { dg-error "built-in" }
+
+  // Logical AND and OR.
+  a = __builtin_trap && p;           // { dg-error "built-in" }
+  a = p && __builtin_trap;           // { dg-error "built-in" }
+
+  a = __builtin_trap || p;           // { dg-error "built-in" }
+  a = p || __builtin_trap;           // { dg-error "built-in" }
+
+  // Conditional operator.
+  a = __builtin_trap ? 1 : 0;        // { dg-error "built-in" }
+  p = a ? __builtin_trap : 0;        // { dg-error "built-in" }
+  p = a ? 0 : __builtin_trap;        // { dg-error "built-in" }
+
+  // Assignment operator.
+  p = __builtin_trap;                // { dg-error "built-in" }
+
+  // Passing as an argument.
+  func_arg (__builtin_trap);         // { dg-error "built-in" }
+  func_arg (&__builtin_trap);        // { dg-error "built-in" }
+  func_arg (*__builtin_trap);        // { dg-error "built-in" }
+
+  // Passing through ellipsis.
+  func_arg (0, __builtin_trap);      // { dg-error "built-in" }
+  func_arg (0, &__builtin_trap);     // { dg-error "built-in" }
+  func_arg (0, *__builtin_trap);     // { dg-error "built-in" }
+
+  {
+    // Template specialization.
+    // GCC issues two diagnostics and we must account for both.
+    TestPointer<__builtin_trap> tp;         // { dg-error "built-in|could not convert" }
+    TestReference<__builtin_trap> tr;       // { dg-error "built-in|could not convert" }
+
+    TestPointers<__builtin_trap> tp1;       // { dg-error "built-in|could not convert" }
+    TestReferences<__builtin_trap> tr1;     // { dg-error "built-in|could not convert" }
+
+    TestPointers<f, __builtin_trap> tp2;    // { dg-error "built-in|could not convert" }
+    TestReferences<f, __builtin_trap> tr2;  // { dg-error "built-in|could not convert" }
+
+    TestPointers<__builtin_trap, f> tp3;    // { dg-error "built-in|could not convert" }
+    TestReferences<__builtin_trap, f> tr3;  // { dg-error "built-in|could not convert" }
+  }
+
+  try {
+    throw __builtin_trap;                 // { dg-error "built-in" }
+  }
+  catch (F) { }
+
+  return __builtin_trap;                    // { dg-error "built-in" }
+
+  (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 (file)
index 0000000..4a0888a
--- /dev/null
@@ -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 "built-in function" }  */
+  p = *__builtin_trap;               /* { dg-error "built-in function" }  */
+
+  /* Unary NOT.  */
+  a = !__builtin_trap;               /* { dg-error "built-in 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 "built-in function" }  */
+  a = (uintptr_t)__builtin_trap;     /* { dg-error "built-in function" }  */
+
+  /* Additive operator.  */
+  p = __builtin_trap + 0;            /* { dg-error "built-in function" }  */
+  p = __builtin_trap - 0;            /* { dg-error "built-in function" }  */
+  a = __builtin_trap - p;            /* { dg-error "built-in function" }  */
+  a = p - __builtin_trap;            /* { dg-error "built-in function" }  */
+
+  /* Relational operators.  */
+  a = __builtin_trap < p;            /* { dg-error "built-in function" }  */
+  a = p < __builtin_trap;            /* { dg-error "built-in function" }  */
+
+  a = __builtin_trap <= p;           /* { dg-error "built-in function" }  */
+  a = p <= __builtin_trap;           /* { dg-error "built-in function" }  */
+
+  a = __builtin_trap > p;            /* { dg-error "built-in function" }  */
+  a = p > __builtin_trap;            /* { dg-error "built-in function" }  */
+
+  a = __builtin_trap > p;            /* { dg-error "built-in function" }  */
+  a = p > __builtin_trap;            /* { dg-error "built-in function" }  */
+
+  a = __builtin_trap <= p;           /* { dg-error "built-in function" }  */
+  a = p <= __builtin_trap;           /* { dg-error "built-in function" }  */
+
+  a = __builtin_trap <= p;           /* { dg-error "built-in function" }  */
+  a = p <= __builtin_trap;           /* { dg-error "built-in function" }  */
+
+  /* Equality operators.  */
+  a = __builtin_trap == p;           /* { dg-error "built-in function" }  */
+  a = p == __builtin_trap;           /* { dg-error "built-in function" }  */
+  a = __builtin_trap != p;           /* { dg-error "built-in function" }  */
+  a = p != __builtin_trap;           /* { dg-error "built-in function" }  */
+
+  /* Logical AND and OR.  */
+  a = __builtin_trap && p;           /* { dg-error "built-in function" }  */
+  a = p && __builtin_trap;           /* { dg-error "built-in function" }  */
+
+  a = __builtin_trap || p;           /* { dg-error "built-in function" }  */
+  a = p || __builtin_trap;           /* { dg-error "built-in function" }  */
+
+  /* Conditional operator.  */
+  a = __builtin_trap ? 1 : 0;        /* { dg-error "built-in function" }  */
+  p = a ? __builtin_trap : 0;        /* { dg-error "built-in function" }  */
+  p = a ? 0 : __builtin_trap;        /* { dg-error "built-in function" }  */
+
+  /* Assignment operator.  */
+  p = __builtin_trap;                /* { dg-error "built-in function" }  */
+
+  q = __builtin_trap;                /* { dg-error "built-in function" }  */
+  a = __builtin_trap;                /* { dg-error "built-in function" }  */
+
+  /* Passing as an argument.  */
+  func_arg (__builtin_trap);         /* { dg-error "built-in function" }  */
+
+  /* Passing through the ellipsis.  */
+  func_arg (0, __builtin_trap);      /* { dg-error "built-in function" }  */
+
+  /* Return statement.  */
+  return __builtin_trap;             /* { dg-error "built-in 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;
+  }
+}