OK.
On Wed, Mar 1, 2017 at 10:16 AM, Marek Polacek <pola...@redhat.com> wrote: > On Tue, Feb 28, 2017 at 01:12:38PM -1000, Jason Merrill wrote: >> On Tue, Feb 28, 2017 at 10:10 AM, Marek Polacek <pola...@redhat.com> wrote: >> > On Fri, Feb 24, 2017 at 11:11:05AM -0800, Jason Merrill wrote: >> >> On Fri, Feb 24, 2017 at 8:22 AM, Marek Polacek <pola...@redhat.com> wrote: >> >> > 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. >> >> >> >> Since q is initialized from p, it shouldn't have a DECL_INITIAL of -1; >> >> that sounds like the bug. >> > >> > The DECL_INITIAL of -1 comes from cp_finish_decl: >> > 7038 The memory occupied by any object of static storage >> > 7039 duration is zero-initialized at program startup before >> > 7040 any other initialization takes place. >> > 7041 >> > 7042 We cannot create an appropriate initializer until after >> > 7043 the type of DECL is finalized. If DECL_INITIAL is set, >> > 7044 then the DECL is statically initialized, and any >> > 7045 necessary zero-initialization has already been >> > performed. */ >> > 7046 if (TREE_STATIC (decl) && !DECL_INITIAL (decl)) >> > 7047 DECL_INITIAL (decl) = build_zero_init (TREE_TYPE (decl), >> > 7048 >> > /*nelts=*/NULL_TREE, >> > 7049 >> > /*static_storage_p=*/true); >> >> Ah, that makes sense. We do want to do constant-initialization with >> -1 before we do dynamic initialization with p. >> >> So we need to detect in constant_value_1 that the variable has a >> dynamic initializer and therefore return the variable rather than -1. >> DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P seems useful, perhaps in >> combination with DECL_NONTRIVIALLY_INITIALIZED. > > Got it. I think the following should be the real fix. I ran g++ dg.exp > with some logging to see how often the new check triggers, and it only > triggered in the two new tests, so I'm fairly happy with that. > > Bootstrapped/regtested on x86_64-linux, ok for trunk and 6? > > 2017-03-01 Marek Polacek <pola...@redhat.com> > > PR c++/79687 > * init.c (constant_value_1): Break if the variable has a dynamic > initializer. > > * g++.dg/expr/ptrmem8.C: New test. > * g++.dg/expr/ptrmem9.C: New test. > > diff --git gcc/cp/init.c gcc/cp/init.c > index 7ded37e..12e6bf4 100644 > --- gcc/cp/init.c > +++ gcc/cp/init.c > @@ -2193,6 +2193,13 @@ constant_value_1 (tree decl, bool strict_p, bool > return_aggregate_cst_ok_p) > if (TREE_CODE (init) == CONSTRUCTOR > && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl)) > break; > + /* If the variable has a dynamic initializer, don't use its > + DECL_INITIAL which doesn't reflect the real value. */ > + if (VAR_P (decl) > + && TREE_STATIC (decl) > + && !DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) > + && DECL_NONTRIVIALLY_INITIALIZED_P (decl)) > + break; > decl = unshare_expr (init); > } > return decl; > 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