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