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 <[email protected]>
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