/* Verify that accessing freed objects by built-in functions is diagnosed.
   { dg-do compile }
   { dg-options "-Wall" }  */

typedef __SIZE_TYPE__ size_t;

#if __cplusplus
#  define EXTERN_C extern "C"
#else
#  define EXTERN_C extern
#endif

EXTERN_C void free (void*);
EXTERN_C void* realloc (void*, size_t);

EXTERN_C void* memcpy (void*, const void*, size_t);
EXTERN_C char* strcpy (char*, const char*);
EXTERN_C size_t strlen (const char*);


void sink (void*, ...);

struct Member { char *p; char a[4]; };

int nowarn_strcpy_memptr (struct Member *p)
{
  char *q = strcpy (p->p, p->a);
  free (p);
  return *q;
}

int nowarn_strlen_memptr (struct Member *p)
{
  const char *q = p->p;

  free (p);

  return strlen (q);
}

int warn_strlen_memptr (struct Member *p)
{
  free (p);                   // { dg-message "call to '\(void \)?free\(\\(void\\*\\)\)?'" "note" }
  return strlen (p->p);       // { dg-warning "-Wuse-after-free" }
}

int warn_strlen_memarray (struct Member *p)
{
  {
    free (p);
    return strlen (p->a);     // { dg-warning "-Wuse-after-free" }
  }

  {
    char *q = p->a;

    free (p);
    return strlen (q);        // { dg-warning "-Wuse-after-free" "pr??????" { xfail *-*-* } }
  }
}

void* nowarn_realloc_success (void *p)
{
  void *q = realloc (p, 7);
  if (!q)
    /* When realloc fails the original pointer remains valid.  */
    return p;

  return q;
}

void* nowarn_realloc_equal (void *p, int *moved)
{
  void *q = realloc (p, 7);
  /* Verify that equality is not diagnosed at the default level
     (it is diagnosed at level 3).  */
  *moved = !(p == q);
  return q;
}

void* nowarn_realloc_unequal (void *p, int *moved)
{
  void *q = realloc (p, 7);
  /* Verify that inequality is not diagnosed at the default level
     (it is diagnosed at level 3).  */
  *moved = p != q;
  return q;
}

void* warn_realloc_relational (void *p, int *rel)
{
  void *q = realloc (p, 7);       // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
  /* Verify that all relational expressions are diagnosed at the default
     level.  */
  rel[0] = (char*)p < (char*)q;  // { dg-warning "-Wuse-after-free" }
  rel[1] = (char*)p <= (char*)q; // { dg-warning "-Wuse-after-free" }
  rel[2] = (char*)p >= (char*)q; // { dg-warning "-Wuse-after-free" }
  rel[3] = (char*)p > (char*)q;  // { dg-warning "-Wuse-after-free" }
  return q;
}

void* warn_realloc_unchecked (void *p, int *moved)
{
  void *q = realloc (p, 7);       // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
  /* Use subtraction rather than inequality to trigger the warning
     at the default level (equality is diagnosed only at level 3).  */
  *moved = (char*)p - (char*)q;   // { dg-warning "-Wuse-after-free" }
  return q;
}

void* nowarn_realloc_unchecked_copy (void *p1, void *p2, const void *s,
				     int n, int *x)
{
  void *p3 = memcpy (p1, s, n);
  void *p4 = realloc (p2, 7);
  *x = p3 != p4;
  return p4;
}

void* warn_realloc_unchecked_copy (void *p, const void *s, int n, int *moved)
{
  void *p2 = memcpy (p, s, n);
  void *q = realloc (p, 7);       // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
  *moved = (char*)p2 - (char*)q;  // { dg-warning "-Wuse-after-free" }
  return q;
}

void* warn_realloc_failed (void *p, int *moved)
{
  void *q = realloc (p, 7);           // { dg-message "call to '\(void\\* \)?realloc\(\\(void\\*, size_t\\)\)?'" "note" }
  if (q)
    {
      /* When realloc succeeds the original pointer is invalid.  */
      *moved = (char*)p - (char*)q;   // { dg-warning "-Wuse-after-free" }
      return q;
    }

  return p;
}

extern void *evp;

void* warn_realloc_extern (void *p, int *moved)
{
  evp = realloc (p, 7);
  if (evp)
    {
      /* When realloc succeeds the original pointer is invalid.  */
      *moved = (char*)p - (char*)evp;   // { dg-warning "-Wuse-after-free" "escaped" }
      return evp;
    }

  return p;                   // { dg-bogus "-Wuse-after-free" "safe use after realloc failure" { xfail *-*-* } }
}

struct A { void *p, *q; int moved; };

void* warn_realloc_arg (struct A *p)
{
  p->q = realloc (p->p, 7);
  if (p->q)
    {
      /* When realloc succeeds the original pointer is invalid.  */
      p->moved = p->p != p->q;  // { dg-warning "-Wuse-after-free" "escaped" { xfail *-*-* } }
      return p->q;
    }

  return p->p;
}