diff mbox series

c++: partially initialized constexpr array [PR99699]

Message ID 20210415195155.2635971-1-ppalka@redhat.com
State New
Headers show
Series c++: partially initialized constexpr array [PR99699] | expand

Commit Message

Patrick Palka April 15, 2021, 7:51 p.m. UTC
Here, reduced_constant_expression_p is incorrectly returning true for a
partially initialized array CONSTRUCTOR, because when the
CONSTRUCTOR_NO_CLEARING flag is set the predicate doesn't check that
every array element is initialized by the CONSTRUCTOR, it just checks
that every initializer within the CONSTRUCTOR is a valid constant
expression.  This patch makes reduced_constant_expression_p check both
conditions in a single iteration over the CONSTRUCTOR elements.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk?

gcc/cp/ChangeLog:

	PR c++/99700
	* constexpr.c (reduced_constant_expression_p): Return false
	if an array CONSTRUCTOR is missing an element at some array
	index when the CONSTRUCTOR_NO_CLEARING flag is set.

gcc/testsuite/ChangeLog:

	PR c++/99700
	* g++.dg/cpp2a/constexpr-init21.C: New test.
---
 gcc/cp/constexpr.c                            | 24 +++++++++++++++--
 gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C | 27 +++++++++++++++++++
 2 files changed, 49 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C

Comments

Jason Merrill April 15, 2021, 8:09 p.m. UTC | #1
On 4/15/21 3:51 PM, Patrick Palka wrote:
> Here, reduced_constant_expression_p is incorrectly returning true for a
> partially initialized array CONSTRUCTOR, because when the
> CONSTRUCTOR_NO_CLEARING flag is set the predicate doesn't check that
> every array element is initialized by the CONSTRUCTOR, it just checks
> that every initializer within the CONSTRUCTOR is a valid constant
> expression.  This patch makes reduced_constant_expression_p check both
> conditions in a single iteration over the CONSTRUCTOR elements.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?

OK.

> gcc/cp/ChangeLog:
> 
> 	PR c++/99700
> 	* constexpr.c (reduced_constant_expression_p): Return false
> 	if an array CONSTRUCTOR is missing an element at some array
> 	index when the CONSTRUCTOR_NO_CLEARING flag is set.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/99700
> 	* g++.dg/cpp2a/constexpr-init21.C: New test.
> ---
>   gcc/cp/constexpr.c                            | 24 +++++++++++++++--
>   gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C | 27 +++++++++++++++++++
>   2 files changed, 49 insertions(+), 2 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C
> 
> diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
> index c8d9dae36fb..b74bbac3cd2 100644
> --- a/gcc/cp/constexpr.c
> +++ b/gcc/cp/constexpr.c
> @@ -46,6 +46,7 @@ do {									\
>   
>   static HOST_WIDE_INT find_array_ctor_elt (tree ary, tree dindex,
>   					  bool insert = false);
> +static int array_index_cmp (tree key, tree index);
>   
>   /* Returns true iff FUN is an instantiation of a constexpr function
>      template or a defaulted constexpr function.  */
> @@ -2910,9 +2911,27 @@ reduced_constant_expression_p (tree t)
>   	    /* An initialized vector would have a VECTOR_CST.  */
>   	    return false;
>   	  else if (cxx_dialect >= cxx20
> -		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
>   		   && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
> -	    field = NULL_TREE;
> +	    {
> +	      /* There must be a valid constant initializer at every array
> +		 index.  */
> +	      tree min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
> +	      tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
> +	      tree cursor = min;
> +	      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), i, idx, val)
> +		{
> +		  if (!reduced_constant_expression_p (val))
> +		    return false;
> +		  if (array_index_cmp (cursor, idx) != 0)
> +		    return false;
> +		  if (TREE_CODE (idx) == RANGE_EXPR)
> +		    cursor = TREE_OPERAND (idx, 1);
> +		  cursor = int_const_binop (PLUS_EXPR, cursor, size_one_node);
> +		}
> +	      if (find_array_ctor_elt (t, max) == -1)
> +		return false;
> +	      goto ok;
> +	    }
>   	  else if (cxx_dialect >= cxx20
>   		   && TREE_CODE (TREE_TYPE (t)) == UNION_TYPE)
>   	    {
> @@ -2946,6 +2965,7 @@ reduced_constant_expression_p (tree t)
>         for (; field; field = next_initializable_field (DECL_CHAIN (field)))
>   	if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
>   	  return false;
> +ok:
>         if (CONSTRUCTOR_NO_CLEARING (t))
>   	/* All the fields are initialized.  */
>   	CONSTRUCTOR_NO_CLEARING (t) = false;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C
> new file mode 100644
> index 00000000000..47b4bff21b5
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C
> @@ -0,0 +1,27 @@
> +// PR c++/99700
> +// { dg-do compile { target c++20 } }
> +
> +template <class T>
> +struct A {
> +  T c[5];
> +  constexpr A(int skip = -1) {
> +    for (int i = 0; i < 5; i++)
> +      if (skip != i)
> +        c[i] = {};
> +  }
> +};
> +
> +constexpr A<int> a;
> +constexpr A<int> a0(0); // { dg-error "not a constant expression|incompletely initialized" }
> +constexpr A<int> a1(1); // { dg-error "not a constant expression|incompletely initialized" }
> +constexpr A<int> a2(2); // { dg-error "not a constant expression|incompletely initialized" }
> +constexpr A<int> a3(3); // { dg-error "not a constant expression|incompletely initialized" }
> +constexpr A<int> a4(4); // { dg-error "not a constant expression|incompletely initialized" }
> +
> +struct s { int n; };
> +constexpr A<s> b;
> +constexpr A<s> b0(0); // {  dg-error "not a constant expression|incompletely initialized" }
> +
> +struct empty {};
> +constexpr A<empty> c;
> +constexpr A<empty> c0(0);
>
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index c8d9dae36fb..b74bbac3cd2 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -46,6 +46,7 @@  do {									\
 
 static HOST_WIDE_INT find_array_ctor_elt (tree ary, tree dindex,
 					  bool insert = false);
+static int array_index_cmp (tree key, tree index);
 
 /* Returns true iff FUN is an instantiation of a constexpr function
    template or a defaulted constexpr function.  */
@@ -2910,9 +2911,27 @@  reduced_constant_expression_p (tree t)
 	    /* An initialized vector would have a VECTOR_CST.  */
 	    return false;
 	  else if (cxx_dialect >= cxx20
-		   /* An ARRAY_TYPE doesn't have any TYPE_FIELDS.  */
 		   && TREE_CODE (TREE_TYPE (t)) == ARRAY_TYPE)
-	    field = NULL_TREE;
+	    {
+	      /* There must be a valid constant initializer at every array
+		 index.  */
+	      tree min = TYPE_MIN_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
+	      tree max = TYPE_MAX_VALUE (TYPE_DOMAIN (TREE_TYPE (t)));
+	      tree cursor = min;
+	      FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (t), i, idx, val)
+		{
+		  if (!reduced_constant_expression_p (val))
+		    return false;
+		  if (array_index_cmp (cursor, idx) != 0)
+		    return false;
+		  if (TREE_CODE (idx) == RANGE_EXPR)
+		    cursor = TREE_OPERAND (idx, 1);
+		  cursor = int_const_binop (PLUS_EXPR, cursor, size_one_node);
+		}
+	      if (find_array_ctor_elt (t, max) == -1)
+		return false;
+	      goto ok;
+	    }
 	  else if (cxx_dialect >= cxx20
 		   && TREE_CODE (TREE_TYPE (t)) == UNION_TYPE)
 	    {
@@ -2946,6 +2965,7 @@  reduced_constant_expression_p (tree t)
       for (; field; field = next_initializable_field (DECL_CHAIN (field)))
 	if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false))
 	  return false;
+ok:
       if (CONSTRUCTOR_NO_CLEARING (t))
 	/* All the fields are initialized.  */
 	CONSTRUCTOR_NO_CLEARING (t) = false;
diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C
new file mode 100644
index 00000000000..47b4bff21b5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C
@@ -0,0 +1,27 @@ 
+// PR c++/99700
+// { dg-do compile { target c++20 } }
+
+template <class T>
+struct A {
+  T c[5];
+  constexpr A(int skip = -1) {
+    for (int i = 0; i < 5; i++)
+      if (skip != i)
+        c[i] = {};
+  }
+};
+
+constexpr A<int> a;
+constexpr A<int> a0(0); // { dg-error "not a constant expression|incompletely initialized" }
+constexpr A<int> a1(1); // { dg-error "not a constant expression|incompletely initialized" }
+constexpr A<int> a2(2); // { dg-error "not a constant expression|incompletely initialized" }
+constexpr A<int> a3(3); // { dg-error "not a constant expression|incompletely initialized" }
+constexpr A<int> a4(4); // { dg-error "not a constant expression|incompletely initialized" }
+
+struct s { int n; };
+constexpr A<s> b;
+constexpr A<s> b0(0); // {  dg-error "not a constant expression|incompletely initialized" }
+
+struct empty {};
+constexpr A<empty> c;
+constexpr A<empty> c0(0);