A static_cast of a pointer to data member used to wrap the PTRMEM_CST in a NOP_EXPR, but the NOP_EXPR is taken, in constexpr, as evidence that there was a reinterpret_cast in the expression. While reinterpret_casts are to be rejected in constexprs, static_casts are ok.
Thus, avoid introducing the NOP_EXPR in static_casts, folding the converted-to type into the PTRMEM_CST type. This requires PTRMEM_CST constant expansion to deal with such up and downcasts. --- I've tested this sucessfully with check-c++-all, but I'm not entirely happy with it, not just because the following testcase still fails (though the testcases in the patch pass), but also because the early folding and the extra work in cplus_expand_constant don't feel quite right. struct A { }; struct B { int x; }; struct C : A, B {}; constexpr int C::*pci = &B::x; constexpr int A::*pai = static_cast<int A::*>(pci); I've experimented with an alternative of marking NOP_EXPRs introduced by static_casts and const_casts with a flag (static_flag), but that felt even more fragile, since we drop and rebuild NOP_EXPRs all the time, redundant ones used to be dropped safely, and so both positive and negative marks for constexpr compatibility could be lost, leading to false positives or missed errors. Still, it seems like we'd be better off with some reliable means to tell constexpr-compatible casts from other conversions. NOP_EXPRs alone just don't cut it. Anyway, at this point I'd appreciate some guidance as to how to proceed. At this stage of GCC8 development, I'm even considering dropping the incorrect complaint about reinterpret_cast, even if that would regress the rejection of casts that don't belong in constexprs. Thoughts? Suggestions? Thanks in advance, --- for gcc/cp/ChangeLog PR c++/85437 * expr.c (cplus_expand_constant): Compute deltas for up and downcasts. * type.c (convert_ptrmem): Convert ptrmem type for static cast. for gcc/testsuite/ChangeLog PR c++/85437 * g++.dg/cpp0x/pr85437.C: New. * g++.dg/cpp0x/pr85437-2.C: New. * g++.dg/cpp0x/pr85437-3.C: New. --- gcc/cp/expr.c | 25 +++++++++++++++++++++++++ gcc/cp/typeck.c | 5 ++++- gcc/testsuite/g++.dg/cpp0x/pr85437-2.C | 7 +++++++ gcc/testsuite/g++.dg/cpp0x/pr85437-3.C | 7 +++++++ gcc/testsuite/g++.dg/cpp0x/pr85437.C | 16 ++++++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr85437-2.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr85437-3.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/pr85437.C diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index 15894fc0b594..28fe2e83398d 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -50,11 +50,36 @@ cplus_expand_constant (tree cst) while (!same_type_p (DECL_CONTEXT (member), TYPE_PTRMEM_CLASS_TYPE (type))) { + tree t1 = TYPE_MAIN_VARIANT (DECL_CONTEXT (member)); + tree t2 = TYPE_MAIN_VARIANT (TYPE_PTRMEM_CLASS_TYPE (type)); + + if (can_convert (t2, t1, 0)) + { + base_kind kind; + tree binfo = lookup_base (t1, t2, ba_unique, &kind, 0); + if (binfo != error_mark_node + && kind != bk_via_virtual) + cst = size_binop (MINUS_EXPR, cst, BINFO_OFFSET (binfo)); + break; + } + + if (can_convert (t1, t2, 0)) + { + base_kind kind; + tree binfo = lookup_base (t2, t1, ba_unique, &kind, 0); + if (binfo != error_mark_node + && kind != bk_via_virtual) + cst = size_binop (PLUS_EXPR, cst, BINFO_OFFSET (binfo)); + break; + } + /* The MEMBER must have been nestled within an anonymous aggregate contained in TYPE. Find the anonymous aggregate. */ member = lookup_anon_field (TYPE_PTRMEM_CLASS_TYPE (type), DECL_CONTEXT (member)); + if (!member) + break; cst = size_binop (PLUS_EXPR, cst, byte_position (member)); } cst = fold (build_nop (type, cst)); diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index b449b1f7f539..0b88181e9574 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6871,7 +6871,10 @@ convert_ptrmem (tree type, tree expr, bool allow_inverse_p, } - return build_nop (type, expr); + if (c_cast_p) + return build_nop (type, expr); + else + return cp_fold_convert (type, expr); } else return build_ptrmemfunc (TYPE_PTRMEMFUNC_FN_TYPE (type), expr, diff --git a/gcc/testsuite/g++.dg/cpp0x/pr85437-2.C b/gcc/testsuite/g++.dg/cpp0x/pr85437-2.C new file mode 100644 index 000000000000..57734a96b475 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr85437-2.C @@ -0,0 +1,7 @@ +// { dg-do compile { target c++11 } } + +struct A { }; +struct B : A { int x; }; + +constexpr int A::*abx += reinterpret_cast<int(A::*)>(&B::x); // { dg-error "reinterpret.*constant" } diff --git a/gcc/testsuite/g++.dg/cpp0x/pr85437-3.C b/gcc/testsuite/g++.dg/cpp0x/pr85437-3.C new file mode 100644 index 000000000000..a956df6b05a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr85437-3.C @@ -0,0 +1,7 @@ +// { dg-do compile { target c++11 } } + +struct A { int y; }; +struct B { int x; }; +struct C : A, B {}; +constexpr int C::*pci = &B::x; +constexpr int A::*pai = static_cast<int A::*>(static_cast<int C::*>(&B::x)); diff --git a/gcc/testsuite/g++.dg/cpp0x/pr85437.C b/gcc/testsuite/g++.dg/cpp0x/pr85437.C new file mode 100644 index 000000000000..d02b1b600158 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/pr85437.C @@ -0,0 +1,16 @@ +// { dg-do compile { target c++11 } } + +struct A { int a; constexpr A() : a(0) {} }; +struct B : A { int x; constexpr B() : x(0) {} }; +struct X { int z; constexpr X() : z(0) {} }; +struct C : X, B {}; +constexpr int C::*cbx = &B::x; +constexpr int B::*bx = &B::x; +constexpr int A::*abx = static_cast<int(A::*)>(&B::x); + +constexpr const C y; +constexpr const B& yb = y; +constexpr const A& ya = y; +constexpr int const *pcbx = &(y.*cbx); +constexpr int const *pbx = &(y.*bx); +constexpr int const *pabx = &(ya.*abx); -- Alexandre Oliva, freedom fighter http://FSFLA.org/~lxoliva/ You must be the change you wish to see in the world. -- Gandhi Be Free! -- http://FSFLA.org/ FSF Latin America board member Free Software Evangelist|Red Hat Brasil GNU Toolchain Engineer