199 lines
6.8 KiB
C
199 lines
6.8 KiB
C
/* 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;
|
|
}
|
|
}
|