I had an interesting time tracking down some of the problems with this code.
Hopefully I've sussed out now how this stuff works.

We've got 

struct A { char c; };
char A::*p = &A::c;
static char A::*const q = p;
and then
&(a.*q) - &a.c
which should evaluate to 0.  Here "p" will be 0, that's the offset from the
start of the struct to "c".  "q" is const-qualified and static and initialized
with "p", so we get to cp_fold_maybe_rvalue -> decl_constant_value ->
constant_value_1.  Now, NULL pointer-to-data-members are represented by -1, so
that a null pointer is distinguishable from an offset of the first member of a
struct (0).  So constant_value_1 looks at the DECL_INITIAL of "q", which is -1,
a constant, we fold "q" to -1, and sadness ensues.  I believe the -1 value is
only an internal representation and shouldn't be used like that.

Hence my attempt to fix this as below.  Anybody see a problem with this
approach?  My first version just skipped folding TYPE_PTRMEMDATA_P, much like
with REFERENCE_TYPEs above, but the problem is just with NULL member pointers,
methinks.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2017-02-24  Marek Polacek  <pola...@redhat.com>

        PR c++/79687
        * cp-gimplify.c (cp_fold_maybe_rvalue): Don't fold NULL
        pointer-to-data-members.

        * g++.dg/expr/ptrmem8.C: New test.
        * g++.dg/expr/ptrmem9.C: New test.

diff --git gcc/cp/cp-gimplify.c gcc/cp/cp-gimplify.c
index 3eec940..6de88af 100644
--- gcc/cp/cp-gimplify.c
+++ gcc/cp/cp-gimplify.c
@@ -1976,7 +1976,9 @@ cp_fold_maybe_rvalue (tree x, bool rval)
          && TREE_CODE (TREE_TYPE (x)) != REFERENCE_TYPE)
        {
          tree v = decl_constant_value (x);
-         if (v != x && v != error_mark_node)
+         if (v != x
+             && v != error_mark_node
+             && !null_member_pointer_value_p (v))
            {
              x = v;
              continue;
diff --git gcc/testsuite/g++.dg/expr/ptrmem8.C 
gcc/testsuite/g++.dg/expr/ptrmem8.C
index e69de29..c5a766a 100644
--- gcc/testsuite/g++.dg/expr/ptrmem8.C
+++ gcc/testsuite/g++.dg/expr/ptrmem8.C
@@ -0,0 +1,15 @@
+// PR c++/79687
+// { dg-do run }
+
+struct A
+{
+  char c;
+};
+
+int main()
+{
+  char A::* p = &A::c;
+  static char A::* const q = p;
+  A a;
+  return &(a.*q) - &a.c;
+}
diff --git gcc/testsuite/g++.dg/expr/ptrmem9.C 
gcc/testsuite/g++.dg/expr/ptrmem9.C
index e69de29..32ce777 100644
--- gcc/testsuite/g++.dg/expr/ptrmem9.C
+++ gcc/testsuite/g++.dg/expr/ptrmem9.C
@@ -0,0 +1,19 @@
+// PR c++/79687
+// { dg-do run }
+
+struct A
+{
+  char c;
+};
+
+int main()
+{
+  static char A::* p1 = &A::c;
+  char A::* const q1 = p1;
+
+  char A::* p2 = &A::c;
+  static char A::* const q2 = p2;
+
+  A a;
+  return (&(a.*q1) - &a.c) || (&(a.*q2) - &a.c);
+}

        Marek

Reply via email to