Hi! On Tue, Oct 01, 2019 at 05:56:00PM -0400, Jason Merrill wrote: > I think we want to factor this function more, so we don't have the same code > in multiple places for handling an array, and an array member, and a pointer > to array. Do you want to take a look at bug 71504 while you're touching > this code?
The following patch does that. I haven't touched fold-const.c (perhaps we could handle multidimensional arrays there, but I wouldn't handle more, because that can be compile time expensive and there it is just an optimization), nor the last else if in cxx_fold_indirect_ref you've tweaked in your patch (nothing in the testcases needed it nor it is needed for the constexpr new stuff). Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? Or, alternatively we could perhaps drop the max_val related stuff, not check the upper bound at all, then constexpr-array13.C and pr63956.C tests wouldn't need to be adjusted. The difference is that with the max_val stuff in the patch we don't create ARRAY_REF in that case and fail constexpr evaluation because we didn't simplify it, while if max_val isn't there, we create ARRAY_REF, but when evaluating it we determine it is out of bounds access and complain. The initial reason for the max_val was to deal with upper bounds for the aggregate members, but that is already handled in the record/union case. And, second comment, not 100% sure about the unions, e.g. whether we shouldn't somehow try to figure what is the active union member and only use the active one rather than trying all. 2019-10-03 Jakub Jelinek <ja...@redhat.com> PR c++/71504 * constexpr.c (cxx_fold_indirect_ref_1): New function. (cxx_fold_indirect_ref): Use it. * g++.dg/cpp0x/constexpr-array13.C: Adjust expected diagnostics. * g++.dg/cpp0x/constexpr-array21.C: New test. * g++.dg/cpp1y/constexpr-array7.C: New test. * g++.dg/cpp1z/constexpr-array1.C: New test. * g++.dg/ubsan/pr63956.C: Adjust expected diagnostics. 2019-10-03 Jason Merrill <ja...@redhat.com> PR c++/71504 * g++.dg/cpp0x/constexpr-array20.C: New test. --- gcc/cp/constexpr.c.jj 2019-10-03 00:32:15.603526965 +0200 +++ gcc/cp/constexpr.c 2019-10-03 14:53:59.268559514 +0200 @@ -3346,6 +3346,111 @@ same_type_ignoring_tlq_and_bounds_p (tre 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<vectorfoo,...> */ + 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; + tree max_val = NULL_TREE; + if (type_domain && TYPE_MIN_VALUE (type_domain)) + min_val = TYPE_MIN_VALUE (type_domain); + if (type_domain && TYPE_MAX_VALUE (type_domain)) + max_val = TYPE_MAX_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) + && (max_val == NULL_TREE + /* Punt on checking VLA bounds here. */ + || TREE_CODE (max_val) != INTEGER_CST + || (tree_fits_uhwi_p (max_val) + && idx <= tree_to_uhwi (max_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 (RECORD_OR_UNION_TYPE_P (optype)) + { + 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 +3458,7 @@ same_type_ignoring_tlq_and_bounds_p (tre 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 +3489,19 @@ cxx_fold_indirect_ref (location_t loc, t 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<vectorfoo,...> */ - 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<vectorfoo,...> */ - 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 --- gcc/testsuite/g++.dg/cpp0x/constexpr-array13.C.jj 2015-11-26 10:40:59.546389024 +0100 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array13.C 2019-10-03 16:13:58.636977962 +0200 @@ -3,4 +3,4 @@ constexpr char c[] = "hello"; constexpr const char *p = c; -constexpr char ch = *(p-1); // { dg-error "array subscript" } +constexpr char ch = *(p-1); // { dg-error "is not a constant expression" } --- gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C.jj 2019-10-03 15:59:29.108161779 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C 2019-10-03 14:29:34.443660433 +0200 @@ -0,0 +1,15 @@ +// PR c++/71504 +// { dg-do compile { target c++11 } } + +enum E { e }; + +constexpr bool arr[1][1] = {{true}}; + +template<E x, E y> +void check() { + static_assert(arr[x][y], ""); +} + +int main() { + check<e, e>(); +} --- gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C.jj 2019-10-03 15:59:32.840105187 +0200 +++ gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C 2019-10-03 15:15:42.260881097 +0200 @@ -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); --- gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C.jj 2019-10-03 16:04:04.280988955 +0200 +++ gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C 2019-10-03 16:01:46.674075678 +0200 @@ -0,0 +1,16 @@ +// PR c++/71504 +// { dg-do compile { target c++14 } } + +template <typename A> +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"); --- gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C.jj 2019-10-03 16:00:05.962602903 +0200 +++ gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C 2019-10-03 15:40:09.290739197 +0200 @@ -0,0 +1,46 @@ +// PR c++/71504 +// { dg-do compile { target c++17 } } + +typedef __SIZE_TYPE__ size_t; +template <typename T, T v> +struct integral_constant +{ + static constexpr T value = v; + typedef T value_type; + typedef integral_constant<T, v> type; + constexpr operator value_type () const noexcept { return value; } + constexpr value_type operator() () const noexcept { return value; } +}; +template <typename T, T v> +constexpr T integral_constant<T, v>::value; +typedef integral_constant<bool, true> true_type; +typedef integral_constant<bool, false> false_type; +template <typename> +struct is_array : public false_type { }; +template <typename T, size_t s> +struct is_array<T[s]> : public true_type { }; +template <typename T> +struct is_array<T[]> : public true_type { }; +template <bool, typename, typename> +struct conditional; +template <bool C, typename T, typename F> +struct conditional { typedef T type; }; +template <typename T, typename F> +struct conditional<false, T, F> { typedef F type; }; +template <typename T> +struct array_ref; +template <typename T> +using ref_t = typename conditional<is_array<T>::value, array_ref<T>, T&>::type; +template <typename T, unsigned N> +struct array_ref<T[N]> +{ + T *a; + using const_reference = const ref_t<T>; + constexpr const_reference operator[] (unsigned I) const { return {a[I]}; } +}; +template <typename A> +array_ref (A&) -> array_ref<A>; +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); --- gcc/testsuite/g++.dg/ubsan/pr63956.C.jj 2019-06-19 10:20:12.390666122 +0200 +++ gcc/testsuite/g++.dg/ubsan/pr63956.C 2019-10-03 18:57:25.541783658 +0200 @@ -80,7 +80,7 @@ constexpr int fn5 (const int *a, int b) { if (b != 2) - b = a[b]; // { dg-error "array subscript" } + b = a[b]; // { dg-error "is not a constant expression" } return b; } Jakub