Hi! On the following testcase we ICE, because we don't diagnose invalid out of bounds stores and create garbage CONSTRUCTOR that the middle-end ICEs on. The following patch enables out of bounds diagnostics even for the case when lval is true, but because lval = true is used not just for the lhs of stores, but also e.g. for ADDR_EXPR handling, for lval = true it allows also ARRAY_REF one past the last element. And then in cxx_eval_store_expression it detects the case of exactly one past the last element and because we are dereferencing it (storing into it), diagnose that.
Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2016-12-14 Jakub Jelinek <ja...@redhat.com> PR c++/77830 * constexpr.c (cxx_eval_array_reference): Perform out of bounds verification even if lval is true, just allow one past the last element in that case. (cxx_eval_store_expression): Detect stores to out of bound ARRAY_REF. * g++.dg/cpp1y/pr77830.C: New test. * g++.dg/cpp0x/pr65398.C: Adjust expected diagnostics. --- gcc/cp/constexpr.c.jj 2016-12-08 23:17:58.000000000 +0100 +++ gcc/cp/constexpr.c 2016-12-14 16:38:54.494658094 +0100 @@ -2183,9 +2183,9 @@ cxx_eval_array_reference (const constexp lval, non_constant_p, overflow_p); tree index, oldidx; - HOST_WIDE_INT i; - tree elem_type; - unsigned len, elem_nchars = 1; + HOST_WIDE_INT i = 0; + tree elem_type = NULL_TREE; + unsigned len = 0, elem_nchars = 1; if (*non_constant_p) return t; oldidx = TREE_OPERAND (t, 1); @@ -2193,39 +2193,38 @@ cxx_eval_array_reference (const constexp false, non_constant_p, overflow_p); VERIFY_CONSTANT (index); - if (lval && ary == oldary && index == oldidx) - return t; - else if (lval) - return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL); - elem_type = TREE_TYPE (TREE_TYPE (ary)); - if (TREE_CODE (ary) == VIEW_CONVERT_EXPR - && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0))) - && TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0)))) - ary = TREE_OPERAND (ary, 0); - if (TREE_CODE (ary) == CONSTRUCTOR) - len = CONSTRUCTOR_NELTS (ary); - else if (TREE_CODE (ary) == STRING_CST) + if (!lval) { - elem_nchars = (TYPE_PRECISION (elem_type) - / TYPE_PRECISION (char_type_node)); - len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars; - } - else if (TREE_CODE (ary) == VECTOR_CST) - len = VECTOR_CST_NELTS (ary); - else - { - /* We can't do anything with other tree codes, so use - VERIFY_CONSTANT to complain and fail. */ - VERIFY_CONSTANT (ary); - gcc_unreachable (); - } + elem_type = TREE_TYPE (TREE_TYPE (ary)); + if (TREE_CODE (ary) == VIEW_CONVERT_EXPR + && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0))) + && TREE_TYPE (t) == TREE_TYPE (TREE_TYPE (TREE_OPERAND (ary, 0)))) + ary = TREE_OPERAND (ary, 0); + if (TREE_CODE (ary) == CONSTRUCTOR) + len = CONSTRUCTOR_NELTS (ary); + else if (TREE_CODE (ary) == STRING_CST) + { + elem_nchars = (TYPE_PRECISION (elem_type) + / TYPE_PRECISION (char_type_node)); + len = (unsigned) TREE_STRING_LENGTH (ary) / elem_nchars; + } + else if (TREE_CODE (ary) == VECTOR_CST) + len = VECTOR_CST_NELTS (ary); + else + { + /* We can't do anything with other tree codes, so use + VERIFY_CONSTANT to complain and fail. */ + VERIFY_CONSTANT (ary); + gcc_unreachable (); + } - if (!tree_fits_shwi_p (index) - || (i = tree_to_shwi (index)) < 0) - { - diag_array_subscript (ctx, ary, index); - *non_constant_p = true; - return t; + if (!tree_fits_shwi_p (index) + || (i = tree_to_shwi (index)) < 0) + { + diag_array_subscript (ctx, ary, index); + *non_constant_p = true; + return t; + } } tree nelts; @@ -2240,13 +2239,20 @@ cxx_eval_array_reference (const constexp nelts = cxx_eval_constant_expression (ctx, nelts, false, non_constant_p, overflow_p); VERIFY_CONSTANT (nelts); - if (!tree_int_cst_lt (index, nelts)) + if (lval + ? !tree_int_cst_le (index, nelts) + : !tree_int_cst_lt (index, nelts)) { diag_array_subscript (ctx, ary, index); *non_constant_p = true; return t; } + if (lval && ary == oldary && index == oldidx) + return t; + else if (lval) + return build4 (ARRAY_REF, TREE_TYPE (t), ary, index, NULL, NULL); + bool found; if (TREE_CODE (ary) == CONSTRUCTOR) { @@ -3281,6 +3287,47 @@ cxx_eval_store_expression (const constex if (*non_constant_p) return t; + /* cxx_eval_array_reference for lval = true allows references one past + end of array, because it does not know if it is just taking address + (which is valid), or actual dereference. Here we know it is + a dereference, so diagnose it here. */ + for (tree probe = target; probe; ) + { + switch (TREE_CODE (probe)) + { + case ARRAY_REF: + tree nelts, ary; + ary = TREE_OPERAND (probe, 0); + if (TREE_CODE (TREE_TYPE (ary)) == ARRAY_TYPE) + nelts = array_type_nelts_top (TREE_TYPE (ary)); + else if (VECTOR_TYPE_P (TREE_TYPE (ary))) + nelts = size_int (TYPE_VECTOR_SUBPARTS (TREE_TYPE (ary))); + else + gcc_unreachable (); + nelts = cxx_eval_constant_expression (ctx, nelts, false, + non_constant_p, overflow_p); + VERIFY_CONSTANT (nelts); + gcc_assert (TREE_CODE (nelts) == INTEGER_CST + && TREE_CODE (TREE_OPERAND (probe, 1)) == INTEGER_CST); + if (wi::eq_p (TREE_OPERAND (probe, 1), nelts)) + { + diag_array_subscript (ctx, ary, TREE_OPERAND (probe, 1)); + *non_constant_p = true; + return t; + } + /* FALLTHRU */ + + case BIT_FIELD_REF: + case COMPONENT_REF: + probe = TREE_OPERAND (probe, 0); + continue; + + default: + probe = NULL_TREE; + continue; + } + } + if (!same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (target), type)) { /* For initialization of an empty base, the original target will be --- gcc/testsuite/g++.dg/cpp1y/pr77830.C.jj 2016-12-13 15:10:36.235371736 +0100 +++ gcc/testsuite/g++.dg/cpp1y/pr77830.C 2016-12-13 15:10:08.000000000 +0100 @@ -0,0 +1,34 @@ +// PR c++/77830 +// { dg-do compile { target c++14 } } + +template <int N> +struct P +{ + char arr[N][1]; + constexpr void foo (const char *, int); +}; + +template <int N> +constexpr void +P<N>::foo (const char *, int i) +{ + for (auto j = 0; j < 2; ++j) + arr[i][j] = true; +} + +template <typename... T> +constexpr auto +bar (T... a) +{ + const char *s[]{a...}; + P<sizeof...(a)> p{}; + for (auto i = 0; i < sizeof...(a); ++i) + p.foo (s[i], i); + return p; +} + +int +main () +{ + constexpr auto a = bar ("", ""); // { dg-error "outside the bounds of array type" } +} --- gcc/testsuite/g++.dg/cpp0x/pr65398.C.jj 2016-04-04 12:28:39.000000000 +0200 +++ gcc/testsuite/g++.dg/cpp0x/pr65398.C 2016-12-14 12:18:12.509277893 +0100 @@ -20,9 +20,9 @@ constexpr char d5 = *(&s[4] - 4); constexpr char d6 = *(&s[4] - 5); // { dg-error "array subscript" } /* Don't accept invalid stuff. */ -constexpr char e1 = *(&s[5] - 1); // { dg-error "is not a constant expression" } -constexpr char e2 = *(&s[5] - 2); // { dg-error "is not a constant expression" } -constexpr char e3 = *(&s[5] - 3); // { dg-error "is not a constant expression" } +constexpr char e1 = *(&s[5] - 1); // { dg-error "array subscript" } +constexpr char e2 = *(&s[5] - 2); // { dg-error "array subscript" } +constexpr char e3 = *(&s[5] - 3); // { dg-error "array subscript" } SA (c1 == 'a'); SA (c2 == 'b'); @@ -53,9 +53,9 @@ constexpr char j5 = *(&l[4] - 4); constexpr char j6 = *(&l[4] - 5); // { dg-error "array subscript" } /* Don't accept invalid stuff. */ -constexpr char k1 = *(&l[5] - 1); // { dg-error "is not a constant expression" } -constexpr char k2 = *(&l[5] - 2); // { dg-error "is not a constant expression" } -constexpr char k3 = *(&l[5] - 3); // { dg-error "is not a constant expression" } +constexpr char k1 = *(&l[5] - 1); // { dg-error "array subscript" } +constexpr char k2 = *(&l[5] - 2); // { dg-error "array subscript" } +constexpr char k3 = *(&l[5] - 3); // { dg-error "array subscript" } SA (i1 == 'c'); SA (i2 == 'd'); Jakub