This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: C++ PATCH to fix wrong-code with pointer-to-data-members (PR c++/79687)
- From: Marek Polacek <polacek at redhat dot com>
- To: Jason Merrill <jason at redhat dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 9 Mar 2017 10:54:26 +0100
- Subject: Re: C++ PATCH to fix wrong-code with pointer-to-data-members (PR c++/79687)
- Authentication-results: sourceware.org; auth=none
- References: <20170224162241.GR3892@redhat.com> <CADzB+2mD5PbjWtxEOYsSGwO78kQJW73hSKRbgrBq4NPZD6kOKQ@mail.gmail.com> <20170228201021.GC3172@redhat.com> <CADzB+2=51KF0rkOS7vbKa2q+NCZhrZ-UcKdiOkJELX5D6oP7Bg@mail.gmail.com> <20170301151656.GF3172@redhat.com>
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 <polacek@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 <polacek@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 <polacek@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
Marek