Message ID | SN6PR11MB3022BE79CE0B7765B9E7A26BCBB49@SN6PR11MB3022.namprd11.prod.outlook.com |
---|---|
State | New |
Headers | show |
Series | [v2] c++: fix string literal member initializer bug [PR90926] | expand |
On 2/3/21 6:31 AM, Tom Greenslade (thomgree) via Gcc-patches wrote: > Update of https://gcc.gnu.org/pipermail/gcc-patches/2020-December/562259.html > > build_aggr_conv did not correctly handle string literal member initializers. > Extended can_convert_array to handle this case. For the additional check of > compatibility of character types, factored out code from digest_init_r into a new function. > > Testcase added for this. > > Bootstrapped/regtested on x86_64-pc-linux-gnu. > > gcc/cp/ChangeLog: > > PR c++/90926 > * call.c (can_convert_array): Extend to handle all valid aggregate > initializers of an array; including by string literals, not just by > brace-init-list. > (build_aggr_conv): Call can_convert_array more often, not just in > brace-init-list case. > * typeck2.c (character_array_from_string_literal): New function. > (digest_init_r): call character_array_from_string_literal > * cp-tree.h: (character_array_from_string_literal): Declare. > * g++.dg/cpp1y/nsdmi-aggr12.C: New test. > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 87a7af12796..b917c67204f 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -895,28 +895,40 @@ strip_standard_conversion (conversion *conv) > return conv; > } > > -/* Subroutine of build_aggr_conv: check whether CTOR, a braced-init-list, > - is a valid aggregate initializer for array type ATYPE. */ > +/* Subroutine of build_aggr_conv: check whether FROM is a valid aggregate > + initializer for array type ATYPE. */ > > static bool > -can_convert_array (tree atype, tree ctor, int flags, tsubst_flags_t complain) > +can_convert_array (tree atype, tree from, int flags, tsubst_flags_t complain) > { > - unsigned i; > tree elttype = TREE_TYPE (atype); > - for (i = 0; i < CONSTRUCTOR_NELTS (ctor); ++i) > + unsigned i; > + > + if (TREE_CODE (from) == CONSTRUCTOR) > { > - tree val = CONSTRUCTOR_ELT (ctor, i)->value; > - bool ok; > - if (TREE_CODE (elttype) == ARRAY_TYPE > - && TREE_CODE (val) == CONSTRUCTOR) > - ok = can_convert_array (elttype, val, flags, complain); > - else > - ok = can_convert_arg (elttype, TREE_TYPE (val), val, flags, > - complain); > - if (!ok) > - return false; > + for (i = 0; i < CONSTRUCTOR_NELTS (from); ++i) > + { > + tree val = CONSTRUCTOR_ELT (from, i)->value; > + bool ok; > + if (TREE_CODE (elttype) == ARRAY_TYPE) > + ok = can_convert_array (elttype, val, flags, complain); > + else > + ok = can_convert_arg (elttype, TREE_TYPE (val), val, flags, > + complain); > + if (!ok) > + return false; > + } > + return true; > } > - return true; > + > + if (char_type_p (TYPE_MAIN_VARIANT (elttype)) > + && TREE_CODE (tree_strip_any_location_wrapper (from)) == STRING_CST) > + { > + return character_array_from_string_literal(atype, from); > + } We generally don't wrap a single statement in { }. And you need a space before the (. > + > + /* No other valid way to aggregate initialize an array. */ > + return false; > } > > /* Helper for build_aggr_conv. Return true if FIELD is in PSET, or if > @@ -973,8 +985,7 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) > tree ftype = TREE_TYPE (idx); > bool ok; > > - if (TREE_CODE (ftype) == ARRAY_TYPE > - && TREE_CODE (val) == CONSTRUCTOR) > + if (TREE_CODE (ftype) == ARRAY_TYPE) > ok = can_convert_array (ftype, val, flags, complain); > else > ok = can_convert_arg (ftype, TREE_TYPE (val), val, flags, > @@ -1021,9 +1032,8 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) > val = empty_ctor; > } > > - if (TREE_CODE (ftype) == ARRAY_TYPE > - && TREE_CODE (val) == CONSTRUCTOR) > - ok = can_convert_array (ftype, val, flags, complain); > + if (TREE_CODE (ftype) == ARRAY_TYPE) > + ok = can_convert_array (ftype, val, flags, complain); > else > ok = can_convert_arg (ftype, TREE_TYPE (val), val, flags, > complain); > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index f31319904eb..8bbbbdfc581 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -7946,6 +7946,7 @@ extern tree split_nonconstant_init (tree, tree); > extern bool check_narrowing (tree, tree, tsubst_flags_t, > bool = false); > extern bool ordinary_char_type_p (tree); > +extern bool character_array_from_string_literal (tree, tree); > extern tree digest_init (tree, tree, tsubst_flags_t); > extern tree digest_init_flags (tree, tree, int, tsubst_flags_t); > extern tree digest_nsdmi_init (tree, tree, tsubst_flags_t); > diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c > index 9ba2897390a..8fbabeb46d9 100644 > --- a/gcc/cp/typeck2.c > +++ b/gcc/cp/typeck2.c > @@ -1003,6 +1003,28 @@ ordinary_char_type_p (tree type) > || type == unsigned_char_type_node); > } > > +/* Checks if character types are compatible. */ > + > +bool > +character_array_from_string_literal(tree type, tree init) Space before ( here as well. This function name suggests to me that it would produce an array, so I've renamed it to array_string_literal_compatible_p, fixed a bit of stray whitespace, tidied the ChangeLog entries, and committed the patch. Thanks! Jason > +{ > + tree to_char_type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); > + tree from_char_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init))); > + > + if (to_char_type == from_char_type) > + return true; > + /* The array element type does not match the initializing string > + literal element type; this is only allowed when both types are > + ordinary character type. There are no string literals of > + signed or unsigned char type in the language, but we can get > + them internally from converting braced-init-lists to > + STRING_CST. */ > + if (ordinary_char_type_p (to_char_type) > + && ordinary_char_type_p (from_char_type)) > + return true; > + return false; > +} > + > /* Process the initializer INIT for a variable of type TYPE, emitting > diagnostics for invalid initializers and converting the initializer as > appropriate. > @@ -1070,30 +1092,13 @@ digest_init_r (tree type, tree init, int nested, int flags, > if (char_type_p (typ1) > && TREE_CODE (stripped_init) == STRING_CST) > { > - tree char_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init))); > - bool incompat_string_cst = false; > - > - if (typ1 != char_type) > - { > - /* The array element type does not match the initializing string > - literal element type; this is only allowed when both types are > - ordinary character type. There are no string literals of > - signed or unsigned char type in the language, but we can get > - them internally from converting braced-init-lists to > - STRING_CST. */ > - if (ordinary_char_type_p (typ1) > - && ordinary_char_type_p (char_type)) > - /* OK */; > - else > - incompat_string_cst = true; > - } > - > - if (incompat_string_cst) > + if (!character_array_from_string_literal (type, init)) > { > if (complain & tf_error) > error_at (loc, "cannot initialize array of %qT from " > - "a string literal with type array of %qT", > - typ1, char_type); > + "a string literal with type array of %qT", > + typ1, > + TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init)))); > return error_mark_node; > } > > diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C > new file mode 100644 > index 00000000000..fcc1f50dd81 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C > @@ -0,0 +1,36 @@ > +// PR c++/90926 > +// { dg-do run { target c++14 } } > + > +#include <cassert> > + > +struct A > +{ > + char str[4] = "foo"; > + char str_array[2][4] = {"bar", "baz"}; > +}; > + > +struct B > +{ > + char16_t str[10]; > +}; > + > +int called = 0; > +void f(A) { called = 1;}; > +void f(B) { called = 2;}; > + > +int > +main () > +{ > + A a; > + a.str[0] = 'g'; > + a.str_array[0][0] = 'g'; > + a = {}; > + > + if (__builtin_strcmp (a.str, "foo") != 0) > + __builtin_abort(); > + if (__builtin_strcmp (a.str_array[0], "bar") != 0) > + __builtin_abort(); > + > + f({"foo"}); assert(called == 1); > + f({u"foo"}); assert(called == 2); > +} >
diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 87a7af12796..b917c67204f 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -895,28 +895,40 @@ strip_standard_conversion (conversion *conv) return conv; } -/* Subroutine of build_aggr_conv: check whether CTOR, a braced-init-list, - is a valid aggregate initializer for array type ATYPE. */ +/* Subroutine of build_aggr_conv: check whether FROM is a valid aggregate + initializer for array type ATYPE. */ static bool -can_convert_array (tree atype, tree ctor, int flags, tsubst_flags_t complain) +can_convert_array (tree atype, tree from, int flags, tsubst_flags_t complain) { - unsigned i; tree elttype = TREE_TYPE (atype); - for (i = 0; i < CONSTRUCTOR_NELTS (ctor); ++i) + unsigned i; + + if (TREE_CODE (from) == CONSTRUCTOR) { - tree val = CONSTRUCTOR_ELT (ctor, i)->value; - bool ok; - if (TREE_CODE (elttype) == ARRAY_TYPE - && TREE_CODE (val) == CONSTRUCTOR) - ok = can_convert_array (elttype, val, flags, complain); - else - ok = can_convert_arg (elttype, TREE_TYPE (val), val, flags, - complain); - if (!ok) - return false; + for (i = 0; i < CONSTRUCTOR_NELTS (from); ++i) + { + tree val = CONSTRUCTOR_ELT (from, i)->value; + bool ok; + if (TREE_CODE (elttype) == ARRAY_TYPE) + ok = can_convert_array (elttype, val, flags, complain); + else + ok = can_convert_arg (elttype, TREE_TYPE (val), val, flags, + complain); + if (!ok) + return false; + } + return true; } - return true; + + if (char_type_p (TYPE_MAIN_VARIANT (elttype)) + && TREE_CODE (tree_strip_any_location_wrapper (from)) == STRING_CST) + { + return character_array_from_string_literal(atype, from); + } + + /* No other valid way to aggregate initialize an array. */ + return false; } /* Helper for build_aggr_conv. Return true if FIELD is in PSET, or if @@ -973,8 +985,7 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) tree ftype = TREE_TYPE (idx); bool ok; - if (TREE_CODE (ftype) == ARRAY_TYPE - && TREE_CODE (val) == CONSTRUCTOR) + if (TREE_CODE (ftype) == ARRAY_TYPE) ok = can_convert_array (ftype, val, flags, complain); else ok = can_convert_arg (ftype, TREE_TYPE (val), val, flags, @@ -1021,9 +1032,8 @@ build_aggr_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) val = empty_ctor; } - if (TREE_CODE (ftype) == ARRAY_TYPE - && TREE_CODE (val) == CONSTRUCTOR) - ok = can_convert_array (ftype, val, flags, complain); + if (TREE_CODE (ftype) == ARRAY_TYPE) + ok = can_convert_array (ftype, val, flags, complain); else ok = can_convert_arg (ftype, TREE_TYPE (val), val, flags, complain); diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f31319904eb..8bbbbdfc581 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7946,6 +7946,7 @@ extern tree split_nonconstant_init (tree, tree); extern bool check_narrowing (tree, tree, tsubst_flags_t, bool = false); extern bool ordinary_char_type_p (tree); +extern bool character_array_from_string_literal (tree, tree); extern tree digest_init (tree, tree, tsubst_flags_t); extern tree digest_init_flags (tree, tree, int, tsubst_flags_t); extern tree digest_nsdmi_init (tree, tree, tsubst_flags_t); diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 9ba2897390a..8fbabeb46d9 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1003,6 +1003,28 @@ ordinary_char_type_p (tree type) || type == unsigned_char_type_node); } +/* Checks if character types are compatible. */ + +bool +character_array_from_string_literal(tree type, tree init) +{ + tree to_char_type = TYPE_MAIN_VARIANT (TREE_TYPE (type)); + tree from_char_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init))); + + if (to_char_type == from_char_type) + return true; + /* The array element type does not match the initializing string + literal element type; this is only allowed when both types are + ordinary character type. There are no string literals of + signed or unsigned char type in the language, but we can get + them internally from converting braced-init-lists to + STRING_CST. */ + if (ordinary_char_type_p (to_char_type) + && ordinary_char_type_p (from_char_type)) + return true; + return false; +} + /* Process the initializer INIT for a variable of type TYPE, emitting diagnostics for invalid initializers and converting the initializer as appropriate. @@ -1070,30 +1092,13 @@ digest_init_r (tree type, tree init, int nested, int flags, if (char_type_p (typ1) && TREE_CODE (stripped_init) == STRING_CST) { - tree char_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init))); - bool incompat_string_cst = false; - - if (typ1 != char_type) - { - /* The array element type does not match the initializing string - literal element type; this is only allowed when both types are - ordinary character type. There are no string literals of - signed or unsigned char type in the language, but we can get - them internally from converting braced-init-lists to - STRING_CST. */ - if (ordinary_char_type_p (typ1) - && ordinary_char_type_p (char_type)) - /* OK */; - else - incompat_string_cst = true; - } - - if (incompat_string_cst) + if (!character_array_from_string_literal (type, init)) { if (complain & tf_error) error_at (loc, "cannot initialize array of %qT from " - "a string literal with type array of %qT", - typ1, char_type); + "a string literal with type array of %qT", + typ1, + TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (init)))); return error_mark_node; } diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C new file mode 100644 index 00000000000..fcc1f50dd81 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C @@ -0,0 +1,36 @@ +// PR c++/90926 +// { dg-do run { target c++14 } } + +#include <cassert> + +struct A +{ + char str[4] = "foo"; + char str_array[2][4] = {"bar", "baz"}; +}; + +struct B +{ + char16_t str[10]; +}; + +int called = 0; +void f(A) { called = 1;}; +void f(B) { called = 2;}; + +int +main () +{ + A a; + a.str[0] = 'g'; + a.str_array[0][0] = 'g'; + a = {}; + + if (__builtin_strcmp (a.str, "foo") != 0) + __builtin_abort(); + if (__builtin_strcmp (a.str_array[0], "bar") != 0) + __builtin_abort(); + + f({"foo"}); assert(called == 1); + f({u"foo"}); assert(called == 2); +}