On 5/24/21 1:48 PM, Patrick Palka wrote:
In the testcase below, the initializer for C::b inside C's default
constructor is encoded as a TARGET_EXPR wrapping the CALL_EXPR f() in
C++17 mode.  During massaging of this constexpr constructor,
build_target_expr_with_type called from bot_manip ends up trying to use
B's implicitly deleted copy constructor rather than preserving the
copy elision.

This patch makes bot_manip use force_target_expr instead of
build_target_expr_with_type so that it copies TARGET_EXPRs in a more
oblivious manner.

Even with that change we should fix build_target_expr_with_type to handle CALL_EXPR properly; adding an extra copy is just wrong.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK
for trunk?

        PR c++/100368

gcc/cp/ChangeLog:

        * tree.c (build_target_expr_with_type): Simplify now that
        bot_manip is no longer a caller.
        (bot_manip): Use force_target_expr instead of
        build_target_expr_with_type.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp1z/elide6.C: New test.
---
  gcc/cp/tree.c                       |  8 +++-----
  gcc/testsuite/g++.dg/cpp1z/elide6.C | 16 ++++++++++++++++
  2 files changed, 19 insertions(+), 5 deletions(-)
  create mode 100644 gcc/testsuite/g++.dg/cpp1z/elide6.C

diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index 72f498f4b3b..84b84621d35 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -848,12 +848,10 @@ build_target_expr_with_type (tree init, tree type, 
tsubst_flags_t complain)
        || init == error_mark_node)
      return init;
    else if (CLASS_TYPE_P (type) && type_has_nontrivial_copy_init (type)
-          && !VOID_TYPE_P (TREE_TYPE (init))
           && TREE_CODE (init) != COND_EXPR
           && TREE_CODE (init) != CONSTRUCTOR
           && TREE_CODE (init) != VA_ARG_EXPR)
-    /* We need to build up a copy constructor call.  A void initializer
-       means we're being called from bot_manip.

In general, a void initializer for a TARGET_EXPR means that the initialization is more complex than initializing the object from the value of the expression. The caller would need to handle making that initialization apply to the new TARGET_EXPR_SLOT (and bot_manip does). If we change bot_manip to not call this function, I think this function should reject void init.

  COND_EXPR is a special
+    /* We need to build up a copy constructor call.  COND_EXPR is a special
         case because we already have copies on the arms and we don't want
         another one here.  A CONSTRUCTOR is aggregate initialization, which
         is handled separately.  A VA_ARG_EXPR is magic creation of an
@@ -3112,8 +3110,8 @@ bot_manip (tree* tp, int* walk_subtrees, void* data_)
            AGGR_INIT_ZERO_FIRST (TREE_OPERAND (u, 1)) = true;
        }
        else
-       u = build_target_expr_with_type (TREE_OPERAND (t, 1), TREE_TYPE (t),
-                                        tf_warning_or_error);
+       u = force_target_expr (TREE_TYPE (t), TREE_OPERAND (t, 1),
+                              tf_warning_or_error);
TARGET_EXPR_IMPLICIT_P (u) = TARGET_EXPR_IMPLICIT_P (t);
        TARGET_EXPR_LIST_INIT_P (u) = TARGET_EXPR_LIST_INIT_P (t);
diff --git a/gcc/testsuite/g++.dg/cpp1z/elide6.C 
b/gcc/testsuite/g++.dg/cpp1z/elide6.C
new file mode 100644
index 00000000000..399e1a9a1ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/elide6.C
@@ -0,0 +1,16 @@
+// PR c++/100368
+// { dg-do compile { target c++11 } }
+
+struct A {
+  A() = default;
+  A(const A&) = delete;
+};
+
+struct B { A a; }; // { dg-error "deleted" "" { target c++14_down } }
+
+constexpr B f() { return {}; }
+
+struct C {
+  constexpr C() : b(f()) {} // { dg-error "deleted" "" { target c++14_down } }
+  B b;
+};


Reply via email to