Message ID | 20210415195155.2635971-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: partially initialized constexpr array [PR99699] | expand |
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 --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);