PR tree-optimization/88993 - GCC 9 -Wformat-overflow=2 should reflect real libc limits
authorMartin Sebor <msebor@gcc.gnu.org>
Fri, 22 Feb 2019 17:38:11 +0000 (10:38 -0700)
committerMartin Sebor <msebor@gcc.gnu.org>
Fri, 22 Feb 2019 17:38:11 +0000 (10:38 -0700)
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/88853
* 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::handle_gimple_call): Fix a typo in a comment.

gcc/testsuite/ChangeLog:

PR tree-optimization/88993
PR tree-optimization/88853
* 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.

From-SVN: r269125

gcc/gimple-ssa-sprintf.c
gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/tree-ssa/builtin-snprintf-warn-3.c
gcc/testsuite/gcc.dg/tree-ssa/builtin-sprintf-warn-18.c

index 4fe666ff0de94d898656d92f0fdfbf3445fc6c49..e40e0db427cf31745a10e74db54f03394efe27f7 100644 (file)
@@ -945,6 +945,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 functions.  */
+  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').  */
@@ -2841,6 +2864,8 @@ format_directive (const sprintf_dom_walker::call_info &info,
   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)))
     {
@@ -2849,7 +2874,9 @@ format_directive (const sprintf_dom_walker::call_info &info,
         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 (),
@@ -2857,14 +2884,18 @@ format_directive (const sprintf_dom_walker::call_info &info,
                          "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 (),
+                         "%<%.*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 (),
-                         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 may exceed minimum required size of "
+                         "4095",
                          dirlen,
                          target_to_host (hostdir, sizeof hostdir, dir.beg),
                          fmtres.range.min, fmtres.range.max);
@@ -2887,24 +2918,48 @@ format_directive (const sprintf_dom_walker::call_info &info,
              && 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)
-       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
+      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 (),
-                         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,
+                         "%<%.*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);
     }
@@ -2944,37 +2999,65 @@ format_directive (const sprintf_dom_walker::call_info &info,
 
   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)
@@ -3501,14 +3584,14 @@ sprintf_dom_walker::compute_format_length (call_info &info,
 }
 
 /* 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 ();
@@ -3523,7 +3606,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
@@ -3844,7 +3927,7 @@ sprintf_dom_walker::handle_gimple_call (gimple_stmt_iterator *gsi)
 
     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;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-fprintf-warn-2.c
new file mode 100644 (file)
index 0000000..4422e4c
--- /dev/null
@@ -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 \[0-9\]+ 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." } */
+}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c b/gcc/testsuite/gcc.dg/tree-ssa/builtin-printf-warn-2.c
new file mode 100644 (file)
index 0000000..31d16bc
--- /dev/null
@@ -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 32c73d0edae86a6d251b2a7826a3a94518585820..3f2b7562cad5b93708b3b8889a5c63e7caf2ea70 100644 (file)
@@ -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 struct Arrays *ar)
   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" } */
-
-  /* Also verify that a %s directive with width greater than 4095
-     triggers a warning even if the argument is not longer than 4k.  */
+     does not trigger a warning.  (No known implementation has trouble
+     with this).  */
+  T (-1, "%s", ar->a4kp1);
+
+  /* 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 struct Arrays *ar)
   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 2203e5263f767173cd338ebddead32a161c3206c..6a18f1776a704519d7a2f2e75bc15f6069f2d03f 100644 (file)
@@ -118,9 +118,9 @@ void test_width_and_precision_out_of_range (char *d)
   /* 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); */