c++: partially initialized constexpr array [PR99700]

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.
This commit is contained in:
Patrick Palka 2021-04-16 09:24:46 -04:00
parent 47f42744f6
commit baf05d54dc
2 changed files with 49 additions and 2 deletions

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

View File

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