PR c++/80452 - Core 1579, implicit move semantics on return/throw
authorJason Merrill <jason@redhat.com>
Thu, 10 Aug 2017 19:07:30 +0000 (15:07 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 10 Aug 2017 19:07:30 +0000 (15:07 -0400)
* cp-tree.h (LOOKUP_PREFER_RVALUE): Now means that we've already
tentatively changed the lvalue to an rvalue.
* call.c (reference_binding): Remove LOOKUP_PREFER_RVALUE handling.
(build_over_call): If LOOKUP_PREFER_RVALUE, check that the first
parameter is an rvalue reference.
* except.c (build_throw): Do maybe-rvalue overload resolution twice.
* typeck.c (check_return_expr): Likewise.

From-SVN: r251035

gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/cp-tree.h
gcc/cp/except.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/cpp0x/move-return1.C [new file with mode: 0644]

index bc0959cfef041f404440f65e54374805e40c3da1..bc4eaa20b46feb7d94de8ae8ed46ddb3bca2e225 100644 (file)
@@ -1,3 +1,14 @@
+2017-08-10  Jason Merrill  <jason@redhat.com>
+
+       PR c++/80452 - Core 1579, implicit move semantics on return/throw
+       * cp-tree.h (LOOKUP_PREFER_RVALUE): Now means that we've already
+       tentatively changed the lvalue to an rvalue.
+       * call.c (reference_binding): Remove LOOKUP_PREFER_RVALUE handling.
+       (build_over_call): If LOOKUP_PREFER_RVALUE, check that the first
+       parameter is an rvalue reference.
+       * except.c (build_throw): Do maybe-rvalue overload resolution twice.
+       * typeck.c (check_return_expr): Likewise.
+
 2017-08-10  David Malcolm  <dmalcolm@redhat.com>
 
        * parser.c (cp_parser_error): Update for new param to
index 4903119adcad0116e75ba7a35c990d1cdeaf2810..37902991f3f8620c2ab8833c4cbeb6ffcae7468e 100644 (file)
@@ -101,7 +101,7 @@ struct conversion {
   /* If KIND is ck_ref_bind, true when either an lvalue reference is
      being bound to an lvalue expression or an rvalue reference is
      being bound to an rvalue expression.  If KIND is ck_rvalue,
-     true when we should treat an lvalue as an rvalue (12.8p33).  If
+     true when we are treating an lvalue as an rvalue (12.8p33).  If
      KIND is ck_base, always false.  */
   BOOL_BITFIELD rvaluedness_matches_p: 1;
   BOOL_BITFIELD check_narrowing: 1;
@@ -1161,6 +1161,7 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
        }
       conv = build_conv (ck_rvalue, from, conv);
       if (flags & LOOKUP_PREFER_RVALUE)
+       /* Tell convert_like_real to set LOOKUP_PREFER_RVALUE.  */
        conv->rvaluedness_matches_p = true;
     }
 
@@ -1629,11 +1630,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
       conv = build_identity_conv (tfrom, expr);
       conv = direct_reference_binding (rto, conv);
 
-      if (flags & LOOKUP_PREFER_RVALUE)
-       /* The top-level caller requested that we pretend that the lvalue
-          be treated as an rvalue.  */
-       conv->rvaluedness_matches_p = TYPE_REF_IS_RVALUE (rto);
-      else if (TREE_CODE (rfrom) == REFERENCE_TYPE)
+      if (TREE_CODE (rfrom) == REFERENCE_TYPE)
        /* Handle rvalue reference to function properly.  */
        conv->rvaluedness_matches_p
          = (TYPE_REF_IS_RVALUE (rto) == TYPE_REF_IS_RVALUE (rfrom));
@@ -1659,8 +1656,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
       /* Don't allow binding of lvalues (other than function lvalues) to
         rvalue references.  */
       if (is_lvalue && TYPE_REF_IS_RVALUE (rto)
-         && TREE_CODE (to) != FUNCTION_TYPE
-          && !(flags & LOOKUP_PREFER_RVALUE))
+         && TREE_CODE (to) != FUNCTION_TYPE)
        conv->bad_p = true;
 
       /* Nor the reverse.  */
@@ -6917,6 +6913,7 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum,
       else
        flags |= LOOKUP_ONLYCONVERTING;
       if (convs->rvaluedness_matches_p)
+       /* standard_conversion got LOOKUP_PREFER_RVALUE.  */
        flags |= LOOKUP_PREFER_RVALUE;
       if (TREE_CODE (expr) == TARGET_EXPR
          && TARGET_EXPR_LIST_INIT_P (expr))
@@ -7716,6 +7713,19 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
          ++arg_index;
          parm = TREE_CHAIN (parm);
        }
+
+      if (flags & LOOKUP_PREFER_RVALUE)
+       {
+         /* The implicit move specified in 15.8.3/3 fails "...if the type of
+            the first parameter of the selected constructor is not an rvalue
+            reference to the object’s type (possibly cv-qualified)...." */
+         gcc_assert (!(complain & tf_error));
+         tree ptype = convs[0]->type;
+         if (TREE_CODE (ptype) != REFERENCE_TYPE
+             || !TYPE_REF_IS_RVALUE (ptype)
+             || CONVERSION_RANK (convs[0]) > cr_exact)
+           return error_mark_node;
+       }
     }
   /* Bypass access control for 'this' parameter.  */
   else if (TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
index 3a0bd1687e8bdc5b78bc859db1b04d6bd7947fe3..6c4153d558b13c37b77b17230a21a9248ca9aefa 100644 (file)
@@ -5296,7 +5296,7 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
    (Normally, these entities are registered in the symbol table, but
    not found by lookup.)  */
 #define LOOKUP_HIDDEN (LOOKUP_PREFER_NAMESPACES << 1)
-/* Prefer that the lvalue be treated as an rvalue.  */
+/* We're trying to treat an lvalue as an rvalue.  */
 #define LOOKUP_PREFER_RVALUE (LOOKUP_HIDDEN << 1)
 /* We're inside an init-list, so narrowing conversions are ill-formed.  */
 #define LOOKUP_NO_NARROWING (LOOKUP_PREFER_RVALUE << 1)
index 208e52a405b4b955ab9356da821ebad9fab48e8c..b25b91b97bec129f4b553f6cddaef9e272865aeb 100644 (file)
@@ -665,6 +665,7 @@ build_throw (tree exp)
        {
          int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
          vec<tree, va_gc> *exp_vec;
+         bool converted = false;
 
          /* Under C++0x [12.8/16 class.copy], a thrown lvalue is sometimes
             treated as an rvalue for the purposes of overload resolution
@@ -675,14 +676,31 @@ build_throw (tree exp)
              && ! TREE_STATIC (exp)
              /* The variable must not have the `volatile' qualifier.  */
              && !(cp_type_quals (TREE_TYPE (exp)) & TYPE_QUAL_VOLATILE))
-           flags = flags | LOOKUP_PREFER_RVALUE;
+           {
+             tree moved = move (exp);
+             exp_vec = make_tree_vector_single (moved);
+             moved = (build_special_member_call
+                      (object, complete_ctor_identifier, &exp_vec,
+                       TREE_TYPE (object), flags|LOOKUP_PREFER_RVALUE,
+                       tf_none));
+             release_tree_vector (exp_vec);
+             if (moved != error_mark_node)
+               {
+                 exp = moved;
+                 converted = true;
+               }
+           }
 
          /* Call the copy constructor.  */
-         exp_vec = make_tree_vector_single (exp);
-         exp = (build_special_member_call
-                (object, complete_ctor_identifier, &exp_vec,
-                 TREE_TYPE (object), flags, tf_warning_or_error));
-         release_tree_vector (exp_vec);
+         if (!converted)
+           {
+             exp_vec = make_tree_vector_single (exp);
+             exp = (build_special_member_call
+                    (object, complete_ctor_identifier, &exp_vec,
+                     TREE_TYPE (object), flags, tf_warning_or_error));
+             release_tree_vector (exp_vec);
+           }
+
          if (exp == error_mark_node)
            {
              error ("  in thrown expression");
index 3ce39061010fface01aa1c73ca5a1e5a14af350b..a5a363bc9424c13281e97449633fca0ae2429413 100644 (file)
@@ -9156,6 +9156,7 @@ check_return_expr (tree retval, bool *no_warning)
 
          Note that these conditions are similar to, but not as strict as,
         the conditions for the named return value optimization.  */
+      bool converted = false;
       if ((cxx_dialect != cxx98)
           && ((VAR_P (retval) && !DECL_HAS_VALUE_EXPR_P (retval))
              || TREE_CODE (retval) == PARM_DECL)
@@ -9163,14 +9164,25 @@ check_return_expr (tree retval, bool *no_warning)
          && !TREE_STATIC (retval)
          /* This is only interesting for class type.  */
          && CLASS_TYPE_P (functype))
-       flags = flags | LOOKUP_PREFER_RVALUE;
+       {
+         tree moved = move (retval);
+         moved = convert_for_initialization
+           (NULL_TREE, functype, moved, flags|LOOKUP_PREFER_RVALUE,
+            ICR_RETURN, NULL_TREE, 0, tf_none);
+         if (moved != error_mark_node)
+           {
+             retval = moved;
+             converted = true;
+           }
+       }
 
       /* First convert the value to the function's return type, then
         to the type of return value's location to handle the
         case that functype is smaller than the valtype.  */
-      retval = convert_for_initialization
-       (NULL_TREE, functype, retval, flags, ICR_RETURN, NULL_TREE, 0,
-         tf_warning_or_error);
+      if (!converted)
+       retval = convert_for_initialization
+         (NULL_TREE, functype, retval, flags, ICR_RETURN, NULL_TREE, 0,
+          tf_warning_or_error);
       retval = convert (valtype, retval);
 
       /* If the conversion failed, treat this just like `return;'.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/move-return1.C b/gcc/testsuite/g++.dg/cpp0x/move-return1.C
new file mode 100644 (file)
index 0000000..dc2b313
--- /dev/null
@@ -0,0 +1,22 @@
+// PR c++/80452
+// { dg-do compile { target c++11 } }
+
+template<typename> struct check { };
+template<typename T> struct check<T&>;
+
+struct A {
+  A() = default;
+  A(A&&) = default;
+  A(const A&) = delete;
+};
+
+template <class T>
+struct B {
+  template <class U> B(U&&) { check<U> u; }
+};
+
+B<A> f()
+{
+  A a;
+  return a;
+}