Ping.
On Wed, Mar 01, 2017 at 04:16:56PM +0100, Marek Polacek 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 <[email protected]> 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 <[email protected]>
> > >> 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 <[email protected]>
>
> 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
Marek