/* PR c/83656 - missing -Wbuiltin-declaration-mismatch on declaration
   without prototype
   { dg-do compile }
   { dg-options "-Wbuiltin-declaration-mismatch" } */

typedef __SIZE_TYPE__ size_t;

/* Built-ins declared without a prototype are not diagnosed by default
   (without -Wextra) except when their return type doesn't match.  */
int abort ();       /* { dg-warning "\\\[-Wbuiltin-declaration-mismatch]" } */

/* Built-ins declared without a prototype are not diagnosed without -Wextra.  */
void exit ();
void* memcpy ();
void* memset ();


void test_call_abort (void)
{
  /* Verify that a valid call to abort() is not diagnosed.  */
  abort ();

  /* Unfortunately, the incompatible declaration above makes GCC "forget"
     that abort() is a built-in and so the invalid calls below aren't
     diagnosed.  The only saving grace is that the invalid declaration
     that differs in the return type is diagnosed by default. */
  abort (1);        /* { dg-warning "too many arguments to built-in function .abort. expecting 0" "pr?????" { xfail *-*-* } } */

  abort (1, 2);     /* { dg-warning "too many arguments" "pr?????" { xfail *-*-* } } */
}


void test_call_exit (void)
{
  /* Verify that valid calls to exit are not diagnosed.  */
  exit ('\0');
  exit (0);

  /* Also verify calls to the built-in.  */
  __builtin_exit ('\0');
  __builtin_exit (0);
  __builtin_exit (0.0);

  exit ();          /* { dg-warning "too few arguments to built-in function 'exit' expecting 1" } */

  exit (1, 2);      /* { dg-warning "too many arguments" } */

  /* Verify that passing incompatible arguments triggers a warning.  */
  exit ("");        /* { dg-warning "\\\[-Wint-conversion]" } */

  struct S { int i; } s = { 0 };
  exit (s);         /* { dg-warning "incompatible type for argument 1" } */
}


void test_call_memcpy (void *p, const void *q, size_t n)
{
  memcpy (p, q, n);

  memcpy ();        /* { dg-warning "too few arguments to built-in function 'memcpy' expecting 3" } */

  memcpy (p);       /* { dg-warning "too few arguments to built-in function 'memcpy' expecting 3" } */

  memcpy (p, q);     /* { dg-warning "too few arguments to built-in function 'memcpy' expecting 3" } */

  memcpy (q, p, n); /* { dg-warning "\\\[-Wdiscarded-qualifiers]" } */

  memcpy (p, n, q); /* { dg-warning "\\\[-Wint-conversion]" } */

  memcpy (p, q, n, 0); /* { dg-warning "too many arguments to built-in function 'memcpy' expecting 3" } */
}


typedef void* (memcpy_t)(void*, const void*, size_t);
typedef void* (memset_t)(void*, int, size_t);

void test_init (void)
{
  /* Verify that initialization of a pointer by the address of a built-in
     function of a matching type declared without a prototype doesn't
     trigger a warning...  */
  memset_t *pmemset = memset;

  /* ...but initialization by the address of an incompatible built-in
     does even without -Wextra.  */
  memcpy_t *pmemcpy = memset;           /* { dg-warning "\\\[-Wincompatible-pointer-types]" } */
}


void test_assign (void)
{
  /* Same as above but for assignment.  */
  memset_t *pmemset;
  pmemset = memset;

  memcpy_t *pmemcpy;
  pmemcpy = memset;                     /* { dg-warning "\\\[-Wincompatible-pointer-types]" } */
}


/* Verify that passing built-ins declared without a prototype to
   functions that expect a pointer to a function of a specific type
   is diagnosed.  Ditto for return statements.  */

void take_memcpy (memcpy_t*);
void take_any (int, ...);

memset_t* pass_args (int i)
{
  take_memcpy (memcpy);
  take_memcpy (memset);                 /* { dg-warning "\\\[-Wincompatible-pointer-types]" } */

  take_any (0, i ? memcpy : memset);    /* { dg-warning "\\\[-Wincompatible-pointer-types]" } */

  return memcpy;                        /* { dg-warning "\\\[-Wincompatible-pointer-types]" } */
}
