]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: constexpr and copy elision within mem init [PR100368]
authorPatrick Palka <ppalka@redhat.com>
Wed, 26 May 2021 12:37:30 +0000 (08:37 -0400)
committerPatrick Palka <ppalka@redhat.com>
Wed, 26 May 2021 12:37:30 +0000 (08:37 -0400)
In the testcase below, the member initializer b(f()) 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 on this initializer
tries to add an extra copy using B's implicitly deleted copy constructor
rather than just preserving the copy elision.

Since it's wrong to introduce an extra copy when initializing a
temporary from a CALL_EXPR, this patch makes build_target_expr_with_type
avoid calling force_rvalue in this case.  Additionally, bot_manip should
be copying TARGET_EXPRs in a more oblivious manner, so this patch makes
bot_manip use force_target_expr instead of build_target_expr_with_type.
And since bot_manip is now no longer a caller, we can remove the void
initializer handling in build_target_expr_with_type.

PR c++/100368

gcc/cp/ChangeLog:

* tree.c (build_target_expr_with_type): Don't call force_rvalue
on CALL_EXPR initializer.  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
gcc/testsuite/g++.dg/cpp1z/elide6.C [new file with mode: 0644]

index 72f498f4b3bc637612609639064bb37f24a6fe41..372d89fa9edd05c79c67bba9914f1c5ab8fe6a32 100644 (file)
@@ -843,21 +843,22 @@ tree
 build_target_expr_with_type (tree init, tree type, tsubst_flags_t complain)
 {
   gcc_assert (!VOID_TYPE_P (type));
+  gcc_assert (!VOID_TYPE_P (TREE_TYPE (init)));
 
   if (TREE_CODE (init) == TARGET_EXPR
       || 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.  COND_EXPR is a special
+          && TREE_CODE (init) != VA_ARG_EXPR
+          && TREE_CODE (init) != CALL_EXPR)
+    /* 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
-       aggregate; there's no additional work to be done.  */
+       aggregate; there's no additional work to be done.  A CALL_EXPR
+       already creates a prvalue.  */
     return force_rvalue (init, complain);
 
   return force_target_expr (type, init, complain);
@@ -3112,8 +3113,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 (file)
index 0000000..399e1a9
--- /dev/null
@@ -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;
+};