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 <jason@redhat.com> PR c++/71504 * g++.dg/cpp0x/constexpr-array20.C: New test. From-SVN: r276563
This commit is contained in:
parent
708935b2b4
commit
0fe2ae2902
@ -1,5 +1,9 @@
|
||||
2019-10-04 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
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) <case CALL_EXPR>: For
|
||||
-fstrong-eval-order ensure CALL_EXPR_FN side-effects are evaluated
|
||||
|
@ -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<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;
|
||||
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<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
|
||||
|
@ -1,3 +1,15 @@
|
||||
2019-10-04 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
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 <jason@redhat.com>
|
||||
|
||||
PR c++/71504
|
||||
* g++.dg/cpp0x/constexpr-array20.C: New test.
|
||||
|
||||
2019-10-04 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR c++/91974
|
||||
|
15
gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C
Normal file
15
gcc/testsuite/g++.dg/cpp0x/constexpr-array20.C
Normal file
@ -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>();
|
||||
}
|
27
gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C
Normal file
27
gcc/testsuite/g++.dg/cpp0x/constexpr-array21.C
Normal file
@ -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);
|
16
gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C
Normal file
16
gcc/testsuite/g++.dg/cpp1y/constexpr-array7.C
Normal file
@ -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");
|
46
gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C
Normal file
46
gcc/testsuite/g++.dg/cpp1z/constexpr-array1.C
Normal file
@ -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);
|
Loading…
x
Reference in New Issue
Block a user