/* Test to verify that snprintf can determine the length of a dynamically
   constructed string argument and fold the result into a constant.
   { dg-do compile }
   { dg-options "-O2 -Wall -fdump-tree-optimized" } */

typedef __SIZE_TYPE__ size_t;

char* strcpy (char * restrict, const char * restrict);
int sprintf (char * restrict, const char *restrict, ...);
int snprintf (char * restrict, size_t, const char *restrict, ...);


#define CONCAT(x, y) x ## y
#define CAT(x, y) CONCAT (x, y)
#define FAILNAME(name, counter)						\
  CAT (CAT (CAT (call_ ## name ##_on_line_, __LINE__), _), counter)

#define FAIL(name, counter) do {			\
    extern void FAILNAME (name, counter) (void);	\
    FAILNAME (name, counter)();				\
  } while (0)

/* Macro to emit a call to funcation named
   call_in_true_branch_not_eliminated_on_line_NNN()
   for each call that's expected to be eliminated.  The dg-final
   scan-tree-dump-time directive at the bottom of the test verifies
   that no such call appears in output.  */
#define ELIM(expr)							\
  if (!(expr)) FAIL (in_true_branch_not_eliminated, __COUNTER__); else (void)0

#define ARGS(...) __VA_ARGS__

#define T(expect, init, fmt, ...)			\
  do {							\
    char a[] = init;					\
    ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__));	\
  } while (0)

/* Exercise a non-const local char array initialized by a string literal.  */
void test_assign_string (void)
{
  T (0, "", "%s", a);
  T (1, "1", "%s", a);
  T (4, "1234", "%s", a);
  T (5, "123", "s=%s", a);
  T (5, "1234", "s=%s", a + 1);
  T (2, "1234", "s=%s", a + 4);
  T (5, "12345", "s=%s", &a[2]);
  T (5, "123456", "s=%.*s", 3, &a[2]);
}

/* Exercise a non-const local char array initialized by an initializer
   list.  */
void test_assign_init_list (void)
{
  T (0, ARGS ({ 0 }), "%s", a);
  T (1, ARGS ({ 1, 0 }), "%s", a);
  T (3, ARGS ({ [3] = 0, [1] = 2, [0] = 1, [2] = 3 }), "%s", a);
  T (3, ARGS ({ [3] = 0, [1] = 2, [0] = 1, [2] = 3, [4] = 0 }), "%s", a);
  T (4, ARGS ({ 1, 2, 3, 4, 0 }), "%s", a);
  T (5, ARGS ({ 1, 2, 3, 0 }), "s=%s", a);
  T (5, ARGS ({ 1, 2, 3, 4, 0 }), "s=%s", a + 1);
  T (2, ARGS ({ 1, 2, 3, 4, 0 }), "s=%s", a + 4);
  T (5, ARGS ({ 1, 2, 3, 4, 5, 0 }), "s=%s", &a[2]);
  T (5, ARGS ({ 1, 2, 3, 4, 5, 6, 0 }), "s=%.*s", 3, &a[2]);
}

#if __x86_64__

/* Enabled only on x86_64 to work around PR 83543.  */

#undef T
#define T(expect, init, fmt, ...)			\
  do {							\
    struct { int n; char a[sizeof init]; }		\
    s = { sizeof init, init };				\
    ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__));	\
  } while (0)

/* Exercise a non-const local struct initialized by an initializer
   list.  */
void test_assign_aggregate (void)
{
  T (0, "", "%s", s.a);
  T (1, "1", "%s", s.a);
  T (4, "1234", "%s", s.a);
  T (5, "123", "s=%s", s.a);
  T (5, "1234", "s=%s", s.a + 1);
  T (2, "1234", "s=%s", s.a + 4);
  T (5, "12345", "s=%s", &s.a[2]);
  T (5, "123456", "s=%.*s", 3, &s.a[2]);
}

/* { dg-final { scan-tree-dump-times "Function test_assign_aggregate" 1 "optimized" { xfail { { ! x86_64-*-* } || { ilp32 } } } } } */

#endif   /* x86_64 */

#undef T
#define T(expect, init, fmt, ...)			\
  do {							\
    char a[sizeof init];				\
    strcpy (a, init);					\
    ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__));	\
  } while (0)

/* Exercise a local char array initialized by a call to strcpy.  */
void test_local_strcpy (void)
{
  T (0, "", "%s", a);
  T (1, "1", "%s", a);
  T (2, "12", "%s", a);
  T (3, "123", "%s", a);
  T (4, "1234", "%s", a);
  T (5, "123", "s=%s", a);
  T (5, "1234", "s=%s", a + 1);
  T (2, "1234", "s=%s", a + 4);
  T (5, "12345", "s=%s", &a[2]);
  T (5, "123456", "s=%.*s", 3, &a[2]);
}

#undef T
#define T(expect, init, fmt, ...)			\
  do {							\
    char a[n];						\
    strcpy (a, init);					\
    ELIM (expect == snprintf (0, 0, fmt, __VA_ARGS__));	\
  } while (0)

/* Exercise a VLA initialized by a call to strcpy.  */
void test_vla_strcpy (unsigned n)
{
  T (0, "", "%s", a);
  T (1, "1", "%s", a);
  T (2, "12", "%s", a);
  T (3, "123", "%s", a);
  T (4, "1234", "%s", a);
  T (5, "123", "s=%s", a);
  T (5, "1234", "s=%s", a + 1);
  T (2, "1234", "s=%s", a + 4);
  T (5, "12345", "s=%s", &a[2]);
  T (5, "123456", "s=%.*s", 3, &a[2]);
}

/* { dg-final { scan-tree-dump-times "printf" 0 "optimized" } }
   { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
   { dg-final { scan-tree-dump-times "not_eliminated" 0 "optimized" } } */
