From 91f6ec2fc627aafe3ba7f0e563e7f06146066ed6 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Wed, 25 Nov 2015 23:29:57 +0000 Subject: [PATCH] PR c/66516 - missing diagnostic on taking the address of a builtin function 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 | 12 +- gcc/testsuite/g++.dg/addr_builtin-1.C | 276 ++++++++++++++++++++++++++ gcc/testsuite/gcc.dg/addr_builtin-1.c | 198 ++++++++++++++++++ 3 files changed, 480 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/g++.dg/addr_builtin-1.C create mode 100644 gcc/testsuite/gcc.dg/addr_builtin-1.c diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index eb52769540d..387a71f7dbc 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2015-11-25 Martin Sebor + + 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 * gcc.target/powerpc/dform-1.c: New test. @@ -7943,12 +7949,6 @@ check the assembly. * gcc.target/aarch64/arm_align_max_stack_pwr.c: Likewise. -2015-09-03 Martin Sebor - - PR c/66516 - * g++.dg/addr_builtin-1.C: New test. - * gcc.dg/addr_builtin-1.c: New test. - 2015-09-03 Bill Schmidt * 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 index 00000000000..e8ba31f994c --- /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 . + 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 struct TestPointer { }; +template struct TestReference { }; + +#if 201103 <= __cplusplus + +template struct TestPointers { }; +template struct TestReferences { }; + +#else + +template struct TestPointers { }; +template 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(__builtin_trap); // { dg-error "built-in" } + p = &static_cast(__builtin_trap); // { dg-error "built-in" } + + p = reinterpret_cast(__builtin_trap); // { dg-error "built-in" } + p = static_cast(__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(__builtin_trap); // { dg-error "invalid" } + a = static_cast(__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(__builtin_trap); // { dg-error "built-in" } + a = reinterpret_cast(__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 tp2; // { dg-error "built-in|could not convert" } + TestReferences 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 index 00000000000..4a0888a55d3 --- /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 "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; + } +} -- 2.30.2