c++: fix string literal member initializer bug [PR90926]

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.

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 (array_string_literal_compatible_p): New function.
	(digest_init_r): call array_string_literal_compatible_p
	* cp-tree.h: (array_string_literal_compatible_p): Declare.

gcc/testsuite/ChangeLog:

	PR c++/90926
	* g++.dg/cpp1y/nsdmi-aggr12.C: New test.
This commit is contained in:
Tom Greenslade (thomgree) 2021-02-03 11:31:53 +00:00 committed by Jason Merrill
parent aa652fb2a0
commit e6cc142ad9
4 changed files with 92 additions and 41 deletions

View File

@ -895,28 +895,38 @@ 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 array_string_literal_compatible_p (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 +983,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,8 +1030,7 @@ 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)
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,

View File

@ -7949,6 +7949,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 array_string_literal_compatible_p (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);

View File

@ -1003,6 +1003,29 @@ ordinary_char_type_p (tree type)
|| type == unsigned_char_type_node);
}
/* True iff the string literal INIT has a type suitable for initializing array
TYPE. */
bool
array_string_literal_compatible_p (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 +1093,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 (!array_string_literal_compatible_p (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;
}

View File

@ -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);
}