public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* C++ PATCH for P0135, C++17 guaranteed copy elision
@ 2016-10-05 22:57 Jason Merrill
  2016-10-05 23:16 ` Pedro Alves
  0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2016-10-05 22:57 UTC (permalink / raw)
  To: gcc-patches List; +Cc: Ville Voutilainen

[-- Attachment #1: Type: text/plain, Size: 387 bytes --]

C++17 changes how we describe prvalues so that they express
initialization of an object to be named later, rather than objects
themselves.  This happens to match the front end's use of TARGET_EXPR
pretty closely, so I think we don't need to do much more than disable
the code that forces us to copy a TARGET_EXPR into another
TARGET_EXPR.

Tested x86_64-pc-linux-gnu, applying to trunk.

[-- Attachment #2: elision.diff --]
[-- Type: text/plain, Size: 1910 bytes --]

commit 054719b1c1f71236999ea4082cb3207c42cf883c
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Oct 5 12:59:30 2016 -0400

            Implement P0135R1, Guaranteed copy elision.
    
            * c-opts.c (set_std_cxx1z): Set flag_elide_constructors to 2.
            * cvt.c (ocp_convert): Don't re-copy a TARGET_EXPR in C++17.

diff --git a/gcc/c-family/c-opts.c b/gcc/c-family/c-opts.c
index c5a699d..977348f1 100644
--- a/gcc/c-family/c-opts.c
+++ b/gcc/c-family/c-opts.c
@@ -1579,6 +1579,7 @@ set_std_cxx1z (int iso)
   flag_isoc94 = 1;
   flag_isoc99 = 1;
   flag_isoc11 = 1;
+  flag_elide_constructors = 2;
   cxx_dialect = cxx1z;
   lang_hooks.name = "GNU C++14"; /* Pretend C++14 till standarization.  */
 }
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 2f5f15a..4de9745 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -693,8 +693,11 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
   if (error_operand_p (e))
     return error_mark_node;
 
-  if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP))
-    /* We need a new temporary; don't take this shortcut.  */;
+  if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP)
+      && !(flag_elide_constructors >= 2
+	   && 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.  */;
   else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
     {
       if (same_type_p (type, TREE_TYPE (e)))
diff --git a/gcc/testsuite/g++.dg/cpp1z/elide1.C b/gcc/testsuite/g++.dg/cpp1z/elide1.C
new file mode 100644
index 0000000..a0538bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/elide1.C
@@ -0,0 +1,16 @@
+// { dg-options -std=c++1z }
+
+struct A
+{
+  A();
+  A(const A&) = delete;
+};
+
+bool b;
+A a = A();
+A a1 = b ? A() : A();
+A a2 = (42, A());
+
+A f();
+A a3 = f();
+A a4 = b ? A() : f();

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: C++ PATCH for P0135, C++17 guaranteed copy elision
  2016-10-05 22:57 C++ PATCH for P0135, C++17 guaranteed copy elision Jason Merrill
@ 2016-10-05 23:16 ` Pedro Alves
  2016-10-05 23:22   ` Jason Merrill
  0 siblings, 1 reply; 5+ messages in thread
From: Pedro Alves @ 2016-10-05 23:16 UTC (permalink / raw)
  To: Jason Merrill, gcc-patches List; +Cc: Ville Voutilainen

On 10/05/2016 11:57 PM, Jason Merrill wrote:
> --- a/gcc/c-family/c-opts.c
> +++ b/gcc/c-family/c-opts.c
> @@ -1579,6 +1579,7 @@ set_std_cxx1z (int iso)
>    flag_isoc94 = 1;
>    flag_isoc99 = 1;
>    flag_isoc11 = 1;
> +  flag_elide_constructors = 2;
>    cxx_dialect = cxx1z;
>    lang_hooks.name = "GNU C++14"; /* Pretend C++14 till standarization.  */

Does -fno-elide-constructors have any effect in C++17 mode?
Should it perhaps be an error, or ignored?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: C++ PATCH for P0135, C++17 guaranteed copy elision
  2016-10-05 23:16 ` Pedro Alves
@ 2016-10-05 23:22   ` Jason Merrill
  2016-10-06 21:26     ` Jason Merrill
  0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2016-10-05 23:22 UTC (permalink / raw)
  To: Pedro Alves; +Cc: gcc-patches List, Ville Voutilainen

[-- Attachment #1: Type: text/plain, Size: 609 bytes --]

On Wed, Oct 5, 2016 at 7:15 PM, Pedro Alves <palves@redhat.com> wrote:
> On 10/05/2016 11:57 PM, Jason Merrill wrote:
>> --- a/gcc/c-family/c-opts.c
>> +++ b/gcc/c-family/c-opts.c
>> @@ -1579,6 +1579,7 @@ set_std_cxx1z (int iso)
>> +  flag_elide_constructors = 2;

Oops, that was an earlier version of the patch.  Here's what I checked
in, which doesn't touch flag_elide_constructors.

> Does -fno-elide-constructors have any effect in C++17 mode?
> Should it perhaps be an error, or ignored?

It does have an effect: it avoids open-coding trivial copies.  I
suppose I should update the documentation.

Jason

[-- Attachment #2: elision.diff --]
[-- Type: text/plain, Size: 1560 bytes --]

commit d47a992158cc6f8a6d84f365e7d4f88ee567b640
Author: jason <jason@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Wed Oct 5 22:59:02 2016 +0000

            Implement P0135R1, Guaranteed copy elision.
    
            * cvt.c (ocp_convert): Don't re-copy a TARGET_EXPR in C++17.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@240820 138bc75d-0d04-0410-961f-82ee72b054a4

diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 2f5f15a..ecc8ef8 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -693,8 +693,11 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
   if (error_operand_p (e))
     return error_mark_node;
 
-  if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP))
-    /* We need a new temporary; don't take this shortcut.  */;
+  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.  */;
   else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
     {
       if (same_type_p (type, TREE_TYPE (e)))
diff --git a/gcc/testsuite/g++.dg/cpp1z/elide1.C b/gcc/testsuite/g++.dg/cpp1z/elide1.C
new file mode 100644
index 0000000..a0538bb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/elide1.C
@@ -0,0 +1,16 @@
+// { dg-options -std=c++1z }
+
+struct A
+{
+  A();
+  A(const A&) = delete;
+};
+
+bool b;
+A a = A();
+A a1 = b ? A() : A();
+A a2 = (42, A());
+
+A f();
+A a3 = f();
+A a4 = b ? A() : f();

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: C++ PATCH for P0135, C++17 guaranteed copy elision
  2016-10-05 23:22   ` Jason Merrill
@ 2016-10-06 21:26     ` Jason Merrill
  2016-10-08 16:27       ` Jason Merrill
  0 siblings, 1 reply; 5+ messages in thread
From: Jason Merrill @ 2016-10-06 21:26 UTC (permalink / raw)
  Cc: gcc-patches List, Ville Voutilainen

[-- Attachment #1: Type: text/plain, Size: 42 bytes --]

Here's an update that handles more cases.

[-- Attachment #2: elision2.diff --]
[-- Type: text/plain, Size: 17380 bytes --]

commit 726447a210cf92a85e8f9d014b85d958a0af62b5
Author: Jason Merrill <jason@redhat.com>
Date:   Wed Oct 5 19:31:09 2016 -0400

            C++17 copy elision improvements.
    
            * 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.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index dac1337..6feaf7e 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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))
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 49cbdf2..9282bbe 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -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);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index ecc8ef8..063457f 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -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)))
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 1c60b08..2f88082 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -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;
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index d1c8274..63c3dab 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -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
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index c70cfc8..f1abb40 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -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);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index d9667e7..22af6e4 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -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
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C
index 644ae63..8d1ebcd 100644
--- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ctor14a.C
@@ -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);
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist9.C b/gcc/testsuite/g++.dg/cpp0x/initlist9.C
index bb6414c..ef806d0 100644
--- a/gcc/testsuite/g++.dg/cpp0x/initlist9.C
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist9.C
@@ -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 };
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept23.C b/gcc/testsuite/g++.dg/cpp0x/noexcept23.C
index 5a01df4..b630b23 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept23.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept23.C
@@ -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, "");
 }
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept24.C b/gcc/testsuite/g++.dg/cpp0x/noexcept24.C
index c17ddfa..60ca443 100644
--- a/gcc/testsuite/g++.dg/cpp0x/noexcept24.C
+++ b/gcc/testsuite/g++.dg/cpp0x/noexcept24.C
@@ -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()
diff --git a/gcc/testsuite/g++.dg/cpp1z/elide1.C b/gcc/testsuite/g++.dg/cpp1z/elide1.C
index a0538bb..71476e2 100644
--- a/gcc/testsuite/g++.dg/cpp1z/elide1.C
+++ b/gcc/testsuite/g++.dg/cpp1z/elide1.C
@@ -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();
+}
diff --git a/gcc/testsuite/g++.dg/init/copy3.C b/gcc/testsuite/g++.dg/init/copy3.C
index fa6a6ea..cfc7394 100644
--- a/gcc/testsuite/g++.dg/init/copy3.C
+++ b/gcc/testsuite/g++.dg/init/copy3.C
@@ -1,4 +1,4 @@
-// { dg-do run }
+// { dg-do run { target c++14_down } }
 // { dg-options "-fno-elide-constructors" }
 
 int copies;
diff --git a/gcc/testsuite/g++.dg/overload/arg3.C b/gcc/testsuite/g++.dg/overload/arg3.C
index 1684fcc..9507c02 100644
--- a/gcc/testsuite/g++.dg/overload/arg3.C
+++ b/gcc/testsuite/g++.dg/overload/arg3.C
@@ -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 } }
 }
diff --git a/gcc/testsuite/g++.dg/template/copy1.C b/gcc/testsuite/g++.dg/template/copy1.C
index bf5a37c..a34221d 100644
--- a/gcc/testsuite/g++.dg/template/copy1.C
+++ b/gcc/testsuite/g++.dg/template/copy1.C
@@ -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 } }
 
diff --git a/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C b/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C
index 9b4adaf..21b27d7 100644
--- a/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C
+++ b/gcc/testsuite/g++.old-deja/g++.eh/ctor1.C
@@ -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 (...) { }
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
index e557384..c855f8f 100644
--- a/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
+++ b/gcc/testsuite/g++.old-deja/g++.jason/temporary2.C
@@ -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.
 }
diff --git a/gcc/testsuite/g++.old-deja/g++.mike/p2431.C b/gcc/testsuite/g++.old-deja/g++.mike/p2431.C
index 8a7ede6..d990a10 100644
--- a/gcc/testsuite/g++.old-deja/g++.mike/p2431.C
+++ b/gcc/testsuite/g++.old-deja/g++.mike/p2431.C
@@ -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 } }
 	}
 };
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C b/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C
index 4a363a2..35e1cc2 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/auto_ptr.C
@@ -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
 }

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: C++ PATCH for P0135, C++17 guaranteed copy elision
  2016-10-06 21:26     ` Jason Merrill
@ 2016-10-08 16:27       ` Jason Merrill
  0 siblings, 0 replies; 5+ messages in thread
From: Jason Merrill @ 2016-10-08 16:27 UTC (permalink / raw)
  To: Jason Merrill; +Cc: gcc-patches List, Ville Voutilainen

[-- Attachment #1: Type: text/plain, Size: 763 bytes --]

On Thu, Oct 6, 2016 at 5:26 PM, Jason Merrill <jason@redhat.com> wrote:
> Here's an update that handles more cases.

And a further update that handles direct-initialization, which the
actual proposal doesn't specify at all.  I'm dealing with
direct-initialization from a type with a conversion operator that
produces a prvalue of the type being initialized by treating
direct-initialization from an unrelated type like copy-initialization
with access to explicit conversions.  And relatedly, for conversion
from a braced-init-list with a single element: we look for
constructors as normal, but also consider conversion functions from
the element type.  I should probably write up some wording for this
for Issaquah.

Tested x86_64-pc-linux-gnu, applied to trunk.

[-- Attachment #2: elision3.diff --]
[-- Type: text/plain, Size: 13915 bytes --]

commit b04f46b73da7ca43b04a5521144c60aa0ceaf8bf
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Oct 6 17:28:40 2016 -0400

            Further P0135 refinement.
    
            * call.c (build_user_type_conversion_1): Consider conversions from
            a single element in an initializer-list.
            (build_temp): Undo early_elide_copy change.
            (build_over_call): Check that we don't try to copy a TARGET_EXPR
            in C++17 mode.  Set user_conv_p here.
            (convert_like_real): Not here.
            (check_self_delegation): Split out from...
            (build_special_member_call): ...here.  Handle C++17 copy elision.
            * cvt.c (early_elide_copy): Remove.
            (ocp_convert): Undo early_elide_copy change.
            * except.c (build_throw): Likewise.
            * init.c (expand_default_init): Likewise.
            * typeck.c (cp_build_modify_expr): Likewise.

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 6feaf7e..4bee487 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -3671,6 +3671,14 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
        creating a garbage BASELINK; constructors can't be inherited.  */
     ctors = lookup_fnfields_slot (totype, complete_ctor_identifier);
 
+  /* FIXME P0135 doesn't say what to do in C++17 about list-initialization from
+     a single element.  For now, let's handle constructors as before and also
+     consider conversion operators from the element.  */
+  if (cxx_dialect >= cxx1z
+      && BRACE_ENCLOSED_INITIALIZER_P (expr)
+      && CONSTRUCTOR_NELTS (expr) == 1)
+    fromtype = TREE_TYPE (CONSTRUCTOR_ELT (expr, 0)->value);
+
   if (MAYBE_CLASS_TYPE_P (fromtype))
     {
       tree to_nonref = non_reference (totype);
@@ -3745,7 +3753,13 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
     }
 
   if (conv_fns)
-    first_arg = expr;
+    {
+      if (BRACE_ENCLOSED_INITIALIZER_P (expr))
+	/* FIXME see above about C++17.  */
+	first_arg = CONSTRUCTOR_ELT (expr, 0)->value;
+      else
+	first_arg = expr;
+    }
 
   for (; conv_fns; conv_fns = TREE_CHAIN (conv_fns))
     {
@@ -6367,11 +6381,6 @@ build_temp (tree expr, tree type, int flags,
 
   *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
@@ -6563,7 +6572,6 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
       {
 	struct z_candidate *cand = convs->cand;
 	tree convfn = cand->fn;
-	unsigned i;
 
 	/* When converting from an init list we consider explicit
 	   constructors, but actually trying to call one is an error.  */
@@ -6609,12 +6617,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
 
 	expr = mark_rvalue_use (expr);
 
-	/* Set user_conv_p on the argument conversions, so rvalue/base
-	   handling knows not to allow any more UDCs.  */
-	for (i = 0; i < cand->num_convs; ++i)
-	  cand->convs[i]->user_conv_p = true;
-
-	expr = build_over_call (cand, LOOKUP_NORMAL, complain);
+	/* Pass LOOKUP_NO_CONVERSION so rvalue/base handling knows not to allow
+	   any more UDCs.  */
+	expr = build_over_call (cand, LOOKUP_NORMAL|LOOKUP_NO_CONVERSION,
+				complain);
 
 	/* If this is a constructor or a function returning an aggr type,
 	   we need to build up a TARGET_EXPR.  */
@@ -6792,6 +6798,10 @@ 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)
 	{
@@ -7710,6 +7720,13 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 		       "  (you can disable this with -fno-deduce-init-list)");
 	    }
 	}
+
+      /* Set user_conv_p on the argument conversions, so rvalue/base handling
+	 knows not to allow any more UDCs.  This needs to happen after we
+	 process cand->warnings.  */
+      if (flags & LOOKUP_NO_CONVERSION)
+	conv->user_conv_p = true;
+
       val = convert_like_with_context (conv, arg, fn, i - is_method,
 				       conversion_warning
 				       ? complain
@@ -7825,8 +7842,7 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 	 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)
+		    || seen_error ()
 		    /* See unsafe_copy_elision_p.  */
 		    || DECL_BASE_CONSTRUCTOR_P (fn));
 
@@ -8089,6 +8105,19 @@ in_charge_arg_for_name (tree name)
   return NULL_TREE;
 }
 
+/* We've built up a constructor call RET.  Complain if it delegates to the
+   constructor we're currently compiling.  */
+
+static void
+check_self_delegation (tree ret)
+{
+  if (TREE_CODE (ret) == TARGET_EXPR)
+    ret = TARGET_EXPR_INITIAL (ret);
+  tree fn = cp_get_callee_fndecl (ret);
+  if (fn && DECL_ABSTRACT_ORIGIN (fn) == current_function_decl)
+    error ("constructor delegates to itself");
+}
+
 /* Build a call to a constructor, destructor, or an assignment
    operator for INSTANCE, an expression with class type.  NAME
    indicates the special member function to call; *ARGS are the
@@ -8162,6 +8191,38 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args,
 
   gcc_assert (instance != NULL_TREE);
 
+  /* 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."  Handle that here to avoid doing overload
+     resolution.  */
+  if (cxx_dialect >= cxx1z
+      && args && vec_safe_length (*args) == 1
+      && name == complete_ctor_identifier)
+    {
+      tree arg = (**args)[0];
+
+      /* FIXME P0135 doesn't say how to handle direct initialization from a
+	 type with a suitable conversion operator.  Let's handle it like
+	 copy-initialization, but allowing explict conversions.  */
+      if (!reference_related_p (class_type, TREE_TYPE (arg)))
+	arg = perform_implicit_conversion_flags (class_type, arg,
+						 tf_warning, flags);
+      if (TREE_CODE (arg) == TARGET_EXPR
+	  && (same_type_ignoring_top_level_qualifiers_p
+	      (class_type, TREE_TYPE (arg))))
+	{
+	  if (is_dummy_object (instance))
+	    return arg;
+	  if ((complain & tf_error)
+	      && (flags & LOOKUP_DELEGATING_CONS))
+	    check_self_delegation (arg);
+	  /* Avoid change of behavior on Wunused-var-2.C.  */
+	  mark_lvalue_use (instance);
+	  return build2 (INIT_EXPR, class_type, instance, arg);
+	}
+    }
+
   fns = lookup_fnfields (binfo, name, 1);
 
   /* When making a call to a constructor or destructor for a subobject
@@ -8206,11 +8267,8 @@ build_special_member_call (tree instance, tree name, vec<tree, va_gc> **args,
 
   if ((complain & tf_error)
       && (flags & LOOKUP_DELEGATING_CONS)
-      && name == complete_ctor_identifier 
-      && TREE_CODE (ret) == CALL_EXPR
-      && (DECL_ABSTRACT_ORIGIN (TREE_OPERAND (CALL_EXPR_FN (ret), 0))
-	  == current_function_decl))
-    error ("constructor delegates to itself");
+      && name == complete_ctor_identifier)
+    check_self_delegation (ret);
 
   return ret;
 }
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 6a08627..8b0442f 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -5692,7 +5692,6 @@ 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);
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index 063457f..2f5f15a 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -658,27 +658,6 @@ 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.  */
@@ -714,8 +693,7 @@ ocp_convert (tree type, tree expr, int convtype, int flags,
   if (error_operand_p (e))
     return error_mark_node;
 
-  if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP)
-      && !early_elide_copy (type, e))
+  if (MAYBE_CLASS_TYPE_P (type) && (convtype & CONV_FORCE_TEMP))
     /* We need a new temporary; don't take this shortcut.  */;
   else if (same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (e)))
     {
diff --git a/gcc/cp/except.c b/gcc/cp/except.c
index 2f88082..1c60b08 100644
--- a/gcc/cp/except.c
+++ b/gcc/cp/except.c
@@ -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) && !early_elide_copy (temp_type, exp))
+      if (CLASS_TYPE_P (temp_type))
 	{
 	  int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
 	  vec<tree, va_gc> *exp_vec;
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index a873bb1..b4b5e0a 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -1644,13 +1644,6 @@ 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
@@ -1661,12 +1654,14 @@ 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 for which early_elide_copy is true represents the whole
-     initialization, so we shouldn't build up another ctor call.  */
-
+     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.  */
   if (init
       && (TREE_CODE (init) == CONSTRUCTOR
-	  || early_elide_copy (type, init))
+	  || (TREE_CODE (init) == TARGET_EXPR
+	      && (TARGET_EXPR_DIRECT_INIT_P (init)
+		  || TARGET_EXPR_LIST_INIT_P (init))))
       && same_type_ignoring_top_level_qualifiers_p (TREE_TYPE (init), type))
     {
       /* Early initialization via a TARGET_EXPR only works for
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 6456269..569442f 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -7639,8 +7639,6 @@ 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);
diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist12.C b/gcc/testsuite/g++.dg/cpp0x/initlist12.C
index 5efdc67..f72a6da 100644
--- a/gcc/testsuite/g++.dg/cpp0x/initlist12.C
+++ b/gcc/testsuite/g++.dg/cpp0x/initlist12.C
@@ -1,6 +1,5 @@
 // PR c++/38698
 // { dg-do compile { target c++11 } }
-// { dg-prune-output "note" }
 
 struct A
 {
diff --git a/gcc/testsuite/g++.dg/cpp1z/elide1.C b/gcc/testsuite/g++.dg/cpp1z/elide1.C
index 71476e2..48b89b1 100644
--- a/gcc/testsuite/g++.dg/cpp1z/elide1.C
+++ b/gcc/testsuite/g++.dg/cpp1z/elide1.C
@@ -23,3 +23,10 @@ A f() {
   else
     return A();
 }
+
+A* ap = new A(f());
+
+struct B {
+  A a;
+  B(): a(A()) {}
+};
diff --git a/libstdc++-v3/testsuite/20_util/variant/compile.cc b/libstdc++-v3/testsuite/20_util/variant/compile.cc
index 85a697f..4016d9e 100644
--- a/libstdc++-v3/testsuite/20_util/variant/compile.cc
+++ b/libstdc++-v3/testsuite/20_util/variant/compile.cc
@@ -91,8 +91,8 @@ void move_ctor()
 {
   static_assert(is_move_constructible_v<variant<int, string>>, "");
   static_assert(!is_move_constructible_v<variant<AllDeleted, string>>, "");
-  static_assert(!noexcept(variant<int, Empty>(variant<int, Empty>())), "");
-  static_assert(noexcept(variant<int, DefaultNoexcept>(variant<int, DefaultNoexcept>())), "");
+  static_assert(!noexcept(variant<int, Empty>(declval<variant<int, Empty>>())), "");
+  static_assert(noexcept(variant<int, DefaultNoexcept>(declval<variant<int, DefaultNoexcept>>())), "");
 }
 
 void arbitrary_ctor()

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2016-10-08 16:27 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-05 22:57 C++ PATCH for P0135, C++17 guaranteed copy elision Jason Merrill
2016-10-05 23:16 ` Pedro Alves
2016-10-05 23:22   ` Jason Merrill
2016-10-06 21:26     ` Jason Merrill
2016-10-08 16:27       ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).