When initializing a nonstring char array when compiled with -Wunterminated-string-initialization the warning trips even when truncating the trailing NUL character from the string constant. Only warn about this when running under -Wc++-compat since under C++ we should not initialize nonstrings from C strings.
PR c/117178 gcc/c/ChangeLog: * c-typeck.cc (digest_init): Check for nonstring attribute and avoid warning when only the trailing NUL is truncated. (build_c_cast): Update function call prototype. (store_init_value): Ditto. (output_init_element): Ditto. gcc/testsuite/ChangeLog: * gcc.dg/Wunterminated-string-initialization.c: Update for new warning text and check for nonstring cases. * gcc.dg/Wunterminated-string-initialization-c++.c: Duplicate C test for -Wc++-compat. --- gcc/c/c-typeck.cc | 73 ++++++++++++------- .../Wunterminated-string-initialization-c++.c | 28 +++++++ .../Wunterminated-string-initialization.c | 26 ++++++- 3 files changed, 98 insertions(+), 29 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wunterminated-string-initialization-c++.c diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 10b02da8752d..5cd0c07b87b1 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -116,8 +116,8 @@ static void push_member_name (tree); static int spelling_length (void); static char *print_spelling (char *); static void warning_init (location_t, int, const char *); -static tree digest_init (location_t, tree, tree, tree, bool, bool, bool, bool, - bool, bool); +static tree digest_init (location_t, tree, tree, tree, tree, bool, bool, bool, + bool, bool, bool); static void output_init_element (location_t, tree, tree, bool, tree, tree, bool, bool, struct obstack *); static void output_pending_init_elements (int, struct obstack *); @@ -6731,7 +6731,7 @@ build_c_cast (location_t loc, tree type, tree expr) t = build_constructor_single (type, field, t); if (!maybe_const) t = c_wrap_maybe_const (t, true); - t = digest_init (loc, type, t, + t = digest_init (loc, field, type, t, NULL_TREE, false, false, false, true, false, false); TREE_CONSTANT (t) = TREE_CONSTANT (value); return t; @@ -8646,8 +8646,8 @@ store_init_value (location_t init_loc, tree decl, tree init, tree origtype) } bool constexpr_p = (VAR_P (decl) && C_DECL_DECLARED_CONSTEXPR (decl)); - value = digest_init (init_loc, type, init, origtype, npc, int_const_expr, - arith_const_expr, true, + value = digest_init (init_loc, decl, type, init, origtype, npc, + int_const_expr, arith_const_expr, true, TREE_STATIC (decl) || constexpr_p, constexpr_p); /* Store the expression if valid; else report error. */ @@ -8996,8 +8996,8 @@ check_constexpr_init (location_t loc, tree type, tree init, on initializers for 'constexpr' objects apply. */ static tree -digest_init (location_t init_loc, tree type, tree init, tree origtype, - bool null_pointer_constant, bool int_const_expr, +digest_init (location_t init_loc, tree field, tree type, tree init, + tree origtype, bool null_pointer_constant, bool int_const_expr, bool arith_const_expr, bool strict_string, bool require_constant, bool require_constexpr) { @@ -9132,27 +9132,46 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype, && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST) { unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (inside_init); - unsigned unit = TYPE_PRECISION (typ1) / BITS_PER_UNIT; - - /* Subtract the size of a single (possibly wide) character - because it's ok to ignore the terminating null char - that is counted in the length of the constant. */ - if (compare_tree_int (TYPE_SIZE_UNIT (type), len - unit) < 0) - pedwarn_init (init_loc, 0, - ("initializer-string for array of %qT " - "is too long"), typ1); - else if (warn_unterminated_string_initialization - && compare_tree_int (TYPE_SIZE_UNIT (type), len) < 0) - warning_at (init_loc, OPT_Wunterminated_string_initialization, - ("initializer-string for array of %qT " - "is too long"), typ1); + if (compare_tree_int (TYPE_SIZE_UNIT (type), len) < 0) { - unsigned HOST_WIDE_INT size - = tree_to_uhwi (TYPE_SIZE_UNIT (type)); - const char *p = TREE_STRING_POINTER (inside_init); - - inside_init = build_string (size, p); + unsigned HOST_WIDE_INT avail + = tree_to_uhwi (TYPE_SIZE_UNIT (type)); + unsigned unit = TYPE_PRECISION (typ1) / BITS_PER_UNIT; + const char *p = TREE_STRING_POINTER (inside_init); + + /* Construct truncated string. */ + inside_init = build_string (avail, p); + + /* Subtract the size of a single (possibly wide) character + because it may be ok to ignore the terminating NUL char + that is counted in the length of the constant. */ + if (warn_cxx_compat || len - unit > avail) + { + pedwarn_init (init_loc, 0, + ("initializer-string for array of %qT " + "is too long (%lu chars into %lu " + "available)"), typ1, len, avail); + } + else if (warn_unterminated_string_initialization) + { + if (len - unit > avail) + warning_at (init_loc, + OPT_Wunterminated_string_initialization, + ("initializer-string for array of %qT " + "is too long (%lu chars into %lu " + "available)"), typ1, len, avail); + else if (get_attr_nonstring_decl (field) == NULL_TREE) + { + warning_at (init_loc, + OPT_Wunterminated_string_initialization, + ("initializer-string for array of %qT " + "truncates NUL terminator but " + "destination lacks 'nonstring' attribute " + "(%lu chars into %lu available)"), + typ1, len, avail); + } + } } } @@ -11173,7 +11192,7 @@ output_init_element (location_t loc, tree value, tree origtype, if (!require_constexpr_value || !npc || TREE_CODE (constructor_type) != POINTER_TYPE) - new_value = digest_init (loc, type, new_value, origtype, npc, + new_value = digest_init (loc, field, type, new_value, origtype, npc, int_const_expr, arith_const_expr, strict_string, require_constant_value, require_constexpr_value); if (new_value == error_mark_node) diff --git a/gcc/testsuite/gcc.dg/Wunterminated-string-initialization-c++.c b/gcc/testsuite/gcc.dg/Wunterminated-string-initialization-c++.c new file mode 100644 index 000000000000..18e8054eb5d3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wunterminated-string-initialization-c++.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-Wc++-compat -Wunterminated-string-initialization" } */ + +char a1[] = "a"; +char a2[1] = "a"; /* { dg-warning "initializer-string for array of 'char' is too long" } */ +char a2nonstring[1] __attribute__((nonstring)) = "a"; /* { dg-warning "initializer-string for array of 'char' is too long" } */ +char a3[1] = "aa"; /* { dg-warning "initializer-string for array of 'char' is too long" } */ +char a4[2] = "a"; + +struct has_str { + int a; + char str1[4]; + char str2[4]; + char str3[4]; + char tag1[4] __attribute__((nonstring)); + char tag2[4] __attribute__((nonstring)); + char tag3[4] __attribute__((nonstring)); + int b; +}; + +struct has_str foo = { + .str1 = "111", + .str2 = "2222", /* { dg-warning "initializer-string for array of 'char' is too long" } */ + .str3 = "33333", /* { dg-warning "initializer-string for array of 'char' is too long" } */ + .tag1 = "AAA", + .tag2 = "BBBB", /* { dg-warning "initializer-string for array of 'char' is too long" } */ + .tag3 = "CCCCC", /* { dg-warning "initializer-string for array of 'char' is too long" } */ +}; diff --git a/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c b/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c index 13d5dbc66400..315e7d53e253 100644 --- a/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c +++ b/gcc/testsuite/gcc.dg/Wunterminated-string-initialization.c @@ -2,5 +2,27 @@ /* { dg-options "-Wunterminated-string-initialization" } */ char a1[] = "a"; -char a2[1] = "a"; /* { dg-warning "initializer-string for array of 'char' is too long" } */ -char a3[2] = "a"; +char a2[1] = "a"; /* { dg-warning "initializer-string for array of 'char' truncates" } */ +char a2nonstring[1] __attribute__((nonstring)) = "a"; +char a3[1] = "aa"; /* { dg-warning "initializer-string for array of 'char' is too long" } */ +char a4[2] = "a"; + +struct has_str { + int a; + char str1[4]; + char str2[4]; + char str3[4]; + char tag1[4] __attribute__((nonstring)); + char tag2[4] __attribute__((nonstring)); + char tag3[4] __attribute__((nonstring)); + int b; +}; + +struct has_str foo = { + .str1 = "111", + .str2 = "2222", /* { dg-warning "initializer-string for array of 'char' truncates" } */ + .str3 = "33333", /* { dg-warning "initializer-string for array of 'char' is too long" } */ + .tag1 = "AAA", + .tag2 = "BBBB", + .tag3 = "CCCCC", /* { dg-warning "initializer-string for array of 'char' is too long" } */ +}; -- 2.34.1