c++: fix string literal member initializer bug [PR90926]
authorTom Greenslade (thomgree) <thomgree@cisco.com>
Wed, 3 Feb 2021 11:31:53 +0000 (11:31 +0000)
committerJason Merrill <jason@redhat.com>
Thu, 4 Feb 2021 15:46:39 +0000 (10:46 -0500)
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.

gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/typeck2.c
gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C [new file with mode: 0644]

index 3068c0f8cfd30a302a80760978d0dfd9f412a8c1..c7e13f3a22bcf73ea58de44af18a161c97b0cb2a 100644 (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,
index aed85d7928715fd9613c455ff78c63f744d526e3..970ed5e77bbb8147389aca8a22ae634f8569448e 100644 (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);
index 9ba2897390a2ac6c132011b034cad80de3f9e155..bde305bd38eecb98c96717d23579521f9258a9d3 100644 (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;
            }
 
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr12.C
new file mode 100644 (file)
index 0000000..fcc1f50
--- /dev/null
@@ -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);
+}