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