diff mbox series

[C++] Fix PLACEHOLDER_EXPR handling (PR c++/79937, PR c++/82410)

Message ID 20180314223346.GN8577@tucnak
State New
Headers show
Series [C++] Fix PLACEHOLDER_EXPR handling (PR c++/79937, PR c++/82410) | expand

Commit Message

Jakub Jelinek March 14, 2018, 10:33 p.m. UTC
Hi!

The following patch is an attempt to fix PLACEHOLDER_EXPR handling.
As e.g.
struct Y
{
  static Y bar (Y y) { return y; }
  int i;
  int n = bar (Y{2,i}).m + bar {Y{2,i,i}).n;
  int m = i;
};
is rejected - one can't use incomplete ctors which would need NSDMIs
until the class is defined, I believe PLACEHOLDER_EXPRs can't be arbitrarily
intermixed, rather some CONSTRUCTORs into which we've added the NSDMI
expressions can contain PLACEHOLDER_EXPRs and other PLACEHOLDER_EXPRs can be
only found inside such CONSTRUCTORs added earlier.

So, this patch adds a new flag on CONSTRUCTORs, which acts as a barrier
boundary for replace_placeholders, where we replace just PLACEHOLDER_EXPRs
from the outermost expressions and don't dive into CONSTRUCTORs into which
we've added other PLACEHOLDER_EXPRs - those will be handled later on
separately, by cp_gimplify_init_expr calling replace_placeholders.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?

2018-03-14  Jakub Jelinek  <jakub@redhat.com>

	PR c++/79937
	PR c++/82410
	* cp-tree.h (CONSTRUCTOR_PLACEHOLDER_BOUNDARY): Define.
	(find_placeholder): Declare.
	* tree.c (struct replace_placeholders_t): Add exp member.
	(replace_placeholders_r): Don't walk into ctors with
	CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set, unless they are equal to
	d->exp.
	(replace_placeholders): Initialize data.exp.
	(find_placeholders_r, find_placeholders): New functions.
	* typeck2.c (process_init_constructor_record,
	process_init_constructor_union): Set CONSTRUCTOR_PLACEHOLDER_BOUNDARY
	if adding NSDMI on which find_placeholder returns true.

	* g++.dg/cpp1y/pr79937-1.C: New test.
	* g++.dg/cpp1y/pr79937-2.C: New test.
	* g++.dg/cpp1y/pr79937-3.C: New test.
	* g++.dg/cpp1y/pr79937-4.C: New test.
	* g++.dg/cpp1y/pr82410.C: New test.


	Jakub

Comments

Jason Merrill March 15, 2018, 12:55 a.m. UTC | #1
On Wed, Mar 14, 2018 at 6:33 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> The following patch is an attempt to fix PLACEHOLDER_EXPR handling.
> As e.g.
> struct Y
> {
>   static Y bar (Y y) { return y; }
>   int i;
>   int n = bar (Y{2,i}).m + bar {Y{2,i,i}).n;
>   int m = i;
> };
> is rejected - one can't use incomplete ctors which would need NSDMIs
> until the class is defined, I believe PLACEHOLDER_EXPRs can't be arbitrarily
> intermixed, rather some CONSTRUCTORs into which we've added the NSDMI
> expressions can contain PLACEHOLDER_EXPRs and other PLACEHOLDER_EXPRs can be
> only found inside such CONSTRUCTORs added earlier.
>
> So, this patch adds a new flag on CONSTRUCTORs, which acts as a barrier
> boundary for replace_placeholders, where we replace just PLACEHOLDER_EXPRs
> from the outermost expressions and don't dive into CONSTRUCTORs into which
> we've added other PLACEHOLDER_EXPRs - those will be handled later on
> separately, by cp_gimplify_init_expr calling replace_placeholders.
>
> Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk?
>
> 2018-03-14  Jakub Jelinek  <jakub@redhat.com>
>
>         PR c++/79937
>         PR c++/82410
>         * cp-tree.h (CONSTRUCTOR_PLACEHOLDER_BOUNDARY): Define.
>         (find_placeholder): Declare.
>         * tree.c (struct replace_placeholders_t): Add exp member.
>         (replace_placeholders_r): Don't walk into ctors with
>         CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set, unless they are equal to
>         d->exp.
>         (replace_placeholders): Initialize data.exp.
>         (find_placeholders_r, find_placeholders): New functions.
>         * typeck2.c (process_init_constructor_record,
>         process_init_constructor_union): Set CONSTRUCTOR_PLACEHOLDER_BOUNDARY
>         if adding NSDMI on which find_placeholder returns true.
>
>         * g++.dg/cpp1y/pr79937-1.C: New test.
>         * g++.dg/cpp1y/pr79937-2.C: New test.
>         * g++.dg/cpp1y/pr79937-3.C: New test.
>         * g++.dg/cpp1y/pr79937-4.C: New test.
>         * g++.dg/cpp1y/pr82410.C: New test.
>
> --- gcc/cp/cp-tree.h.jj 2018-03-14 16:21:50.745925263 +0100
> +++ gcc/cp/cp-tree.h    2018-03-14 15:57:51.171323825 +0100
> @@ -425,6 +425,7 @@ extern GTY(()) tree cp_global_trees[CPTI
>        DECL_VTABLE_OR_VTT_P (in VAR_DECL)
>        FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
>        CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
> +      CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
>     6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
>        DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
>        TYPE_MARKED_P (in _TYPE)
> @@ -4144,6 +4145,12 @@ more_aggr_init_expr_args_p (const aggr_i
>  #define CONSTRUCTOR_C99_COMPOUND_LITERAL(NODE) \
>    (TREE_LANG_FLAG_3 (CONSTRUCTOR_CHECK (NODE)))
>
> +/* True if this CONSTRUCTOR contains PLACEHOLDER_EXPRs referencing the
> +   CONSTRUCTOR's type not nested inside another CONSTRUCTOR marked with
> +   CONSTRUCTOR_PLACEHOLDER_BOUNDARY.  */
> +#define CONSTRUCTOR_PLACEHOLDER_BOUNDARY(NODE) \
> +  (TREE_LANG_FLAG_5 (CONSTRUCTOR_CHECK (NODE)))
> +
>  #define DIRECT_LIST_INIT_P(NODE) \
>     (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
>
> @@ -7021,6 +7028,7 @@ extern tree array_type_nelts_top          (tree)
>  extern tree break_out_target_exprs             (tree);
>  extern tree build_ctor_subob_ref               (tree, tree, tree);
>  extern tree replace_placeholders               (tree, tree, bool * = NULL);
> +extern bool find_placeholders                  (tree);
>  extern tree get_type_decl                      (tree);
>  extern tree decl_namespace_context             (tree);
>  extern bool decl_anon_ns_mem_p                 (const_tree);
> --- gcc/cp/tree.c.jj    2018-03-14 11:49:58.926816421 +0100
> +++ gcc/cp/tree.c       2018-03-14 16:24:29.036987505 +0100
> @@ -3096,6 +3096,7 @@ build_ctor_subob_ref (tree index, tree t
>  struct replace_placeholders_t
>  {
>    tree obj;        /* The object to be substituted for a PLACEHOLDER_EXPR.  */
> +  tree exp;        /* The outermost exp.  */
>    bool seen;       /* Whether we've encountered a PLACEHOLDER_EXPR.  */
>    hash_set<tree> *pset;        /* To avoid walking same trees multiple times.  */
>  };
> @@ -3134,7 +3135,12 @@ replace_placeholders_r (tree* t, int* wa
>        {
>         constructor_elt *ce;
>         vec<constructor_elt,va_gc> *v = CONSTRUCTOR_ELTS (*t);
> -       if (d->pset->add (*t))
> +       /* Don't walk into CONSTRUCTOR_PLACEHOLDER_BOUNDARY ctors
> +          other than the d->exp one, those have PLACEHOLDER_EXPRs
> +          related to another object.  */
> +       if ((CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t)
> +            && *t != d->exp)
> +           || d->pset->add (*t))
>           {
>             *walk_subtrees = false;
>             return NULL_TREE;
> @@ -3192,16 +3198,70 @@ replace_placeholders (tree exp, tree obj
>      return exp;
>
>    tree *tp = &exp;
> -  hash_set<tree> pset;
> -  replace_placeholders_t data = { obj, false, &pset };
> +  /* Use exp instead of *(type *)&exp.  */
> +  while (TREE_CODE (exp) == INDIRECT_REF)
> +    {
> +      tree t = TREE_OPERAND (exp, 0);
> +      STRIP_NOPS (t);
> +      if (TREE_CODE (t) == ADDR_EXPR
> +         && (t = TREE_OPERAND (t, 0))
> +         && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (exp),
> +                                                       TREE_TYPE (t)))
> +       exp = t;
> +      else
> +       break;
> +    }

What's the motivation for this?  I suspect that it should only apply
if REFERENCE_REF_P.

Jason
Jakub Jelinek March 15, 2018, 8:32 a.m. UTC | #2
On Wed, Mar 14, 2018 at 08:55:47PM -0400, Jason Merrill wrote:
> > @@ -3192,16 +3198,70 @@ replace_placeholders (tree exp, tree obj
> >      return exp;
> >
> >    tree *tp = &exp;
> > -  hash_set<tree> pset;
> > -  replace_placeholders_t data = { obj, false, &pset };
> > +  /* Use exp instead of *(type *)&exp.  */
> > +  while (TREE_CODE (exp) == INDIRECT_REF)
> > +    {
> > +      tree t = TREE_OPERAND (exp, 0);
> > +      STRIP_NOPS (t);
> > +      if (TREE_CODE (t) == ADDR_EXPR
> > +         && (t = TREE_OPERAND (t, 0))
> > +         && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (exp),
> > +                                                       TREE_TYPE (t)))
> > +       exp = t;
> > +      else
> > +       break;
> > +    }
> 
> What's the motivation for this?

g++.dg/cpp0x/nsdmi13.C ICEs without that, we have there:
a = A({});
and build_over_call does:
8163	      else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
8164		{
8165		  arg = cp_build_fold_indirect_ref (arg);
8166		  val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
8167		  /* Handle NSDMI that refer to the object being initialized.  */
8168		  replace_placeholders (arg, to);
8169		}
where arg is after the cp_build_fold_indirect_ref
*(struct A &) &TARGET_EXPR <D.2403, {.p=&<PLACEHOLDER_EXPR struct A>}>
This is in MODIFY_EXPR rather than INIT_EXPR and the gimplifier through
gimple_fold_indirect_ref_rhs folds the *(struct A &) & away and so there is
no further temporary and thus cp_gimplify_init_expr isn't called to
replace_placeholders, so if we don't replace it here (with to being the a
VAR_DECL), we don't replace it ever.

> I suspect that it should only apply if REFERENCE_REF_P.

Incremental
--- gcc/cp/tree.c~	2018-03-15 09:13:38.000000000 +0100
+++ gcc/cp/tree.c	2018-03-15 09:15:43.029087739 +0100
@@ -3199,7 +3199,7 @@ replace_placeholders (tree exp, tree obj
 
   tree *tp = &exp;
   /* Use exp instead of *(type *)&exp.  */
-  while (TREE_CODE (exp) == INDIRECT_REF)
+  while (REFERENCE_REF_P (exp))
     {
       tree t = TREE_OPERAND (exp, 0);
       STRIP_NOPS (t);
works too.  I think the gimplifier would gimple_fold_indirect_ref_rhs
away even *(A *) &TARGET_EXPR, but maybe that just can't happen in C++.

The point in the replace_placeholders data.exp computation is to
differentiate between binding a CONSTRUCTOR to a particular object
and e.g. the C c = bar (X {1}); case from pr79937-1.C where it is just an
expression that contains a CONSTRUCTOR in it.  In the former case, e.g. the
nsdmi13.C, we want to replace placeholders in it, while in the latter we
don't; in both cases there is just one CONSTRUCTOR, so say ignoring the new
flag on outermost CONSTRUCTOR we process doesn't work.

	Jakub
Jason Merrill March 15, 2018, 5:15 p.m. UTC | #3
On Thu, Mar 15, 2018 at 4:32 AM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Wed, Mar 14, 2018 at 08:55:47PM -0400, Jason Merrill wrote:
>> > @@ -3192,16 +3198,70 @@ replace_placeholders (tree exp, tree obj
>> >      return exp;
>> >
>> >    tree *tp = &exp;
>> > -  hash_set<tree> pset;
>> > -  replace_placeholders_t data = { obj, false, &pset };
>> > +  /* Use exp instead of *(type *)&exp.  */
>> > +  while (TREE_CODE (exp) == INDIRECT_REF)
>> > +    {
>> > +      tree t = TREE_OPERAND (exp, 0);
>> > +      STRIP_NOPS (t);
>> > +      if (TREE_CODE (t) == ADDR_EXPR
>> > +         && (t = TREE_OPERAND (t, 0))
>> > +         && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (exp),
>> > +                                                       TREE_TYPE (t)))
>> > +       exp = t;
>> > +      else
>> > +       break;
>> > +    }
>>
>> What's the motivation for this?
>
> g++.dg/cpp0x/nsdmi13.C ICEs without that, we have there:
> a = A({});
> and build_over_call does:
> 8163          else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
> 8164            {
> 8165              arg = cp_build_fold_indirect_ref (arg);
> 8166              val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
> 8167              /* Handle NSDMI that refer to the object being initialized.  */
> 8168              replace_placeholders (arg, to);
> 8169            }
> where arg is after the cp_build_fold_indirect_ref
> *(struct A &) &TARGET_EXPR <D.2403, {.p=&<PLACEHOLDER_EXPR struct A>}>
> This is in MODIFY_EXPR rather than INIT_EXPR and the gimplifier through
> gimple_fold_indirect_ref_rhs folds the *(struct A &) & away and so there is
> no further temporary and thus cp_gimplify_init_expr isn't called to
> replace_placeholders, so if we don't replace it here (with to being the a
> VAR_DECL), we don't replace it ever.

Ah.  That's a problem: the language says there's a temporary, so after
the assignment a.p should not point to a.

So the existing call to replace_placeholders in build_over_call is wrong.

Seems like if the initializer for a TARGET_EXPR involves
PLACEHOLDER_EXPR, the gimple_fold_modify_expr_rhs treatment of
TARGET_EXPR isn't safe for MODIFY_EXPR, for the same reason that
cp_gimplify_expr treats INIT_EXPR and MODIFY_EXPR differently.

Jason
Jakub Jelinek March 15, 2018, 5:59 p.m. UTC | #4
On Thu, Mar 15, 2018 at 01:15:53PM -0400, Jason Merrill wrote:
> > g++.dg/cpp0x/nsdmi13.C ICEs without that, we have there:
> > a = A({});
> > and build_over_call does:
> > 8163          else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
> > 8164            {
> > 8165              arg = cp_build_fold_indirect_ref (arg);
> > 8166              val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
> > 8167              /* Handle NSDMI that refer to the object being initialized.  */
> > 8168              replace_placeholders (arg, to);
> > 8169            }
> > where arg is after the cp_build_fold_indirect_ref
> > *(struct A &) &TARGET_EXPR <D.2403, {.p=&<PLACEHOLDER_EXPR struct A>}>
> > This is in MODIFY_EXPR rather than INIT_EXPR and the gimplifier through
> > gimple_fold_indirect_ref_rhs folds the *(struct A &) & away and so there is
> > no further temporary and thus cp_gimplify_init_expr isn't called to
> > replace_placeholders, so if we don't replace it here (with to being the a
> > VAR_DECL), we don't replace it ever.
> 
> Ah.  That's a problem: the language says there's a temporary, so after
> the assignment a.p should not point to a.

Seems the gimplify.c stuff:
        case INDIRECT_REF:
          {
            /* If we have code like

             *(const A*)(A*)&x

             where the type of "x" is a (possibly cv-qualified variant
             of "A"), treat the entire expression as identical to "x".
             This kind of code arises in C++ when an object is bound
             to a const reference, and if "x" is a TARGET_EXPR we want
             to take advantage of the optimization below.  */
...
has been added in r92539 as part of PR16405 fix.  So, do we want to stop
doing that unconditionally if t is a TARGET_EXPR, or for selected kinds of
types of TARGET_EXPR, or ask some langhook whether it is ok to do so
(say not ok if find_placeholders (t))?  Or contains_placeholder_p?
Though the last one could also affect Ada and could return true even if
the PLACEHOLDER_EXPRs are for some nested TARGET_EXPR in it.

> So the existing call to replace_placeholders in build_over_call is wrong.

Shall it be just dropped if we tweak the gimplifier somehow?

> Seems like if the initializer for a TARGET_EXPR involves
> PLACEHOLDER_EXPR, the gimple_fold_modify_expr_rhs treatment of
> TARGET_EXPR isn't safe for MODIFY_EXPR, for the same reason that
> cp_gimplify_expr treats INIT_EXPR and MODIFY_EXPR differently.

	Jakub
Jason Merrill March 15, 2018, 7:33 p.m. UTC | #5
On Thu, Mar 15, 2018 at 1:59 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Mar 15, 2018 at 01:15:53PM -0400, Jason Merrill wrote:
>> > g++.dg/cpp0x/nsdmi13.C ICEs without that, we have there:
>> > a = A({});
>> > and build_over_call does:
>> > 8163          else if (tree_int_cst_equal (TYPE_SIZE (type), TYPE_SIZE (as_base)))
>> > 8164            {
>> > 8165              arg = cp_build_fold_indirect_ref (arg);
>> > 8166              val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
>> > 8167              /* Handle NSDMI that refer to the object being initialized.  */
>> > 8168              replace_placeholders (arg, to);
>> > 8169            }
>> > where arg is after the cp_build_fold_indirect_ref
>> > *(struct A &) &TARGET_EXPR <D.2403, {.p=&<PLACEHOLDER_EXPR struct A>}>
>> > This is in MODIFY_EXPR rather than INIT_EXPR and the gimplifier through
>> > gimple_fold_indirect_ref_rhs folds the *(struct A &) & away and so there is
>> > no further temporary and thus cp_gimplify_init_expr isn't called to
>> > replace_placeholders, so if we don't replace it here (with to being the a
>> > VAR_DECL), we don't replace it ever.
>>
>> Ah.  That's a problem: the language says there's a temporary, so after
>> the assignment a.p should not point to a.
>
> Seems the gimplify.c stuff:
>         case INDIRECT_REF:
>           {
>             /* If we have code like
>
>              *(const A*)(A*)&x
>
>              where the type of "x" is a (possibly cv-qualified variant
>              of "A"), treat the entire expression as identical to "x".
>              This kind of code arises in C++ when an object is bound
>              to a const reference, and if "x" is a TARGET_EXPR we want
>              to take advantage of the optimization below.  */
> ...
> has been added in r92539 as part of PR16405 fix.  So, do we want to stop
> doing that unconditionally if t is a TARGET_EXPR, or for selected kinds of

Folding away the INDIRECT_REF is fine.  It's the TARGET_EXPR handling
that is wrong.

> types of TARGET_EXPR, or ask some langhook whether it is ok to do so
> (say not ok if find_placeholders (t))?  Or contains_placeholder_p?
> Though the last one could also affect Ada and could return true even if
> the PLACEHOLDER_EXPRs are for some nested TARGET_EXPR in it.

The existing test for whether to collapse a TARGET_EXPR is

            if (init
                && !VOID_TYPE_P (TREE_TYPE (init)))

so we need this test to fail somehow when the constructor contains
placeholders, either by adding an additional test (like the ones you
mention) or by making the initialization void in a way that other
gimplification knows how to handle.

>> So the existing call to replace_placeholders in build_over_call is wrong.
>
> Shall it be just dropped if we tweak the gimplifier somehow?

It must be dropped.

>> Seems like if the initializer for a TARGET_EXPR involves
>> PLACEHOLDER_EXPR, the gimple_fold_modify_expr_rhs treatment of
>> TARGET_EXPR isn't safe for MODIFY_EXPR, for the same reason that
>> cp_gimplify_expr treats INIT_EXPR and MODIFY_EXPR differently.
Jakub Jelinek March 15, 2018, 8:28 p.m. UTC | #6
On Thu, Mar 15, 2018 at 03:33:12PM -0400, Jason Merrill wrote:
> Folding away the INDIRECT_REF is fine.  It's the TARGET_EXPR handling
> that is wrong.

Ah, ok.

> > types of TARGET_EXPR, or ask some langhook whether it is ok to do so
> > (say not ok if find_placeholders (t))?  Or contains_placeholder_p?
> > Though the last one could also affect Ada and could return true even if
> > the PLACEHOLDER_EXPRs are for some nested TARGET_EXPR in it.
> 
> The existing test for whether to collapse a TARGET_EXPR is
> 
>             if (init
>                 && !VOID_TYPE_P (TREE_TYPE (init)))
> 
> so we need this test to fail somehow when the constructor contains
> placeholders, either by adding an additional test (like the ones you
> mention) or by making the initialization void in a way that other
> gimplification knows how to handle.

So what about this?  It uses an unused bit on TARGET_EXPRs rather than
a langhook, but if you prefer a langhook, I can add it.

Tested so far just with
make check-c++-all RUNTESTFLAGS="dg.exp='pr79937* pr82410.C nsdmi*'"

2018-03-15  Jakub Jelinek  <jakub@redhat.com>

	PR c++/79937
	PR c++/82410
	* tree.h (TARGET_EXPR_NO_ELIDE): Define.
	* gimplify.c (gimplify_arg, gimplify_modify_expr_rhs): Don't elide
	TARGET_EXPRs with TARGET_EXPR_NO_ELIDE flag set.

	* cp-tree.h (CONSTRUCTOR_PLACEHOLDER_BOUNDARY): Define.
	(find_placeholder): Declare.
	* tree.c (struct replace_placeholders_t): Add exp member.
	(replace_placeholders_r): Don't walk into ctors with
	CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set, unless they are equal to
	d->exp.
	(replace_placeholders): Initialize data.exp.
	(find_placeholders_r, find_placeholders): New functions.
	* typeck2.c (process_init_constructor_record,
	process_init_constructor_union): Set CONSTRUCTOR_PLACEHOLDER_BOUNDARY
	if adding NSDMI on which find_placeholder returns true.
	* call.c (build_over_call): Don't call replace_placeholders here.
	* cp-gimplify.c (cp_genericize_r): Set TARGET_EXPR_NO_ELIDE on
	TARGET_EXPRs with CONSTRUCTOR_PLACEHOLDER_BOUNDARY set on
	TARGET_EXPR_INITIAL.
	(cp_fold): Copy over CONSTRUCTOR_PLACEHOLDER_BOUNDARY bit to new
	ctor.

	* g++.dg/cpp1y/pr79937-1.C: New test.
	* g++.dg/cpp1y/pr79937-2.C: New test.
	* g++.dg/cpp1y/pr79937-3.C: New test.
	* g++.dg/cpp1y/pr79937-4.C: New test.
	* g++.dg/cpp1y/pr82410.C: New test.

--- gcc/tree.h.jj	2018-02-22 12:37:02.566387732 +0100
+++ gcc/tree.h	2018-03-15 20:42:57.968858551 +0100
@@ -1197,6 +1197,8 @@ extern tree maybe_wrap_with_location (tr
 #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 0)
 #define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 1)
 #define TARGET_EXPR_CLEANUP(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 2)
+/* Don't elide the initialization of TARGET_EXPR_SLOT for this TARGET_EXPR.  */
+#define TARGET_EXPR_NO_ELIDE(NODE) (TARGET_EXPR_CHECK (NODE)->base.private_flag)
 
 /* DECL_EXPR accessor. This gives access to the DECL associated with
    the given declaration statement.  */
--- gcc/gimplify.c.jj	2018-01-18 21:28:49.743301440 +0100
+++ gcc/gimplify.c	2018-03-15 21:06:15.290828320 +0100
@@ -3155,7 +3155,8 @@ gimplify_arg (tree *arg_p, gimple_seq *p
     {
       test = is_gimple_lvalue, fb = fb_either;
       /* Also strip a TARGET_EXPR that would force an extra copy.  */
-      if (TREE_CODE (*arg_p) == TARGET_EXPR)
+      if (TREE_CODE (*arg_p) == TARGET_EXPR
+	  && !TARGET_EXPR_NO_ELIDE (*arg_p))
 	{
 	  tree init = TARGET_EXPR_INITIAL (*arg_p);
 	  if (init
@@ -5211,6 +5212,7 @@ gimplify_modify_expr_rhs (tree *expr_p,
 	    tree init = TARGET_EXPR_INITIAL (*from_p);
 
 	    if (init
+		&& !TARGET_EXPR_NO_ELIDE (*from_p)
 		&& !VOID_TYPE_P (TREE_TYPE (init)))
 	      {
 		*from_p = init;
--- gcc/cp/cp-tree.h.jj	2018-03-15 18:44:21.646300907 +0100
+++ gcc/cp/cp-tree.h	2018-03-15 20:40:49.280818226 +0100
@@ -425,6 +425,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       DECL_VTABLE_OR_VTT_P (in VAR_DECL)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
+      CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
    6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
       DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
       TYPE_MARKED_P (in _TYPE)
@@ -4144,6 +4145,12 @@ more_aggr_init_expr_args_p (const aggr_i
 #define CONSTRUCTOR_C99_COMPOUND_LITERAL(NODE) \
   (TREE_LANG_FLAG_3 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR contains PLACEHOLDER_EXPRs referencing the
+   CONSTRUCTOR's type not nested inside another CONSTRUCTOR marked with
+   CONSTRUCTOR_PLACEHOLDER_BOUNDARY.  */
+#define CONSTRUCTOR_PLACEHOLDER_BOUNDARY(NODE) \
+  (TREE_LANG_FLAG_5 (CONSTRUCTOR_CHECK (NODE)))
+
 #define DIRECT_LIST_INIT_P(NODE) \
    (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
 
@@ -7021,6 +7028,7 @@ extern tree array_type_nelts_top		(tree)
 extern tree break_out_target_exprs		(tree);
 extern tree build_ctor_subob_ref		(tree, tree, tree);
 extern tree replace_placeholders		(tree, tree, bool * = NULL);
+extern bool find_placeholders			(tree);
 extern tree get_type_decl			(tree);
 extern tree decl_namespace_context		(tree);
 extern bool decl_anon_ns_mem_p			(const_tree);
--- gcc/cp/tree.c.jj	2018-03-15 18:44:21.661300889 +0100
+++ gcc/cp/tree.c	2018-03-15 20:49:03.518973106 +0100
@@ -3096,6 +3096,7 @@ build_ctor_subob_ref (tree index, tree t
 struct replace_placeholders_t
 {
   tree obj;	    /* The object to be substituted for a PLACEHOLDER_EXPR.  */
+  tree exp;	    /* The outermost exp.  */
   bool seen;	    /* Whether we've encountered a PLACEHOLDER_EXPR.  */
   hash_set<tree> *pset;	/* To avoid walking same trees multiple times.  */
 };
@@ -3134,7 +3135,12 @@ replace_placeholders_r (tree* t, int* wa
       {
 	constructor_elt *ce;
 	vec<constructor_elt,va_gc> *v = CONSTRUCTOR_ELTS (*t);
-	if (d->pset->add (*t))
+	/* Don't walk into CONSTRUCTOR_PLACEHOLDER_BOUNDARY ctors
+	   other than the d->exp one, those have PLACEHOLDER_EXPRs
+	   related to another object.  */
+	if ((CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t)
+	     && *t != d->exp)
+	    || d->pset->add (*t))
 	  {
 	    *walk_subtrees = false;
 	    return NULL_TREE;
@@ -3192,16 +3198,58 @@ replace_placeholders (tree exp, tree obj
     return exp;
 
   tree *tp = &exp;
-  hash_set<tree> pset;
-  replace_placeholders_t data = { obj, false, &pset };
+  /* Use exp instead of *(type *)&exp.  */
   if (TREE_CODE (exp) == TARGET_EXPR)
     tp = &TARGET_EXPR_INITIAL (exp);
+  hash_set<tree> pset;
+  replace_placeholders_t data = { obj, *tp, false, &pset };
   cp_walk_tree (tp, replace_placeholders_r, &data, NULL);
   if (seen_p)
     *seen_p = data.seen;
   return exp;
 }
 
+/* Callback function for find_placeholders.  */
+
+static tree
+find_placeholders_r (tree *t, int *walk_subtrees, void *)
+{
+  if (TYPE_P (*t) || TREE_CONSTANT (*t))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  switch (TREE_CODE (*t))
+    {
+    case PLACEHOLDER_EXPR:
+      return *t;
+
+    case CONSTRUCTOR:
+      if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t))
+	*walk_subtrees = false;
+      break;
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if EXP contains a PLACEHOLDER_EXPR.  Don't walk into
+   ctors with CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set.  */
+
+bool
+find_placeholders (tree exp)
+{
+  /* This is only relevant for C++14.  */
+  if (cxx_dialect < cxx14)
+    return false;
+
+  return cp_walk_tree_without_duplicates (&exp, find_placeholders_r, NULL);
+}
+
 /* Similar to `build_nt', but for template definitions of dependent
    expressions  */
 
--- gcc/cp/typeck2.c.jj	2018-03-15 18:44:21.661300889 +0100
+++ gcc/cp/typeck2.c	2018-03-15 20:40:49.282818227 +0100
@@ -1470,6 +1470,9 @@ process_init_constructor_record (tree ty
 	    }
 	  /* C++14 aggregate NSDMI.  */
 	  next = get_nsdmi (field, /*ctor*/false, complain);
+	  if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
+	      && find_placeholders (next))
+	    CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
 	}
       else if (type_build_ctor_call (TREE_TYPE (field)))
 	{
@@ -1608,10 +1611,11 @@ process_init_constructor_union (tree typ
 	  if (TREE_CODE (field) == FIELD_DECL
 	      && DECL_INITIAL (field) != NULL_TREE)
 	    {
-	      CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init),
-				      field,
-				      get_nsdmi (field, /*in_ctor=*/false,
-						 complain));
+	      tree val = get_nsdmi (field, /*in_ctor=*/false, complain);
+	      if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
+		  && find_placeholders (val))
+		CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
+	      CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init), field, val);
 	      break;
 	    }
 	}
--- gcc/cp/call.c.jj	2018-03-11 17:48:36.359061434 +0100
+++ gcc/cp/call.c	2018-03-15 21:07:31.781961783 +0100
@@ -8164,8 +8164,6 @@ build_over_call (struct z_candidate *can
 	{
 	  arg = cp_build_fold_indirect_ref (arg);
 	  val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
-	  /* Handle NSDMI that refer to the object being initialized.  */
-	  replace_placeholders (arg, to);
 	}
       else
 	{
--- gcc/cp/cp-gimplify.c.jj	2018-03-07 22:51:58.742478684 +0100
+++ gcc/cp/cp-gimplify.c	2018-03-15 21:19:27.353619519 +0100
@@ -1515,6 +1515,13 @@ cp_genericize_r (tree *stmt_p, int *walk
 	}
       break;
 
+    case TARGET_EXPR:
+      if (TARGET_EXPR_INITIAL (stmt)
+	  && TREE_CODE (TARGET_EXPR_INITIAL (stmt)) == CONSTRUCTOR
+	  && CONSTRUCTOR_PLACEHOLDER_BOUNDARY (TARGET_EXPR_INITIAL (stmt)))
+	TARGET_EXPR_NO_ELIDE (stmt) = 1;
+      break;
+
     default:
       if (IS_TYPE_OR_DECL_P (stmt))
 	*walk_subtrees = 0;
@@ -2469,7 +2476,11 @@ cp_fold (tree x)
 	      }
 	  }
 	if (nelts)
-	  x = build_constructor (TREE_TYPE (x), nelts);
+	  {
+	    x = build_constructor (TREE_TYPE (x), nelts);
+	    CONSTRUCTOR_PLACEHOLDER_BOUNDARY (x)
+	      = CONSTRUCTOR_PLACEHOLDER_BOUNDARY (org_x);
+	  }
 	break;
       }
     case TREE_VEC:
--- gcc/testsuite/g++.dg/cpp1y/pr79937-1.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-1.C	2018-03-15 20:40:49.282818227 +0100
@@ -0,0 +1,23 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct C {};
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+};
+
+C
+bar (X x)
+{
+  if (x.i != 1 || x.n != 1)
+    __builtin_abort ();
+  return {};
+}
+
+int
+main ()
+{
+  C c = bar (X {1});
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-2.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-2.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,24 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct C {};
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+  unsigned m = i;
+};
+
+C
+bar (X x)
+{
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+  return {};
+}
+
+int
+main ()
+{
+  C c = bar (X {1, X {2}.n});
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-3.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-3.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,24 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+  unsigned m = i;
+};
+
+X
+bar (X x)
+{
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+  return x;
+}
+
+int
+main ()
+{
+  X x = bar (X {1, X {2}.n});
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-4.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-4.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,32 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+};
+
+X
+bar (X x)
+{
+  return x;
+}
+
+struct Y
+{
+  static Y bar (Y y) { return y; }
+  unsigned i;
+  unsigned n = bar (Y{2,i}).n;
+};
+
+int
+main ()
+{
+  X x { 1, bar (X{2}).n };
+  if (x.n != 2)
+    __builtin_abort ();
+
+  Y y { 1 };
+  if (y.n != 1)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr82410.C.jj	2018-03-15 20:40:49.284818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr82410.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,16 @@
+// PR c++/82410
+// { dg-do compile { target c++14 } }
+
+int
+main ()
+{
+  struct A {};
+  struct S
+  {
+    int & p;
+    int x = p;
+    operator A () { return {}; }
+  };
+  int l;
+  [] (A) {} (S{l});
+}


	Jakub
Jason Merrill March 15, 2018, 8:50 p.m. UTC | #7
On Thu, Mar 15, 2018 at 4:28 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Mar 15, 2018 at 03:33:12PM -0400, Jason Merrill wrote:
>> Folding away the INDIRECT_REF is fine.  It's the TARGET_EXPR handling
>> that is wrong.
>
> Ah, ok.
>
>> > types of TARGET_EXPR, or ask some langhook whether it is ok to do so
>> > (say not ok if find_placeholders (t))?  Or contains_placeholder_p?
>> > Though the last one could also affect Ada and could return true even if
>> > the PLACEHOLDER_EXPRs are for some nested TARGET_EXPR in it.
>>
>> The existing test for whether to collapse a TARGET_EXPR is
>>
>>             if (init
>>                 && !VOID_TYPE_P (TREE_TYPE (init)))
>>
>> so we need this test to fail somehow when the constructor contains
>> placeholders, either by adding an additional test (like the ones you
>> mention) or by making the initialization void in a way that other
>> gimplification knows how to handle.
>
> So what about this?  It uses an unused bit on TARGET_EXPRs rather than
> a langhook, but if you prefer a langhook, I can add it.
>
> Tested so far just with
> make check-c++-all RUNTESTFLAGS="dg.exp='pr79937* pr82410.C nsdmi*'"
>
> 2018-03-15  Jakub Jelinek  <jakub@redhat.com>
>
>         PR c++/79937
>         PR c++/82410
>         * tree.h (TARGET_EXPR_NO_ELIDE): Define.
>         * gimplify.c (gimplify_arg, gimplify_modify_expr_rhs): Don't elide
>         TARGET_EXPRs with TARGET_EXPR_NO_ELIDE flag set.
>
>         * cp-tree.h (CONSTRUCTOR_PLACEHOLDER_BOUNDARY): Define.
>         (find_placeholder): Declare.
>         * tree.c (struct replace_placeholders_t): Add exp member.
>         (replace_placeholders_r): Don't walk into ctors with
>         CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set, unless they are equal to
>         d->exp.
>         (replace_placeholders): Initialize data.exp.
>         (find_placeholders_r, find_placeholders): New functions.
>         * typeck2.c (process_init_constructor_record,
>         process_init_constructor_union): Set CONSTRUCTOR_PLACEHOLDER_BOUNDARY
>         if adding NSDMI on which find_placeholder returns true.
>         * call.c (build_over_call): Don't call replace_placeholders here.
>         * cp-gimplify.c (cp_genericize_r): Set TARGET_EXPR_NO_ELIDE on
>         TARGET_EXPRs with CONSTRUCTOR_PLACEHOLDER_BOUNDARY set on
>         TARGET_EXPR_INITIAL.
>         (cp_fold): Copy over CONSTRUCTOR_PLACEHOLDER_BOUNDARY bit to new
>         ctor.
>
>         * g++.dg/cpp1y/pr79937-1.C: New test.
>         * g++.dg/cpp1y/pr79937-2.C: New test.
>         * g++.dg/cpp1y/pr79937-3.C: New test.
>         * g++.dg/cpp1y/pr79937-4.C: New test.
>         * g++.dg/cpp1y/pr82410.C: New test.
>
> +/* Don't elide the initialization of TARGET_EXPR_SLOT for this TARGET_EXPR.  */
> +#define TARGET_EXPR_NO_ELIDE(NODE) (TARGET_EXPR_CHECK (NODE)->base.private_flag)

This should be specifically on the rhs of a MODIFY_EXPR; it's OK to
elide on the rhs of an INIT_EXPR.

> @@ -3155,7 +3155,8 @@ gimplify_arg (tree *arg_p, gimple_seq *p
>      {
>        test = is_gimple_lvalue, fb = fb_either;
>        /* Also strip a TARGET_EXPR that would force an extra copy.  */
> -      if (TREE_CODE (*arg_p) == TARGET_EXPR)
> +      if (TREE_CODE (*arg_p) == TARGET_EXPR
> +         && !TARGET_EXPR_NO_ELIDE (*arg_p))

This is also an initialization context, so we don't need to check it here.

> @@ -5211,6 +5212,7 @@ gimplify_modify_expr_rhs (tree *expr_p,
>             tree init = TARGET_EXPR_INITIAL (*from_p);
>
>             if (init
> +               && !TARGET_EXPR_NO_ELIDE (*from_p)

Here should check TREE_CODE (*expr_p).

Jason
Jakub Jelinek March 15, 2018, 11:33 p.m. UTC | #8
On Thu, Mar 15, 2018 at 04:50:57PM -0400, Jason Merrill wrote:
> > +/* Don't elide the initialization of TARGET_EXPR_SLOT for this TARGET_EXPR.  */
> > +#define TARGET_EXPR_NO_ELIDE(NODE) (TARGET_EXPR_CHECK (NODE)->base.private_flag)
> 
> This should be specifically on the rhs of a MODIFY_EXPR; it's OK to
> elide on the rhs of an INIT_EXPR.
> 
> > @@ -3155,7 +3155,8 @@ gimplify_arg (tree *arg_p, gimple_seq *p
> >      {
> >        test = is_gimple_lvalue, fb = fb_either;
> >        /* Also strip a TARGET_EXPR that would force an extra copy.  */
> > -      if (TREE_CODE (*arg_p) == TARGET_EXPR)
> > +      if (TREE_CODE (*arg_p) == TARGET_EXPR
> > +         && !TARGET_EXPR_NO_ELIDE (*arg_p))
> 
> This is also an initialization context, so we don't need to check it here.

Ah, ok, changed in the patch.

> > @@ -5211,6 +5212,7 @@ gimplify_modify_expr_rhs (tree *expr_p,
> >             tree init = TARGET_EXPR_INITIAL (*from_p);
> >
> >             if (init
> > +               && !TARGET_EXPR_NO_ELIDE (*from_p)
> 
> Here should check TREE_CODE (*expr_p).

The following (except the *t = unshare_expr (x) change) has successfully
bootstrapped/regtested on x86_64-linux and i686-linux (without the
unshare_expr it regressed g++.dg/cpp0x/pr83556.C, but with that change it
succeeds; I'm afraid right now there is no easy way to determine if we
need to unshare or not, if replace_placeholders is called before
unshare_body at the start of gimplification, we don't need to unshare, but
during gimplification we have to).  Ok for trunk?

2018-03-15  Jakub Jelinek  <jakub@redhat.com>

	PR c++/79937
	PR c++/82410
	* tree.h (TARGET_EXPR_NO_ELIDE): Define.
	* gimplify.c (gimplify_modify_expr_rhs): Don't elide TARGET_EXPRs with
	TARGET_EXPR_NO_ELIDE flag set unless *expr_p is INIT_EXPR.

	* cp-tree.h (CONSTRUCTOR_PLACEHOLDER_BOUNDARY): Define.
	(find_placeholder): Declare.
	* tree.c (struct replace_placeholders_t): Add exp member.
	(replace_placeholders_r): Don't walk into ctors with
	CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set, unless they are equal to
	d->exp.  Replace PLACEHOLDER_EXPR with unshare_expr (x) rather than x.
	(replace_placeholders): Initialize data.exp.
	(find_placeholders_r, find_placeholders): New functions.
	* typeck2.c (process_init_constructor_record,
	process_init_constructor_union): Set CONSTRUCTOR_PLACEHOLDER_BOUNDARY
	if adding NSDMI on which find_placeholder returns true.
	* call.c (build_over_call): Don't call replace_placeholders here.
	* cp-gimplify.c (cp_genericize_r): Set TARGET_EXPR_NO_ELIDE on
	TARGET_EXPRs with CONSTRUCTOR_PLACEHOLDER_BOUNDARY set on
	TARGET_EXPR_INITIAL.
	(cp_fold): Copy over CONSTRUCTOR_PLACEHOLDER_BOUNDARY bit to new
	ctor.

	* g++.dg/cpp1y/pr79937-1.C: New test.
	* g++.dg/cpp1y/pr79937-2.C: New test.
	* g++.dg/cpp1y/pr79937-3.C: New test.
	* g++.dg/cpp1y/pr79937-4.C: New test.
	* g++.dg/cpp1y/pr82410.C: New test.

--- gcc/tree.h.jj	2018-02-22 12:37:02.566387732 +0100
+++ gcc/tree.h	2018-03-15 20:42:57.968858551 +0100
@@ -1197,6 +1197,9 @@ extern tree maybe_wrap_with_location (tr
 #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 0)
 #define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 1)
 #define TARGET_EXPR_CLEANUP(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 2)
+/* Don't elide the initialization of TARGET_EXPR_SLOT for this TARGET_EXPR
+   on rhs of MODIFY_EXPR.  */
+#define TARGET_EXPR_NO_ELIDE(NODE) (TARGET_EXPR_CHECK (NODE)->base.private_flag)
 
 /* DECL_EXPR accessor. This gives access to the DECL associated with
    the given declaration statement.  */
--- gcc/gimplify.c.jj	2018-01-18 21:28:49.743301440 +0100
+++ gcc/gimplify.c	2018-03-15 21:06:15.290828320 +0100
@@ -5211,6 +5211,8 @@ gimplify_modify_expr_rhs (tree *expr_p,
 	    tree init = TARGET_EXPR_INITIAL (*from_p);
 
 	    if (init
+		&& (TREE_CODE (*expr_p) != MODIFY_EXPR
+		    || !TARGET_EXPR_NO_ELIDE (*from_p))
 		&& !VOID_TYPE_P (TREE_TYPE (init)))
 	      {
 		*from_p = init;
--- gcc/cp/cp-tree.h.jj	2018-03-15 18:44:21.646300907 +0100
+++ gcc/cp/cp-tree.h	2018-03-15 20:40:49.280818226 +0100
@@ -425,6 +425,7 @@ extern GTY(()) tree cp_global_trees[CPTI
       DECL_VTABLE_OR_VTT_P (in VAR_DECL)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
+      CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
    6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
       DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
       TYPE_MARKED_P (in _TYPE)
@@ -4144,6 +4145,12 @@ more_aggr_init_expr_args_p (const aggr_i
 #define CONSTRUCTOR_C99_COMPOUND_LITERAL(NODE) \
   (TREE_LANG_FLAG_3 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR contains PLACEHOLDER_EXPRs referencing the
+   CONSTRUCTOR's type not nested inside another CONSTRUCTOR marked with
+   CONSTRUCTOR_PLACEHOLDER_BOUNDARY.  */
+#define CONSTRUCTOR_PLACEHOLDER_BOUNDARY(NODE) \
+  (TREE_LANG_FLAG_5 (CONSTRUCTOR_CHECK (NODE)))
+
 #define DIRECT_LIST_INIT_P(NODE) \
    (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
 
@@ -7021,6 +7028,7 @@ extern tree array_type_nelts_top		(tree)
 extern tree break_out_target_exprs		(tree);
 extern tree build_ctor_subob_ref		(tree, tree, tree);
 extern tree replace_placeholders		(tree, tree, bool * = NULL);
+extern bool find_placeholders			(tree);
 extern tree get_type_decl			(tree);
 extern tree decl_namespace_context		(tree);
 extern bool decl_anon_ns_mem_p			(const_tree);
--- gcc/cp/tree.c.jj	2018-03-15 18:44:21.661300889 +0100
+++ gcc/cp/tree.c	2018-03-15 20:49:03.518973106 +0100
@@ -3096,6 +3096,7 @@ build_ctor_subob_ref (tree index, tree t
 struct replace_placeholders_t
 {
   tree obj;	    /* The object to be substituted for a PLACEHOLDER_EXPR.  */
+  tree exp;	    /* The outermost exp.  */
   bool seen;	    /* Whether we've encountered a PLACEHOLDER_EXPR.  */
   hash_set<tree> *pset;	/* To avoid walking same trees multiple times.  */
 };
@@ -3124,7 +3125,7 @@ replace_placeholders_r (tree* t, int* wa
 							   TREE_TYPE (x));
 	     x = TREE_OPERAND (x, 0))
 	  gcc_assert (TREE_CODE (x) == COMPONENT_REF);
-	*t = x;
+	*t = unshare_expr (x);
 	*walk_subtrees = false;
 	d->seen = true;
       }
@@ -3134,7 +3135,12 @@ replace_placeholders_r (tree* t, int* wa
       {
 	constructor_elt *ce;
 	vec<constructor_elt,va_gc> *v = CONSTRUCTOR_ELTS (*t);
-	if (d->pset->add (*t))
+	/* Don't walk into CONSTRUCTOR_PLACEHOLDER_BOUNDARY ctors
+	   other than the d->exp one, those have PLACEHOLDER_EXPRs
+	   related to another object.  */
+	if ((CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t)
+	     && *t != d->exp)
+	    || d->pset->add (*t))
 	  {
 	    *walk_subtrees = false;
 	    return NULL_TREE;
@@ -3192,16 +3198,58 @@ replace_placeholders (tree exp, tree obj
     return exp;
 
   tree *tp = &exp;
-  hash_set<tree> pset;
-  replace_placeholders_t data = { obj, false, &pset };
+  /* Use exp instead of *(type *)&exp.  */
   if (TREE_CODE (exp) == TARGET_EXPR)
     tp = &TARGET_EXPR_INITIAL (exp);
+  hash_set<tree> pset;
+  replace_placeholders_t data = { obj, *tp, false, &pset };
   cp_walk_tree (tp, replace_placeholders_r, &data, NULL);
   if (seen_p)
     *seen_p = data.seen;
   return exp;
 }
 
+/* Callback function for find_placeholders.  */
+
+static tree
+find_placeholders_r (tree *t, int *walk_subtrees, void *)
+{
+  if (TYPE_P (*t) || TREE_CONSTANT (*t))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  switch (TREE_CODE (*t))
+    {
+    case PLACEHOLDER_EXPR:
+      return *t;
+
+    case CONSTRUCTOR:
+      if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t))
+	*walk_subtrees = false;
+      break;
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if EXP contains a PLACEHOLDER_EXPR.  Don't walk into
+   ctors with CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set.  */
+
+bool
+find_placeholders (tree exp)
+{
+  /* This is only relevant for C++14.  */
+  if (cxx_dialect < cxx14)
+    return false;
+
+  return cp_walk_tree_without_duplicates (&exp, find_placeholders_r, NULL);
+}
+
 /* Similar to `build_nt', but for template definitions of dependent
    expressions  */
 
--- gcc/cp/typeck2.c.jj	2018-03-15 18:44:21.661300889 +0100
+++ gcc/cp/typeck2.c	2018-03-15 20:40:49.282818227 +0100
@@ -1470,6 +1470,9 @@ process_init_constructor_record (tree ty
 	    }
 	  /* C++14 aggregate NSDMI.  */
 	  next = get_nsdmi (field, /*ctor*/false, complain);
+	  if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
+	      && find_placeholders (next))
+	    CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
 	}
       else if (type_build_ctor_call (TREE_TYPE (field)))
 	{
@@ -1608,10 +1611,11 @@ process_init_constructor_union (tree typ
 	  if (TREE_CODE (field) == FIELD_DECL
 	      && DECL_INITIAL (field) != NULL_TREE)
 	    {
-	      CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init),
-				      field,
-				      get_nsdmi (field, /*in_ctor=*/false,
-						 complain));
+	      tree val = get_nsdmi (field, /*in_ctor=*/false, complain);
+	      if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
+		  && find_placeholders (val))
+		CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
+	      CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init), field, val);
 	      break;
 	    }
 	}
--- gcc/cp/call.c.jj	2018-03-11 17:48:36.359061434 +0100
+++ gcc/cp/call.c	2018-03-15 21:07:31.781961783 +0100
@@ -8164,8 +8164,6 @@ build_over_call (struct z_candidate *can
 	{
 	  arg = cp_build_fold_indirect_ref (arg);
 	  val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
-	  /* Handle NSDMI that refer to the object being initialized.  */
-	  replace_placeholders (arg, to);
 	}
       else
 	{
--- gcc/cp/cp-gimplify.c.jj	2018-03-07 22:51:58.742478684 +0100
+++ gcc/cp/cp-gimplify.c	2018-03-15 21:19:27.353619519 +0100
@@ -1515,6 +1515,13 @@ cp_genericize_r (tree *stmt_p, int *walk
 	}
       break;
 
+    case TARGET_EXPR:
+      if (TARGET_EXPR_INITIAL (stmt)
+	  && TREE_CODE (TARGET_EXPR_INITIAL (stmt)) == CONSTRUCTOR
+	  && CONSTRUCTOR_PLACEHOLDER_BOUNDARY (TARGET_EXPR_INITIAL (stmt)))
+	TARGET_EXPR_NO_ELIDE (stmt) = 1;
+      break;
+
     default:
       if (IS_TYPE_OR_DECL_P (stmt))
 	*walk_subtrees = 0;
@@ -2469,7 +2476,11 @@ cp_fold (tree x)
 	      }
 	  }
 	if (nelts)
-	  x = build_constructor (TREE_TYPE (x), nelts);
+	  {
+	    x = build_constructor (TREE_TYPE (x), nelts);
+	    CONSTRUCTOR_PLACEHOLDER_BOUNDARY (x)
+	      = CONSTRUCTOR_PLACEHOLDER_BOUNDARY (org_x);
+	  }
 	break;
       }
     case TREE_VEC:
--- gcc/testsuite/g++.dg/cpp1y/pr79937-1.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-1.C	2018-03-15 20:40:49.282818227 +0100
@@ -0,0 +1,23 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct C {};
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+};
+
+C
+bar (X x)
+{
+  if (x.i != 1 || x.n != 1)
+    __builtin_abort ();
+  return {};
+}
+
+int
+main ()
+{
+  C c = bar (X {1});
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-2.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-2.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,24 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct C {};
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+  unsigned m = i;
+};
+
+C
+bar (X x)
+{
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+  return {};
+}
+
+int
+main ()
+{
+  C c = bar (X {1, X {2}.n});
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-3.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-3.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,24 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+  unsigned m = i;
+};
+
+X
+bar (X x)
+{
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+  return x;
+}
+
+int
+main ()
+{
+  X x = bar (X {1, X {2}.n});
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-4.C.jj	2018-03-15 20:40:49.283818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-4.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,32 @@
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+};
+
+X
+bar (X x)
+{
+  return x;
+}
+
+struct Y
+{
+  static Y bar (Y y) { return y; }
+  unsigned i;
+  unsigned n = bar (Y{2,i}).n;
+};
+
+int
+main ()
+{
+  X x { 1, bar (X{2}).n };
+  if (x.n != 2)
+    __builtin_abort ();
+
+  Y y { 1 };
+  if (y.n != 1)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr82410.C.jj	2018-03-15 20:40:49.284818227 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr82410.C	2018-03-15 20:40:49.283818227 +0100
@@ -0,0 +1,16 @@
+// PR c++/82410
+// { dg-do compile { target c++14 } }
+
+int
+main ()
+{
+  struct A {};
+  struct S
+  {
+    int & p;
+    int x = p;
+    operator A () { return {}; }
+  };
+  int l;
+  [] (A) {} (S{l});
+}


	Jakub
Jason Merrill March 16, 2018, 2:17 a.m. UTC | #9
On Thu, Mar 15, 2018 at 7:33 PM, Jakub Jelinek <jakub@redhat.com> wrote:
> On Thu, Mar 15, 2018 at 04:50:57PM -0400, Jason Merrill wrote:
>> > +/* Don't elide the initialization of TARGET_EXPR_SLOT for this TARGET_EXPR.  */
>> > +#define TARGET_EXPR_NO_ELIDE(NODE) (TARGET_EXPR_CHECK (NODE)->base.private_flag)
>>
>> This should be specifically on the rhs of a MODIFY_EXPR; it's OK to
>> elide on the rhs of an INIT_EXPR.
>>
>> > @@ -3155,7 +3155,8 @@ gimplify_arg (tree *arg_p, gimple_seq *p
>> >      {
>> >        test = is_gimple_lvalue, fb = fb_either;
>> >        /* Also strip a TARGET_EXPR that would force an extra copy.  */
>> > -      if (TREE_CODE (*arg_p) == TARGET_EXPR)
>> > +      if (TREE_CODE (*arg_p) == TARGET_EXPR
>> > +         && !TARGET_EXPR_NO_ELIDE (*arg_p))
>>
>> This is also an initialization context, so we don't need to check it here.
>
> Ah, ok, changed in the patch.
>
>> > @@ -5211,6 +5212,7 @@ gimplify_modify_expr_rhs (tree *expr_p,
>> >             tree init = TARGET_EXPR_INITIAL (*from_p);
>> >
>> >             if (init
>> > +               && !TARGET_EXPR_NO_ELIDE (*from_p)
>>
>> Here should check TREE_CODE (*expr_p).
>
> The following (except the *t = unshare_expr (x) change) has successfully
> bootstrapped/regtested on x86_64-linux and i686-linux (without the
> unshare_expr it regressed g++.dg/cpp0x/pr83556.C, but with that change it
> succeeds; I'm afraid right now there is no easy way to determine if we
> need to unshare or not, if replace_placeholders is called before
> unshare_body at the start of gimplification, we don't need to unshare, but
> during gimplification we have to).  Ok for trunk?

> 2018-03-15  Jakub Jelinek  <jakub@redhat.com>
>
>         PR c++/79937
>         PR c++/82410
>         * tree.h (TARGET_EXPR_NO_ELIDE): Define.
>         * gimplify.c (gimplify_modify_expr_rhs): Don't elide TARGET_EXPRs with
>         TARGET_EXPR_NO_ELIDE flag set unless *expr_p is INIT_EXPR.
>
>         * cp-tree.h (CONSTRUCTOR_PLACEHOLDER_BOUNDARY): Define.
>         (find_placeholder): Declare.
>         * tree.c (struct replace_placeholders_t): Add exp member.
>         (replace_placeholders_r): Don't walk into ctors with
>         CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set, unless they are equal to
>         d->exp.  Replace PLACEHOLDER_EXPR with unshare_expr (x) rather than x.
>         (replace_placeholders): Initialize data.exp.
>         (find_placeholders_r, find_placeholders): New functions.
>         * typeck2.c (process_init_constructor_record,
>         process_init_constructor_union): Set CONSTRUCTOR_PLACEHOLDER_BOUNDARY
>         if adding NSDMI on which find_placeholder returns true.
>         * call.c (build_over_call): Don't call replace_placeholders here.
>         * cp-gimplify.c (cp_genericize_r): Set TARGET_EXPR_NO_ELIDE on
>         TARGET_EXPRs with CONSTRUCTOR_PLACEHOLDER_BOUNDARY set on
>         TARGET_EXPR_INITIAL.
>         (cp_fold): Copy over CONSTRUCTOR_PLACEHOLDER_BOUNDARY bit to new
>         ctor.
>
>         * g++.dg/cpp1y/pr79937-1.C: New test.
>         * g++.dg/cpp1y/pr79937-2.C: New test.
>         * g++.dg/cpp1y/pr79937-3.C: New test.
>         * g++.dg/cpp1y/pr79937-4.C: New test.
>         * g++.dg/cpp1y/pr82410.C: New test.
>
> --- gcc/tree.h.jj       2018-02-22 12:37:02.566387732 +0100
> +++ gcc/tree.h  2018-03-15 20:42:57.968858551 +0100
> @@ -1197,6 +1197,9 @@ extern tree maybe_wrap_with_location (tr
>  #define TARGET_EXPR_SLOT(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 0)
>  #define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 1)
>  #define TARGET_EXPR_CLEANUP(NODE) TREE_OPERAND_CHECK_CODE (NODE, TARGET_EXPR, 2)
> +/* Don't elide the initialization of TARGET_EXPR_SLOT for this TARGET_EXPR
> +   on rhs of MODIFY_EXPR.  */
> +#define TARGET_EXPR_NO_ELIDE(NODE) (TARGET_EXPR_CHECK (NODE)->base.private_flag)
>
>  /* DECL_EXPR accessor. This gives access to the DECL associated with
>     the given declaration statement.  */
> --- gcc/gimplify.c.jj   2018-01-18 21:28:49.743301440 +0100
> +++ gcc/gimplify.c      2018-03-15 21:06:15.290828320 +0100
> @@ -5211,6 +5211,8 @@ gimplify_modify_expr_rhs (tree *expr_p,
>             tree init = TARGET_EXPR_INITIAL (*from_p);
>
>             if (init
> +               && (TREE_CODE (*expr_p) != MODIFY_EXPR
> +                   || !TARGET_EXPR_NO_ELIDE (*from_p))
>                 && !VOID_TYPE_P (TREE_TYPE (init)))
>               {
>                 *from_p = init;
> --- gcc/cp/cp-tree.h.jj 2018-03-15 18:44:21.646300907 +0100
> +++ gcc/cp/cp-tree.h    2018-03-15 20:40:49.280818226 +0100
> @@ -425,6 +425,7 @@ extern GTY(()) tree cp_global_trees[CPTI
>        DECL_VTABLE_OR_VTT_P (in VAR_DECL)
>        FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
>        CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
> +      CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
>     6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
>        DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
>        TYPE_MARKED_P (in _TYPE)
> @@ -4144,6 +4145,12 @@ more_aggr_init_expr_args_p (const aggr_i
>  #define CONSTRUCTOR_C99_COMPOUND_LITERAL(NODE) \
>    (TREE_LANG_FLAG_3 (CONSTRUCTOR_CHECK (NODE)))
>
> +/* True if this CONSTRUCTOR contains PLACEHOLDER_EXPRs referencing the
> +   CONSTRUCTOR's type not nested inside another CONSTRUCTOR marked with
> +   CONSTRUCTOR_PLACEHOLDER_BOUNDARY.  */
> +#define CONSTRUCTOR_PLACEHOLDER_BOUNDARY(NODE) \
> +  (TREE_LANG_FLAG_5 (CONSTRUCTOR_CHECK (NODE)))
> +
>  #define DIRECT_LIST_INIT_P(NODE) \
>     (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
>
> @@ -7021,6 +7028,7 @@ extern tree array_type_nelts_top          (tree)
>  extern tree break_out_target_exprs             (tree);
>  extern tree build_ctor_subob_ref               (tree, tree, tree);
>  extern tree replace_placeholders               (tree, tree, bool * = NULL);
> +extern bool find_placeholders                  (tree);
>  extern tree get_type_decl                      (tree);
>  extern tree decl_namespace_context             (tree);
>  extern bool decl_anon_ns_mem_p                 (const_tree);
> --- gcc/cp/tree.c.jj    2018-03-15 18:44:21.661300889 +0100
> +++ gcc/cp/tree.c       2018-03-15 20:49:03.518973106 +0100
> @@ -3096,6 +3096,7 @@ build_ctor_subob_ref (tree index, tree t
>  struct replace_placeholders_t
>  {
>    tree obj;        /* The object to be substituted for a PLACEHOLDER_EXPR.  */
> +  tree exp;        /* The outermost exp.  */
>    bool seen;       /* Whether we've encountered a PLACEHOLDER_EXPR.  */
>    hash_set<tree> *pset;        /* To avoid walking same trees multiple times.  */
>  };
> @@ -3124,7 +3125,7 @@ replace_placeholders_r (tree* t, int* wa
>                                                            TREE_TYPE (x));
>              x = TREE_OPERAND (x, 0))
>           gcc_assert (TREE_CODE (x) == COMPONENT_REF);
> -       *t = x;
> +       *t = unshare_expr (x);
>         *walk_subtrees = false;
>         d->seen = true;
>        }
> @@ -3134,7 +3135,12 @@ replace_placeholders_r (tree* t, int* wa
>        {
>         constructor_elt *ce;
>         vec<constructor_elt,va_gc> *v = CONSTRUCTOR_ELTS (*t);
> -       if (d->pset->add (*t))
> +       /* Don't walk into CONSTRUCTOR_PLACEHOLDER_BOUNDARY ctors
> +          other than the d->exp one, those have PLACEHOLDER_EXPRs
> +          related to another object.  */
> +       if ((CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t)
> +            && *t != d->exp)
> +           || d->pset->add (*t))
>           {
>             *walk_subtrees = false;
>             return NULL_TREE;
> @@ -3192,16 +3198,58 @@ replace_placeholders (tree exp, tree obj
>      return exp;
>
>    tree *tp = &exp;
> -  hash_set<tree> pset;
> -  replace_placeholders_t data = { obj, false, &pset };
> +  /* Use exp instead of *(type *)&exp.  */

This comment should have been removed with the code it described.  OK
with that change.

>    if (TREE_CODE (exp) == TARGET_EXPR)
>      tp = &TARGET_EXPR_INITIAL (exp);
> +  hash_set<tree> pset;
> +  replace_placeholders_t data = { obj, *tp, false, &pset };
>    cp_walk_tree (tp, replace_placeholders_r, &data, NULL);
>    if (seen_p)
>      *seen_p = data.seen;
>    return exp;
>  }
>
> +/* Callback function for find_placeholders.  */
> +
> +static tree
> +find_placeholders_r (tree *t, int *walk_subtrees, void *)
> +{
> +  if (TYPE_P (*t) || TREE_CONSTANT (*t))
> +    {
> +      *walk_subtrees = false;
> +      return NULL_TREE;
> +    }
> +
> +  switch (TREE_CODE (*t))
> +    {
> +    case PLACEHOLDER_EXPR:
> +      return *t;
> +
> +    case CONSTRUCTOR:
> +      if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t))
> +       *walk_subtrees = false;
> +      break;
> +
> +    default:
> +      break;
> +    }
> +
> +  return NULL_TREE;
> +}
> +
> +/* Return true if EXP contains a PLACEHOLDER_EXPR.  Don't walk into
> +   ctors with CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set.  */
> +
> +bool
> +find_placeholders (tree exp)
> +{
> +  /* This is only relevant for C++14.  */
> +  if (cxx_dialect < cxx14)
> +    return false;
> +
> +  return cp_walk_tree_without_duplicates (&exp, find_placeholders_r, NULL);
> +}
> +
>  /* Similar to `build_nt', but for template definitions of dependent
>     expressions  */
>
> --- gcc/cp/typeck2.c.jj 2018-03-15 18:44:21.661300889 +0100
> +++ gcc/cp/typeck2.c    2018-03-15 20:40:49.282818227 +0100
> @@ -1470,6 +1470,9 @@ process_init_constructor_record (tree ty
>             }
>           /* C++14 aggregate NSDMI.  */
>           next = get_nsdmi (field, /*ctor*/false, complain);
> +         if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
> +             && find_placeholders (next))
> +           CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
>         }
>        else if (type_build_ctor_call (TREE_TYPE (field)))
>         {
> @@ -1608,10 +1611,11 @@ process_init_constructor_union (tree typ
>           if (TREE_CODE (field) == FIELD_DECL
>               && DECL_INITIAL (field) != NULL_TREE)
>             {
> -             CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init),
> -                                     field,
> -                                     get_nsdmi (field, /*in_ctor=*/false,
> -                                                complain));
> +             tree val = get_nsdmi (field, /*in_ctor=*/false, complain);
> +             if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
> +                 && find_placeholders (val))
> +               CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
> +             CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init), field, val);
>               break;
>             }
>         }
> --- gcc/cp/call.c.jj    2018-03-11 17:48:36.359061434 +0100
> +++ gcc/cp/call.c       2018-03-15 21:07:31.781961783 +0100
> @@ -8164,8 +8164,6 @@ build_over_call (struct z_candidate *can
>         {
>           arg = cp_build_fold_indirect_ref (arg);
>           val = build2 (MODIFY_EXPR, TREE_TYPE (to), to, arg);
> -         /* Handle NSDMI that refer to the object being initialized.  */
> -         replace_placeholders (arg, to);
>         }
>        else
>         {
> --- gcc/cp/cp-gimplify.c.jj     2018-03-07 22:51:58.742478684 +0100
> +++ gcc/cp/cp-gimplify.c        2018-03-15 21:19:27.353619519 +0100
> @@ -1515,6 +1515,13 @@ cp_genericize_r (tree *stmt_p, int *walk
>         }
>        break;
>
> +    case TARGET_EXPR:
> +      if (TARGET_EXPR_INITIAL (stmt)
> +         && TREE_CODE (TARGET_EXPR_INITIAL (stmt)) == CONSTRUCTOR
> +         && CONSTRUCTOR_PLACEHOLDER_BOUNDARY (TARGET_EXPR_INITIAL (stmt)))
> +       TARGET_EXPR_NO_ELIDE (stmt) = 1;
> +      break;
> +
>      default:
>        if (IS_TYPE_OR_DECL_P (stmt))
>         *walk_subtrees = 0;
> @@ -2469,7 +2476,11 @@ cp_fold (tree x)
>               }
>           }
>         if (nelts)
> -         x = build_constructor (TREE_TYPE (x), nelts);
> +         {
> +           x = build_constructor (TREE_TYPE (x), nelts);
> +           CONSTRUCTOR_PLACEHOLDER_BOUNDARY (x)
> +             = CONSTRUCTOR_PLACEHOLDER_BOUNDARY (org_x);
> +         }
>         break;
>        }
>      case TREE_VEC:
> --- gcc/testsuite/g++.dg/cpp1y/pr79937-1.C.jj   2018-03-15 20:40:49.283818227 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr79937-1.C      2018-03-15 20:40:49.282818227 +0100
> @@ -0,0 +1,23 @@
> +// PR c++/79937
> +// { dg-do run { target c++14 } }
> +
> +struct C {};
> +
> +struct X {
> +  unsigned i;
> +  unsigned n = i;
> +};
> +
> +C
> +bar (X x)
> +{
> +  if (x.i != 1 || x.n != 1)
> +    __builtin_abort ();
> +  return {};
> +}
> +
> +int
> +main ()
> +{
> +  C c = bar (X {1});
> +}
> --- gcc/testsuite/g++.dg/cpp1y/pr79937-2.C.jj   2018-03-15 20:40:49.283818227 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr79937-2.C      2018-03-15 20:40:49.283818227 +0100
> @@ -0,0 +1,24 @@
> +// PR c++/79937
> +// { dg-do run { target c++14 } }
> +
> +struct C {};
> +
> +struct X {
> +  unsigned i;
> +  unsigned n = i;
> +  unsigned m = i;
> +};
> +
> +C
> +bar (X x)
> +{
> +  if (x.i != 1 || x.n != 2 || x.m != 1)
> +    __builtin_abort ();
> +  return {};
> +}
> +
> +int
> +main ()
> +{
> +  C c = bar (X {1, X {2}.n});
> +}
> --- gcc/testsuite/g++.dg/cpp1y/pr79937-3.C.jj   2018-03-15 20:40:49.283818227 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr79937-3.C      2018-03-15 20:40:49.283818227 +0100
> @@ -0,0 +1,24 @@
> +// PR c++/79937
> +// { dg-do run { target c++14 } }
> +
> +struct X {
> +  unsigned i;
> +  unsigned n = i;
> +  unsigned m = i;
> +};
> +
> +X
> +bar (X x)
> +{
> +  if (x.i != 1 || x.n != 2 || x.m != 1)
> +    __builtin_abort ();
> +  return x;
> +}
> +
> +int
> +main ()
> +{
> +  X x = bar (X {1, X {2}.n});
> +  if (x.i != 1 || x.n != 2 || x.m != 1)
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp1y/pr79937-4.C.jj   2018-03-15 20:40:49.283818227 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr79937-4.C      2018-03-15 20:40:49.283818227 +0100
> @@ -0,0 +1,32 @@
> +// PR c++/79937
> +// { dg-do run { target c++14 } }
> +
> +struct X {
> +  unsigned i;
> +  unsigned n = i;
> +};
> +
> +X
> +bar (X x)
> +{
> +  return x;
> +}
> +
> +struct Y
> +{
> +  static Y bar (Y y) { return y; }
> +  unsigned i;
> +  unsigned n = bar (Y{2,i}).n;
> +};
> +
> +int
> +main ()
> +{
> +  X x { 1, bar (X{2}).n };
> +  if (x.n != 2)
> +    __builtin_abort ();
> +
> +  Y y { 1 };
> +  if (y.n != 1)
> +    __builtin_abort ();
> +}
> --- gcc/testsuite/g++.dg/cpp1y/pr82410.C.jj     2018-03-15 20:40:49.284818227 +0100
> +++ gcc/testsuite/g++.dg/cpp1y/pr82410.C        2018-03-15 20:40:49.283818227 +0100
> @@ -0,0 +1,16 @@
> +// PR c++/82410
> +// { dg-do compile { target c++14 } }
> +
> +int
> +main ()
> +{
> +  struct A {};
> +  struct S
> +  {
> +    int & p;
> +    int x = p;
> +    operator A () { return {}; }
> +  };
> +  int l;
> +  [] (A) {} (S{l});
> +}
>
>
>         Jakub
diff mbox series

Patch

--- gcc/cp/cp-tree.h.jj	2018-03-14 16:21:50.745925263 +0100
+++ gcc/cp/cp-tree.h	2018-03-14 15:57:51.171323825 +0100
@@ -425,6 +425,7 @@  extern GTY(()) tree cp_global_trees[CPTI
       DECL_VTABLE_OR_VTT_P (in VAR_DECL)
       FUNCTION_RVALUE_QUALIFIED (in FUNCTION_TYPE, METHOD_TYPE)
       CALL_EXPR_REVERSE_ARGS (in CALL_EXPR, AGGR_INIT_EXPR)
+      CONSTRUCTOR_PLACEHOLDER_BOUNDARY (in CONSTRUCTOR)
    6: IDENTIFIER_REPO_CHOSEN (in IDENTIFIER_NODE)
       DECL_CONSTRUCTION_VTABLE_P (in VAR_DECL)
       TYPE_MARKED_P (in _TYPE)
@@ -4144,6 +4145,12 @@  more_aggr_init_expr_args_p (const aggr_i
 #define CONSTRUCTOR_C99_COMPOUND_LITERAL(NODE) \
   (TREE_LANG_FLAG_3 (CONSTRUCTOR_CHECK (NODE)))
 
+/* True if this CONSTRUCTOR contains PLACEHOLDER_EXPRs referencing the
+   CONSTRUCTOR's type not nested inside another CONSTRUCTOR marked with
+   CONSTRUCTOR_PLACEHOLDER_BOUNDARY.  */
+#define CONSTRUCTOR_PLACEHOLDER_BOUNDARY(NODE) \
+  (TREE_LANG_FLAG_5 (CONSTRUCTOR_CHECK (NODE)))
+
 #define DIRECT_LIST_INIT_P(NODE) \
    (BRACE_ENCLOSED_INITIALIZER_P (NODE) && CONSTRUCTOR_IS_DIRECT_INIT (NODE))
 
@@ -7021,6 +7028,7 @@  extern tree array_type_nelts_top		(tree)
 extern tree break_out_target_exprs		(tree);
 extern tree build_ctor_subob_ref		(tree, tree, tree);
 extern tree replace_placeholders		(tree, tree, bool * = NULL);
+extern bool find_placeholders			(tree);
 extern tree get_type_decl			(tree);
 extern tree decl_namespace_context		(tree);
 extern bool decl_anon_ns_mem_p			(const_tree);
--- gcc/cp/tree.c.jj	2018-03-14 11:49:58.926816421 +0100
+++ gcc/cp/tree.c	2018-03-14 16:24:29.036987505 +0100
@@ -3096,6 +3096,7 @@  build_ctor_subob_ref (tree index, tree t
 struct replace_placeholders_t
 {
   tree obj;	    /* The object to be substituted for a PLACEHOLDER_EXPR.  */
+  tree exp;	    /* The outermost exp.  */
   bool seen;	    /* Whether we've encountered a PLACEHOLDER_EXPR.  */
   hash_set<tree> *pset;	/* To avoid walking same trees multiple times.  */
 };
@@ -3134,7 +3135,12 @@  replace_placeholders_r (tree* t, int* wa
       {
 	constructor_elt *ce;
 	vec<constructor_elt,va_gc> *v = CONSTRUCTOR_ELTS (*t);
-	if (d->pset->add (*t))
+	/* Don't walk into CONSTRUCTOR_PLACEHOLDER_BOUNDARY ctors
+	   other than the d->exp one, those have PLACEHOLDER_EXPRs
+	   related to another object.  */
+	if ((CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t)
+	     && *t != d->exp)
+	    || d->pset->add (*t))
 	  {
 	    *walk_subtrees = false;
 	    return NULL_TREE;
@@ -3192,16 +3198,70 @@  replace_placeholders (tree exp, tree obj
     return exp;
 
   tree *tp = &exp;
-  hash_set<tree> pset;
-  replace_placeholders_t data = { obj, false, &pset };
+  /* Use exp instead of *(type *)&exp.  */
+  while (TREE_CODE (exp) == INDIRECT_REF)
+    {
+      tree t = TREE_OPERAND (exp, 0);
+      STRIP_NOPS (t);
+      if (TREE_CODE (t) == ADDR_EXPR
+	  && (t = TREE_OPERAND (t, 0))
+	  && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (exp),
+							TREE_TYPE (t)))
+	exp = t;
+      else
+	break;
+    }
   if (TREE_CODE (exp) == TARGET_EXPR)
     tp = &TARGET_EXPR_INITIAL (exp);
+  hash_set<tree> pset;
+  replace_placeholders_t data = { obj, *tp, false, &pset };
   cp_walk_tree (tp, replace_placeholders_r, &data, NULL);
   if (seen_p)
     *seen_p = data.seen;
   return exp;
 }
 
+/* Callback function for find_placeholders.  */
+
+static tree
+find_placeholders_r (tree *t, int *walk_subtrees, void *)
+{
+  if (TYPE_P (*t) || TREE_CONSTANT (*t))
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  switch (TREE_CODE (*t))
+    {
+    case PLACEHOLDER_EXPR:
+      return *t;
+
+    case CONSTRUCTOR:
+      if (CONSTRUCTOR_PLACEHOLDER_BOUNDARY (*t))
+	*walk_subtrees = false;
+      break;
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
+/* Return true if EXP contains a PLACEHOLDER_EXPR.  Don't walk into
+   ctors with CONSTRUCTOR_PLACEHOLDER_BOUNDARY flag set.  */
+
+bool
+find_placeholders (tree exp)
+{
+  /* This is only relevant for C++14.  */
+  if (cxx_dialect < cxx14)
+    return false;
+
+  return cp_walk_tree_without_duplicates (&exp, find_placeholders_r, NULL);
+}
+
 /* Similar to `build_nt', but for template definitions of dependent
    expressions  */
 
--- gcc/cp/typeck2.c.jj	2018-03-14 16:25:49.622019191 +0100
+++ gcc/cp/typeck2.c	2018-03-14 16:02:36.093441530 +0100
@@ -1470,6 +1470,9 @@  process_init_constructor_record (tree ty
 	    }
 	  /* C++14 aggregate NSDMI.  */
 	  next = get_nsdmi (field, /*ctor*/false, complain);
+	  if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
+	      && find_placeholders (next))
+	    CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
 	}
       else if (type_build_ctor_call (TREE_TYPE (field)))
 	{
@@ -1608,10 +1611,11 @@  process_init_constructor_union (tree typ
 	  if (TREE_CODE (field) == FIELD_DECL
 	      && DECL_INITIAL (field) != NULL_TREE)
 	    {
-	      CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init),
-				      field,
-				      get_nsdmi (field, /*in_ctor=*/false,
-						 complain));
+	      tree val = get_nsdmi (field, /*in_ctor=*/false, complain);
+	      if (!CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init)
+		  && find_placeholders (val))
+		CONSTRUCTOR_PLACEHOLDER_BOUNDARY (init) = 1;
+	      CONSTRUCTOR_APPEND_ELT (CONSTRUCTOR_ELTS (init), field, val);
 	      break;
 	    }
 	}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-1.C.jj	2018-03-14 14:32:12.230097853 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-1.C	2018-03-14 14:32:12.230097853 +0100
@@ -0,0 +1,23 @@ 
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct C {};
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+};
+
+C
+bar (X x)
+{
+  if (x.i != 1 || x.n != 1)
+    __builtin_abort ();
+  return {};
+}
+
+int
+main ()
+{
+  C c = bar (X {1});
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-2.C.jj	2018-03-14 14:32:12.231097853 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-2.C	2018-03-14 14:32:12.231097853 +0100
@@ -0,0 +1,24 @@ 
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct C {};
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+  unsigned m = i;
+};
+
+C
+bar (X x)
+{
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+  return {};
+}
+
+int
+main ()
+{
+  C c = bar (X {1, X {2}.n});
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-3.C.jj	2018-03-14 14:32:12.231097853 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-3.C	2018-03-14 14:32:12.231097853 +0100
@@ -0,0 +1,24 @@ 
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+  unsigned m = i;
+};
+
+X
+bar (X x)
+{
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+  return x;
+}
+
+int
+main ()
+{
+  X x = bar (X {1, X {2}.n});
+  if (x.i != 1 || x.n != 2 || x.m != 1)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr79937-4.C.jj	2018-03-14 16:14:50.842757308 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr79937-4.C	2018-03-14 16:14:45.761755127 +0100
@@ -0,0 +1,32 @@ 
+// PR c++/79937
+// { dg-do run { target c++14 } }
+
+struct X {
+  unsigned i;
+  unsigned n = i;
+};
+
+X
+bar (X x)
+{
+  return x;
+}
+
+struct Y
+{
+  static Y bar (Y y) { return y; }
+  unsigned i;
+  unsigned n = bar (Y{2,i}).n;
+};
+
+int
+main ()
+{
+  X x { 1, bar (X{2}).n };
+  if (x.n != 2)
+    __builtin_abort ();
+
+  Y y { 1 };
+  if (y.n != 1)
+    __builtin_abort ();
+}
--- gcc/testsuite/g++.dg/cpp1y/pr82410.C.jj	2018-03-14 16:20:07.180884544 +0100
+++ gcc/testsuite/g++.dg/cpp1y/pr82410.C	2018-03-14 16:20:01.626882356 +0100
@@ -0,0 +1,16 @@ 
+// PR c++/82410
+// { dg-do compile { target c++14 } }
+
+int
+main ()
+{
+  struct A {};
+  struct S
+  {
+    int & p;
+    int x = p;
+    operator A () { return {}; }
+  };
+  int l;
+  [] (A) {} (S{l});
+}