view gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-23.c @ 152:2b5abeee2509

update gcc11
author anatofuz
date Mon, 25 May 2020 07:50:57 +0900
parents 1830386684a0
children
line wrap: on
line source

/* PR tree-optimization/35503 - Warning about restricted pointers?
   { dg-do compile }
   { dg-options "-O2 -Wno-format-overflow -Wrestrict -ftrack-macro-expansion=0" }
*/

void sink (int);

#define S10 "0123456789"

extern char a2[2][22];

#define T(d, ...) do {					\
    char a[22] = S10;					\
    sink (__builtin_sprintf ((d), __VA_ARGS__));	\
  } while (0)

void test_ptr (char *d, int i)
{
  T (d, "%s", d);       /* { dg-warning "argument 3 overlaps destination object 'd'" } */
  T (d, "%s", d + 0);   /* { dg-warning "overlaps" } */

  /* The following only overlaps if d[1] is non-zero.  */
  T (d, "%s", d + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", d + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", d + i);   /* { dg-warning "may overlap" } */

  T (d, "%s", &d[0]);   /* { dg-warning "overlaps" } */
  T (d, "%s", &d[1]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &d[2]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &d[i]);   /* { dg-warning "may overlap" } */

  T (d + 0, "%s", d);   /* { dg-warning "overlaps" } */
  T (d + 1, "%s", d);   /* { dg-warning "may overlap" } */
  T (d + 2, "%s", d);   /* { dg-warning "may overlap" } */
  T (d + i, "%s", d);   /* { dg-warning "may overlap" } */

  T (&d[0], "%s", d);   /* { dg-warning "overlaps" } */
  T (&d[1], "%s", d);   /* { dg-warning "may overlap" } */
  T (&d[2], "%s", d);   /* { dg-warning "may overlap" } */
  T (&d[i], "%s", d);   /* { dg-warning "may overlap" } */

  const char *s = d;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */
}

void test_ptr_plus (char *d, int i)
{
  const char *s = d;

  T (d, "%s", s++);     /* { dg-warning "overlaps" } */
  T (d, "%s", s++);     /* { dg-warning "may overlap" } */
  T (d, "%s", s++);     /* { dg-warning "may overlap" } */
  T (d, "%s", s++);     /* { dg-warning "may overlap" } */

  s += i;
  T (d, "%s", s);       /* { dg-warning "may overlap" } */
}

void test_array_1_dim (int i)
{
  T (a, "%s", a);       /* { dg-warning "overlaps" } */
  T (a, "%s", a + 0);   /* { dg-warning "overlaps" } */
  T (a, "%s", a + 1);   /* { dg-warning "overlaps" } */
  T (a, "%s", a + 2);   /* { dg-warning "overlaps" } */
  T (a, "%s", a + i);   /* { dg-warning "may overlap" } */

  T (a, "%s", &a[0]);   /* { dg-warning "overlaps" } */
  T (a, "%s", &a[1]);   /* { dg-warning "overlaps" } */
  T (a, "%s", &a[2]);   /* { dg-warning "overlaps" } */
  T (a, "%s", &a[i]);   /* { dg-warning "may overlap" } */

  T (a + 0, "%s", a);   /* { dg-warning "overlaps" } */
  T (a + 1, "%s", a);   /* { dg-warning "overlaps" } */
  T (a + 2, "%s", a);   /* { dg-warning "overlaps" } */
  T (a + i, "%s", a);   /* { dg-warning "may overlap" } */

  T (&a[0], "%s", a);   /* { dg-warning "overlaps" } */
  T (&a[1], "%s", a);   /* { dg-warning "overlaps" } */
  T (&a[2], "%s", a);   /* { dg-warning "overlaps" } */
  T (&a[i], "%s", a);   /* { dg-warning "may overlap" } */
}


void test_array_2_dim (int i)
{
  T (a2[0], "%s", a2[0]);       /* { dg-warning "overlaps" } */
  T (a2[0], "%s", a2[0] + 0);   /* { dg-warning "overlaps" } */
  T (a2[0], "%s", a2[0] + 1);   /* { dg-warning "may overlap" } */
  T (a2[0], "%s", a2[0] + 2);   /* { dg-warning "may overlap" } */
  T (a2[0], "%s", a2[0] + i);   /* { dg-warning "may overlap" } */

  T (a2[0], "%s", &a2[0][0]);   /* { dg-warning "overlaps" } */
  T (a2[0], "%s", &a2[0][1]);   /* { dg-warning "may overlap" } */
  T (a2[0], "%s", &a2[0][2]);   /* { dg-warning "may overlap" } */
  T (a2[0], "%s", &a2[0][i]);   /* { dg-warning "may overlap" } */

  T (a2[0] + 0, "%s", a2[0]);   /* { dg-warning "overlaps" } */
  T (a2[0] + 1, "%s", a2[0]);   /* { dg-warning "may overlap" } */
  T (a2[0] + 2, "%s", a2[0]);   /* { dg-warning "may overlap" } */
  T (a2[0] + i, "%s", a2[0]);   /* { dg-warning "may overlap" } */

  T (&a2[0][0], "%s", a2[0]);   /* { dg-warning "overlaps" } */
  T (&a2[0][1], "%s", a2[0]);   /* { dg-warning "may overlap" } */
  T (&a2[0][2], "%s", a2[0]);   /* { dg-warning "may overlap" } */
  T (&a2[0][i], "%s", a2[0]);   /* { dg-warning "may overlap" } */


  T (a2[0], "%s", a2[1]);
  T (a2[0], "%s", a2[1] + 0);
  T (a2[0], "%s", a2[1] + 1);
  T (a2[0], "%s", a2[1] + 2);
  T (a2[0], "%s", a2[1] + i);

  T (a2[0], "%s", &a2[1][0]);
  T (a2[0], "%s", &a2[1][1]);
  T (a2[0], "%s", &a2[1][2]);

  /* a2[0] is represented as &a in Gimple, and &a2[1][i] as &a + _2,
     with _1 defined to something like 10 + _1, and _1 to i.  That
     makes it virtually impossible to reliably determine that the
     two pointers refer to distinct sub-arrays of the same multi-
     dimensional array.  */
  T (a2[0], "%s", &a2[1][i]);   /* { dg-bogus "overlap" "" { xfail *-*-* } } */

  T (a2[0] + 0, "%s", a2[1]);
  T (a2[0] + 1, "%s", a2[1]);
  T (a2[0] + 2, "%s", a2[1]);
  T (a2[0] + i, "%s", a2[1]);

  T (&a2[0][0], "%s", a2[1]);
  T (&a2[0][1], "%s", a2[1]);
  T (&a2[0][2], "%s", a2[1]);
  T (&a2[0][i], "%s", a2[1]);


  T (a2[1], "%s", a2[0]);
  T (a2[1], "%s", a2[0] + 0);
  T (a2[1], "%s", a2[0] + 1);
  T (a2[1], "%s", a2[0] + 2);
  T (a2[1], "%s", a2[0] + i);

  T (a2[1], "%s", &a2[0][0]);
  T (a2[1], "%s", &a2[0][1]);
  T (a2[1], "%s", &a2[0][2]);
  T (a2[1], "%s", &a2[0][i]);

  T (a2[1] + 0, "%s", a2[0]);
  T (a2[1] + 1, "%s", a2[0]);
  T (a2[1] + 2, "%s", a2[0]);
  T (a2[1] + i, "%s", a2[0]);

  T (&a2[1][0], "%s", a2[0]);
  T (&a2[1][1], "%s", a2[0]);
  T (&a2[1][2], "%s", a2[0]);
  T (&a2[1][i], "%s", a2[0]);   /* { dg-bogus "overlap" "" { xfail *-*-* } } */


  T (a2[1], "%s", a2[1]);       /* { dg-warning "overlaps" } */
  T (a2[1], "%s", a2[1] + 0);   /* { dg-warning "overlaps" } */
  T (a2[1], "%s", a2[1] + 1);   /* { dg-warning "may overlap" } */
  T (a2[1], "%s", a2[1] + 2);   /* { dg-warning "may overlap" } */
  T (a2[1], "%s", a2[1] + i);   /* { dg-warning "may overlap" } */

  T (a2[1], "%s", &a2[1][0]);   /* { dg-warning "overlaps" } */
  T (a2[1], "%s", &a2[1][1]);   /* { dg-warning "may overlap" } */
  T (a2[1], "%s", &a2[1][2]);   /* { dg-warning "may overlap" } */
  T (a2[1], "%s", &a2[1][i]);   /* { dg-warning "may overlap" "" { xfail *-*-* } } */

  T (a2[1] + 0, "%s", a2[1]);   /* { dg-warning "overlaps" } */
  T (a2[1] + 1, "%s", a2[1]);   /* { dg-warning "may overlap" } */
  T (a2[1] + 2, "%s", a2[1]);   /* { dg-warning "may overlap" } */
  T (a2[1] + i, "%s", a2[1]);   /* { dg-warning "may overlap" } */

  T (&a2[1][0], "%s", a2[1]);   /* { dg-warning "overlaps" } */
  T (&a2[1][1], "%s", a2[1]);   /* { dg-warning "may overlap" } */
  T (&a2[1][2], "%s", a2[1]);   /* { dg-warning "may overlap" } */
  T (&a2[1][i], "%s", a2[1]);   /* { dg-warning "may overlap" "" { xfail *-*-* } } */
}

struct S {
  char a[4];
  char b[4];
};

struct S2 {
  struct S s_1;
  struct S s_2;
  struct S sa3[3];
};

struct S3 {
  struct S2 s2_1;
  struct S2 s2_2;

  struct {
    struct {
      struct {
	struct S sa_3[3];
      } a_1[3];
    } a_2[3][3];
  } a_3[3][3][3];

  char fa[];
};

void test_struct_member_array (struct S3 *s3, int i)
{
  char *d = s3->s2_1.s_1.a;

  T (d, "%s", d);       /* { dg-warning "overlaps" } */
  T (d, "%s", d + 0);   /* { dg-warning "overlaps" } */
  T (d, "%s", d + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", d + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", d + i);   /* { dg-warning "may overlap" } */

  T (d, "%s", &d[0]);   /* { dg-warning "overlaps" } */
  T (d, "%s", &d[1]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &d[2]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &d[i]);   /* { dg-warning "may overlap" } */

  T (d + 0, "%s", d);   /* { dg-warning "overlaps" } */
  T (d + 1, "%s", d);   /* { dg-warning "may overlap" } */
  T (d + 2, "%s", d);   /* { dg-warning "may overlap" } */
  T (d + i, "%s", d);   /* { dg-warning "may overlap" } */

  T (&d[0], "%s", d);   /* { dg-warning "overlaps" } */
  T (&d[1], "%s", d);   /* { dg-warning "may overlap" } */
  T (&d[2], "%s", d);   /* { dg-warning "may overlap" } */
  T (&d[i], "%s", d);   /* { dg-warning "may overlap" } */

  const char *s = d;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  s = s3->s2_1.s_1.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_1.s_2.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_1.s_2.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  d = s3->s2_1.s_1.b;

  s = s3->s2_1.s_2.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_1.s_2.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_1.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_1.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_2.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_2.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  d = s3->s2_2.s_1.a;

  s = s3->s2_1.s_1.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_1.s_1.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_1.a;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  s = s3->s2_2.s_2.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_2.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  d = s3->s2_2.s_1.b;

  s = s3->s2_1.s_1.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_1.s_1.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_1.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_1.b;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  s = s3->s2_2.s_2.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = s3->s2_2.s_2.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);
}

void test_struct_member_array_array (struct S3 *s3, int i)
{
  char *d = s3->s2_1.sa3[0].a;
  char *s = s3->s2_1.sa3[0].a;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 0);   /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  T (d, "%s", &s[0]);   /* { dg-warning "overlaps" } */
  T (d, "%s", &s[1]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[2]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[i]);   /* { dg-warning "may overlap" } */

  T (d + 0, "%s", s);   /* { dg-warning "overlaps" } */
  T (d + 1, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + 2, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + i, "%s", s);   /* { dg-warning "may overlap" } */

  T (&d[0], "%s", s);   /* { dg-warning "overlaps" } */
  T (&d[1], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[2], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[i], "%s", s);   /* { dg-warning "may overlap" } */

  s = s3->s2_1.sa3[0].b;

  T (d, "%s", s);
  T (d, "%s", s + 0);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  T (d, "%s", &s[0]);
  T (d, "%s", &s[1]);
  T (d, "%s", &s[2]);
  T (d, "%s", &s[i]);

  T (d + 0, "%s", s);
  T (d + 1, "%s", s);
  T (d + 2, "%s", s);
  T (d + i, "%s", s);

  T (&d[0], "%s", s);
  T (&d[1], "%s", s);
  T (&d[2], "%s", s);
  T (&d[i], "%s", s);
}

void test_struct_member_nested_array (struct S3 *s3, int i)
{
#define PFX(sfx) s3->a_3[i3_0][i3_1][i3_2]	\
    .a_2[i2_0][i2_1]				\
    .a_1[i1_0].sfx

#define TT(d, s)				\
  T (PFX (d), "%s", PFX (s));			\
  T (PFX (d), "%s", &PFX (s)[0]);		\
  T (PFX (d), "%s", &PFX (s)[1]);		\
  T (PFX (d), "%s", &PFX (s)[2]);		\
  T (PFX (d), "%s", &PFX (s)[i])		\

#define T1(i0)	do						\
    {								\
      enum {i1_0 = i0 };					\
								\
      TT (sa_3[0].a, sa_3[0].b); TT (sa_3[0].b, sa_3[0].a);	\
      								\
      TT (sa_3[0].a, sa_3[1].a); TT (sa_3[0].a, sa_3[1].b);	\
      TT (sa_3[0].b, sa_3[1].a); TT (sa_3[0].b, sa_3[1].b);	\
    								\
      TT (sa_3[0].a, sa_3[2].a); TT (sa_3[0].a, sa_3[2].b);	\
      TT (sa_3[0].b, sa_3[2].a); TT (sa_3[0].b, sa_3[2].b);	\
								\
      TT (sa_3[1].a, sa_3[0].a); TT (sa_3[1].a, sa_3[0].b);	\
      TT (sa_3[1].b, sa_3[0].a); TT (sa_3[1].b, sa_3[0].b);	\
								\
      TT (sa_3[1].a, sa_3[1].b); TT (sa_3[1].b, sa_3[1].a);	\
								\
      TT (sa_3[1].a, sa_3[2].a); TT (sa_3[1].a, sa_3[2].b);	\
      TT (sa_3[1].b, sa_3[2].a); TT (sa_3[1].b, sa_3[2].b);	\
    } while (0)

#define T2(i0, i1) do				\
    {						\
      enum { i2_0 = i0, i2_1 = i1 };		\
      T1 (0); T1 (1); T1 (2);			\
    } while (0)

#define T3(i0, i1, i2) do			\
    {						\
      enum { i3_0 = i0, i3_1 = i1, i3_2 = i2 };	\
      T2 (0, 0); T2 (0, 1); T2 (0, 2);		\
      T2 (1, 0); T2 (1, 1); T2 (1, 2);		\
      T2 (2, 0); T2 (2, 1); T2 (2, 2);		\
    } while (0)

#if 0
  /* These tests take forever and a day to compile.  Enable them only
     during the development of this feature but leave them otherwise
     disabled to avoid slowing everything down for others.  */
  T3 (0, 0, 0); T3 (0, 0, 1); T3 (0, 0, 2);
  T3 (0, 1, 0); T3 (0, 1, 1); T3 (0, 1, 2);
  T3 (0, 2, 0); T3 (0, 2, 1); T3 (0, 2, 2);

  T3 (1, 0, 0); T3 (1, 0, 1); T3 (1, 0, 2);
  T3 (1, 1, 0); T3 (1, 1, 1); T3 (1, 1, 2);
  T3 (1, 2, 0); T3 (1, 2, 1); T3 (1, 2, 2);

  T3 (2, 0, 0); T3 (2, 0, 1); T3 (2, 0, 2);
  T3 (2, 1, 0); T3 (2, 1, 1); T3 (2, 1, 2);
  T3 (2, 2, 0); T3 (2, 2, 1); T3 (2, 2, 2);
#endif
}

void test_struct_member_flexarray (struct S3 *s3, int i, int j)
{
  char *d = s3->fa;
  char *s = s3->fa;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 0);   /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  T (d, "%s", &s[0]);   /* { dg-warning "overlaps" } */
  T (d, "%s", &s[1]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[2]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[i]);   /* { dg-warning "may overlap" } */

  T (d + 0, "%s", s);   /* { dg-warning "overlaps" } */
  T (d + 1, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + 2, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + i, "%s", s);   /* { dg-warning "may overlap" } */

  T (&d[0], "%s", s);   /* { dg-warning "overlaps" } */
  T (&d[1], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[2], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[i], "%s", s);   /* { dg-warning "may overlap" } */

  d = s3->fa + i;
  s = s3->fa + j;

  T (d, "%s", s);       /* { dg-warning "may overlap" } */
  T (d, "%s", s + 0);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  T (d, "%s", &s[0]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[1]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[2]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &s[i]);   /* { dg-warning "may overlap" } */

  T (d + 0, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + 1, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + 2, "%s", s);   /* { dg-warning "may overlap" } */
  T (d + j, "%s", s);   /* { dg-warning "may overlap" } */

  T (&d[0], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[1], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[2], "%s", s);   /* { dg-warning "may overlap" } */
  T (&d[j], "%s", s);   /* { dg-warning "may overlap" } */
}

union U
{
  struct S2 s2_1;
  struct S2 s2_2;
};

void test_union_member_array (union U *un, int i)
{
  char *d = un->s2_1.s_1.a;

  T (d, "%s", d);       /* { dg-warning "overlaps" } */
  T (d, "%s", d + 0);   /* { dg-warning "overlaps" } */
  T (d, "%s", d + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", d + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", d + i);   /* { dg-warning "may overlap" } */

  T (d, "%s", &d[0]);   /* { dg-warning "overlaps" } */
  T (d, "%s", &d[1]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &d[2]);   /* { dg-warning "may overlap" } */
  T (d, "%s", &d[i]);   /* { dg-warning "may overlap" } */

  T (d + 0, "%s", d);   /* { dg-warning "overlaps" } */
  T (d + 1, "%s", d);   /* { dg-warning "may overlap" } */
  T (d + 2, "%s", d);   /* { dg-warning "may overlap" } */
  T (d + i, "%s", d);   /* { dg-warning "may overlap" } */

  T (&d[0], "%s", d);   /* { dg-warning "overlaps" } */
  T (&d[1], "%s", d);   /* { dg-warning "may overlap" } */
  T (&d[2], "%s", d);   /* { dg-warning "may overlap" } */
  T (&d[i], "%s", d);   /* { dg-warning "may overlap" } */

  const char *s = d;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  s = un->s2_1.s_1.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = un->s2_1.s_2.a;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = un->s2_1.s_2.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);

  s = un->s2_2.s_1.a;

  T (d, "%s", s);       /* { dg-warning "overlaps" } */
  T (d, "%s", s + 1);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + 2);   /* { dg-warning "may overlap" } */
  T (d, "%s", s + i);   /* { dg-warning "may overlap" } */

  s = un->s2_2.s_1.b;

  T (d, "%s", s);
  T (d, "%s", s + 1);
  T (d, "%s", s + 2);
  T (d, "%s", s + i);
}

void test_multiple_overlap (int i)
{
  {
    char a[3] = "";           /* { dg-message "declared here" } */

    /* Both a3 and a4 definitely overlap the output even if the are
       empty because of the terminating nul.  */
    char *d = a;
    char *a3 = a + 0;
    char *a4 = a - 0;

    T (d, "%s%s", a3, a4);    /* { dg-warning "arguments 3, 4 overlap destination object .a." }*/
  }

  {
    char a[4];

    /* There is no overlap here because the length of a3 is at most 1
       and a4 is necessarily the empty string.  */
    char *d = a;
    char *a3 = a + 2;
    char *a4 = a + 3;

    T (d, "%s%s", a3, a4);
  }

  {
    char a[5];                /* { dg-message "declared here" } */

    /* a3 and a4 may overlap the output.  They will only not overlap
       it when a3 is empty, and a4 is at most 1 character long.  */
    char *d = a;
    char *a3 = a + 2;
    char *a4 = a + 3;

    T (d, "%s%s", a3, a4);    /* { dg-warning "arguments 3, 4 may overlap destination object .a." }*/
  }

  {
    char a[5];                /* { dg-message "declared here" } */

    /* a3 certaibly overlaps the output, but a4 may or may not depending
       in the value of i.  */
    char *d = a;
    char *a3 = a + 0;
    char *a4 = a + i;

    T (d, "%s%s", a3, a4);    /* { dg-warning "arguments 3 and maybe 4 overlap destination object .a." }*/
  }
}

void test_overlap_with_precision (char *d, int i, int j)
{
  /* None of the following is diagnosed because no copying takes place
     between the %s argument and the destination.  */
  T (d, "%.0s", d + 0);
  T (d, "%.0s", d + 1);
  T (d, "%.0s", d + 2);
  T (d, "%.0s", d + i);

  T (d, "%.1s", d + 0);       /* { dg-warning "overlaps" } */
  /* Unlike the %.0s case the following deserves a warning because
     when d[1] isn't nul, it will be overwritten by the terminating
     nul added by sprintf.  */
  T (d, "%.1s", d + 1);       /* { dg-warning "may overlap" } */
  T (d, "%.1s", d + 2);
  T (d, "%.1s", d + i);       /* { dg-warning "may overlap" } */

  T (d + 1, "%.0s", d);
  T (d + 2, "%.0s", d);

  T (d + 1, "%.1s", d);       /* { dg-warning "may overlap" } */
  T (d + 2, "%.1s", d);

  T (d + 2, "%.1s", d + 1);   /* { dg-warning "may overlap" } */
  T (d + 2, "%.1s", d + i);   /* { dg-warning "may overlap" } */

  /* The following should be "overlaps" but tracking that the offset
     is the same variable doesn't seem worth the effort.  */
  T (d + i, "%.1s", d + i);   /* { dg-warning "overlap" } */

  T (d + i, "%.1s", d + j);   /* { dg-warning "may overlap" } */

  /* Exercise overlap with precison in the range [0, 1].  */
  if (i < 0 || 1 < i)
    i = 0;

  T (d, "%.*s", i, d + 0);    /* { dg-warning "may overlap" } */
  T (d, "%.*s", i, d + 1);    /* { dg-warning "may overlap" } */
  T (d, "%.*s", i, d + 2);
  T (d, "%.*s", i, d + i);    /* { dg-warning "may overlap" "" { xfail *-*-* } } */
}