From bec1da64aec26a490a6de5a9aa4ee667805e8445 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Tue, 19 Mar 2019 22:43:10 +0000 Subject: [PATCH] PR tree-optimization/89688 - -Wstringop-overflow confused by const 2D array of char gcc/c/ChangeLog: PR tree-optimization/89688 * c-decl.c (finish_decl): Call braced_lists_to_string for more kinds of initializers. gcc/c-family/ChangeLog: PR tree-optimization/89688 * c-common.c (braced_list_to_string): Make static. (braced_lists_to_strings): Define new function. * c-common.h (braced_list_to_string): Remove. (braced_lists_to_strings): Declare. gcc/cp/ChangeLog: PR tree-optimization/89688 * typeck2.c (store_init_value): Call braced_lists_to_string for more kinds of initializers. gcc/testsuite/ChangeLog: PR tree-optimization/89688 * gcc.dg/strlenopt-61.c: New test. * g++.dg/warn/Wstringop-overflow-2.C: New test. From-SVN: r269814 --- gcc/c-family/ChangeLog | 8 + gcc/c-family/c-common.c | 50 +++- gcc/c-family/c-common.h | 3 +- gcc/c/ChangeLog | 6 + gcc/c/c-decl.c | 9 +- gcc/cp/ChangeLog | 6 + gcc/cp/typeck2.c | 7 +- gcc/testsuite/ChangeLog | 6 + .../g++.dg/warn/Wstringop-overflow-2.C | 29 +++ gcc/testsuite/gcc.dg/strlenopt-61.c | 218 ++++++++++++++++++ 10 files changed, 331 insertions(+), 11 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/Wstringop-overflow-2.C create mode 100644 gcc/testsuite/gcc.dg/strlenopt-61.c diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 910cee85941..df61b693205 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,11 @@ +2019-03-19 Martin Sebor + + PR tree-optimization/89688 + * c-common.c (braced_list_to_string): Make static. + (braced_lists_to_strings): Define new function. + * c-common.h (braced_list_to_string): Remove. + (braced_lists_to_strings): Declare. + 2019-03-12 Martin Liska * c-opts.c (c_common_handle_option): Wrap option with %< and %>. diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 019f1082922..0e17fef8818 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -8814,7 +8814,7 @@ maybe_add_include_fixit (rich_location *richloc, const char *header, TYPE into a STRING_CST for convenience and efficiency. Return the converted string on success or the original ctor on failure. */ -tree +static tree braced_list_to_string (tree type, tree ctor) { if (!tree_fits_uhwi_p (TYPE_SIZE_UNIT (type))) @@ -8895,4 +8895,52 @@ braced_list_to_string (tree type, tree ctor) return res; } +/* Attempt to convert a CTOR containing braced array initializer lists + for array TYPE into one containing STRING_CSTs, for convenience and + efficiency. Recurse for arrays of arrays and member initializers. + Return the converted CTOR or STRING_CST on success or the original + CTOR otherwise. */ + +tree +braced_lists_to_strings (tree type, tree ctor) +{ + if (TREE_CODE (ctor) != CONSTRUCTOR) + return ctor; + + tree_code code = TREE_CODE (type); + + tree ttp; + if (code == ARRAY_TYPE) + ttp = TREE_TYPE (type); + else if (code == RECORD_TYPE) + { + ttp = TREE_TYPE (ctor); + if (TREE_CODE (ttp) == ARRAY_TYPE) + { + type = ttp; + ttp = TREE_TYPE (ttp); + } + } + else + return ctor; + + if (TYPE_STRING_FLAG (ttp)) + return braced_list_to_string (type, ctor); + + code = TREE_CODE (ttp); + if (code == ARRAY_TYPE || code == RECORD_TYPE) + { + /* Handle array of arrays or struct member initializers. */ + tree val; + unsigned HOST_WIDE_INT idx; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (ctor), idx, val) + { + val = braced_lists_to_strings (ttp, val); + CONSTRUCTOR_ELT (ctor, idx)->value = val; + } + } + + return ctor; +} + #include "gt-c-family-c-common.h" diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 394a0ea1c89..104c74226de 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -1372,7 +1372,8 @@ extern void maybe_add_include_fixit (rich_location *, const char *, bool); extern void maybe_suggest_missing_token_insertion (rich_location *richloc, enum cpp_ttype token_type, location_t prev_token_loc); -extern tree braced_list_to_string (tree, tree); +extern tree braced_lists_to_strings (tree, tree); + extern bool has_attribute (location_t, tree, tree, tree (*)(tree)); #if CHECKING_P diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index fd3d009f13e..fc8049e832e 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,9 @@ +2019-03-19 Martin Sebor + + PR tree-optimization/89688 + * c-decl.c (finish_decl): Call braced_lists_to_string for more + kinds of initializers. + 2019-03-19 Jakub Jelinek PR c/89734 diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index e8b7ca4ae5b..8d5c35ab475 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -5165,11 +5165,10 @@ finish_decl (tree decl, location_t init_loc, tree init, relayout_decl (decl); } - if (TREE_CODE (type) == ARRAY_TYPE - && TYPE_STRING_FLAG (TREE_TYPE (type)) - && DECL_INITIAL (decl) - && TREE_CODE (DECL_INITIAL (decl)) == CONSTRUCTOR) - DECL_INITIAL (decl) = braced_list_to_string (type, DECL_INITIAL (decl)); + /* Look for braced array initializers for character arrays and + recursively convert them into STRING_CSTs. */ + if (tree init = DECL_INITIAL (decl)) + DECL_INITIAL (decl) = braced_lists_to_strings (type, init); if (VAR_P (decl)) { diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 76bc8ffaa68..2157496745e 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,9 @@ +2019-03-19 Martin Sebor + + PR tree-optimization/89688 + * typeck2.c (store_init_value): Call braced_lists_to_string for more + kinds of initializers. + 2019-03-18 Jason Merrill PR c++/89630 - ICE with dependent using-decl as template arg. diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 456c4fcb748..e50d6ed83c3 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -824,10 +824,9 @@ store_init_value (tree decl, tree init, vec** cleanups, int flags) value = digest_init_flags (type, init, flags, tf_warning_or_error); } - if (TREE_CODE (type) == ARRAY_TYPE - && TYPE_STRING_FLAG (TREE_TYPE (type)) - && TREE_CODE (value) == CONSTRUCTOR) - value = braced_list_to_string (type, value); + /* Look for braced array initializers for character arrays and + recursively convert them into STRING_CSTs. */ + value = braced_lists_to_strings (type, value); current_ref_temp_count = 0; value = extend_ref_init_temps (decl, value, cleanups); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7ad6ccc1e4a..e60ec93ced3 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2019-03-19 Martin Sebor + + PR tree-optimization/89688 + * gcc.dg/strlenopt-61.c: New test. + * g++.dg/warn/Wstringop-overflow-2.C: New test. + 2019-03-19 Jim Wilson PR target/89411 diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-2.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-2.C new file mode 100644 index 00000000000..425ba83841b --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-2.C @@ -0,0 +1,29 @@ +/* PR tree-optimization/89688 - -Wstringop-overflow confused by const 2D + array of char + { dg-do compile } + { dg-options "-Wall -fdump-tree-gimple -fdump-tree-optimized" } */ + +extern "C" __SIZE_TYPE__ strlen (const char*); + +const char a2[2] = { '1' }; + +void a2_len () +{ + if (strlen (a2) != 1) + __builtin_abort (); +} + +const char a2_2[2][3] = { { '1' }, { '1', '2' } }; + +void a2_2_len () +{ + if (strlen (a2_2[0]) != 1) // { dg-bogus "-Wstringop-overflow" } + __builtin_abort (); + + if (strlen (a2_2[1]) != 2) // { dg-bogus "-Wstringop-overflow" } + __builtin_abort (); +} + + +/* { dg-final { scan-tree-dump-not "abort" "optimized" } } + { dg-final { scan-tree-dump-not "strlen" "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-61.c b/gcc/testsuite/gcc.dg/strlenopt-61.c new file mode 100644 index 00000000000..4f8e9c053e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-61.c @@ -0,0 +1,218 @@ +/* PR tree-optimization/89688 - -Wstringop-overflow confused by const + 2D array of char + { dg-do compile } + { dg-options "-Wall -fdump-tree-gimple -fdump-tree-optimized" } */ + +typedef __SIZE_TYPE__ size_t; + +size_t strlen (const char*); +#define CAT(x, y) x ## y +#define CONCAT(x, y) CAT (x, y) +#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__) + +#define FAIL(name) do { \ + extern __attribute__ ((noreturn)) void FAILNAME (name) (void); \ + FAILNAME (name)(); \ + } while (0) + +#define A(ref, len) \ + if (strlen (ref) != len) FAIL (failure); else (void)0 + +const char a3_4[3][4] = { { 1 }, { 1, 2 }, { 1, 2, 3 } }; + +void test_a4_4 (void) +{ + A (a3_4[0], 1); + A (a3_4[1], 2); + A (a3_4[2], 3); + + A (&a3_4[0][0], 1); + A (&a3_4[0][1], 0); + A (&a3_4[0][2], 0); + A (&a3_4[0][3], 0); + + A (&a3_4[1][0], 2); + A (&a3_4[1][1], 1); + A (&a3_4[1][2], 0); + A (&a3_4[1][3], 0); + + A (&a3_4[2][0], 3); + A (&a3_4[2][1], 2); + A (&a3_4[2][2], 1); + A (&a3_4[2][3], 0); +} + + +const char a3_4_5[3][4][5] = + { + { { 1 }, { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 } }, + { { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } }, + { { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 }, { 1, 2 } }, + }; + +void test_a3_4_5 (void) +{ + A (a3_4_5[0][0], 1); + A (a3_4_5[0][1], 2); + A (a3_4_5[0][2], 3); + A (a3_4_5[0][3], 4); + + A (a3_4_5[1][0], 2); + A (a3_4_5[1][1], 3); + A (a3_4_5[1][2], 4); + A (a3_4_5[1][3], 1); + + A (a3_4_5[2][0], 3); + A (a3_4_5[2][1], 4); + A (a3_4_5[2][2], 1); + A (a3_4_5[2][3], 2); +} + + +struct S +{ + char a3[3]; + char a4_5[4][5]; +}; + +const struct S sa4[4] = + { + { .a3 = { 0 }, + .a4_5 = + { + { 1 }, { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 } + } + }, + { .a3 = { 1 }, + .a4_5 = + { + { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } + } + }, + { .a3 = { 1, 2 }, + .a4_5 = + { + { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 }, { 1, 2 } + } + }, + { .a3 = { 1 }, + .a4_5 = + { + { 1, 2, 3, 4 }, "1", { 1, 2 }, "123" + } + } + }; + +void test_sa4 (void) +{ + A (sa4[0].a3, 0); + A (sa4[0].a4_5[0], 1); + A (sa4[0].a4_5[1], 2); + A (sa4[0].a4_5[2], 3); + A (sa4[0].a4_5[3], 4); + + A (sa4[1].a3, 1); + A (sa4[1].a4_5[0], 2); + A (sa4[1].a4_5[1], 3); + A (sa4[1].a4_5[2], 4); + A (sa4[1].a4_5[3], 1); + + A (sa4[2].a3, 2); + A (sa4[2].a4_5[0], 3); + A (sa4[2].a4_5[1], 4); + A (sa4[2].a4_5[2], 1); + A (sa4[2].a4_5[3], 2); + + A (sa4[3].a3, 1); + A (sa4[3].a4_5[0], 4); + A (sa4[3].a4_5[1], 1); + A (sa4[3].a4_5[2], 2); + A (sa4[3].a4_5[3], 3); +} + + +struct T +{ + struct S sa2[2]; + char a4[4]; +}; + +const struct T ta2[2] = + { + [0] = + { + .sa2 = + { + [0] = + { .a3 = { 0 }, + .a4_5 = + { + { 1 }, { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 } + } + }, + [1] = + { .a3 = { 1 }, + .a4_5 = + { + { 1, 2 }, { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 } + } + }, + }, + .a4 = "12" + }, + + [1] = + { + .sa2 = + { + [0] = + { .a3 = { 1, 2 }, + .a4_5 = + { + { 1, 2, 3 }, { 1, 2, 3, 4 }, { 1 }, { 1, 2 } + } + }, + { .a3 = { 1 }, + .a4_5 = + { + { 1, 2, 3, 4 }, "1", { 1, 2 }, "123" + } + } + }, + .a4 = "123" + } + }; + +void test_ta2 (void) +{ + A (ta2[0].sa2[0].a3, 0); + A (ta2[0].sa2[0].a4_5[0], 1); + A (ta2[0].sa2[0].a4_5[1], 2); + A (ta2[0].sa2[0].a4_5[2], 3); + A (ta2[0].sa2[0].a4_5[3], 4); + + A (ta2[0].sa2[1].a3, 1); + A (ta2[0].sa2[1].a4_5[0], 2); + A (ta2[0].sa2[1].a4_5[1], 3); + A (ta2[0].sa2[1].a4_5[2], 4); + A (ta2[0].sa2[1].a4_5[3], 1); + + A (ta2[0].a4, 2); + + A (ta2[1].sa2[0].a3, 2); + A (ta2[1].sa2[0].a4_5[0], 3); + A (ta2[1].sa2[0].a4_5[1], 4); + A (ta2[1].sa2[0].a4_5[2], 1); + A (ta2[1].sa2[0].a4_5[3], 2); + + A (ta2[1].sa2[1].a3, 1); + A (ta2[1].sa2[1].a4_5[0], 4); + A (ta2[1].sa2[1].a4_5[1], 1); + A (ta2[1].sa2[1].a4_5[2], 2); + A (ta2[1].sa2[1].a4_5[3], 3); + + A (ta2[1].a4, 3); +} + +/* { dg-final { scan-tree-dump-not "failure" "optimized" } } + { dg-final { scan-tree-dump-not "strlen" "gimple" } } */ -- 2.30.2