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;
+};