PR tree-optimization/88993 - GCC 9 -Wformat-overflow=2 should reflect real libc limits
PR tree-optimization/88835 - overly aggressive -Werror=format-overflow for printf

gcc/ChangeLog:

	PR tree-optimization/88993
	PR tree-optimization/88835
	* gimple-ssa-sprintf.c (sprintf_dom_walker::call_info::is_file_func):
	New helper.
	(sprintf_dom_walker::call_info::is_string_func): New helper.
	(format_directive): Only issue "may exceed" 4095/INT_MAX warnings
	for formatted string functions.
	(sprintf_dom_walker::compute_format_length): Return HWI_MAX rather than -1.
	(sprintf_dom_walker::handle_gimple_call): Fix a typo in a comment.

gcc/testsuite/ChangeLog:

	PR tree-optimization/88993
	PR tree-optimization/88835
	* gcc.dg/tree-ssa/builtin-fprintf-warn-2.c: New test.
	* gcc.dg/tree-ssa/builtin-printf-warn-2.c: New test.
	* gcc.dg/tree-ssa/builtin-snprintf-warn-3.c: Adjust.
	* gcc.dg/tree-ssa/builtin-sprintf-warn-18.c: Same.

Index: gcc/gimple-ssa-sprintf.c
===================================================================
--- gcc/gimple-ssa-sprintf.c	(revision 269022)
+++ gcc/gimple-ssa-sprintf.c	(working copy)
@@ -943,6 +943,29 @@ struct sprintf_dom_walker::call_info
   {
     return bounded ? OPT_Wformat_truncation_ : OPT_Wformat_overflow_;
   }
+
+  /* Return true for calls to file formatted functions.  */
+  bool is_file_func () const
+  {
+    return (fncode == BUILT_IN_FPRINTF
+	    || fncode == BUILT_IN_FPRINTF_CHK
+	    || fncode == BUILT_IN_FPRINTF_UNLOCKED
+	    || fncode == BUILT_IN_VFPRINTF
+	    || fncode == BUILT_IN_VFPRINTF_CHK);
+  }
+
+  /* Return true for calls to string formatted fncodetions.  */
+  bool is_string_func () const
+  {
+    return (fncode == BUILT_IN_SPRINTF
+	    || fncode == BUILT_IN_SPRINTF_CHK
+	    || fncode == BUILT_IN_SNPRINTF
+	    || fncode == BUILT_IN_SNPRINTF_CHK
+	    || fncode == BUILT_IN_VSPRINTF
+	    || fncode == BUILT_IN_VSPRINTF_CHK
+	    || fncode == BUILT_IN_VSNPRINTF
+	    || fncode == BUILT_IN_VSNPRINTF_CHK);
+  }
 };
 
 /* Return the result of formatting a no-op directive (such as '%n').  */
@@ -2839,6 +2862,8 @@ format_directive (const sprintf_dom_walker::call_i
   if (!warned
       /* Only warn at level 2.  */
       && warn_level > 1
+      /* Only warn for string functions.  */
+      && info.is_string_func ()
       && (!minunder4k
 	  || (!maxunder4k && fmtres.range.max < HOST_WIDE_INT_MAX)))
     {
@@ -2847,7 +2872,9 @@ format_directive (const sprintf_dom_walker::call_i
 	 of C11.  Warn on this only at level 2 but remember this and
 	 prevent folding the return value when done.  This allows for
 	 the possibility of the actual libc call failing due to ENOMEM
-	 (like Glibc does under some conditions).  */
+	 (like Glibc does with very large precision or width).
+	 Issue the "may exceed" warning only for string functions and
+	 not for fprintf or printf.  */
 
       if (fmtres.range.min == fmtres.range.max)
 	warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
@@ -2855,17 +2882,21 @@ format_directive (const sprintf_dom_walker::call_i
 			  "minimum required size of 4095", dirlen,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  fmtres.range.min);
-      else
+      else if (!minunder4k)
 	warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
-			  minunder4k
-			  ? G_("%<%.*s%> directive output between %wu and %wu "
-			       "bytes may exceed minimum required size of "
-			       "4095")
-			  : G_("%<%.*s%> directive output between %wu and %wu "
-			       "bytes exceeds minimum required size of 4095"),
+			  "%<%.*s%> directive output between %wu and %wu "
+			  "bytes exceeds minimum required size of 4095",
 			  dirlen,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  fmtres.range.min, fmtres.range.max);
+      else if (!info.retval_used () && info.is_string_func ())
+	warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+			  "%<%.*s%> directive output between %wu and %wu "
+			  "bytes may exceed minimum required size of "
+			  "4095",
+			  dirlen,
+			  target_to_host (hostdir, sizeof hostdir, dir.beg),
+			  fmtres.range.min, fmtres.range.max);
     }
 
   /* Has the likely and maximum directive output exceeded INT_MAX?  */
@@ -2885,25 +2916,49 @@ format_directive (const sprintf_dom_walker::call_i
 	      && maxximax
 	      && fmtres.range.max < HOST_WIDE_INT_MAX)))
     {
-      /* The directive output causes the total length of output
-	 to exceed INT_MAX bytes.  */
-
-      if (fmtres.range.min == fmtres.range.max)
+      if (fmtres.range.min > target_int_max ())
+	{
+	  /* The directive output exceeds INT_MAX bytes.  */
+	  if (fmtres.range.min == fmtres.range.max)
+	    warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+			      "%<%.*s%> directive output of %wu bytes exceeds "
+			      "%<INT_MAX%>", dirlen,
+			      target_to_host (hostdir, sizeof hostdir, dir.beg),
+			      fmtres.range.min);
+	  else
+	    warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+			      "%<%.*s%> directive output between %wu and "
+			      "%wu bytes exceeds %<INT_MAX%>", dirlen,
+			      target_to_host (hostdir, sizeof hostdir, dir.beg),
+			      fmtres.range.min, fmtres.range.max);
+	}
+      else if (res->range.min > target_int_max ())
+	{
+	  /* The directive output is under INT_MAX but causes the result
+	     to exceed INT_MAX bytes.  */	
+	  if (fmtres.range.min == fmtres.range.max)
+	    warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+			      "%<%.*s%> directive output of %wu bytes causes "
+			      "result to exceed %<INT_MAX%>", dirlen,
+			      target_to_host (hostdir, sizeof hostdir, dir.beg),
+			      fmtres.range.min);
+	  else
+	    warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
+			      "%<%.*s%> directive output between %wu and "
+			      "%wu bytes causes result to exceed %<INT_MAX%>",
+			      dirlen,
+			      target_to_host (hostdir, sizeof hostdir, dir.beg),
+			      fmtres.range.min, fmtres.range.max);
+	}
+      else if ((!info.retval_used () || !info.bounded)
+	       && (info.is_string_func ()))
+	/* Warn for calls to string functions that either aren't bounded
+	   (sprintf) or whose return value isn't used.  */
 	warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
-			  "%<%.*s%> directive output of %wu bytes causes "
-			  "result to exceed %<INT_MAX%>", dirlen,
+			  "%<%.*s%> directive output between %wu and "
+			  "%wu bytes may cause result to exceed "
+			  "%<INT_MAX%>", dirlen,
 			  target_to_host (hostdir, sizeof hostdir, dir.beg),
-			  fmtres.range.min);
-      else
-	warned = fmtwarn (dirloc, argloc, NULL, info.warnopt (),
-			  fmtres.range.min > target_int_max ()
-			  ? G_("%<%.*s%> directive output between %wu and "
-			       "%wu bytes causes result to exceed "
-			       "%<INT_MAX%>")
-			  : G_("%<%.*s%> directive output between %wu and "
-			       "%wu bytes may cause result to exceed "
-			       "%<INT_MAX%>"), dirlen,
-			  target_to_host (hostdir, sizeof hostdir, dir.beg),
 			  fmtres.range.min, fmtres.range.max);
     }
 
@@ -2942,37 +2997,65 @@ format_directive (const sprintf_dom_walker::call_i
 
   res->warned |= warned;
 
-  if (!dir.beg[0] && res->warned && info.objsize < HOST_WIDE_INT_MAX)
+  if (!dir.beg[0] && res->warned)
     {
-      /* If a warning has been issued for buffer overflow or truncation
-	 (but not otherwise) help the user figure out how big a buffer
-	 they need.  */
-
       location_t callloc = gimple_location (info.callstmt);
 
       unsigned HOST_WIDE_INT min = res->range.min;
       unsigned HOST_WIDE_INT max = res->range.max;
 
-      if (min == max)
-	inform (callloc,
-		(min == 1
-		 ? G_("%qE output %wu byte into a destination of size %wu")
-		 : G_("%qE output %wu bytes into a destination of size %wu")),
-		info.func, min, info.objsize);
-      else if (max < HOST_WIDE_INT_MAX)
-	inform (callloc,
-		"%qE output between %wu and %wu bytes into "
-		"a destination of size %wu",
-		info.func, min, max, info.objsize);
-      else if (min < res->range.likely && res->range.likely < max)
-	inform (callloc,
-		"%qE output %wu or more bytes (assuming %wu) into "
-		"a destination of size %wu",
-		info.func, min, res->range.likely, info.objsize);
-      else
-	inform (callloc,
-		"%qE output %wu or more bytes into a destination of size %wu",
-		info.func, min, info.objsize);
+      if (info.objsize < HOST_WIDE_INT_MAX)
+	{
+	  /* If a warning has been issued for buffer overflow or truncation
+	     help the user figure out how big a buffer they need.  */
+
+	  if (min == max)
+	    inform (callloc,
+		    (min == 1
+		     ? G_("%qE output %wu byte into a destination of size %wu")
+		     : G_("%qE output %wu bytes into a destination of size "
+			  "%wu")),
+		    info.func, min, info.objsize);
+	  else if (max < HOST_WIDE_INT_MAX)
+	    inform (callloc,
+		    "%qE output between %wu and %wu bytes into "
+		    "a destination of size %wu",
+		    info.func, min, max, info.objsize);
+	  else if (min < res->range.likely && res->range.likely < max)
+	    inform (callloc,
+		    "%qE output %wu or more bytes (assuming %wu) into "
+		    "a destination of size %wu",
+		    info.func, min, res->range.likely, info.objsize);
+	  else
+	    inform (callloc,
+		    "%qE output %wu or more bytes into a destination of size "
+		    "%wu",
+		    info.func, min, info.objsize);
+	}
+      else if (!info.is_string_func ())
+	{
+	  /* If the warning is for a file function function like fprintf
+	     of printf with no destination size just print the computed
+	     result.  */
+	  if (min == max)
+	    inform (callloc,
+		    (min == 1
+		     ? G_("%qE output %wu byte")
+		     : G_("%qE output %wu bytes")),
+		    info.func, min);
+	  else if (max < HOST_WIDE_INT_MAX)
+	    inform (callloc,
+		    "%qE output between %wu and %wu bytes",
+		    info.func, min, max);
+	  else if (min < res->range.likely && res->range.likely < max)
+	    inform (callloc,
+		    "%qE output %wu or more bytes (assuming %wu)",
+		    info.func, min, res->range.likely);
+	  else
+	    inform (callloc,
+		    "%qE output %wu or more bytes",
+		    info.func, min);
+	}
     }
 
   if (dump_file && *dir.beg)
@@ -3499,14 +3582,14 @@ sprintf_dom_walker::compute_format_length (call_in
 }
 
 /* Return the size of the object referenced by the expression DEST if
-   available, or -1 otherwise.  */
+   available, or the maximum possible size otherwise.  */
 
 static unsigned HOST_WIDE_INT
 get_destination_size (tree dest)
 {
-  /* When there is no destination return -1.  */
+  /* When there is no destination return the maximum.  */
   if (!dest)
-    return HOST_WIDE_INT_M1U;
+    return HOST_WIDE_INT_MAX;
 
   /* Initialize object size info before trying to compute it.  */
   init_object_sizes ();
@@ -3521,7 +3604,7 @@ get_destination_size (tree dest)
   if (compute_builtin_object_size (dest, ost, &size))
     return size;
 
-  return HOST_WIDE_INT_M1U;
+  return HOST_WIDE_INT_MAX;
 }
 
 /* Return true if the call described by INFO with result RES safe to
@@ -3842,7 +3925,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stm
 
     case BUILT_IN_PRINTF_CHK:
       // Signature:
-      //   __builtin_printf_chk (it, format, ...)
+      //   __builtin_printf_chk (ost, format, ...)
       idx_format = 1;
       info.argidx = 2;
       idx_dstptr = -1;
Index: gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c
===================================================================
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c	(working copy)
@@ -0,0 +1,324 @@
+/* PR middle-end/88993 - GCC 9 -Wformat-overflow=2 should reflect real
+   libc limits
+   Verify that -Wformat-overflow=2 "may exceed" warnings are not issued
+   for printf family of functions.
+   { dg-do compile }
+   { dg-options "-O -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" }
+   { dg-require-effective-target int32plus } */
+
+#define INT_MAX __INT_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+#if !__cplusplus
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+typedef struct FILE FILE;
+
+FILE *fp;
+
+#define T(...) __builtin_fprintf (__VA_ARGS__)
+
+/* Exercise the "%c" directive with constant arguments.  */
+
+void test_fprintf_c_const (int width)
+{
+  /* Verify that a warning is only issued when the output is definitely
+     exceeded but not when exceeding it is possible but not inevitable.
+     Also verify that a note is printed with amount of output produced
+     by the call (the result - 1).  */
+  T (fp, "%2147483647c", '1');
+  T (fp, "X%2147483647c", '2');   /* { dg-warning ".%2147483647c. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  /* { dg-message ".__builtin_fprintf. output 2147483649 bytes" "note" { target *-*-* } .-1 } */
+  T (fp, "%2147483647cY", '3');   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "%2147483648c", '1');    /* { dg-warning ".%2147483648c. directive output of 2147483648 bytes exceeds .INT_MAX." } */
+  T (fp, "X%2147483649c", '2');   /* { dg-warning ".%2147483649c. directive output of 2147483649 bytes exceeds .INT_MAX." } */
+  T (fp, "%2147483650cY", '3');   /* { dg-warning ".%2147483650c. directive output of 2147483650 bytes exceeds .INT_MAX." } */
+
+  T (fp, "%*c", INT_MAX, '1');
+  T (fp, "X%*c", INT_MAX, '1');   /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%*cY", INT_MAX, '1');   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "X%*c", INT_MAX - 1, '1');
+  T (fp, "%*cY", INT_MAX - 1, '1');
+
+  T (fp, "%*cY", INT_MAX, '1');   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "X%*c", INT_MAX, '1');   /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+}
+
+/* Exercise the "%c" directive with arguments in a known range.  */
+
+void test_fprintf_c_range (int width)
+{
+  /* Verify that an known upper bound doesn't trigger a warning.  */
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*c", width, '1');
+  T (fp, "X%*c", width, '1');
+  T (fp, "%*cY", width, '1');
+
+  T (fp, "%*c", width, '1');
+  T (fp, "X%*c", width, '1');
+  T (fp, "%*cY", width, '1');
+
+  T (fp, "%*c%*c", width, '1', width, '2');
+  T (fp, "X%*cY%*cZ", width, '1', width, '2');
+
+  /* Verify that a lower bound in excess of 4095 doesn't trigger
+     a warning.  */
+  if (width < 4096)
+    width = 4096;
+
+  T (fp, "%*c", width, '1');
+  T (fp, "X%*c", width, '1');
+  T (fp, "%*cY", width, '1');
+
+  /* Verify that a large lower bound triggers a warning when the total
+     result of the function definitely exceeds INT_MAX.  */
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*c", width, '1');
+  T (fp, "X%*c", width, '2');
+  T (fp, "%*cY", width, '3');
+  T (fp, "X%*cY", width, '4');    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  /* { dg-message ".__builtin_fprintf. output 2147483649 bytes" "note" { target *-*-* } .-1 } */
+}
+
+
+/* Exercise the "%s" directive.  */
+
+void test_fprintf_s_const (int width, const char *s)
+{
+  T (fp, "%2147483647s", s);
+  T (fp, "%2147483647s", "1");
+
+  T (fp, "%2147483647.2147483647s", s);
+  T (fp, "%2147483647.2147483647s", "12");
+
+  T (fp, "X%2147483647s", s);     /* { dg-warning ".%2147483647s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%2147483647sY", s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "X%2147483647.1s", s);   /* { dg-warning ".%2147483647\\\.1s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%2147483647.2sY", s);   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "X%1.2147483647s", s);
+  T (fp, "%2.2147483647sY", s);
+
+  T (fp, "X%1.2147483647s", "123");
+  T (fp, "%2.2147483647sY", "1234");
+
+  T (fp, "%2147483648s", s);      /* { dg-warning "%2147483648s. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T (fp, "X%2147483649s", s);     /* { dg-warning "%2147483649s. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T (fp, "%2147483650sY", s);     /* { dg-warning ".%2147483650s. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+
+  T (fp, "%*s", INT_MAX, s);
+  T (fp, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "X%*s", INT_MAX - 1, s);
+  T (fp, "%*sY", INT_MAX - 1, s);
+
+  T (fp, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+
+  T (fp, "%*s%*s", width, s, width, s);
+  T (fp, "X%*sY%*sZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+  T (fp, "X%*sY", width, s);      /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+/* Exercise the "%ls" directive.  */
+
+void test_fprintf_ls_const (int width, const wchar_t *s)
+{
+  T (fp, "%2147483647ls", s);
+  T (fp, "X%2147483647ls", s);    /* { dg-warning ".%2147483647ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%2147483647lsY", s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "%2147483648ls", s);     /* { dg-warning "%2147483648ls. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T (fp, "X%2147483649ls", s);    /* { dg-warning "%2147483649ls. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T (fp, "%2147483650lsY", s);    /* { dg-warning ".%2147483650ls. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+
+  T (fp, "%*ls", INT_MAX, s);
+  T (fp, "X%*ls", INT_MAX, s);    /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%*lsY", INT_MAX, s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "X%*ls", INT_MAX - 1, s);
+  T (fp, "%*lsY", INT_MAX - 1, s);
+
+  T (fp, "%*lsY", INT_MAX, s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "X%*ls", INT_MAX, s);     /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*ls", width, s);
+  T (fp, "X%*ls", width, s);
+  T (fp, "%*lsY", width, s);
+
+  T (fp, "%*ls", width, s);
+  T (fp, "X%*ls", width, s);
+  T (fp, "%*lsY", width, s);
+
+  T (fp, "%*ls%*ls", width, s, width, s);
+  T (fp, "X%*lsY%*lsZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T (fp, "%*ls", width, s);
+  T (fp, "X%*ls", width, s);
+  T (fp, "%*lsY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*ls", width, s);
+  T (fp, "X%*ls", width, s);
+  T (fp, "%*lsY", width, s);
+  T (fp, "X%*lsY", width, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+
+/* Also exercise fprintf_chk.  */
+
+#undef T
+#define T(...) __builtin___fprintf_chk (__VA_ARGS__)
+
+void test_fprintf_chk_s_const (int width)
+{
+  const char *s = "0123456789";
+
+  T (fp, 0, "%2147483647s", s);
+  T (fp, 0, "X%2147483647s", s);    /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (fp, 0, "%2147483647sY", s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, 0, "%2147483648s", s);      /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */
+  T (fp, 0, "X%2147483649s", s);     /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */
+  T (fp, 0, "%2147483650sY", s);     /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */
+
+  T (fp, 0, "%*s", INT_MAX, s);
+  T (fp, 0, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (fp, 0, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, 0, "X%*s", INT_MAX - 1, s);
+  T (fp, 0, "%*sY", INT_MAX - 1, s);
+
+  T (fp, 0, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T (fp, 0, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, 0, "%*s", width, s);
+  T (fp, 0, "X%*s", width, s);
+  T (fp, 0, "%*sY", width, s);
+
+  T (fp, 0, "%*s", width, s);
+  T (fp, 0, "X%*s", width, s);
+  T (fp, 0, "%*sY", width, s);
+
+  T (fp, 0, "%*s%*s", width, s, width, s);
+  T (fp, 0, "X%*sY%*sZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T (fp, 0, "%*s", width, s);
+  T (fp, 0, "X%*s", width, s);
+  T (fp, 0, "%*sY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, 0, "%*s", width, s);
+  T (fp, 0, "X%*s", width, s);
+  T (fp, 0, "%*sY", width, s);
+  T (fp, 0, "X%*sY", width, s);      /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+
+/* And finally exercise fprintf_unlocked.  */
+
+#undef T
+#define T(...) __builtin_fprintf_unlocked (__VA_ARGS__)
+
+void test_fprintf_unlocked_s_const (int width)
+{
+  const char *s = "0123456789";
+
+  T (fp, "%2147483647s", s);
+  T (fp, "X%2147483647s", s);     /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%2147483647sY", s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "%2147483648s", s);      /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */
+  T (fp, "X%2147483649s", s);     /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */
+  T (fp, "%2147483650sY", s);     /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */
+
+  T (fp, "%*s", INT_MAX, s);
+  T (fp, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (fp, "X%*s", INT_MAX - 1, s);
+  T (fp, "%*sY", INT_MAX - 1, s);
+
+  T (fp, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T (fp, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+
+  T (fp, "%*s%*s", width, s, width, s);
+  T (fp, "X%*sY%*sZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (fp, "%*s", width, s);
+  T (fp, "X%*s", width, s);
+  T (fp, "%*sY", width, s);
+  T (fp, "X%*sY", width, s);      /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
Index: gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c
===================================================================
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c	(working copy)
@@ -0,0 +1,293 @@
+/* PR middle-end/88993 - GCC 9 -Wformat-overflow=2 should reflect real
+   libc limits
+   Verify that -Wformat-overflow=2 "may exceed" warnings are not issued
+   for printf family of functions.
+   { dg-do compile }
+   { dg-options "-O -Wformat -Wformat-overflow=2 -ftrack-macro-expansion=0" }
+   { dg-require-effective-target int32plus } */
+
+
+#define INT_MAX __INT_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+#if !__cplusplus
+typedef __WCHAR_TYPE__ wchar_t;
+#endif
+
+#define T(...) __builtin_printf (__VA_ARGS__)
+
+/* Exercise the "%c" directive with constant arguments.  */
+
+void test_printf_c_const (int width)
+{
+  /* Verify that a warning is only issued when the output is definitely
+     exceeded but not when exceeding it is possible but not inevitable.  */
+  T ("%2147483647c", '1');
+  T ("X%2147483647c", '2');   /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T ("%2147483647cY", '3');   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("%2147483648c", '1');    /* { dg-warning ".%2147483648c. directive output of 2147483648 bytes exceeds .INT_MAX." } */
+  T ("X%2147483649c", '2');   /* { dg-warning ".%2147483649c. directive output of 2147483649 bytes exceeds .INT_MAX." } */
+  T ("%2147483650cY", '3');   /* { dg-warning ".%2147483650c. directive output of 2147483650 bytes exceeds .INT_MAX." } */
+
+  T ("%*c", INT_MAX, '1');
+  T ("X%*c", INT_MAX, '1');   /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T ("%*cY", INT_MAX, '1');   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("X%*c", INT_MAX - 1, '1');
+  T ("%*cY", INT_MAX - 1, '1');
+
+  T ("%*cY", INT_MAX, '1');   /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T ("X%*c", INT_MAX, '1');   /* { dg-warning ".%*c. directive output of \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*c", width, '1');
+  T ("X%*c", width, '1');
+  T ("%*cY", width, '1');
+
+  T ("%*c", width, '1');
+  T ("X%*c", width, '1');
+  T ("%*cY", width, '1');
+
+  T ("%*c%*c", width, '1', width, '2');
+  T ("X%*cY%*cZ", width, '1', width, '2');
+
+  if (width < 4096)
+    width = 4096;
+
+  T ("%*c", width, '1');
+  T ("X%*c", width, '1');
+  T ("%*cY", width, '1');
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*c", width, '1');
+  T ("X%*c", width, '2');
+  T ("%*cY", width, '3');
+  T ("X%*cY", width, '4');    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+
+/* Exercise the "%s" directive with constant arguments.  */
+
+void test_printf_s_const (int width, const char *s)
+{
+  T ("%2147483647s", s);
+  T ("X%2147483647s", s);     /* { dg-warning ".%2147483647s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T ("%2147483647sY", s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("%2147483648s", s);      /* { dg-warning "%2147483648s. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T ("X%2147483649s", s);     /* { dg-warning "%2147483649s. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T ("%2147483650sY", s);     /* { dg-warning ".%2147483650s. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+
+  T ("%*s", INT_MAX, s);
+  T ("X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T ("%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("X%*s", INT_MAX - 1, s);
+  T ("%*sY", INT_MAX - 1, s);
+
+  T ("%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T ("X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+
+  T ("%*s%*s", width, s, width, s);
+  T ("X%*sY%*sZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+  T ("X%*sY", width, s);      /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+/* Exercise the "%ls" directive with constant arguments.  */
+
+void test_printf_ls_const (int width, const wchar_t *s)
+{
+  T ("%2147483647ls", s);
+  T ("X%2147483647ls", s);    /* { dg-warning ".%2147483647ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T ("%2147483647lsY", s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("%2147483648ls", s);     /* { dg-warning "%2147483648ls. directive output between 2147483648 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T ("X%2147483649ls", s);    /* { dg-warning "%2147483649ls. directive output between 2147483649 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+  T ("%2147483650lsY", s);    /* { dg-warning ".%2147483650ls. directive output between 2147483650 and \[0-9\]+ bytes exceeds .INT_MAX." } */
+
+  T ("%*ls", INT_MAX, s);
+  T ("X%*ls", INT_MAX, s);    /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+  T ("%*lsY", INT_MAX, s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("X%*ls", INT_MAX - 1, s);
+  T ("%*lsY", INT_MAX - 1, s);
+
+  T ("%*lsY", INT_MAX, s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T ("X%*ls", INT_MAX, s);     /* { dg-warning ".%\\\*ls. directive output between 2147483647 and \[0-9\]+ bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*ls", width, s);
+  T ("X%*ls", width, s);
+  T ("%*lsY", width, s);
+
+  T ("%*ls", width, s);
+  T ("X%*ls", width, s);
+  T ("%*lsY", width, s);
+
+  T ("%*ls%*ls", width, s, width, s);
+  T ("X%*lsY%*lsZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T ("%*ls", width, s);
+  T ("X%*ls", width, s);
+  T ("%*lsY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*ls", width, s);
+  T ("X%*ls", width, s);
+  T ("%*lsY", width, s);
+  T ("X%*lsY", width, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+
+/* Also exercise printf_chk.  */
+
+#undef T
+#define T(...) __builtin___printf_chk (__VA_ARGS__)
+
+void test_printf_chk_s_const (int width)
+{
+  const char *s = "0123456789";
+
+  T (0, "%2147483647s", s);
+  T (0, "X%2147483647s", s);    /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (0, "%2147483647sY", s);    /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (0, "%2147483648s", s);      /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */
+  T (0, "X%2147483649s", s);     /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */
+  T (0, "%2147483650sY", s);     /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */
+
+  T (0, "%*s", INT_MAX, s);
+  T (0, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T (0, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T (0, "X%*s", INT_MAX - 1, s);
+  T (0, "%*sY", INT_MAX - 1, s);
+
+  T (0, "%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T (0, "X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (0, "%*s", width, s);
+  T (0, "X%*s", width, s);
+  T (0, "%*sY", width, s);
+
+  T (0, "%*s", width, s);
+  T (0, "X%*s", width, s);
+  T (0, "%*sY", width, s);
+
+  T (0, "%*s%*s", width, s, width, s);
+  T (0, "X%*sY%*sZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T (0, "%*s", width, s);
+  T (0, "X%*s", width, s);
+  T (0, "%*sY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T (0, "%*s", width, s);
+  T (0, "X%*s", width, s);
+  T (0, "%*sY", width, s);
+  T (0, "X%*sY", width, s);      /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
+
+
+/* And finally exercise printf_unlocked.  */
+
+#undef T
+#define T(...) __builtin_printf_unlocked (__VA_ARGS__)
+
+void test_printf_unlocked_s_const (int width)
+{
+  const char *s = "0123456789";
+
+  T ("%2147483647s", s);
+  T ("X%2147483647s", s);     /* { dg-warning ".%2147483647s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T ("%2147483647sY", s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("%2147483648s", s);      /* { dg-warning "%2147483648s. directive output of 2147483648 bytes exceeds .INT_MAX." } */
+  T ("X%2147483649s", s);     /* { dg-warning "%2147483649s. directive output of 2147483649 bytes exceeds .INT_MAX." } */
+  T ("%2147483650sY", s);     /* { dg-warning ".%2147483650s. directive output of 2147483650 bytes exceeds .INT_MAX." } */
+
+  T ("%*s", INT_MAX, s);
+  T ("X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+  T ("%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+
+  T ("X%*s", INT_MAX - 1, s);
+  T ("%*sY", INT_MAX - 1, s);
+
+  T ("%*sY", INT_MAX, s);     /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+  T ("X%*s", INT_MAX, s);     /* { dg-warning ".%\\\*s. directive output of 2147483647 bytes causes result to exceed .INT_MAX." } */
+
+  if (width > INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+
+  T ("%*s%*s", width, s, width, s);
+  T ("X%*sY%*sZ", width, s, width, s);
+
+  if (width < 4096)
+    width = 4096;
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+
+  if (width < INT_MAX - 1)
+    width = INT_MAX - 1;
+
+  T ("%*s", width, s);
+  T ("X%*s", width, s);
+  T ("%*sY", width, s);
+  T ("X%*sY", width, s);      /* { dg-warning ".Y. directive output of 1 bytes causes result to exceed .INT_MAX." } */
+}
Index: gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c
===================================================================
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c	(revision 269022)
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c	(working copy)
@@ -1,6 +1,6 @@
 /* PR middle-end/79448 - unhelpful -Wformat-truncation=2 warning
    { dg-do compile }
-   { dg-options "-O2 -Wformat -Wformat-truncation=2 -ftrack-macro-expansion=0" } 
+   { dg-options "-O2 -Wformat -Wformat-truncation=2 -ftrack-macro-expansion=0" }
    { dg-require-effective-target ptr32plus } */
 
 typedef __SIZE_TYPE__  size_t;
@@ -166,11 +166,17 @@ void test_string_checked (const char *s, const str
   T (-1, "%s%s", ar->a4k, ar->ax);
 
   /* Verify that an array that fits a string longer than 4095 bytes
-     does trigger a warning.  */
-  T (-1, "%-s", ar->a4kp1);   /* { dg-warning "directive output between 0 and 4096 bytes may exceed minimum required size of 4095" } */
+     does not trigger a warning.  (No known implementation has trouble
+     with this).  */
+  T (-1, "%s", ar->a4kp1);
 
-  /* Also verify that a %s directive with width greater than 4095
-     triggers a warning even if the argument is not longer than 4k.  */
+  /* Verify that a %s directive with width greater than 4095 does
+     trigger a warning even if the string argument is not longer
+     than 4k.  Glibc only has trouble with directives whose width
+     or precision exceeds 64K or so:
+     https://bugzilla.redhat.com/show_bug.cgi?id=441945 *
+     but hardcoding that as the limit and assuming no other
+     implementation has a lower one seems unwise.  */
   T (-1, "%*s", 4096, ar->a4k);   /* { dg-warning "directive output of 4096 bytes exceeds minimum required size of 4095" } */
 
   /* Verify that precision constrains the putput and suppresses the 4k
@@ -190,5 +196,7 @@ void test_string_checked (const char *s, const str
   T (-1, "%s %s %s", ar->a4k, ar->a4k, ar->a4k);
   T (-1, "%s %s %s", ar->ax, ar->ax, ar->ax);
 
-  T (-1, "%-s", ar->amax);   /* { dg-warning "directive output between 0 and \[0-9\]+ bytes may exceed minimum required size of 4095" } */
+  /* Similar to the above, verify there's no warning for an array
+     just because its size is INT_MAX bytes.  */
+  T (-1, "%s", ar->amax);
 }
Index: gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c
===================================================================
--- gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c	(revision 269022)
+++ gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c	(working copy)
@@ -118,9 +118,9 @@ void test_width_and_precision_out_of_range (char *
   /* The range here happens to be a property of the compiler, not
      one of the target.  */
   T ("%9223372036854775808i", 0);    /* { dg-warning "width out of range" "first" } */
-  /* { dg-warning "result to exceed .INT_MAX." "second" { target *-*-* } .-1 } */
+  /* { dg-warning "exceeds .INT_MAX." "second" { target *-*-* } .-1 } */
   T ("%.9223372036854775808i", 0);   /* { dg-warning "precision out of range" "first" } */
-  /* { dg-warning "causes result to exceed .INT_MAX." "second" { target *-*-* } .-1 } */
+  /* { dg-warning "exceeds .INT_MAX." "second" { target *-*-* } .-1 } */
 
   /* The following is diagnosed by -Wformat (disabled here).  */
   /* T ("%9223372036854775808$i", 0); */
