PR tree-optimization/84468 - bogus -Wstringop-truncation despite assignment after...
authorMartin Sebor <msebor@redhat.com>
Wed, 7 Mar 2018 19:30:31 +0000 (19:30 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 7 Mar 2018 19:30:31 +0000 (12:30 -0700)
gcc/ChangeLog:

PR tree-optimization/84468
* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Consider successor
basic block when looking for nul assignment.

gcc/testsuite/ChangeLog:

PR tree-optimization/84468
* g++.dg/warn/Wstringop-truncation-2.C: New test.
* gcc.dg/Wstringop-truncation.c: New test.
* gcc.dg/Wstringop-truncation-2.c: New test.

From-SVN: r258339

gcc/ChangeLog
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/warn/Wstringop-truncation-1.C
gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-truncation-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wstringop-truncation.c [new file with mode: 0644]
gcc/tree-ssa-strlen.c

index 00b79a5..1e988ed 100644 (file)
@@ -1,3 +1,9 @@
+2018-03-07  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/84468
+       * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Consider successor
+       basic block when looking for nul assignment.
+
 2018-03-07  Eric Botcazou  <ebotcazou@adacore.com>
 
        PR target/84277
index 2bf1abb..8e273c6 100644 (file)
@@ -1,3 +1,10 @@
+2018-03-07  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/84468
+       * g++.dg/warn/Wstringop-truncation-2.C: New test.
+       * gcc.dg/Wstringop-truncation.c: New test.
+       * gcc.dg/Wstringop-truncation-2.c: New test.
+
 2018-03-07  Jakub Jelinek  <jakub@redhat.com>
 
        PR fortran/84565
index a502b78..8306601 100644 (file)
@@ -37,7 +37,7 @@ void good_nowarn_size_m1 ()
   sink (&str);
 }
 
-void good_nowarn_size_m1_var (const char* s)
+static void good_nowarn_size_m1_var (const char* s)
 {
   GoodString<3> str (s);        // { dg-bogus "\\\[-Wstringop-truncation]" }
   sink (&str);
@@ -112,7 +112,7 @@ private:
   char str[N + 1];
 };
 
-void bad3_warn_size_m1_var (const char *s)
+static void bad3_warn_size_m1_var (const char *s)
 {
   BadString3<3> str (s);
   sink (&str);
diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C b/gcc/testsuite/g++.dg/warn/Wstringop-truncation-2.C
new file mode 100644 (file)
index 0000000..ebbd443
--- /dev/null
@@ -0,0 +1,164 @@
+// PR tree-optimization/84468 - bogus -Wstringop-truncation despite
+// assignment after conditional strncpy
+// Compile with -g to verify the warning deals properly with debug
+// statements.
+// { dg-do compile }
+// { dg-options "-O2 -Wstringop-truncation -g" }
+
+extern "C" char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+char d[3];
+
+void g ();
+
+void fnowarn1 (const char *s)
+{
+  // Update dummy but never actually use it so it's eliminated
+  // but causes debugging statements to be emitted for each
+  // modification.
+  int dummy = 0;
+
+  try
+    {
+      g ();
+      strncpy (d, s, sizeof d);   // { dg-bogus "\\\[-Wstringop-truncation]" }
+      ++dummy;
+    }
+  catch (...)
+    {
+      ++dummy;
+      d[0] = 0;
+    }
+
+  ++dummy;
+  d[sizeof d - 1] = 0;
+}
+
+void fnowarn2 (const char *s)
+{
+  int dummy = 0;
+
+  try
+    {
+      g ();
+      strncpy (d, s, sizeof d);
+      ++dummy;
+    }
+  catch (...)
+    {
+      ++dummy;
+      return;
+    }
+
+  ++dummy;
+  d[sizeof d - 1] = 0;
+}
+
+void fnowarn3 (const char *s)
+{
+  int dummy = 0;
+
+  try
+    {
+      g ();
+      strncpy (d, s, sizeof d);
+      ++dummy;
+      try
+       {
+         ++dummy;
+         d[sizeof d - 1] = 0;
+         g ();
+       }
+      catch (...)
+       {
+         ++dummy;
+       }
+    }
+  catch (...)
+    {
+      ++dummy;
+      return;
+    }
+
+  ++dummy;
+  d[sizeof d - 1] = 0;
+}
+
+void fnowarn4 (const char *s)
+{
+  int dummy = 0;
+
+  try
+    {
+      g ();
+    }
+  catch (...)
+    {
+      strncpy (d, s, sizeof d);   // { dg-bogus "\\\[-Wstringop-truncation]" "bug 84468" { xfail *-*-*} }
+      ++dummy;
+    }
+
+  ++dummy;
+  d[sizeof d - 1] = 0;
+}
+
+void fwarn1 (const char *s)
+{
+  int dummy = 0;
+
+  try
+    {
+      ++dummy;
+      g ();
+      ++dummy;
+      strncpy (d, s, sizeof d);   // { dg-warning "\\\[-Wstringop-truncation]" }
+      ++dummy;
+    }
+  catch (...)
+    {
+      ++dummy;
+    }
+
+  ++dummy;
+}
+
+void fwarn2 (const char *s)
+{
+  int dummy = 0;
+
+  try
+    {
+      ++dummy;
+      strncpy (d, s, sizeof d);   // { dg-warning "\\\[-Wstringop-truncation]" }
+      ++dummy;
+      g ();
+      ++dummy;
+    }
+  catch (...)
+    {
+      ++dummy;
+    }
+
+  ++dummy;
+}
+
+void fwarn3 (const char *s)
+{
+  int dummy = 0;
+
+  try
+    {
+      ++dummy;
+      g ();
+      ++dummy;
+      strncpy (d, s, sizeof d);   // { dg-warning "\\\[-Wstringop-truncation]" }
+      ++dummy;
+    }
+  catch (...)
+    {
+      ++dummy;
+      d[0] = 0;
+    }
+
+  ++dummy;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-truncation-2.c b/gcc/testsuite/gcc.dg/Wstringop-truncation-2.c
new file mode 100644 (file)
index 0000000..707a511
--- /dev/null
@@ -0,0 +1,126 @@
+/* PR tree-optimization/84468 - bogus -Wstringop-truncation despite
+   assignment after conditional strncpy
+   { dg-do compile }
+   { dg-options "-O2 -Wstringop-truncation -g" } */
+
+extern char* strncpy (char*, const char*, __SIZE_TYPE__);
+
+char a[4];
+
+void f1 (char *s)
+{
+  int i = 0;
+
+  if (s[0] == '0')
+    {
+      i += 1;
+      strncpy (a, s, sizeof a);                   /* { dg-bogus "\\\[-Wstringop-truncation]" } */
+    }
+  else
+    i += 2;
+
+  a[sizeof a - 1] = 0;
+}
+
+void f2 (char *s)
+{
+  int i = 0;
+
+  if (s[0] == '0')
+    {
+      i += 1;
+      if (s[1] == '1')
+       {
+         i += 2;
+         strncpy (a, s, sizeof a);               /* { dg-bogus "\\\[-Wstringop-truncation]" } */
+       }
+      else
+       i += 3;
+    }
+  else
+    i += 4;
+
+  a[sizeof a - 1] = 0;
+}
+
+void f3 (char *s)
+{
+  int i = 0;
+
+  if (s[0] == '0')
+    {
+      i += 1;
+      if (s[1] == '1')
+       {
+         i += 2;
+         if (s[2] == '2')
+           strncpy (a, s, sizeof a);             /* { dg-bogus "\\\[-Wstringop-truncation]" } */
+         else
+           i += 3;
+       }
+      else
+       i += 4;
+    }
+  else
+    i += 5;
+
+  a[sizeof a - 1] = 0;
+}
+
+void f4 (char *s)
+{
+  int i = 0;
+
+  if (s[0] == '0')
+    {
+      i += 1;
+      if (s[1] == '1')
+       {
+         i += 2;
+         if (s[2] == '2')
+           {
+             i += 3;
+             if (s[3] == '3')
+               strncpy (a, s, sizeof a);         /* { dg-bogus "\\\[-Wstringop-truncation]" } */
+             else
+               i += 4;
+           }
+         else
+           i += 5;
+       }
+      else
+       i += 6;
+    }
+  else
+    i += 7;
+
+  a[sizeof a - 1] = 0;
+}
+
+void f4_warn (char *s)
+{
+  int i = 0;
+
+  if (s[0] == '0')
+    {
+      i += 1;
+      if (s[1] == '1')
+       {
+         i += 2;
+         if (s[2] == '2')
+           {
+             i += 3;
+             if (s[3] == '3')
+               strncpy (a, s, sizeof a);         /* { dg-warning "\\\[-Wstringop-truncation]" } */
+             else
+               i += 4;
+           }
+         else
+           i += 5;
+       }
+      else
+       i += 6;
+    }
+  else
+    i += 7;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-truncation.c b/gcc/testsuite/gcc.dg/Wstringop-truncation.c
new file mode 100644 (file)
index 0000000..b537296
--- /dev/null
@@ -0,0 +1,131 @@
+/* PR tree-optimization/84468 - Inconsistent -Wstringop-truncation warnings
+   with -O2
+   { dg-do compile }
+   { dg-options "-O2 -Wstringop-truncation -ftrack-macro-expansion=0 -g" }  */
+
+#define strncpy __builtin_strncpy
+
+struct A
+{
+  char a[4];
+};
+
+void no_pred_succ_lit (struct A *p)
+{
+  /* The following is folded early on, before the strncpy statement
+     has a basic block.  Verify that the case is handled gracefully
+     (i.e., there's no assumption that the statement does have
+     a basic block).  */
+  strncpy (p->a, "1234", sizeof p->a - 1);    /* { dg-warning "\\\[-Wstringop-truncation" } */
+}
+
+/* Verify a strncpy call in a basic block with no predecessor or
+   successor.  */
+void no_pred_succ (struct A *p, const struct A *q)
+{
+  strncpy (p->a, q->a, sizeof p->a - 1);      /* { dg-warning "\\\[-Wstringop-truncation" } */
+}
+
+
+/* Verify a strncpy call in a basic block with no successor.  */
+void no_succ (struct A *p, const struct A *q)
+{
+  if (q->a)
+    strncpy (p->a, q->a, sizeof p->a - 1);    /* { dg-warning "\\\[-Wstringop-truncation" } */
+}
+
+/* Verify a strncpy call in a basic block with nul assignment in
+   a successor block.  */
+void succ (struct A *p, const struct A *q)
+{
+  /* Verify that the assignment suppresses the warning for the conditional
+     strcnpy call.  The conditional should be folded to true since the
+     address of an array can never be null (see bug 84470).  */
+  if (q->a)
+    strncpy (p->a, q->a, sizeof p->a - 1);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+
+  p->a[sizeof p->a - 1] = 0;
+}
+
+
+void succ_2 (struct A *p, const struct A *q, int i)
+{
+  /* Same as above but with a conditional that cannot be eliminated.  */
+  if (i < 0)
+    strncpy (p->a, q->a, sizeof p->a - 1);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+
+  p->a[sizeof p->a - 1] = 0;
+}
+
+
+/* Verify a strncpy call in a basic block with nul assignment in
+   the next successor block.  */
+int next_succ (struct A *p, const struct A *q, int i, int j)
+{
+  /* Same as above but with a nested conditionals with else clauses.  */
+  if (i < 0)
+    {
+      if (j < 0)
+       strncpy (p->a, q->a, sizeof p->a - 1);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+    }
+  else
+    __builtin_strcpy (p->a, q->a);
+
+  p->a[sizeof p->a - 1] = 0;
+  return 0;
+}
+
+
+int next_succ_1 (struct A *p, const struct A *q, int i, int j)
+{
+  /* Same as above but with a nested conditionals with else clauses.  */
+  if (i < 0)
+    {
+      if (j < 0)
+       strncpy (p->a, q->a, sizeof p->a - 1);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+      else
+       strncpy (p->a, q->a, sizeof p->a - 2);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+    }
+
+  p->a[sizeof p->a - 2] = 0;
+  return 1;
+}
+
+
+int next_succ_2 (struct A *p, const struct A *q, int i, int j)
+{
+  /* Same as above but with a nested conditionals with else clauses.  */
+  if (i < 0)
+    {
+      if (j < 0)
+       strncpy (p->a, q->a, sizeof p->a - 1);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+      else
+       strncpy (p->a, q->a, sizeof p->a - 2);    /* { dg-bogus "\\\[-Wstringop-truncation" } */
+    }
+  else
+    __builtin_strcpy (p->a, q->a);
+
+  p->a[sizeof p->a - 2] = 0;
+  return 2;
+}
+
+
+void cond_succ_warn (struct A *p, const struct A *q, int i)
+{
+  /* Verify that a conditional assignment doesn't suppress the warning.  */
+  strncpy (p->a, q->a, sizeof p->a - 1);      /* { dg-warning "\\\[-Wstringop-truncation" } */
+
+  if (i < 0)
+    p->a[sizeof p->a - 1] = 0;
+}
+
+void cond_succ_nowarn (struct A *p, const struct A *q)
+{
+  /* Verify that distinct but provably equivalent conditionals are
+     recognized and don't trigger the warning.  */
+  if (p != q)
+    strncpy (p->a, q->a, sizeof p->a - 1);
+
+  if (p->a != q->a)
+    p->a[sizeof p->a - 1] = 0;
+}
index 1266f39..72f6a17 100644 (file)
@@ -1856,8 +1856,33 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
      avoid the truncation warning.  */
   gsi_next_nondebug (&gsi);
   gimple *next_stmt = gsi_stmt (gsi);
+  if (!next_stmt)
+    {
+      /* When there is no statement in the same basic block check
+        the immediate successor block.  */
+      if (basic_block bb = gimple_bb (stmt))
+       {
+         if (single_succ_p (bb))
+           {
+             /* For simplicity, ignore blocks with multiple outgoing
+                edges for now and only consider successor blocks along
+                normal edges.  */
+             edge e = EDGE_SUCC (bb, 0);
+             if (!(e->flags & EDGE_ABNORMAL))
+               {
+                 gsi = gsi_start_bb (e->dest);
+                 next_stmt = gsi_stmt (gsi);
+                 if (next_stmt && is_gimple_debug (next_stmt))
+                   {
+                     gsi_next_nondebug (&gsi);
+                     next_stmt = gsi_stmt (gsi);
+                   }
+               }
+           }
+       }
+    }
 
-  if (!gsi_end_p (gsi) && is_gimple_assign (next_stmt))
+  if (next_stmt && is_gimple_assign (next_stmt))
     {
       tree lhs = gimple_assign_lhs (next_stmt);
       tree_code code = TREE_CODE (lhs);