From 0fe2ae2902e9e65a4ce6cf56b3c34c02677d33a4 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Fri, 4 Oct 2019 08:56:02 +0200 Subject: [PATCH] re PR c++/71504 ([C++11] constexpr fails with multidimensional arrays) PR c++/71504 * constexpr.c (cxx_fold_indirect_ref_1): New function. (cxx_fold_indirect_ref): Use it. * g++.dg/cpp0x/constexpr-array21.C: New test. * g++.dg/cpp1y/constexpr-array7.C: New test. * g++.dg/cpp1z/constexpr-array1.C: New test. 2019-10-04 Jason Merrill PR c++/71504 * g++.dg/cpp0x/constexpr-array20.C: New test. From-SVN: r276563 --- gcc/cp/ChangeLog | 4 + gcc/cp/constexpr.c | 231 ++++++++---------- gcc/testsuite/ChangeLog | 12 + .../g++.dg/cpp0x/constexpr-array20.C | 15 ++ .../g++.dg/cpp0x/constexpr-array21.C | 27 ++ gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C | 16 ++ gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C | 46 ++++ 7 files changed, 223 insertions(+), 128 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index a11bdaf693a..e705bcce438 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,9 @@ 2019-10-04 Jakub Jelinek + PR c++/71504 + * constexpr.c (cxx_fold_indirect_ref_1): New function. + (cxx_fold_indirect_ref): Use it. + PR c++/91974 * cp-gimplify.c (cp_gimplify_expr) : For -fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 200879378c5..2dc57f145c3 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -3346,6 +3346,103 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2) return same_type_ignoring_top_level_qualifiers_p (type1, type2); } +/* Helper function for cxx_fold_indirect_ref_1, called recursively. */ + +static tree +cxx_fold_indirect_ref_1 (location_t loc, tree type, tree op, + unsigned HOST_WIDE_INT off, bool *empty_base) +{ + tree optype = TREE_TYPE (op); + unsigned HOST_WIDE_INT const_nunits; + if (off == 0) + { + if (similar_type_p (optype, type)) + return op; + /* Also handle conversion to an empty base class, which + is represented with a NOP_EXPR. */ + /* *(foo *)&complexfoo => __real__ complexfoo */ + else if (TREE_CODE (optype) == COMPLEX_TYPE + && similar_type_p (type, TREE_TYPE (optype))) + return build1_loc (loc, REALPART_EXPR, type, op); + } + /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */ + else if (TREE_CODE (optype) == COMPLEX_TYPE + && similar_type_p (type, TREE_TYPE (optype)) + && tree_to_uhwi (TYPE_SIZE_UNIT (type)) == off) + return build1_loc (loc, IMAGPART_EXPR, type, op); + if (is_empty_class (type) + && CLASS_TYPE_P (optype) + && DERIVED_FROM_P (type, optype)) + { + *empty_base = true; + return op; + } + /* ((foo*)&vectorfoo)[x] => BIT_FIELD_REF */ + else if (VECTOR_TYPE_P (optype) + && similar_type_p (type, TREE_TYPE (optype)) + && TYPE_VECTOR_SUBPARTS (optype).is_constant (&const_nunits)) + { + unsigned HOST_WIDE_INT part_width = tree_to_uhwi (TYPE_SIZE_UNIT (type)); + unsigned HOST_WIDE_INT max_offset = part_width * const_nunits; + if (off < max_offset && off % part_width == 0) + { + tree index = bitsize_int (off * BITS_PER_UNIT); + return build3_loc (loc, BIT_FIELD_REF, type, op, + TYPE_SIZE (type), index); + } + } + /* ((foo *)&fooarray)[x] => fooarray[x] */ + else if (TREE_CODE (optype) == ARRAY_TYPE + && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (optype))) + && !integer_zerop (TYPE_SIZE_UNIT (TREE_TYPE (optype)))) + { + tree type_domain = TYPE_DOMAIN (optype); + tree min_val = size_zero_node; + if (type_domain && TYPE_MIN_VALUE (type_domain)) + min_val = TYPE_MIN_VALUE (type_domain); + unsigned HOST_WIDE_INT el_sz + = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (optype))); + unsigned HOST_WIDE_INT idx = off / el_sz; + unsigned HOST_WIDE_INT rem = off % el_sz; + if (tree_fits_uhwi_p (min_val)) + { + tree index = size_int (idx + tree_to_uhwi (min_val)); + op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index, + NULL_TREE, NULL_TREE); + return cxx_fold_indirect_ref_1 (loc, type, op, rem, + empty_base); + } + } + /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */ + else if (TREE_CODE (optype) == RECORD_TYPE) + { + for (tree field = TYPE_FIELDS (optype); + field; field = DECL_CHAIN (field)) + if (TREE_CODE (field) == FIELD_DECL + && TREE_TYPE (field) != error_mark_node + && tree_fits_uhwi_p (TYPE_SIZE_UNIT (TREE_TYPE (field)))) + { + tree pos = byte_position (field); + if (!tree_fits_uhwi_p (pos)) + continue; + unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos); + unsigned el_sz + = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); + if (upos <= off && off < upos + el_sz) + { + tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), + op, field, NULL_TREE); + if (tree ret = cxx_fold_indirect_ref_1 (loc, type, cop, + off - upos, + empty_base)) + return ret; + } + } + } + + return NULL_TREE; +} + /* A less strict version of fold_indirect_ref_1, which requires cv-quals to match. We want to be less strict for simple *& folding; if we have a non-const temporary that we access through a const pointer, that should @@ -3353,9 +3450,7 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2) because we're dealing with things like ADDR_EXPR of INTEGER_CST which don't really make sense outside of constant expression evaluation. Also we want to allow folding to COMPONENT_REF, which could cause trouble - with TBAA in fold_indirect_ref_1. - - Try to keep this function synced with fold_indirect_ref_1. */ + with TBAA in fold_indirect_ref_1. */ static tree cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) @@ -3386,139 +3481,19 @@ cxx_fold_indirect_ref (location_t loc, tree type, tree op0, bool *empty_base) else return op; } - /* *(foo *)&fooarray => fooarray[0] */ - else if (TREE_CODE (optype) == ARRAY_TYPE - && similar_type_p (type, TREE_TYPE (optype))) - { - tree type_domain = TYPE_DOMAIN (optype); - tree min_val = size_zero_node; - if (type_domain && TYPE_MIN_VALUE (type_domain)) - min_val = TYPE_MIN_VALUE (type_domain); - return build4_loc (loc, ARRAY_REF, type, op, min_val, - NULL_TREE, NULL_TREE); - } - /* *(foo *)&complexfoo => __real__ complexfoo */ - else if (TREE_CODE (optype) == COMPLEX_TYPE - && similar_type_p (type, TREE_TYPE (optype))) - return fold_build1_loc (loc, REALPART_EXPR, type, op); - /* *(foo *)&vectorfoo => BIT_FIELD_REF */ - else if (VECTOR_TYPE_P (optype) - && similar_type_p (type, TREE_TYPE (optype))) - { - tree part_width = TYPE_SIZE (type); - tree index = bitsize_int (0); - return fold_build3_loc (loc, BIT_FIELD_REF, type, op, part_width, - index); - } - /* Also handle conversion to an empty base class, which - is represented with a NOP_EXPR. */ - else if (is_empty_class (type) - && CLASS_TYPE_P (optype) - && DERIVED_FROM_P (type, optype)) - { - *empty_base = true; - return op; - } - /* *(foo *)&struct_with_foo_field => COMPONENT_REF */ - else if (RECORD_OR_UNION_TYPE_P (optype)) - { - tree field = TYPE_FIELDS (optype); - for (; field; field = DECL_CHAIN (field)) - if (TREE_CODE (field) == FIELD_DECL - && TREE_TYPE (field) != error_mark_node - && integer_zerop (byte_position (field)) - && similar_type_p (TREE_TYPE (field), type)) - return fold_build3 (COMPONENT_REF, type, op, field, NULL_TREE); - } + else + return cxx_fold_indirect_ref_1 (loc, type, op, 0, empty_base); } else if (TREE_CODE (sub) == POINTER_PLUS_EXPR - && poly_int_tree_p (TREE_OPERAND (sub, 1), &const_op01)) + && tree_fits_uhwi_p (TREE_OPERAND (sub, 1))) { tree op00 = TREE_OPERAND (sub, 0); tree op01 = TREE_OPERAND (sub, 1); STRIP_NOPS (op00); if (TREE_CODE (op00) == ADDR_EXPR) - { - tree op00type; - op00 = TREE_OPERAND (op00, 0); - op00type = TREE_TYPE (op00); - - /* ((foo*)&vectorfoo)[1] => BIT_FIELD_REF */ - if (VECTOR_TYPE_P (op00type) - && similar_type_p (type, TREE_TYPE (op00type)) - /* POINTER_PLUS_EXPR second operand is sizetype, unsigned, - but we want to treat offsets with MSB set as negative. - For the code below negative offsets are invalid and - TYPE_SIZE of the element is something unsigned, so - check whether op01 fits into poly_int64, which implies - it is from 0 to INTTYPE_MAXIMUM (HOST_WIDE_INT), and - then just use poly_uint64 because we want to treat the - value as unsigned. */ - && tree_fits_poly_int64_p (op01)) - { - tree part_width = TYPE_SIZE (type); - poly_uint64 max_offset - = (tree_to_uhwi (part_width) / BITS_PER_UNIT - * TYPE_VECTOR_SUBPARTS (op00type)); - if (known_lt (const_op01, max_offset)) - { - tree index = bitsize_int (const_op01 * BITS_PER_UNIT); - return fold_build3_loc (loc, - BIT_FIELD_REF, type, op00, - part_width, index); - } - } - /* ((foo*)&complexfoo)[1] => __imag__ complexfoo */ - else if (TREE_CODE (op00type) == COMPLEX_TYPE - && similar_type_p (type, TREE_TYPE (op00type))) - { - if (known_eq (wi::to_poly_offset (TYPE_SIZE_UNIT (type)), - const_op01)) - return fold_build1_loc (loc, IMAGPART_EXPR, type, op00); - } - /* ((foo *)&fooarray)[1] => fooarray[1] */ - else if (TREE_CODE (op00type) == ARRAY_TYPE - && similar_type_p (type, TREE_TYPE (op00type))) - { - tree type_domain = TYPE_DOMAIN (op00type); - tree min_val = size_zero_node; - if (type_domain && TYPE_MIN_VALUE (type_domain)) - min_val = TYPE_MIN_VALUE (type_domain); - offset_int off = wi::to_offset (op01); - offset_int el_sz = wi::to_offset (TYPE_SIZE_UNIT (type)); - offset_int remainder; - off = wi::divmod_trunc (off, el_sz, SIGNED, &remainder); - if (remainder == 0 && TREE_CODE (min_val) == INTEGER_CST) - { - off = off + wi::to_offset (min_val); - op01 = wide_int_to_tree (sizetype, off); - return build4_loc (loc, ARRAY_REF, type, op00, op01, - NULL_TREE, NULL_TREE); - } - } - /* Also handle conversion to an empty base class, which - is represented with a NOP_EXPR. */ - else if (is_empty_class (type) - && CLASS_TYPE_P (op00type) - && DERIVED_FROM_P (type, op00type)) - { - *empty_base = true; - return op00; - } - /* ((foo *)&struct_with_foo_field)[1] => COMPONENT_REF */ - else if (RECORD_OR_UNION_TYPE_P (op00type)) - { - tree field = TYPE_FIELDS (op00type); - for (; field; field = DECL_CHAIN (field)) - if (TREE_CODE (field) == FIELD_DECL - && TREE_TYPE (field) != error_mark_node - && tree_int_cst_equal (byte_position (field), op01) - && similar_type_p (TREE_TYPE (field), type)) - return fold_build3 (COMPONENT_REF, type, op00, - field, NULL_TREE); - } - } + return cxx_fold_indirect_ref_1 (loc, type, TREE_OPERAND (op00, 0), + tree_to_uhwi (op01), empty_base); } /* *(foo *)fooarrptr => (*fooarrptr)[0] */ else if (TREE_CODE (TREE_TYPE (subtype)) == ARRAY_TYPE diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 1dce341bd58..60e6caa2520 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2019-10-04 Jakub Jelinek + + PR c++/71504 + * g++.dg/cpp0x/constexpr-array21.C: New test. + * g++.dg/cpp1y/constexpr-array7.C: New test. + * g++.dg/cpp1z/constexpr-array1.C: New test. + +2019-10-04 Jason Merrill + + PR c++/71504 + * g++.dg/cpp0x/constexpr-array20.C: New test. + 2019-10-04 Jakub Jelinek PR c++/91974 diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C new file mode 100644 index 00000000000..217bbf4efb7 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C @@ -0,0 +1,15 @@ +// PR c++/71504 +// { dg-do compile { target c++11 } } + +enum E { e }; + +constexpr bool arr[1][1] = {{true}}; + +template +void check() { + static_assert(arr[x][y], ""); +} + +int main() { + check(); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C new file mode 100644 index 00000000000..e085098445a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C @@ -0,0 +1,27 @@ +// PR c++/71504 +// { dg-do compile { target c++11 } } + +typedef const char A4 [10]; + +constexpr A4 a [] = { "123", "123456", "123456789" }; + +constexpr int len (const char *s) +{ + return *s ? 1 + len (s + 1) : 0; +} + +constexpr const char *s = a[0]; +constexpr const char *t = (a + 2)[-2]; + +constexpr int n0 = len (s); +constexpr int n1 = len (t); + +constexpr int n2 = len (a[0]); +constexpr int n3 = len ((a + 2)[-2]); + +#define A(e) static_assert ((e), #e) + +A (n0 == 3); +A (n0 == n1); +A (n0 == n2); +A (n0 == n3); diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C new file mode 100644 index 00000000000..30bd8fd023b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C @@ -0,0 +1,16 @@ +// PR c++/71504 +// { dg-do compile { target c++14 } } + +template +constexpr auto +sum (A const &a) +{ + int tot = 0; + for (auto &row : a) + for (auto elem : row) + tot += elem; + return tot; +} + +constexpr int const a22[2][2] = {{1,2},{3,4}}; +static_assert (sum(a22) == 10, "badsum"); diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C new file mode 100644 index 00000000000..02435d505de --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C @@ -0,0 +1,46 @@ +// PR c++/71504 +// { dg-do compile { target c++17 } } + +typedef __SIZE_TYPE__ size_t; +template +struct integral_constant +{ + static constexpr T value = v; + typedef T value_type; + typedef integral_constant type; + constexpr operator value_type () const noexcept { return value; } + constexpr value_type operator() () const noexcept { return value; } +}; +template +constexpr T integral_constant::value; +typedef integral_constant true_type; +typedef integral_constant false_type; +template +struct is_array : public false_type { }; +template +struct is_array : public true_type { }; +template +struct is_array : public true_type { }; +template +struct conditional; +template +struct conditional { typedef T type; }; +template +struct conditional { typedef F type; }; +template +struct array_ref; +template +using ref_t = typename conditional::value, array_ref, T&>::type; +template +struct array_ref +{ + T *a; + using const_reference = const ref_t; + constexpr const_reference operator[] (unsigned I) const { return {a[I]}; } +}; +template +array_ref (A&) -> array_ref; +constexpr int a2[2] = {1,2}; +static_assert (array_ref{a2}[0] == 1); +constexpr int a22[2][2] = {{1,2},{3,4}}; +static_assert (array_ref{a22}[0][0] == 1); -- 2.30.2