This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: C++ PATCH to fix wrong-code with pointer-to-data-members (PR c++/79687)


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]