C++17 copy elision improvements.
authorJason Merrill <jason@redhat.com>
Thu, 6 Oct 2016 21:24:40 +0000 (17:24 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 6 Oct 2016 21:24:40 +0000 (17:24 -0400)
* call.c (build_temp, convert_like_real): Don't re-copy
TARGET_EXPR.  Handle packed fields.
(build_x_va_arg): Wrap it in a TARGET_EXPR.
(build_over_call): Add sanity check.
* cvt.c (early_elide_copy): New.
(ocp_convert): Use it.
* except.c (build_throw): Use it.
* init.c (get_nsdmi): Put back the TARGET_EXPR.
(expand_default_init): Call early_elide_copy.
* typeck.c (cp_build_modify_expr): Call early_elide_copy.

From-SVN: r240845

20 files changed:
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/cvt.c
gcc/cp/except.c
gcc/cp/init.c
gcc/cp/typeck.c
gcc/doc/invoke.texi
gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C
gcc/testsuite/g++.dg/cpp0x/initlist9.C
gcc/testsuite/g++.dg/cpp0x/noexcept23.C
gcc/testsuite/g++.dg/cpp0x/noexcept24.C
gcc/testsuite/g++.dg/cpp1z/elide1.C
gcc/testsuite/g++.dg/init/copy3.C
gcc/testsuite/g++.dg/overload/arg3.C
gcc/testsuite/g++.dg/template/copy1.C
gcc/testsuite/g++.old-deja/g++.eh/ctor1.C
gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
gcc/testsuite/g++.old-deja/g++.mike/p2431.C
gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C

index 008d6461acffe56c2214dd780654b43b867156f2..553658f42d152ce3bc249eacfdccc03dea93d7e9 100644 (file)
@@ -1,3 +1,16 @@
+2016-10-06  Jason Merrill  <jason@redhat.com>
+
+       * call.c (build_temp, convert_like_real): Don't re-copy
+       TARGET_EXPR.  Handle packed fields.
+       (build_x_va_arg): Wrap it in a TARGET_EXPR.
+       (build_over_call): Add sanity check.
+       * cvt.c (early_elide_copy): New.
+       (ocp_convert): Use it.
+       * except.c (build_throw): Use it.
+       * init.c (get_nsdmi): Put back the TARGET_EXPR.
+       (expand_default_init): Call early_elide_copy.
+       * typeck.c (cp_build_modify_expr): Call early_elide_copy.
+
 2016-10-06  Jakub Jelinek  <jakub@redhat.com>
 
        Implement P0258R2 - helper for C++17
index dac1337cc4954919fe12f43cccf83d1652524194..6feaf7e22c9563132a08f401b186a29bf27ad74c 100644 (file)
@@ -6365,6 +6365,22 @@ build_temp (tree expr, tree type, int flags,
   int savew, savee;
   vec<tree, va_gc> *args;
 
+  *diagnostic_kind = DK_UNSPECIFIED;
+
+  if (TREE_CODE (expr) == CONSTRUCTOR)
+    expr = get_target_expr_sfinae (expr, complain);
+  if (early_elide_copy (type, expr))
+    return expr;
+
+  /* If the source is a packed field, calling the copy constructor will require
+     binding the field to the reference parameter to the copy constructor, and
+     we'll end up with an infinite loop.  If we can use a bitwise copy, then
+     do that now.  */
+  if ((lvalue_kind (expr) & clk_packed)
+      && CLASS_TYPE_P (TREE_TYPE (expr))
+      && !type_has_nontrivial_copy_init (TREE_TYPE (expr)))
+    return get_target_expr_sfinae (expr, complain);
+
   savew = warningcount + werrorcount, savee = errorcount;
   args = make_tree_vector_single (expr);
   expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
@@ -6374,8 +6390,6 @@ build_temp (tree expr, tree type, int flags,
     *diagnostic_kind = DK_WARNING;
   else if (errorcount > savee)
     *diagnostic_kind = DK_ERROR;
-  else
-    *diagnostic_kind = DK_UNSPECIFIED;
   return expr;
 }
 
@@ -6778,10 +6792,6 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
        flags |= LOOKUP_ONLYCONVERTING;
       if (convs->rvaluedness_matches_p)
        flags |= LOOKUP_PREFER_RVALUE;
-      if (TREE_CODE (expr) == TARGET_EXPR
-         && TARGET_EXPR_LIST_INIT_P (expr))
-       /* Copy-list-initialization doesn't actually involve a copy.  */
-       return expr;
       expr = build_temp (expr, totype, flags, &diag_kind, complain);
       if (diag_kind && complain)
        {
@@ -7068,7 +7078,12 @@ build_x_va_arg (source_location loc, tree expr, tree type)
       return convert_from_reference (expr);
     }
 
-  return build_va_arg (loc, expr, type);
+  tree ret = build_va_arg (loc, expr, type);
+  if (CLASS_TYPE_P (type))
+    /* Wrap the VA_ARG_EXPR in a TARGET_EXPR now so other code doesn't need to
+       know how to handle it.  */
+    ret = get_target_expr (ret);
+  return ret;
 }
 
 /* TYPE has been given to va_arg.  Apply the default conversions which
@@ -7806,6 +7821,15 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
       else
        arg = cp_build_indirect_ref (arg, RO_NULL, complain);
 
+      /* In C++17 we shouldn't be copying a TARGET_EXPR except into a base
+        subobject.  */
+      if (CHECKING_P && cxx_dialect >= cxx1z)
+       gcc_assert (TREE_CODE (arg) != TARGET_EXPR
+                   // FIXME we shouldn't copy for direct-init either
+                   || !(flags & LOOKUP_ONLYCONVERTING)
+                   /* See unsafe_copy_elision_p.  */
+                   || DECL_BASE_CONSTRUCTOR_P (fn));
+
       /* [class.copy]: the copy constructor is implicitly defined even if
         the implementation elided its use.  */
       if (!trivial || DECL_DELETED_FN (fn))
index 49cbdf26fcfa6924cec41784b7e8fa8045621558..9282bbe388302a416ad1b5a0d8459ea86d1169b4 100644 (file)
@@ -5692,6 +5692,7 @@ extern tree convert_to_reference          (tree, tree, int, int, tree,
                                                 tsubst_flags_t);
 extern tree convert_from_reference             (tree);
 extern tree force_rvalue                       (tree, tsubst_flags_t);
+extern bool early_elide_copy                   (tree, tree);
 extern tree ocp_convert                                (tree, tree, int, int,
                                                 tsubst_flags_t);
 extern tree cp_convert                         (tree, tree, tsubst_flags_t);
index ecc8ef82b2a25b49b1a968ca3d7766fda104efb0..063457f1c5e5c8cf28a244e4aea358649858aa87 100644 (file)
@@ -658,6 +658,27 @@ cp_convert_and_check (tree type, tree expr, tsubst_flags_t complain)
   return result;
 }
 
+/* Returns true if we should avoid even doing overload resolution for copying
+   EXPR to initialize a TYPE.  */
+
+bool
+early_elide_copy (tree type, tree expr)
+{
+  if (TREE_CODE (expr) != TARGET_EXPR)
+    return false;
+  /* List-initialization and direct-initialization don't involve a copy.  */
+  if (TARGET_EXPR_LIST_INIT_P (expr)
+      || TARGET_EXPR_DIRECT_INIT_P (expr))
+    return true;
+  /* In C++17, "If the initializer expression is a prvalue and the
+     cv-unqualified version of the source type is the same class as the class
+     of the destination, the initializer expression is used to initialize the
+     destination object."  */
+  return (cxx_dialect >= cxx1z
+         && (same_type_ignoring_top_level_qualifiers_p
+             (type, TREE_TYPE (expr))));
+}
+
 /* Conversion...
 
    FLAGS indicates how we should behave.  */
@@ -694,10 +715,8 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
     return error_mark_node;
 
   if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP)
-      && !(cxx_dialect >= cxx1z
-          && TREE_CODE (e) == TARGET_EXPR))
-    /* We need a new temporary; don't take this shortcut.  But in C++17, don't
-       force a temporary if we already have one.  */;
+      && !early_elide_copy (type, e))
+    /* We need a new temporary; don't take this shortcut.  */;
   else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
     {
       if (same_type_p (type, TREE_TYPE (e)))
index 1c60b08bb1089a1167e6cae836057ee24a1f0e4f..2f88082bf5e7ba2f0f2a4813dbf85b5f4b22751f 100644 (file)
@@ -683,7 +683,7 @@ build_throw (tree exp)
       object = cp_build_indirect_ref (object, RO_NULL, tf_warning_or_error);
 
       /* And initialize the exception object.  */
-      if (CLASS_TYPE_P (temp_type))
+      if (CLASS_TYPE_P (temp_type) && !early_elide_copy (temp_type, exp))
        {
          int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
          vec<tree, va_gc> *exp_vec;
index d1c8274003cefb4dfacf846889d239523ab80f0b..63c3dab9b643ccff287c11331e476db1ca3d46f7 100644 (file)
@@ -584,9 +584,13 @@ get_nsdmi (tree member, bool in_ctor)
        }
       /* Strip redundant TARGET_EXPR so we don't need to remap it, and
         so the aggregate init code below will see a CONSTRUCTOR.  */
-      if (init && SIMPLE_TARGET_EXPR_P (init))
+      bool simple_target = (init && SIMPLE_TARGET_EXPR_P (init));
+      if (simple_target)
        init = TARGET_EXPR_INITIAL (init);
       init = break_out_target_exprs (init);
+      if (simple_target && TREE_CODE (init) != CONSTRUCTOR)
+       /* Now put it back so C++17 copy elision works.  */
+       init = get_target_expr (init);
     }
   current_class_ptr = save_ccp;
   current_class_ref = save_ccr;
@@ -1638,6 +1642,13 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
        init = reshape_init (type, init, complain);
     }
 
+  /* Also pull out a TARGET_EXPR that we want to avoid copying.  */
+  if (init && true_exp == exp
+      && TREE_CODE (init) == TREE_LIST
+      && list_length (init) == 1
+      && early_elide_copy (type, TREE_VALUE (init)))
+    init = TREE_VALUE (init);
+
   if (init && BRACE_ENCLOSED_INITIALIZER_P (init)
       && CP_AGGREGATE_TYPE_P (type))
     /* A brace-enclosed initializer for an aggregate.  In C++0x this can
@@ -1648,14 +1659,12 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags,
      initializer, whether that happened just above or in
      cp_parser_late_parsing_nsdmi.
 
-     A TARGET_EXPR with TARGET_EXPR_DIRECT_INIT_P or TARGET_EXPR_LIST_INIT_P
-     set represents the whole initialization, so we shouldn't build up
-     another ctor call.  */
+     A TARGET_EXPR for which early_elide_copy is true represents the whole
+     initialization, so we shouldn't build up another ctor call.  */
+
   if (init
       && (TREE_CODE (init) == CONSTRUCTOR
-         || (TREE_CODE (init) == TARGET_EXPR
-             && (TARGET_EXPR_DIRECT_INIT_P (init)
-                 || TARGET_EXPR_LIST_INIT_P (init))))
+         || early_elide_copy (type, init))
       && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), type))
     {
       /* Early initialization via a TARGET_EXPR only works for
index c70cfc80ff796b36fee541fabfcd4a2db6f163a3..f1abb40a775edaa295215a80590804df97b3f591 100644 (file)
@@ -7616,6 +7616,8 @@ cp_build_modify_expr (location_t loc, tree lhs, enum tree_code modifycode,
        }
       else if (! MAYBE_CLASS_TYPE_P (lhstype))
        /* Do the default thing.  */;
+      else if (early_elide_copy (lhstype, rhs))
+       /* Do the default thing.  */;
       else
        {
          vec<tree, va_gc> *rhs_vec = make_tree_vector_single (rhs);
index d9667e7f5d91b25e4160fdfc6aae2e5d64ba260d..22af6e4567381bbcc528fae66779233d64dc7b55 100644 (file)
@@ -2325,7 +2325,11 @@ release of G++.
 The C++ standard allows an implementation to omit creating a temporary
 that is only used to initialize another object of the same type.
 Specifying this option disables that optimization, and forces G++ to
-call the copy constructor in all cases.
+call the copy constructor in all cases.  This option also causes G++
+to call trivial member functions which otherwise would be expanded inline.
+
+In C++17, the compiler is required to omit these temporaries, but this
+option still affects trivial member functions.
 
 @item -fno-enforce-eh-specs
 @opindex fno-enforce-eh-specs
index 644ae63fe0d0e81703fb870beb7afa2bc54c2862..8d1ebcd94f174a13e58b8ea82957815fd8aab399 100644 (file)
@@ -8,7 +8,7 @@ struct A
 };
 
 constexpr A a;
-constexpr A b = A();           // { dg-error "" }
+constexpr A b = A();           // { dg-error "" "" { target c++14_down } }
 
 #define SA(X) static_assert ((X), #X)
 SA(a.p == &a);
index bb6414c622813f46561b8bb862fe6b8d99ccc034..ef806d0c8b7db2b4cfcf25cc243120479d6a94be 100644 (file)
@@ -8,7 +8,7 @@ struct b
   b() = default;
   ~b() = default;
   b& operator=(const b&) = delete;
-  b(const b&) = delete;                // { dg-message "declared" }
+  b(const b&) = delete;                // { dg-message "declared" "" { target c++14_down } }
 
   b(bool _t): t (_t) { }
 };
@@ -19,7 +19,7 @@ int main()
   b tst1 = { false };
 
   // copy initialization.
-  b tst2 = false;              // { dg-error "use" }
+  b tst2 = false;              // { dg-error "use" "" { target c++14_down } }
 
   // direct list initialization
   b tst3 { false };
index 5a01df46c657a50a8f40d4e8e1784773e73a0dca..b630b23a3c429aac020a2dbfff2882bd15a4243c 100644 (file)
@@ -10,5 +10,10 @@ void a(A) noexcept {}
 
 void f()
 {
-  static_assert(!noexcept(a(A{})), "");
+#if __cplusplus <= 201402L
+  const bool val = false;
+#else
+  const bool val = true;
+#endif
+  static_assert(noexcept(a(A{})) == val, "");
 }
index c17ddfa52a75a1cbf2445b4590849c86e48fa712..60ca44395b282c2c472217109395925d799544fc 100644 (file)
@@ -13,7 +13,12 @@ void a(A<T>) noexcept {}
 template<typename T>
 void f()
 {
-  static_assert(!noexcept(a(A<T>{})), "");
+#if __cplusplus <= 201402L
+  const bool val = false;
+#else
+  const bool val = true;
+#endif
+  static_assert(val == noexcept(a(A<T>{})), "");
 }
 
 void g()
index a0538bbbef89b436026011b6706b632a7948c165..71476e239709a05bca702d415a5ca607c9dc620a 100644 (file)
@@ -14,3 +14,12 @@ A a2 = (42, A());
 A f();
 A a3 = f();
 A a4 = b ? A() : f();
+
+void g(A);
+A f() {
+  g(A());
+  if (b)
+    throw A();
+  else
+    return A();
+}
index fa6a6ea18841975d068e9f8e6f91a7dddf1bf570..cfc739439284caf016ec046e9a00626555da495f 100644 (file)
@@ -1,4 +1,4 @@
-// { dg-do run }
+// { dg-do run { target c++14_down } }
 // { dg-options "-fno-elide-constructors" }
 
 int copies;
index 1684fccf5c73033e6a21d2078ab438ea2a78c3eb..9507c027252791a928c31e10440bfd63a1987ee6 100644 (file)
@@ -11,12 +11,12 @@ struct A {};
 struct B : A
 {
   B(int);
-  B(B&);  // { dg-message "note" "" }
+  B(B&);  // { dg-message "note" "" { target c++14_down } }
 };
 
-void foo(B);                   // { dg-message "initializing" }
+void foo(B);                   // { dg-message "initializing" "" { target c++14_down } }
 
 void bar()
 {
-  foo(0); // { dg-error "" }
+  foo(0); // { dg-error "" "" { target c++14_down } }
 }
index bf5a37ce0ab63404d6dacde50c8ac0ecccfb846e..a34221df38b649b33479f953fe0b4b0feb55f284 100644 (file)
@@ -6,9 +6,9 @@
 
 struct A
 {
-  A(A&);                       // { dg-message "A::A" }
-  template <class T> A(T);     // { dg-message "A::A" }
+  A(A&);                       // { dg-message "A::A" "" { target c++14_down } }
+  template <class T> A(T);     // { dg-message "A::A" "" { target c++14_down } }
 };
 
-A a = 0; // { dg-error "" }
+A a = 0; // { dg-error "" "" { target c++14_down } }
 
index 9b4adaf0e4a6027aa2ed3b153f90d1db7e92aea8..21b27d707f2878c7355adcc33861698597056087 100644 (file)
@@ -2,7 +2,7 @@
 struct A
 {
   A();
-  A(A&);                       // { dg-message "A::A|no known conversion" } referenced below
+  A(A&);                       // { dg-message "A::A|no known conversion" "" { target c++14_down } } referenced below
 };
 
 int
@@ -10,8 +10,8 @@ main ()
 {
   try
     {
-      throw A();               // { dg-error "rvalue" "" } can't copy
-// { dg-error "thrown expression" "expr" { target *-*-* } 13 }
+      throw A();               // { dg-error "rvalue" "" { target c++14_down } } can't copy
+      // { dg-error "thrown expression" "expr" { target c++14_down } 13 }
     }
   catch (...) { }
 }
index e55738477f22e0866cba819687c116552e9f88ee..c855f8f4a076ec21380a5440052f5738d90d5e5c 100644 (file)
@@ -3,7 +3,7 @@ class X // Indentation has been done so to see the similarities.
 {
 public:
   X() {}
-         X(X& x) {x.i=7;} // { dg-message "note" } Both functions modify the
+         X(X& x) {x.i=7;} // { dg-message "note" "" { target c++14_down } } Both functions modify the
   void bar(X& x) {x.i=7;} // { dg-message "note" } reference parameter x.
   int i;
 };
@@ -12,6 +12,6 @@ X foo() { X x; return x; }
 
 int main() 
 {
-  X   x(foo()); // { dg-error "rvalue" } Compiler doesn't warn about temporary reference.
+  X   x(foo()); // { dg-error "rvalue" "" { target c++14_down } } Compiler doesn't warn about temporary reference.
   x.bar(foo()); // { dg-error "rvalue" } The same mistake is warned about in this case.
 }
index 8a7ede660ba3397e74f412a58107bd2274b44e67..d990a106e55c1864a2dc810bc27fdcfd1afd38c0 100644 (file)
@@ -3,7 +3,7 @@
 class A
 {
        public:
-      A(A &); // { dg-message "note" }
+      A(A &); // { dg-message "note" "" { target c++14_down } }
 };
 
 class B
@@ -18,6 +18,6 @@ class C
        C()
        {
                B       b;
-               A a = b;// { dg-error "rvalue" }
+               A a = b;// { dg-error "rvalue" "" { target c++14_down } }
        }
 };
index 4a363a27f30f1e920b5c60348b2f64fd4cd56ce8..35e1cc264ccf1bbfd20ef09ff74c86c96db68596 100644 (file)
@@ -9,7 +9,7 @@ template<typename X> struct auto_ptr {
    typedef X element_type;
 
    explicit auto_ptr(X* p =0) throw() : px(p) {}
-   auto_ptr(auto_ptr& r) throw() : px(r.release()) {} // { dg-message "note" } candidate
+   auto_ptr(auto_ptr& r) throw() : px(r.release()) {} // { dg-message "note" "" { target c++14_down } } candidate
    template<typename Y>
       auto_ptr(auto_ptr<Y>& r) throw() : px(r.release()) {}
 
@@ -44,12 +44,12 @@ struct Derived : Base { Derived() {} };
 
 auto_ptr<Derived> f() { auto_ptr<Derived> null(0); return null; }
 void g(auto_ptr<Derived>) { }
-void h(auto_ptr<Base>) { }     // { dg-message "initializing" }
+void h(auto_ptr<Base>) { }     // { dg-message "initializing" "" { target c++14_down } }
 
 int main() {
     auto_ptr<Base> x(f());
     auto_ptr<Derived> y(f());
     x = y;
     g(f());
-    h(f());                    // { dg-error "rvalue" "" } no usable copy ctor
+    h(f());                    // { dg-error "rvalue" "" { target c++14_down } } no usable copy ctor
 }