/* PR middle-end/88232 - Please implement -Winfinite-recursion
   Verify simple cases without optimization.
   { dg-do compile }
   { dg-options "-Wall -Winfinite-recursion" } */

#define NORETURN __attribute__ ((noreturn))

typedef __SIZE_TYPE__ size_t;

extern void abort (void);
extern void exit (int);

extern int ei;
int (*pfi_v)(void);


/* Make sure the warning doesn't assume every call has a DECL.  */

int nowarn_pfi_v (void)
{
  return pfi_v ();
}


int warn_fi_v (void)                // { dg-warning "-Winfinite-recursion" }
{
  return warn_fi_v ();              // { dg-message "recursive call" }
}

/* Verify #pragma suppression works.  */

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Winfinite-recursion"

int suppress_warn_fi_v (void)
{
  return warn_fi_v ();
}

#pragma GCC diagnostic pop


int nowarn_fi_v (void)
{
  if (ei++ == 0)
    return nowarn_fi_v ();
  return 0;
}

int warn_if_i (int i)               // { dg-warning "-Winfinite-recursion" }
{
  if (i > 0)
    return warn_if_i (--i);         // { dg-message "recursive call" }
  else if (i < 0)
    return warn_if_i (-i);          // { dg-message "recursive call" }
  else
    return warn_if_i (7);           // { dg-message "recursive call" }
}


int nowarn_if_i (int i)
{
  if (i > 0)
    return nowarn_if_i (--i);
  else if (i < 0)
    return nowarn_if_i (-i);
  else
    return -1;
}

int nowarn_switch (int i, int a[])
{
  switch (i)
    {
    case 0: return nowarn_switch (a[3], a + 1);
    case 1: return nowarn_switch (a[5], a + 2);
    case 2: return nowarn_switch (a[7], a + 3);
    case 3: return nowarn_switch (a[9], a + 4);
    }
  return 77;
}

int warn_switch (int i, int a[])    // { dg-warning "-Winfinite-recursion" }
{
  switch (i)
    {
    case 0: return warn_switch (a[3], a + 1);
    case 1: return warn_switch (a[5], a + 2);
    case 2: return warn_switch (a[7], a + 3);
    case 3: return warn_switch (a[9], a + 4);
    default: return warn_switch (a[1], a + 5);
    }
}

NORETURN void fnoreturn (void);

/* Verify there's no warning for a function that doesn't return.  */
int nowarn_call_noret (void)
{
  fnoreturn ();
}

int warn_call_noret_r (void)        // { dg-warning "-Winfinite-recursion" }
{
  warn_call_noret_r ();             // { dg-message "recursive call" }
  fnoreturn ();
}

/* Verify a warning even though the abort() call would prevent the infinite
   recursion.  There's no good way to tell the two cases apart and letting
   a simple abort prevent the warning would make it ineffective in cases
   where it's the result of assert() expansion and not meant to actually
   prevent recursion.  */

int
warn_noret_call_abort_r (char *s, int n)  // { dg-warning "-Winfinite-recursion" }
{
  if (!s)
    abort ();

  if (n > 7)
    abort ();

  return n + warn_noret_call_abort_r (s, n - 1);  // { dg-message "recursive call" }
}

/* Verify that a warning is not issued for an apparently infinitely
   recursive function like the one above where the recursion would be
   prevented by a call to a noreturn function if the recursive function
   is itself declared noreturn.  */

NORETURN void nowarn_noret_call_abort_r (int n)
{
  if (n > 7)
    abort ();

  nowarn_noret_call_abort_r (n - 1);
}

int warn_call_abort_r (int n)       // { dg-warning "-Winfinite-recursion" }
{
  n += warn_call_abort_r (n - 1);   // { dg-message "recursive call" }
  if (n > 7)   // unreachable
    abort ();
  return n;
}


/* And again with exit() for good measure.  */

int warn_call_exit_r (int n)        // { dg-warning "-Winfinite-recursion" }
{
  n += warn_call_exit_r (n - 1);    // { dg-message "recursive call" }
  if (n > 7)
    exit (0);
  return n;
}

struct __jmp_buf_tag { };
typedef struct __jmp_buf_tag jmp_buf[1];

extern jmp_buf jmpbuf;

/* A call to longjmp() breaks infinite recursion.  Verify it suppresses
   the warning.  */

int nowarn_call_longjmp_r (int n)
{
  if (n > 7)
    __builtin_longjmp (jmpbuf, 1);
  return n + nowarn_call_longjmp_r (n - 1);
}

int warn_call_longjmp_r (int n)     // { dg-warning "-Winfinite-recursion" }
{
  n += warn_call_longjmp_r (n - 1); // { dg-message "recursive call" }
  if (n > 7)
    __builtin_longjmp (jmpbuf, 1);
  return n;
}


struct __sigjmp_buf_tag { };
typedef struct __sigjmp_buf_tag sigjmp_buf[1];

extern sigjmp_buf sigjmpbuf;

/* GCC has no __builtin_siglongjmp().  */
extern void siglongjmp (sigjmp_buf, int);

/* A call to longjmp() breaks infinite recursion.  Verify it suppresses
   the warning.  */

int nowarn_call_siglongjmp_r (int n)
{
  if (n > 7)
    siglongjmp (sigjmpbuf, 1);
  return n + nowarn_call_siglongjmp_r (n - 1);
}


int nowarn_while_do_call_r (int n)
{
  int z = 0;
  while (n)
    z += nowarn_while_do_call_r (n--);
  return z;
}

int warn_do_while_call_r (int n)    // { dg-warning "-Winfinite-recursion" }
{
  int z = 0;
  do
    z += warn_do_while_call_r (n);  // { dg-message "recursive call" }
  while (--n);
  return z;
}

/* Verify warnings for a naive replacement of a built-in fucntion.  */

void* malloc (size_t n)             // { dg-warning "-Winfinite-recursion" }
{
  size_t *p =
    (size_t*)__builtin_malloc (n + sizeof n);   // { dg-message "recursive call" }
  *p = n;
  return p + 1;
}