c++: partially initialized constexpr array [PR99700]
authorPatrick Palka <ppalka@redhat.com>
Fri, 16 Apr 2021 13:24:46 +0000 (09:24 -0400)
committerPatrick Palka <ppalka@redhat.com>
Fri, 16 Apr 2021 13:24:46 +0000 (09:24 -0400)
Here, reduced_constant_expression_p is incorrectly returning true for a
partially initialized array CONSTRUCTOR (in C++20 mode) because when the
CONSTRUCTOR_NO_CLEARING flag is set, the predicate doesn't check that
the CONSTRUCTOR spans the entire array like it does for class CONSTRUCTORS.
This patch adds a dedicated loop for the array case that simultaneously
verifies the CONSTRUCTOR spans the entire array and is made up of valid
constant expressions.

gcc/cp/ChangeLog:

PR c++/99700
* constexpr.c (reduced_constant_expression_p): For array
CONSTRUCTORs, use a dedicated loop that additionally verifies
the CONSTRUCTOR spans the entire array.

gcc/testsuite/ChangeLog:

PR c++/99700
* g++.dg/cpp2a/constexpr-init21.C: New test.

gcc/cp/constexpr.c
gcc/testsuite/g++.dg/cpp2a/constexpr-init21.C [new file with mode: 0644]

index c8d9dae..b74bbac 100644 (file)
@@ -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 (file)
index 0000000..f5e1b3e
--- /dev/null
@@ -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 (i != skip)
+        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);