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);
+    }
+
+  /* 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);
+}

Reply via email to