diff mbox series

[PR,c++/85437] accept static_casted ptrmem in constexpr

Message ID or604puqhd.fsf@lxoliva.fsfla.org
State New
Headers show
Series [PR,c++/85437] accept static_casted ptrmem in constexpr | expand

Commit Message

Alexandre Oliva April 18, 2018, 3:45 a.m. UTC
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

Comments

Jason Merrill April 18, 2018, 5:07 a.m. UTC | #1
I wonder if it would work to use CONVERT_EXPR for reinterpret_cast.

On Tue, Apr 17, 2018, 9:45 PM Alexandre Oliva <aoliva@redhat.com> wrote:

>
> 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
>
Jakub Jelinek April 18, 2018, 12:59 p.m. UTC | #2
On Wed, Apr 18, 2018 at 05:07:33AM +0000, Jason Merrill wrote:
> I wonder if it would work to use CONVERT_EXPR for reinterpret_cast.

I have tried following (still need Alex' expr.c changes, otherwise it ICEs),
on the bright side it fixes the new pr85437-4.C testcase,
but it causes Excess errors:
/usr/src/gcc/gcc/testsuite/g++.dg/Wcast-function-type.C:15:19: error: invalid conversion to type 'void (S::*)(int)' from type 'void (S::*)(int*)'
/usr/src/gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-ptrmem4.C:23:41:   in 'constexpr' expansion of 'Bar(&Foo::test)'
/usr/src/gcc/gcc/testsuite/g++.dg/cpp0x/constexpr-ptrmem4.C:23:41: error: a reinterpret_cast is not a constant expression
/usr/src/gcc/gcc/testsuite/g++.dg/cpp0x/pr68116.C:12:1: error: invalid conversion to type 'void (C::*)(int)' from type 'void (C::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/template/ptrmem19.C:19:23: error: invalid conversion to type 'void (C::*)(F)' from type 'void (C::*)(B*)'
/usr/src/gcc/gcc/testsuite/g++.dg/template/ptrmem19.C:19:23: error: invalid conversion to type 'void (C::*)(B*)' from type 'void (C::*)(F)'
/usr/src/gcc/gcc/testsuite/g++.dg/init/static2.C:20:1: error: invalid conversion to type 'void (A::*)()' from type 'void (B::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/init/static2.C:20:1: error: type 'B' is not a base type for type 'A'
/usr/src/gcc/gcc/testsuite/g++.dg/init/static2.C:20:1: error:    in pointer to member conversion
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem4.C:39:33: error: type 'D' is not a base type for type 'B'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem4.C:39:33: error:    in pointer to member conversion
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem4.C:43:29: error: type 'D' is not a base type for type 'X'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem4.C:43:29: error:    in pointer to member conversion
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:31:41: error: invalid conversion to type 'float (B::*)()' from type 'int (D::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:31:41: error: type 'D' is not a base type for type 'B'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:31:41: error:    in pointer to member conversion
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:32:41: error: invalid conversion to type 'float (D::*)()' from type 'int (B::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:35:37: error: invalid conversion to type 'int (X::*)()' from type 'int (D::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:35:37: error: type 'D' is not a base type for type 'X'
/usr/src/gcc/gcc/testsuite/g++.dg/conversion/ptrmem5.C:35:37: error:    in pointer to member conversion
/usr/src/gcc/gcc/testsuite/g++.dg/init/ptrmem3.C:8:66: error: invalid conversion to type 'void (foo::*)()' from type 'int (foo::*)(int)'
/usr/src/gcc/gcc/testsuite/g++.dg/pr69379.C:20:1: error: invalid conversion to type 'void (Dict::*)(T)' {aka 'void (Dict::*)(int)'} from type 'void (Dict::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/other/default6.C:13:33: error: invalid conversion to type 'void (BaseRobot::*)()' from type 'int (BaseRobot::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/other/default6.C:17:33: error: invalid conversion to type 'void (BaseRobot::*)()' from type 'int (BaseRobot::*)()'
/usr/src/gcc/gcc/testsuite/g++.dg/torture/pr37354.C:13:1: error: invalid conversion to type 'void (GenericClass::*)()' from type 'int (AlsaDriver::*)(unsigned int)'
/usr/src/gcc/gcc/testsuite/g++.dg/torture/pr37354.C:13:1: error: type 'AlsaDriver' is not a base type for type 'GenericClass'
/usr/src/gcc/gcc/testsuite/g++.dg/torture/pr37354.C:13:1: error:    in pointer to member conversion
etc.

--- gcc/cp/constexpr.c.jj	2018-04-13 10:33:03.078658240 +0200
+++ gcc/cp/constexpr.c	2018-04-18 13:42:54.097312535 +0200
@@ -4595,10 +4595,12 @@ cxx_eval_constant_expression (const cons
 	if (TREE_CODE (op) == PTRMEM_CST
 	    && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
-	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
+	if (TREE_CODE (op) == PTRMEM_CST
+	    && (tcode == NOP_EXPR || tcode == CONVERT_EXPR))
 	  {
-	    if (same_type_ignoring_top_level_qualifiers_p (type,
-							   TREE_TYPE (op))
+	    if (tcode == NOP_EXPR
+		|| same_type_ignoring_top_level_qualifiers_p (type,
+							      TREE_TYPE (op))
 		|| can_convert_qual (type, op))
 	      return cp_fold_convert (type, op);
 	    else
--- gcc/cp/typeck.c.jj	2018-04-18 08:57:09.555612419 +0200
+++ gcc/cp/typeck.c	2018-04-18 13:50:14.278498305 +0200
@@ -5758,6 +5758,16 @@ build_nop (tree type, tree expr)
   return build1_loc (EXPR_LOCATION (expr), NOP_EXPR, type, expr);
 }
 
+/* Similarly, but build CONVERT_EXPR instead.  */
+
+static tree
+build_convert_expr (tree type, tree expr)
+{
+  if (type == error_mark_node || error_operand_p (expr))
+    return expr;
+  return build1_loc (EXPR_LOCATION (expr), CONVERT_EXPR, type, expr);
+}
+
 /* Take the address of ARG, whatever that means under C++ semantics.
    If STRICT_LVALUE is true, require an lvalue; otherwise, allow xvalues
    and class rvalues as well.
@@ -7409,7 +7419,7 @@ build_reinterpret_cast_1 (tree type, tre
 	warning (OPT_Wcast_function_type,
 		 "cast between incompatible function types"
 		 " from %qH to %qI", intype, type);
-      return build_nop (type, expr);
+      return build_convert_expr (type, expr);
     }
   else if (TYPE_PTRMEMFUNC_P (type) && TYPE_PTRMEMFUNC_P (intype))
     {
@@ -7420,7 +7430,7 @@ build_reinterpret_cast_1 (tree type, tre
 	warning (OPT_Wcast_function_type,
 		 "cast between incompatible pointer to member types"
 		 " from %qH to %qI", intype, type);
-      return build_nop (type, expr);
+      return build_convert_expr (type, expr);
     }
   else if ((TYPE_PTRDATAMEM_P (type) && TYPE_PTRDATAMEM_P (intype))
 	   || (TYPE_PTROBV_P (type) && TYPE_PTROBV_P (intype)))
@@ -7446,7 +7456,7 @@ build_reinterpret_cast_1 (tree type, tre
 	/* strict_aliasing_warning STRIP_NOPs its expr.  */
 	strict_aliasing_warning (EXPR_LOCATION (expr), type, expr);
 
-      return build_nop (type, expr);
+      return build_convert_expr (type, expr);
     }
   else if ((TYPE_PTRFN_P (type) && TYPE_PTROBV_P (intype))
 	   || (TYPE_PTRFN_P (intype) && TYPE_PTROBV_P (type)))
@@ -7457,7 +7467,7 @@ build_reinterpret_cast_1 (tree type, tre
 	warning (OPT_Wconditionally_supported,
 		 "casting between pointer-to-function and pointer-to-object "
 		 "is conditionally-supported");
-      return build_nop (type, expr);
+      return build_convert_expr (type, expr);
     }
   else if (VECTOR_TYPE_P (type))
     return convert_to_vector (type, expr);
--- gcc/cp/expr.c.jj	2018-03-09 23:22:23.682236182 +0100
+++ gcc/cp/expr.c	2018-04-18 14:24:52.692310448 +0200
@@ -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));
--- gcc/testsuite/g++.dg/cpp0x/pr85437.C.jj	2018-04-18 14:25:50.655332890 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437.C	2018-04-18 14:28:34.234396893 +0200
@@ -0,0 +1,17 @@
+// PR c++/85437
+// { 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);	// { dg-bogus "not a constant expression" }
+
+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);
--- gcc/testsuite/g++.dg/cpp0x/pr85437-2.C.jj	2018-04-18 14:25:50.654332889 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-2.C	2018-04-18 14:28:45.613401350 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { 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" }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-3.C.jj	2018-04-18 14:25:50.655332890 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-3.C	2018-04-18 14:29:57.691429562 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { 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)); // { dg-bogus "not a constant expression" }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-4.C.jj	2018-04-18 14:29:29.028418341 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-4.C	2018-04-18 14:29:39.829422569 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+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);	// { dg-bogus "not a constant expression" }


	Jakub
Jakub Jelinek April 18, 2018, 1:14 p.m. UTC | #3
On Wed, Apr 18, 2018 at 02:59:34PM +0200, Jakub Jelinek wrote:
> On Wed, Apr 18, 2018 at 05:07:33AM +0000, Jason Merrill wrote:
> > I wonder if it would work to use CONVERT_EXPR for reinterpret_cast.

As for reinterpret_cast, we e.g. accept
constexpr int a = reinterpret_cast<int> (1);
which clang++ rejects, shouldn't we simply in build_reinterpret_cast_1
when not returning error_mark_node wrap the return value inside some new
FE tree (or reuse REINTERPRET_CAST_EXPR) and remove it only during
genericization, so that we can reliably reject those in constexpr handling,
perhaps only if not !ctx->quiet?

	Jakub
Jakub Jelinek April 18, 2018, 7:32 p.m. UTC | #4
On Wed, Apr 18, 2018 at 03:14:19PM +0200, Jakub Jelinek wrote:
> On Wed, Apr 18, 2018 at 02:59:34PM +0200, Jakub Jelinek wrote:
> > On Wed, Apr 18, 2018 at 05:07:33AM +0000, Jason Merrill wrote:
> > > I wonder if it would work to use CONVERT_EXPR for reinterpret_cast.
> 
> As for reinterpret_cast, we e.g. accept
> constexpr int a = reinterpret_cast<int> (1);
> which clang++ rejects, shouldn't we simply in build_reinterpret_cast_1
> when not returning error_mark_node wrap the return value inside some new
> FE tree (or reuse REINTERPRET_CAST_EXPR) and remove it only during
> genericization, so that we can reliably reject those in constexpr handling,
> perhaps only if not !ctx->quiet?

So, I've tried that approach, and while it fixed some issues (e.g. PR49171),
even after changing various testcases (double checked with clang++ if it
indeed is invalid and should be rejected etc.), there are still some extra
FAILs with the patch, see below.  I think the addressof1.C are right,
pr55081.C is unfortunate optimization regression, while reinterpret_cast is
pedantically not a constant expression, if we are just trying to get a
constant value if possible it would be nice to get it.  Wonder if for
ctx->quiet we just could pretend the REINTERPRET_CAST_EXPR isn't in there.

Also, I've added removal of the REINTERPRET_CAST_EXPR trees (replacement
with their operand) into the gimplification langhook, so that we get 3
errors on:
constexpr int a = reinterpret_cast<int> (1);
constexpr int foo (int x) { return reinterpret_cast<int> (x); }
template <int N>
constexpr int bar (int x) { return reinterpret_cast<int> (x); }
constexpr int b = foo (1);
constexpr int c = bar <1> (1);
but nothing then removes it from static initializers.  I've added code
to do that into cp_fold if !current_function_decl, unfortunately
cp_fully_fold/cp_fold doesn't really walk all trees (e.g. it doesn't handle
COMPONENT_REF), so the cp_fully_fold called from store_init_value wasn't
enough; thus the patch does another call with cp_fold_r recursion.

In any case, this reinterpret_cast constexpr pedantic stuff looks too
large/risky at this point to me, I wonder if we accept-invalid even the
simple constexpr int a = reinterpret_cast<int> (1); whether it is not ok for
GCC8 to not error out on reinterpret_cast from PTRMEM_CSTs too, essentially
just use Alex's patch for expr.c and the first hunk from this patch from
constexpr.c, plus the new testcases and deal with the rest for stage1. 
Thoughts on this?

FAIL: g++.dg/cpp0x/addressof1.C  -std=c++11 (test for excess errors)
FAIL: g++.dg/cpp0x/addressof1.C  -std=c++14 (test for excess errors)
FAIL: g++.dg/cpp0x/addressof1.C  -std=c++17 (test for excess errors)
FAIL: g++.dg/cpp0x/addressof1.C  -std=c++2a (test for excess errors)
FAIL: g++.dg/cpp0x/addressof1.C  -std=c++17 -fconcepts (test for excess errors)
FAIL: g++.dg/opt/pr55081.C  -std=c++98  scan-assembler-not _ZGVZ3foovE5array
FAIL: g++.dg/opt/pr55081.C  -std=c++11  scan-assembler-not _ZGVZ3foovE5array
FAIL: g++.dg/opt/pr55081.C  -std=c++14  scan-assembler-not _ZGVZ3foovE5array
FAIL: g++.dg/opt/pr55081.C  -std=c++17  scan-assembler-not _ZGVZ3foovE5array
FAIL: g++.dg/opt/pr55081.C  -std=c++2a  scan-assembler-not _ZGVZ3foovE5array
FAIL: g++.dg/opt/pr55081.C  -std=c++17 -fconcepts  scan-assembler-not _ZGVZ3foovE5array
FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++98  (test for warnings, line 128)
FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++11  (test for warnings, line 128)
FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++14  (test for warnings, line 128)
FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++17  (test for warnings, line 128)
FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++2a  (test for warnings, line 128)
FAIL: g++.dg/warn/Wplacement-new-size.C  -std=gnu++17 -fconcepts  (test for warnings, line 128)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++11  (test for bogus messages, line 12)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++11 (test for excess errors)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++14  (test for bogus messages, line 12)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++14 (test for excess errors)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++17  (test for bogus messages, line 12)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++17 (test for excess errors)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++2a  (test for bogus messages, line 12)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++2a (test for excess errors)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++17 -fconcepts  (test for bogus messages, line 12)
FAIL: c-c++-common/Waddress-1.c  -std=gnu++17 -fconcepts (test for excess errors)
FAIL: c-c++-common/Wsizeof-pointer-memaccess2.c  -std=gnu++98 (test for excess errors)
FAIL: c-c++-common/Wsizeof-pointer-memaccess2.c  -std=gnu++11 (test for excess errors)
FAIL: c-c++-common/Wsizeof-pointer-memaccess2.c  -std=gnu++14 (test for excess errors)
FAIL: c-c++-common/Wsizeof-pointer-memaccess2.c  -std=gnu++17 (test for excess errors)
FAIL: c-c++-common/Wsizeof-pointer-memaccess2.c  -std=gnu++2a (test for excess errors)
FAIL: c-c++-common/Wsizeof-pointer-memaccess2.c  -std=gnu++17 -fconcepts (test for excess errors)
FAIL: g++.dg/torture/Wsizeof-pointer-memaccess1.C   -O0  (test for excess errors)
FAIL: g++.dg/torture/Wsizeof-pointer-memaccess1.C   -O2  (test for excess errors)
FAIL: g++.dg/torture/Wsizeof-pointer-memaccess2.C   -O0  (test for excess errors)
FAIL: g++.dg/torture/Wsizeof-pointer-memaccess2.C   -O2  (test for excess errors)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++11  (test for errors, line 26)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++11  (test for errors, line 27)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++14  (test for errors, line 26)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++14  (test for errors, line 27)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++17  (test for errors, line 26)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++17  (test for errors, line 27)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++2a  (test for errors, line 26)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++2a  (test for errors, line 27)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++17 -fconcepts  (test for errors, line 26)
FAIL: g++.old-deja/g++.pt/ptrmem6.C  -std=c++17 -fconcepts  (test for errors, line 27)

--- gcc/cp/typeck2.c.jj	2018-04-05 20:34:31.547020700 +0200
+++ gcc/cp/typeck2.c	2018-04-18 17:15:39.743824283 +0200
@@ -846,6 +846,8 @@ store_init_value (tree decl, tree init,
 	TREE_CONSTANT (decl) = const_init && decl_maybe_constant_var_p (decl);
     }
   value = cp_fully_fold (value);
+  if (!processing_template_decl)
+    value = cp_fold_init (value);
 
   /* Handle aggregate NSDMI in non-constant initializers, too.  */
   value = replace_placeholders (value, decl);
--- gcc/cp/constexpr.c.jj	2018-04-13 10:33:03.078658240 +0200
+++ gcc/cp/constexpr.c	2018-04-18 15:53:07.936504106 +0200
@@ -4596,20 +4596,7 @@ cxx_eval_constant_expression (const cons
 	    && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
 	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
-	  {
-	    if (same_type_ignoring_top_level_qualifiers_p (type,
-							   TREE_TYPE (op))
-		|| can_convert_qual (type, op))
-	      return cp_fold_convert (type, op);
-	    else
-	      {
-		if (!ctx->quiet)
-		  error_at (EXPR_LOC_OR_LOC (t, input_location),
-			    "a reinterpret_cast is not a constant expression");
-		*non_constant_p = true;
-		return t;
-	      }
-	  }
+	  return cp_fold_convert (type, op);
 
 	if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
 	  {
@@ -4669,6 +4656,13 @@ cxx_eval_constant_expression (const cons
       }
       break;
 
+    case REINTERPRET_CAST_EXPR:
+      if (!ctx->quiet)
+        error_at (EXPR_LOC_OR_LOC (t, input_location),
+		  "a %<reinterpret_cast%> is not a constant expression");
+      *non_constant_p = true;
+      return t;
+
     case EMPTY_CLASS_EXPR:
       /* This is good enough for a function argument that might not get
 	 used, and they can't do anything with it, so just return it.  */
--- gcc/cp/typeck.c.jj	2018-04-18 08:57:09.555612419 +0200
+++ gcc/cp/typeck.c	2018-04-18 18:57:02.422847534 +0200
@@ -7281,7 +7281,7 @@ convert_member_func_to_ptr (tree type, t
    indicate whether or not reinterpret_cast was valid.  */
 
 static tree
-build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
+build_reinterpret_cast_2 (tree type, tree expr, bool c_cast_p,
 			  bool *valid_p, tsubst_flags_t complain)
 {
   tree intype;
@@ -7334,9 +7334,8 @@ build_reinterpret_cast_1 (tree type, tre
 	strict_aliasing_warning (EXPR_LOCATION (expr), type, expr);
 
       if (expr != error_mark_node)
-	expr = build_reinterpret_cast_1
-	  (build_pointer_type (TREE_TYPE (type)), expr, c_cast_p,
-	   valid_p, complain);
+	expr = build_reinterpret_cast_2 (build_pointer_type (TREE_TYPE (type)),
+					 expr, c_cast_p, valid_p, complain);
       if (expr != error_mark_node)
 	/* cp_build_indirect_ref isn't right for rvalue refs.  */
 	expr = convert_from_reference (fold_convert (type, expr));
@@ -7476,6 +7475,20 @@ build_reinterpret_cast_1 (tree type, tre
   return cp_convert (type, expr, complain);
 }
 
+static tree
+build_reinterpret_cast_1 (tree type, tree expr, bool c_cast_p,
+			  bool *valid_p, tsubst_flags_t complain)
+{
+  tree ret = build_reinterpret_cast_2 (type, expr, c_cast_p, valid_p,
+				       complain);
+  if (error_operand_p (ret))
+    return ret;
+  ret = build1_loc (EXPR_LOC_OR_LOC (ret, input_location),
+		    REINTERPRET_CAST_EXPR, TREE_TYPE (ret), ret);
+  REINTERPRET_CAST_EXPR_NONCONST_P (ret) = 1;
+  return ret;
+}
+
 tree
 build_reinterpret_cast (tree type, tree expr, tsubst_flags_t complain)
 {
--- gcc/cp/cp-tree.h.jj	2018-04-18 08:48:40.650853869 +0200
+++ gcc/cp/cp-tree.h	2018-04-18 18:23:55.383829396 +0200
@@ -372,6 +372,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       TEMPLATE_TYPE_PARM_FOR_CLASS (TEMPLATE_TYPE_PARM)
       DECL_NAMESPACE_INLINE_P (in NAMESPACE_DECL)
       SWITCH_STMT_ALL_CASES_P (in SWITCH_STMT)
+      REINTERPRET_CAST_EXPR_NONCONST_P (in REINTERPRET_CAST_EXPR)
    1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE)
       TI_PENDING_TEMPLATE_FLAG.
       TEMPLATE_PARMS_FOR_INLINE.
@@ -4954,6 +4955,11 @@ more_aggr_init_expr_args_p (const aggr_i
 #define SIZEOF_EXPR_TYPE_P(NODE) \
   TREE_LANG_FLAG_0 (SIZEOF_EXPR_CHECK (NODE))
 
+/* True if REINTERPRET_CAST_EXPR is added just for constexpr handling
+   purposes.  */
+#define REINTERPRET_CAST_EXPR_NONCONST_P(NODE) \
+  TREE_LANG_FLAG_0 (REINTERPRET_CAST_EXPR_CHECK (NODE))
+
 /* An enumeration of the kind of tags that C++ accepts.  */
 enum tag_types {
   none_type = 0, /* Not a tag type.  */
@@ -7402,6 +7408,7 @@ extern void cxx_omp_finish_clause		(tree
 extern bool cxx_omp_privatize_by_reference	(const_tree);
 extern bool cxx_omp_disregard_value_expr	(tree, bool);
 extern void cp_fold_function			(tree);
+extern tree cp_fold_init			(tree);
 extern tree cp_fully_fold			(tree);
 extern void clear_fold_cache			(void);
 
--- gcc/cp/cp-gimplify.c.jj	2018-04-18 08:57:09.556612418 +0200
+++ gcc/cp/cp-gimplify.c	2018-04-18 16:55:59.771231237 +0200
@@ -740,6 +740,11 @@ cp_gimplify_expr (tree *expr_p, gimple_s
       ret = GS_OK;
       break;
 
+    case REINTERPRET_CAST_EXPR:
+      *expr_p = TREE_OPERAND (*expr_p, 0);
+      ret = GS_OK;
+      break;
+
     case UNARY_PLUS_EXPR:
       {
 	tree arg = TREE_OPERAND (*expr_p, 0);
@@ -1038,6 +1043,16 @@ cp_fold_function (tree fndecl)
   cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &pset, NULL);
 }
 
+/* Similarly, but for initializer expression.  */
+
+tree
+cp_fold_init (tree init)
+{
+  hash_set<tree> pset;
+  cp_walk_tree (&init, cp_fold_r, &pset, NULL);
+  return init;
+}
+
 /* Perform any pre-gimplification lowering of C++ front end trees to
    GENERIC.  */
 
@@ -2205,6 +2224,12 @@ cp_fold (tree x)
 
       break;
 
+    case REINTERPRET_CAST_EXPR:
+      if (TREE_TYPE (x) == NULL_TREE || current_function_decl)
+	return x;
+      /* Remove REINTERPRET_CAST_EXPRs outside of functions.  */
+      return cp_fold (TREE_OPERAND (x, 0));
+
     case INDIRECT_REF:
       /* We don't need the decltype(auto) obfuscation anymore.  */
       if (REF_PARENTHESIZED_P (x))
--- gcc/cp/tree.c.jj	2018-04-11 09:16:20.076859100 +0200
+++ gcc/cp/tree.c	2018-04-18 18:25:58.107895003 +0200
@@ -96,6 +96,16 @@ lvalue_kind (const_tree ref)
     case IMAGPART_EXPR:
       return lvalue_kind (TREE_OPERAND (ref, 0));
 
+    case REINTERPRET_CAST_EXPR:
+      if (REINTERPRET_CAST_EXPR_NONCONST_P (ref))
+        return lvalue_kind (TREE_OPERAND (ref, 0));
+      if (!TREE_TYPE (ref))
+	return clk_none;
+      if (CLASS_TYPE_P (TREE_TYPE (ref))
+	  || TREE_CODE (TREE_TYPE (ref)) == ARRAY_TYPE)
+	return clk_class;
+      break;
+
     case MEMBER_REF:
     case DOTSTAR_EXPR:
       if (TREE_CODE (ref) == MEMBER_REF)
--- gcc/cp/expr.c.jj	2018-03-09 23:22:23.682236182 +0100
+++ gcc/cp/expr.c	2018-04-18 14:24:52.692310448 +0200
@@ -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));
--- gcc/testsuite/g++.dg/template/pr79650.C.jj	2017-12-14 09:16:03.000000000 +0100
+++ gcc/testsuite/g++.dg/template/pr79650.C	2018-04-18 18:55:00.215787515 +0200
@@ -11,10 +11,10 @@ foo ()
   static int a, b;
 lab1:
 lab2:
-  A<(intptr_t)&&lab1 - (__INTPTR_TYPE__)&&lab2> c;	// { dg-error "not a constant integer" }
-  A<(intptr_t)&&lab1 - (__INTPTR_TYPE__)&&lab1> d;
+  A<(intptr_t)&&lab1 - (__INTPTR_TYPE__)&&lab2> c;	// { dg-error "is not a constant expression" }
+  A<(intptr_t)&&lab1 - (__INTPTR_TYPE__)&&lab1> d;	// { dg-error "is not a constant expression" }
   A<(intptr_t)&a - (intptr_t)&b> e;			// { dg-error "is not a constant expression" }
-  A<(intptr_t)&a - (intptr_t)&a> f;
-  A<(intptr_t)sizeof(a) + (intptr_t)&a> g;		// { dg-error "not a constant integer" }
-  A<(intptr_t)&a> h;					// { dg-error "conversion from pointer type" }
+  A<(intptr_t)&a - (intptr_t)&a> f;			// { dg-error "is not a constant expression" }
+  A<(intptr_t)sizeof(a) + (intptr_t)&a> g;		// { dg-error "is not a constant expression" }
+  A<(intptr_t)&a> h;					// { dg-error "is not a constant expression" }
 }
--- gcc/testsuite/g++.dg/template/arg9.C.jj	2013-07-03 22:19:53.000000000 +0200
+++ gcc/testsuite/g++.dg/template/arg9.C	2018-04-18 17:50:04.116741326 +0200
@@ -5,4 +5,4 @@ template <int N>
 struct S {};
 
 S <static_cast <int> (4>>2)> s1;
-S <reinterpret_cast <int> (4>>2)> s2;
+S <reinterpret_cast <int> (4>>2)> s2;	// { dg-error "not a constant expression" }
--- gcc/testsuite/g++.dg/other/vararg-2.C.jj	2008-09-05 12:55:01.000000000 +0200
+++ gcc/testsuite/g++.dg/other/vararg-2.C	2018-04-18 18:50:03.687641434 +0200
@@ -5,7 +5,7 @@ typedef __gnuc_va_list va_list;
  QString & sprintf(QString &s,const QString &szFmt,...)
  {
   va_list list;
-  __builtin_va_start(list,((const char *)(&(szFmt))));
+  __builtin_va_start(list,((const QString *)(&(szFmt))));
   __builtin_va_end(list);
   return s;
  }
--- gcc/testsuite/g++.dg/ubsan/vptr-4.C.jj	2015-01-15 23:57:07.634969888 +0100
+++ gcc/testsuite/g++.dg/ubsan/vptr-4.C	2018-04-18 20:07:27.912955144 +0200
@@ -15,11 +15,11 @@ struct T : S {
   int b;
   int g() { return 0; }
   virtual int v() { return 1; }
-  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); }
+  constexpr const T *foo() { return (const T *) reinterpret_cast<const S *> (this); } // { dg-error "not a constant" }
 };
 
 constexpr T t;
-constexpr const T *p = t.foo ();
+constexpr const T *p = t.foo ();	// { dg-message "in .constexpr. expansion" }
 
 template <typename U>
 struct V {
@@ -35,19 +35,19 @@ struct W : V<U> {
   int b;
   int g() { return 0; }
   virtual int v() { return 1; }
-  constexpr const W<U> *foo() { return (const W<U> *) reinterpret_cast<const V<U> *> (this); }
+  constexpr const W<U> *foo() { return (const W<U> *) reinterpret_cast<const V<U> *> (this); } // { dg-error "not a constant" }
 };
 
 constexpr W<int> w;
-constexpr const W<int> *s = w.foo ();
+constexpr const W<int> *s = w.foo ();	// { dg-message "in .constexpr. expansion" }
 
 template <typename U>
 int foo (void)
 {
   static constexpr T t;
-  static constexpr const T *p = t.foo ();
+  static constexpr const T *p = t.foo ();	// { dg-message "in .constexpr. expansion" }
   static constexpr W<U> w;
-  static constexpr const W<U> *s = w.foo ();
+  static constexpr const W<U> *s = w.foo ();	// { dg-message "in .constexpr. expansion" }
   return t.b + w.b;
 }
 
--- gcc/testsuite/g++.dg/cpp1y/constexpr-shift1.C.jj	2017-11-21 20:22:59.000000000 +0100
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-shift1.C	2018-04-18 17:54:39.633875863 +0200
@@ -6,4 +6,4 @@ constexpr __PTRDIFF_TYPE__ bar (int a)
   return ((__PTRDIFF_TYPE__) &p) << a; // { dg-error "is not a constant expression" }
 }
 constexpr __PTRDIFF_TYPE__ r = bar (2); // { dg-message "in .constexpr. expansion of" }
-constexpr __PTRDIFF_TYPE__ s = bar (0); // { dg-error "conversion from pointer" }
+constexpr __PTRDIFF_TYPE__ s = bar (0); // { dg-message "in .constexpr. expansion of" }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C.jj	2018-04-18 08:57:09.000000000 +0200
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-nullptr-2.C	2018-04-18 19:07:24.750136620 +0200
@@ -99,9 +99,7 @@ constexpr const volatile void* pv3 = p0;
 constexpr void* pv4 = static_cast<void*>(p0);
 constexpr const void* pv5 = static_cast<const void*>(p0);
 
-// The following should be rejected but isn't because of bug c++/49171
-// - [C++0x][constexpr] Constant expressions support reinterpret_cast
-constexpr void* pv6 = reinterpret_cast<void*>(p0);   // { dg-error "" "bug c++/49171" { xfail *-*-* } }
+constexpr void* pv6 = reinterpret_cast<void*>(p0);   // { dg-error "not a constant" }
 
 // Adding or subtracting zero from a null pointer is valid in C++.
 constexpr int* p1 = p0 + 0;
--- gcc/testsuite/g++.dg/cpp0x/pr85437.C.jj	2018-04-18 14:25:50.655332890 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437.C	2018-04-18 14:28:34.234396893 +0200
@@ -0,0 +1,17 @@
+// PR c++/85437
+// { 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);	// { dg-bogus "not a constant expression" }
+
+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);
--- gcc/testsuite/g++.dg/cpp0x/constexpr-union.C.jj	2014-03-10 10:50:13.000000000 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-union.C	2018-04-18 18:44:16.139470221 +0200
@@ -13,4 +13,4 @@ constexpr float f = u.f;
 constexpr unsigned char c = u.ca[0]; // { dg-error "U::ca" }
 
 constexpr double d = 1.0;
-constexpr unsigned char c2 = (unsigned char&)d; // { dg-error "char. glvalue" }
+constexpr unsigned char c2 = (unsigned char&)d; // { dg-error "char. glvalue|not a constant expression" }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-3.C.jj	2018-04-18 14:25:50.655332890 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-3.C	2018-04-18 14:29:57.691429562 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { 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)); // { dg-bogus "not a constant expression" }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-2.C.jj	2018-04-18 14:25:50.654332889 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-2.C	2018-04-18 14:28:45.613401350 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { 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" }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-4.C.jj	2018-04-18 14:29:29.028418341 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-4.C	2018-04-18 14:29:39.829422569 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+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);	// { dg-bogus "not a constant expression" }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C.jj	2018-01-30 12:30:26.000000000 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C	2018-04-18 18:51:34.156686004 +0200
@@ -17,8 +17,8 @@ public:
   constexpr static Inner & getInner()
   /* I am surprised this is considered a constexpr */
   {
-    return *((Inner *)4); // { dg-error "reinterpret_cast" }
-  }
+    return *((Inner *)4);
+  } // { dg-error "reinterpret_cast" }
 };
 
 B B::instance;
--- gcc/testsuite/g++.dg/cpp0x/rv-reinterpret.C.jj	2014-03-10 10:50:13.134984620 +0100
+++ gcc/testsuite/g++.dg/cpp0x/rv-reinterpret.C	2018-04-18 17:36:11.261363805 +0200
@@ -4,7 +4,7 @@ void f(int &);
 void f(int &&ir) { ir = 42; }
 int main()
 {
-  int x;
+  int x = 6;
   f(reinterpret_cast<int&&>(x));
   return (x != 42);
 }
--- gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C.jj	2017-11-21 20:22:57.000000000 +0100
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-cast.C	2018-04-18 19:13:28.837305748 +0200
@@ -4,20 +4,19 @@
 
 int i;
 
-// The following is accepted due to bug 49171.
-constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error "" "bug c++/49171" { xfail *-*-* } }
+constexpr void *q = reinterpret_cast<void*>(&i);    // { dg-error ".reinterpret_cast. is not a constant expression" }
 
 constexpr void *r0 = reinterpret_cast<void*>(1);    // { dg-error "not a constant expression|reinterpret_cast from integer to pointer" }
-constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error ".reinterpret_cast<void\\*>\\(1\[ul\]\*\\). is not a constant expression" }
+constexpr void *r1 = reinterpret_cast<void*>(sizeof 'x');  // { dg-error ".reinterpret_cast. is not a constant expression" }
 
 template <class T>
 constexpr bool f ()
 {
 #if __cplusplus > 201103L
-  T *p = reinterpret_cast<T*>(sizeof (T));
+  T *p = reinterpret_cast<T*>(sizeof (T));	// { dg-error "not a constant expression" "" { target c++14 } }
   return p;
 #else
-  return *reinterpret_cast<T*>(sizeof (T));
+  return *reinterpret_cast<T*>(sizeof (T));	// { dg-error "not a constant expression" "" { target c++11_only } }
 #endif
 }
 
--- gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C.jj	2014-03-10 10:49:46.000000000 +0100
+++ gcc/testsuite/g++.old-deja/g++.pt/ptrmem6.C	2018-04-18 20:10:20.627042624 +0200
@@ -23,10 +23,10 @@ int main() {
   h<&A::i>();
   g<&B::f>(); // { dg-error "" } 
   h<&B::j>(); // { dg-error "" } 
-  g<(void (A::*)()) &A::f>(); // { dg-error "" "" { xfail c++11 } }
-  h<(int A::*) &A::i>(); // { dg-error "" "" { xfail c++11 } }
+  g<(void (A::*)()) &A::f>(); // { dg-error "" }
+  h<(int A::*) &A::i>(); // { dg-error "" }
   g<(void (A::*)()) &B::f>(); // { dg-error "" "" { xfail { c++11 && { aarch64*-*-* arm*-*-* mips*-*-* } } } }
-  h<(int A::*) &B::j>(); // { dg-error "" } 
+  h<(int A::*) &B::j>(); // { dg-error "" "" { xfail { c++17 } } } 
   g<(void (A::*)()) 0>(); // { dg-error "" "" { target { ! c++11 } } }
   h<(int A::*) 0>(); // { dg-error "" "" { target { ! c++11 } } }
 

	Jakub
Jakub Jelinek April 19, 2018, 3:58 p.m. UTC | #5
On Wed, Apr 18, 2018 at 09:32:53PM +0200, Jakub Jelinek wrote:
> In any case, this reinterpret_cast constexpr pedantic stuff looks too
> large/risky at this point to me, I wonder if we accept-invalid even the
> simple constexpr int a = reinterpret_cast<int> (1); whether it is not ok for
> GCC8 to not error out on reinterpret_cast from PTRMEM_CSTs too, essentially
> just use Alex's patch for expr.c and the first hunk from this patch from
> constexpr.c, plus the new testcases and deal with the rest for stage1. 
> Thoughts on this?

I've tried following patch, but it regresses
+FAIL: g++.dg/conversion/ptrmem5.C  -std=c++11 (test for excess errors)
+FAIL: g++.dg/conversion/ptrmem5.C  -std=c++14 (test for excess errors)
+FAIL: g++.dg/conversion/ptrmem5.C  -std=c++98 (test for excess errors)
+FAIL: g++.dg/init/static2.C  -std=c++11 (test for excess errors)
+UNRESOLVED: g++.dg/init/static2.C  -std=c++11 compilation failed to produce executable
+FAIL: g++.dg/init/static2.C  -std=c++14 (test for excess errors)
+UNRESOLVED: g++.dg/init/static2.C  -std=c++14 compilation failed to produce executable
+FAIL: g++.dg/init/static2.C  -std=c++98 (test for excess errors)
+UNRESOLVED: g++.dg/init/static2.C  -std=c++98 compilation failed to produce executable
apparently a reinterpret_cast in that case is something that shouldn't be
handled by cxx_eval_constant_expression, or we ICE.  And if instead of the
constexpr.c hunk I try to add || can_convert (type, TREE_TYPE (op), tf_none)
then the testcases from the PR don't work anymore.

So I'm afraid I need to give up on this, Alex, if you have ideas what to do,
I'd appreciate if you could look at this, otherwise we'll need to defer this
P1 for next week.

--- gcc/cp/constexpr.c.jj	2018-04-13 10:33:03.078658240 +0200
+++ gcc/cp/constexpr.c	2018-04-18 15:53:07.936504106 +0200
@@ -4596,20 +4596,7 @@ cxx_eval_constant_expression (const cons
 	    && !TYPE_PTRMEM_P (type))
 	  op = cplus_expand_constant (op);
 	if (TREE_CODE (op) == PTRMEM_CST && tcode == NOP_EXPR)
-	  {
-	    if (same_type_ignoring_top_level_qualifiers_p (type,
-							   TREE_TYPE (op))
-		|| can_convert_qual (type, op))
-	      return cp_fold_convert (type, op);
-	    else
-	      {
-		if (!ctx->quiet)
-		  error_at (EXPR_LOC_OR_LOC (t, input_location),
-			    "a reinterpret_cast is not a constant expression");
-		*non_constant_p = true;
-		return t;
-	      }
-	  }
+	  return cp_fold_convert (type, op);
 
 	if (POINTER_TYPE_P (type) && TREE_CODE (op) == INTEGER_CST)
 	  {
--- gcc/cp/expr.c.jj	2018-03-09 23:22:23.682236182 +0100
+++ gcc/cp/expr.c	2018-04-18 14:24:52.692310448 +0200
@@ -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));
--- gcc/testsuite/g++.dg/cpp0x/pr85437.C.jj	2018-04-18 14:25:50.655332890 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437.C	2018-04-18 14:28:34.234396893 +0200
@@ -0,0 +1,17 @@
+// PR c++/85437
+// { 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);	// { dg-bogus "not a constant expression" }
+
+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);
--- gcc/testsuite/g++.dg/cpp0x/pr85437-2.C.jj	2018-04-18 14:25:50.654332889 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-2.C	2018-04-18 14:28:45.613401350 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { 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" "" { xfail *-*-* } }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-3.C.jj	2018-04-18 14:25:50.655332890 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-3.C	2018-04-18 14:29:57.691429562 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { 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)); // { dg-bogus "not a constant expression" }
--- gcc/testsuite/g++.dg/cpp0x/pr85437-4.C.jj	2018-04-18 14:29:29.028418341 +0200
+++ gcc/testsuite/g++.dg/cpp0x/pr85437-4.C	2018-04-18 14:29:39.829422569 +0200
@@ -0,0 +1,8 @@
+// PR c++/85437
+// { dg-do compile { target c++11 } }
+
+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);	// { dg-bogus "not a constant expression" }


	Jakub
Nathan Sidwell April 20, 2018, 2:28 p.m. UTC | #6
On 04/18/2018 01:07 AM, Jason Merrill wrote:
> I wonder if it would work to use CONVERT_EXPR for reinterpret_cast.

That's kind of the wrong way round, isn't it?  NOP_EXPRs are for things 
that don't generate code, which a reinterpret_cast is.  static_cast adds 
a constant, which is only zero for conversions through the primary base 
(or suitably placed empty base).  CONVERT_EXPR would seem more natural 
there?

nathan
Jason Merrill April 20, 2018, 5:44 p.m. UTC | #7
On Fri, Apr 20, 2018, 8:28 AM Nathan Sidwell <nathan@acm.org> wrote:

> On 04/18/2018 01:07 AM, Jason Merrill wrote:
> > I wonder if it would work to use CONVERT_EXPR for reinterpret_cast.
>
> That's kind of the wrong way round, isn't it?  NOP_EXPRs are for things
> that don't generate code, which a reinterpret_cast is.  static_cast adds
> a constant, which is only zero for conversions through the primary base
> (or suitably placed empty base).  CONVERT_EXPR would seem more natural
> there?
>

Any time we need an actual adjustment, there will be a PLUS_EXPR. The issue
is somehow distinguishing between a reinterpret_cast and one of the many
other sources of NOP_EXPR.

I'm not really clear on what the difference between NOP and CONVERT is
supposed to be, anyway...

Jason
Nathan Sidwell April 20, 2018, 6:52 p.m. UTC | #8
On 04/20/2018 01:44 PM, Jason Merrill wrote:

> Any time we need an actual adjustment, there will be a PLUS_EXPR. The 
> issue is somehow distinguishing between a reinterpret_cast and one of 
> the many other sources of NOP_EXPR.

yeah, I see that now.  Perhaps VIEW_CONVERT_EXPR is more appropriate for 
the reinterpret_cast case?

Anyway, such a change would require auditing a lot of NOP_EXPR uses. 
This less invasive patch instead adds a REINTERPRET_CAST_P flag, which 
we set on NOP_EXPRs coming out of build_reinterpret_1.  Then in 
cxx_eval_constant_expression we reject any NOP_EXPR that has that flag 
set.  We can get rid of the subsequent special casing of a NOP_EXPR 
involving a PTRMEM_CST.  I have to change convert_ptrmem to always 
expand the constant (into an OFFSET_TYPE) so that 
initializer_constant_valid_p (used by reduced_constant_expression_p) 
doesn't get confused by a zero-adjusting conversion of a ptrmem_cst.

cpp0x/addressof1.C thinks thinks like
'static_assert (reinterpret_cast <T*>(&thing) == &thing.member)'
are constant expressions, but AFAICT they are not
cpp0x/constexpr-pmf1.C is checking an optimization occurs at the 
genericization level without turning the optimizer on.  IMHO we only 
need to check this is happening at some point when the optimizer is 
turned on.  (The original bug was wrong code, but then perhaps it should 
be a runtime check?)

WDYT?

nathan
Jakub Jelinek April 20, 2018, 7:10 p.m. UTC | #9
On Fri, Apr 20, 2018 at 02:52:32PM -0400, Nathan Sidwell wrote:
> On 04/20/2018 01:44 PM, Jason Merrill wrote:
> 
> > Any time we need an actual adjustment, there will be a PLUS_EXPR. The
> > issue is somehow distinguishing between a reinterpret_cast and one of
> > the many other sources of NOP_EXPR.
> 
> yeah, I see that now.  Perhaps VIEW_CONVERT_EXPR is more appropriate for the

Thanks for taking this over.

> 	* cp-tree.h (REINTERPRETT_CAST_P): New.

s/TT/T/

> +/* Build a NOP_EXPR to TYPE, but mark it as a reinterpret_cast so that
> +   constexpr evaluation knows to reject it.  */
> +
> +static tree
> +build_nop_reinterpret (tree type, tree expr)
> +{
> +  expr = build_nop (type, expr);
> +  REINTERPRET_CAST_P (expr) = true;
> +  return expr;

build_nop can return the expr passed to it if it is error_operand or
type is error_mark_node.  Shouldn't this e.g. check if build_nop returned
something other than expr before setting the flag on it?
  tree ret = build_nop (type, expr);
  if (ret != expr)
    REINTERPRET_CAST_P (ret) = true;
  return ret;
?

	Jakub
Jason Merrill April 23, 2018, 5:19 p.m. UTC | #10
On Fri, Apr 20, 2018, 12:52 PM Nathan Sidwell <nathan@acm.org> wrote:

> On 04/20/2018 01:44 PM, Jason Merrill wrote:
>
> > Any time we need an actual adjustment, there will be a PLUS_EXPR. The
> > issue is somehow distinguishing between a reinterpret_cast and one of
> > the many other sources of NOP_EXPR.
>
> yeah, I see that now.  Perhaps VIEW_CONVERT_EXPR is more appropriate for
> the reinterpret_cast case?
>

No, VIEW_CONVERT_EXPR is a glvalue; it would only be appropriate for
conversion to reference type.

Anyway, such a change would require auditing a lot of NOP_EXPR uses.
> This less invasive patch instead adds a REINTERPRET_CAST_P flag, which
> we set on NOP_EXPRs coming out of build_reinterpret_1.  Then in
> cxx_eval_constant_expression we reject any NOP_EXPR that has that flag
> set.  We can get rid of the subsequent special casing of a NOP_EXPR
> involving a PTRMEM_CST.


Sounds good.


> I have to change convert_ptrmem to always
> expand the constant (into an OFFSET_TYPE) so that
> initializer_constant_valid_p (used by reduced_constant_expression_p)
> doesn't get confused by a zero-adjusting conversion of a ptrmem_cst.
>

Hmm, I'm nervous about this change.  Maybe teach r_c_e_p to handle this
case properly?

cpp0x/addressof1.C thinks thinks like
> 'static_assert (reinterpret_cast <T*>(&thing) == &thing.member)'
> are constant expressions, but AFAICT they are not
>

Agreed.

cpp0x/constexpr-pmf1.C is checking an optimization occurs at the
> genericization level without turning the optimizer on.  IMHO we only
> need to check this is happening at some point when the optimizer is
> turned on.  (The original bug was wrong code, but then perhaps it should
> be a runtime check?)
>

Maybe test this with just -O1?

Jason
Nathan Sidwell April 24, 2018, 4:31 p.m. UTC | #11
On 04/23/2018 01:19 PM, Jason Merrill wrote:
> On Fri, Apr 20, 2018, 12:52 PM Nathan Sidwell <nathan@acm.org 

>     I have to change convert_ptrmem to always
>     expand the constant (into an OFFSET_TYPE) so that
>     initializer_constant_valid_p (used by reduced_constant_expression_p)
>     doesn't get confused by a zero-adjusting conversion of a ptrmem_cst.
> 
> 
> Hmm, I'm nervous about this change.  Maybe teach r_c_e_p to handle this 
> case properly?

Here's an updated patch.  We keep convert_ptrmem unchanged.  I didn't 
need to teach RCEP anything new, fortunately.  In 
cxx_eval_constant_expression I call cplus_expand_constant on the cases 
that we previously complained were reinterpret_casts.  This does seem 
somewhat ugly though.

bootstrapped on x86_64-linux

nathan
Nathan Sidwell April 24, 2018, 4:32 p.m. UTC | #12
Now with added patchy goodness.

On 04/24/2018 12:31 PM, Nathan Sidwell wrote:
> On 04/23/2018 01:19 PM, Jason Merrill wrote:
>> On Fri, Apr 20, 2018, 12:52 PM Nathan Sidwell <nathan@acm.org 
> 
>>     I have to change convert_ptrmem to always
>>     expand the constant (into an OFFSET_TYPE) so that
>>     initializer_constant_valid_p (used by reduced_constant_expression_p)
>>     doesn't get confused by a zero-adjusting conversion of a ptrmem_cst.
>>
>>
>> Hmm, I'm nervous about this change.  Maybe teach r_c_e_p to handle 
>> this case properly?
> 
> Here's an updated patch.  We keep convert_ptrmem unchanged.  I didn't 
> need to teach RCEP anything new, fortunately.  In 
> cxx_eval_constant_expression I call cplus_expand_constant on the cases 
> that we previously complained were reinterpret_casts.  This does seem 
> somewhat ugly though.
> 
> bootstrapped on x86_64-linux
> 
> nathan
>
Jason Merrill April 24, 2018, 8:40 p.m. UTC | #13
On Tue, Apr 24, 2018 at 12:31 PM, Nathan Sidwell <nathan@acm.org> wrote:
> On 04/23/2018 01:19 PM, Jason Merrill wrote:
>>
>> On Fri, Apr 20, 2018, 12:52 PM Nathan Sidwell <nathan@acm.org
>
>
>>     I have to change convert_ptrmem to always
>>     expand the constant (into an OFFSET_TYPE) so that
>>     initializer_constant_valid_p (used by reduced_constant_expression_p)
>>     doesn't get confused by a zero-adjusting conversion of a ptrmem_cst.
>>
>>
>> Hmm, I'm nervous about this change.  Maybe teach r_c_e_p to handle this
>> case properly?
>
>
> Here's an updated patch.  We keep convert_ptrmem unchanged.  I didn't need
> to teach RCEP anything new, fortunately.  In cxx_eval_constant_expression I
> call cplus_expand_constant on the cases that we previously complained were
> reinterpret_casts.  This does seem somewhat ugly though.

Well, in such a static_cast we are no longer referring to a member of
the class type, so abandoning the more symbolic PTRMEM_CST seems
reasonable.

The patch looks good to me.

Jason
diff mbox series

Patch

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);