In the committed patch I forgot that the CONSTRUCTOR to STRING_CST
transformation introduced this summer only takes place for arrays
of char and not also those of wide characters.  That resulted in
sprintf incorrectly getting STRING_CSTs for individual elements
of constant wide character arrays like in the example below, and
issuing a warning when that element was not nul and was passed
to %s.

The test that was supposed top exercise this was incomplete (it
was testing the %s directive rather than %ls), and so the bug
sneaked in unnoticed.

The attached patch restricts the change committed in r266418 to
only do its thing for non-array variables of wide character types
or for plain chars.  It also moves the wide string test into a new
file and corrects the problems there.  Since the the CONSTRUCTOR
to STRING_CST transformation doesn't take place for wide arrays
the test fails a bunch of cases.  I have XFAILed those until
the transformation is implemented.

I have tested this fix on x86_64-linux with no regression.  Unless
there are concerns with this I'll commit it in the next day or so
to unblock the failing Glibc builds.

Martin

On 11/26/18 12:27 PM, Martin Sebor wrote:
On 11/26/18 10:27 AM, Joseph Myers wrote:
On Fri, 23 Nov 2018, Martin Sebor wrote:

I have now committed this patch as r266418.

This commit introduced spurious warnings that break the glibc testsuite
build.

bug-ungetwc2.c:62:17: error: '%ls' directive argument is not a nul-terminated string [-Werror=format-overflow=]
    62 |   fprintf (fp, "%ls", write_wchars);
       |                 ^~~   ~~~~~~~~~~~~
bug-ungetwc2.c:10:15: note: referenced argument declared here
    10 | const wchar_t write_wchars[] = {L'A', 0x00C4, L'B', L'\0'};
       |               ^~~~~~~~~~~~

Reduced test (compile with -Wall, should not produce any warnings but
does after that commit):

const __WCHAR_TYPE__ s[] = { L'A', L'\0' };
void f (void) { __builtin_printf ("%ls", s); }

Thanks for the test case.  I see the problem and I'm testing a simple
fix.

Martin

gcc/ChangeLog:

	* expr.c (string_constant): Handle top-level decls of all character
	types and subobjects of narrow character type.

gcc/testsuite/ChangeLog:

	* gcc.dg/warn-sprintf-no-nul-2.c: Move incomplete tests from here...
	* gcc.dg/warn-sprintf-no-nul-3.c: ...to here and complete them.

Index: gcc/expr.c
===================================================================
--- gcc/expr.c	(revision 266472)
+++ gcc/expr.c	(working copy)
@@ -11497,10 +11497,16 @@ string_constant (tree arg, tree *ptr_offset, tree
   if (decl)
     *decl = array;
 
-  if (TREE_CODE (init) == INTEGER_CST)
+  if (TREE_CODE (init) == INTEGER_CST
+      && (TREE_CODE (TREE_TYPE (array)) == INTEGER_TYPE
+	  || TYPE_MAIN_VARIANT (eltype) == char_type_node))
     {
       /* For a reference to (address of) a single constant character,
-	 store the native representation of the character in CHARBUF.   */
+	 store the native representation of the character in CHARBUF.
+	 If the reference is to an element of an array or a member
+	 of a struct, only consider narrow characters until ctors
+	 for wide character arrays are transformed to STRING_CSTs
+	 like those for narrow arrays.  */
       unsigned char charbuf[MAX_BITSIZE_MODE_ANY_MODE / BITS_PER_UNIT];
       int len = native_encode_expr (init, charbuf, sizeof charbuf, 0);
       if (len > 0)
Index: gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c
===================================================================
--- gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c	(revision 266472)
+++ gcc/testsuite/gcc.dg/warn-sprintf-no-nul-2.c	(working copy)
@@ -3,9 +3,6 @@
    { dg-do compile }
    { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
 
-typedef __SIZE_TYPE__ size_t;
-typedef __WCHAR_TYPE__ wchar_t;
-
 int sprintf (char*, const char*, ...);
 
 extern char* dest (void);
@@ -71,61 +68,3 @@ void test_sprintf_s (void)
   T (sprintf (D, "%s", &str3[3]));
   T (sprintf (D, "%s", &str3[4]));      /* { dg-warning "\\\[-Warray-bounds" } */
 }
-
-
-const char wnul = '\0';
-const char wnonul = 'a';
-const char wcs3[] = "123";
-
-const struct
-{
-  char a, b, s[3];
-} w1 = { '\0', 'b', "123" },
-  w2[2] = {
-  { '\0', 'c', "12" },
-  { 'd', '\0', "123" }
-  };
-
-void test_sprintf_ls (void)
-{
-  T (sprintf (D, "%s", &wnul));
-  T (sprintf (D, "%s", &wnonul));       /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%.1s", &wnonul));
-  T (sprintf (D, "%.2s", &wnonul));     /* { dg-warning "nul-terminated" } */
-
-  T (sprintf (D, "%s", &w1.a));
-  T (sprintf (D, "%s", &w1.b));         /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%.1s", &w1.b));
-  T (sprintf (D, "%.2s", &w1.b));       /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%s", w1.s));          /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%.3s", w1.s));
-  T (sprintf (D, "%.4s", w1.s));        /* { dg-warning "nul-terminated" } */
-
-  T (sprintf (D, "%.2s", w1.s + 1));
-  T (sprintf (D, "%.3s", w1.s + 1));    /* { dg-warning "nul-terminated" } */
-
-  T (sprintf (D, "%s", &w2[0].a));
-  T (sprintf (D, "%s", &w2[0].b));      /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%.1s", &w2[0].b));
-  T (sprintf (D, "%.2s", &w2[0].b));    /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%s", w2[0].s));
-  T (sprintf (D, "%.3s", w2[0].s));
-  T (sprintf (D, "%.4s", w2[0].s));
-
-  T (sprintf (D, "%.2s", w2[0].s + 1));
-  T (sprintf (D, "%.3s", w2[0].s + 1));
-
-  T (sprintf (D, "%s", &w2[1].a));      /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%.1s", &w2[1].a));
-  T (sprintf (D, "%.2s", &w2[1].a));    /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%s", &w2[1].b));
-  T (sprintf (D, "%s", w2[1].s));       /* { dg-warning "nul-terminated" } */
-  T (sprintf (D, "%.3s", w2[1].s));
-  T (sprintf (D, "%.4s", w2[1].s));     /* { dg-warning "nul-terminated" } */
-
-  T (sprintf (D, "%.2s", w2[1].s + 1));
-  T (sprintf (D, "%.3s", w2[1].s + 1)); /* { dg-warning "nul-terminated" } */
-
-  T (sprintf (D, "%s", &wcs3[3]));
-  T (sprintf (D, "%s", &wcs3[4]));      /* { dg-warning "\\\[-Warray-bounds" } */
-}
Index: gcc/testsuite/gcc.dg/warn-sprintf-no-nul-3.c
===================================================================
--- gcc/testsuite/gcc.dg/warn-sprintf-no-nul-3.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/warn-sprintf-no-nul-3.c	(working copy)
@@ -0,0 +1,71 @@
+/* PR tree-optimization/87756 - missing unterminated argument warning
+   using address of a constant character
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+typedef __WCHAR_TYPE__ wchar_t;
+
+int sprintf (char*, const char*, ...);
+
+extern char* dest (void);
+extern void sink (int, ...);
+
+#define D dest ()
+#define T(expr)   sink (0, (expr))
+
+const wchar_t wnul = L'\0';
+const wchar_t wnonul = L'a';
+const wchar_t wcs3[] = L"123";
+
+const struct
+{
+  wchar_t a, b, s[3];
+} w1 = { L'\0', L'b', L"123" },
+  w2[2] = {
+  { L'\0', L'c', L"12" },
+  { L'd', L'\0', L"123" }
+  };
+
+void test_sprintf_ls (void)
+{
+  T (sprintf (D, "%ls", &wnul));
+  T (sprintf (D, "%ls", &wnonul));      /* { dg-warning "nul-terminated" } */
+  T (sprintf (D, "%.1ls", &wnonul));
+  T (sprintf (D, "%.2ls", &wnonul));    /* { dg-warning "nul-terminated" } */
+
+  T (sprintf (D, "%ls", &w1.a));
+  T (sprintf (D, "%ls", &w1.b));        /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+  T (sprintf (D, "%.1ls", &w1.b));
+  T (sprintf (D, "%.2ls", &w1.b));      /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+  T (sprintf (D, "%ls", w1.s));         /* { dg-warning "nul-terminated" } */
+  T (sprintf (D, "%.3ls", w1.s));
+  T (sprintf (D, "%.4ls", w1.s));       /* { dg-warning "nul-terminated" } */
+
+  T (sprintf (D, "%.2ls", w1.s + 1));
+  T (sprintf (D, "%.3ls", w1.s + 1));   /* { dg-warning "nul-terminated" } */
+
+  T (sprintf (D, "%ls", &w2[0].a));
+  T (sprintf (D, "%ls", &w2[0].b));     /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+  T (sprintf (D, "%.1ls", &w2[0].b));
+  T (sprintf (D, "%.2ls", &w2[0].b));   /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+  T (sprintf (D, "%ls", w2[0].s));
+  T (sprintf (D, "%.3ls", w2[0].s));
+  T (sprintf (D, "%.4ls", w2[0].s));
+
+  T (sprintf (D, "%.2ls", w2[0].s + 1));
+  T (sprintf (D, "%.3ls", w2[0].s + 1));
+
+  T (sprintf (D, "%ls", &w2[1].a));     /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+  T (sprintf (D, "%.1ls", &w2[1].a));
+  T (sprintf (D, "%.2ls", &w2[1].a));   /* { dg-warning "nul-terminated" "pr?????" { xfail *-*-* } } */
+  T (sprintf (D, "%ls", &w2[1].b));
+  T (sprintf (D, "%ls", w2[1].s));      /* { dg-warning "nul-terminated" } */
+  T (sprintf (D, "%.3ls", w2[1].s));
+  T (sprintf (D, "%.4ls", w2[1].s));    /* { dg-warning "nul-terminated" } */
+
+  T (sprintf (D, "%.2ls", w2[1].s + 1));
+  T (sprintf (D, "%.3ls", w2[1].s + 1));/* { dg-warning "nul-terminated" } */
+
+  T (sprintf (D, "%ls", &wcs3[3]));
+  T (sprintf (D, "%ls", &wcs3[4]));     /* { dg-warning "\\\[-Warray-bounds" } */
+}

Reply via email to