diff mbox series

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

Message ID SN6PR11MB3022BE79CE0B7765B9E7A26BCBB49@SN6PR11MB3022.namprd11.prod.outlook.com
State New
Headers show
Series [v2] c++: fix string literal member initializer bug [PR90926] | expand

Commit Message

Tom Greenslade \(thomgree\) Feb. 3, 2021, 11:31 a.m. UTC
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.

Comments

Jason Merrill Feb. 4, 2021, 3:47 p.m. UTC | #1
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 mbox series

Patch

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