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


Reply via email to