PR tree-optimization/87028 - false positive -Wstringop-truncation strncpy with global variable source string gcc/ChangeLog: PR tree-optimization/87028 * calls.c (get_attr_nonstring_decl): Avoid setting *REF to SSA_NAME_VAR. * gimple-fold.c (gimple_fold_builtin_strncpy): Avoid folding when statement doesn't belong to a basic block. * tree.h (SSA_NAME_VAR): Update comment. * tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Simplify. gcc/testsuite/ChangeLog: PR tree-optimization/87028 * c-c++-common/Wstringop-truncation.c: Remove xfails. * gcc.dg/Wstringop-truncation-5.c: New test. Index: gcc/tree.h =================================================================== --- gcc/tree.h (revision 263925) +++ gcc/tree.h (working copy) @@ -1697,7 +1697,10 @@ extern tree maybe_wrap_with_location (tree, locati : NULL_TREE) /* Returns the variable being referenced. This can be NULL_TREE for - temporaries not associated with any user variable. + temporaries not associated with any user variable. The result + is mainly useful for debugging, diagnostics, or as the target + declaration referenced by an SSA_NAME. Otherwise, because + it has no dataflow information, it should not be used. Once released, this is the only field that can be relied upon. */ #define SSA_NAME_VAR(NODE) \ (SSA_NAME_CHECK (NODE)->ssa_name.var == NULL_TREE \ Index: gcc/calls.c =================================================================== --- gcc/calls.c (revision 263928) +++ gcc/calls.c (working copy) @@ -1503,6 +1503,7 @@ tree get_attr_nonstring_decl (tree expr, tree *ref) { tree decl = expr; + tree var = NULL_TREE; if (TREE_CODE (decl) == SSA_NAME) { gimple *def = SSA_NAME_DEF_STMT (decl); @@ -1515,17 +1516,25 @@ get_attr_nonstring_decl (tree expr, tree *ref) || code == VAR_DECL) decl = gimple_assign_rhs1 (def); } - else if (tree var = SSA_NAME_VAR (decl)) - decl = var; + else + var = SSA_NAME_VAR (decl); } if (TREE_CODE (decl) == ADDR_EXPR) decl = TREE_OPERAND (decl, 0); + /* To simplify calling code, store the referenced DECL regardless of + the attribute determined below, but avoid storing the SSA_NAME_VAR + obtained above (it's not useful for dataflow purposes). */ if (ref) *ref = decl; - if (TREE_CODE (decl) == ARRAY_REF) + /* Use the SSA_NAME_VAR that was determined above to see if it's + declared nonstring. Otherwise drill down into the referenced + DECL. */ + if (var) + decl = var; + else if (TREE_CODE (decl) == ARRAY_REF) decl = TREE_OPERAND (decl, 0); else if (TREE_CODE (decl) == COMPONENT_REF) decl = TREE_OPERAND (decl, 1); Index: gcc/gimple-fold.c =================================================================== --- gcc/gimple-fold.c (revision 263925) +++ gcc/gimple-fold.c (working copy) @@ -1702,6 +1702,11 @@ gimple_fold_builtin_strncpy (gimple_stmt_iterator if (tree_int_cst_lt (ssize, len)) return false; + /* Defer warning (and folding) until the next statement in the basic + block is reachable. */ + if (!gimple_bb (stmt)) + return false; + /* Diagnose truncation that leaves the copy unterminated. */ maybe_diag_stxncpy_trunc (*gsi, src, len); Index: gcc/tree-ssa-strlen.c =================================================================== --- gcc/tree-ssa-strlen.c (revision 263925) +++ gcc/tree-ssa-strlen.c (working copy) @@ -1904,8 +1904,6 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi if (TREE_CODE (dstdecl) == ADDR_EXPR) dstdecl = TREE_OPERAND (dstdecl, 0); - tree ref = NULL_TREE; - if (!sidx) { /* If the source is a non-string return early to avoid warning @@ -1914,12 +1912,14 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi tree srcdecl = gimple_call_arg (stmt, 1); if (TREE_CODE (srcdecl) == ADDR_EXPR) srcdecl = TREE_OPERAND (srcdecl, 0); - if (get_attr_nonstring_decl (srcdecl, &ref)) + if (get_attr_nonstring_decl (srcdecl, NULL)) return false; } - /* Likewise, if the destination refers to a an array/pointer declared - nonstring return early. */ + /* Likewise, if the destination refers to an array/pointer declared + nonstring return early. REF will be set to the referenced enclosing + object or pointer either way. */ + tree ref; if (get_attr_nonstring_decl (dstdecl, &ref)) return false; Index: gcc/testsuite/c-c++-common/Wstringop-truncation.c =================================================================== --- gcc/testsuite/c-c++-common/Wstringop-truncation.c (revision 263925) +++ gcc/testsuite/c-c++-common/Wstringop-truncation.c (working copy) @@ -329,9 +329,8 @@ void test_strncpy_array (Dest *pd, int i, const ch of the array to NUL is not diagnosed. */ { /* This might be better written using memcpy() but it's safe so - it probably shouldn't be diagnosed. It currently triggers - a warning because of bug 81704. */ - strncpy (dst7, "0123456", sizeof dst7); /* { dg-bogus "\\\[-Wstringop-truncation]" "bug 81704" { xfail *-*-* } } */ + it isn't diagnosed. See pr81704 and pr87028. */ + strncpy (dst7, "0123456", sizeof dst7); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ dst7[sizeof dst7 - 1] = '\0'; sink (dst7); } @@ -350,7 +349,7 @@ void test_strncpy_array (Dest *pd, int i, const ch } { - strncpy (pd->a5, "01234", sizeof pd->a5); /* { dg-bogus "\\\[-Wstringop-truncation]" "bug 81704" { xfail *-*-* } } */ + strncpy (pd->a5, "01234", sizeof pd->a5); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ pd->a5[sizeof pd->a5 - 1] = '\0'; sink (pd); } Index: gcc/testsuite/gcc.dg/Wstringop-truncation-5.c =================================================================== --- gcc/testsuite/gcc.dg/Wstringop-truncation-5.c (nonexistent) +++ gcc/testsuite/gcc.dg/Wstringop-truncation-5.c (working copy) @@ -0,0 +1,64 @@ +/* PR tree-optimization/87028 - false positive -Wstringop-truncation + strncpy with global variable source string + { dg-do compile } + { dg-options "-O2 -Wstringop-truncation" } */ + +char *strncpy (char *, const char *, __SIZE_TYPE__); + +#define STR "1234567890" + +struct S +{ + char a[5], b[5]; +}; + +const char arr[] = STR; +const char* const ptr = STR; + +const char arr2[][10] = { "123", STR }; + +void test_literal (struct S *s) +{ + strncpy (s->a, STR, sizeof s->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + s->a[sizeof s->a - 1] = '\0'; +} + +void test_global_arr (struct S *s) +{ + strncpy (s->a, arr, sizeof s->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + s->a [sizeof s->a - 1] = '\0'; +} + +void test_global_arr2 (struct S *s) +{ + strncpy (s->a, arr2[1], sizeof s->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + s->a [sizeof s->a - 1] = '\0'; + + strncpy (s->b, arr2[0], sizeof s->a - 1); +} + +void test_global_ptr (struct S *s) +{ + strncpy (s->a, ptr, sizeof s->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + s->a [sizeof s->a - 1] = '\0'; +} + +void test_local_arr (struct S *s) +{ + const char arr[] = STR; + strncpy (s->a, arr, sizeof s->a - 1); + s->a [sizeof s->a - 1] = '\0'; +} + +void test_local_ptr (struct S *s) +{ + const char* const ptr = STR; + strncpy (s->a, ptr, sizeof s->a - 1); /* { dg-bogus "\\\[-Wstringop-truncation]" } */ + s->a [sizeof s->a - 1] = '\0'; +} + +void test_compound_literal (struct S *s) +{ + strncpy (s->a, (char[]){ STR }, sizeof s->a - 1); + s->a [sizeof s->a - 1] = '\0'; +}