public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Marek Polacek <polacek@redhat.com>
To: GCC Patches <gcc-patches@gcc.gnu.org>, Jason Merrill <jason@redhat.com>
Subject: Re: [PATCH] c++: Remove maybe-rvalue OR in implicit move
Date: Mon, 10 Oct 2022 15:19:29 -0400	[thread overview]
Message-ID: <Y0RwQSbKc3RuLMpx@redhat.com> (raw)
In-Reply-To: <20220928212634.1275032-1-polacek@redhat.com>

Ping.

On Wed, Sep 28, 2022 at 05:26:34PM -0400, Marek Polacek via Gcc-patches wrote:
> This patch removes the two-stage overload resolution when performing
> implicit move, whereby the compiler does two separate overload resolutions:
> one treating the operand as an rvalue, and then (if that resolution fails)
> another one treating the operand as an lvalue.  In the standard this was
> introduced via CWG 1579 and implemented in gcc in r251035.  In r11-2412,
> we disabled the fallback OR in C++20 (but not in C++17).  Then C++23 P2266
> removed the fallback overload resolution, and changed the implicit move rules
> once again.  So we wound up with three different behaviors.
> 
> The two overload resolutions approach was complicated and quirky, so
> users should transition to the newer model.  Removing the maybe-rvalue
> OR also allows us to simplify our code, for instance, now we can get
> rid of LOOKUP_PREFER_RVALUE altogether.
> 
> This change means that code that previously didn't compile in C++17 will
> now compile, for example:
> 
>   struct S1 { S1(S1 &&); };
>   struct S2 : S1 {};
> 
>   S1
>   f (S2 s)
>   {
>     return s; // OK, derived-to-base, use S1::S1(S1&&)
>   }
> 
> And conversely, code that used to work in C++17 may not compile anymore:
> 
>   struct W {
>     W();
>   };
> 
>   struct F {
>     F(W&);
>     F(W&&) = delete;
>   };
> 
>   F fn ()
>   {
>     W w;
>     return w; // use w as rvalue -> use of deleted function F::F(W&&)
>   }
> 
> I plan to add a note to porting_to.html.
> 
> Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> 
> gcc/cp/ChangeLog:
> 
> 	* call.cc (standard_conversion): Remove LOOKUP_PREFER_RVALUE code.
> 	(reference_binding): Honor clk_implicit_rval even pre-C++20.
> 	(implicit_conversion_1): Remove LOOKUP_PREFER_RVALUE code.
> 	(build_user_type_conversion_1): Likewise.
> 	(convert_like_internal): Likewise.
> 	(build_over_call): Likewise.
> 	* cp-tree.h (LOOKUP_PREFER_RVALUE): Remove.
> 	(LOOKUP_NO_NARROWING): Adjust definition.
> 	* except.cc (build_throw): Don't perform two overload resolutions.
> 	* typeck.cc (maybe_warn_pessimizing_move): Don't use
> 	LOOKUP_PREFER_RVALUE.
> 	(check_return_expr): Don't perform two overload resolutions.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp0x/Wredundant-move10.C: Adjust dg-warning.
> 	* g++.dg/cpp0x/Wredundant-move7.C: Likewise.
> 	* g++.dg/cpp0x/move-return2.C: Remove dg-error.
> 	* g++.dg/cpp0x/move-return4.C: Likewise.
> 	* g++.dg/cpp0x/ref-qual20.C: Adjust expected return value.
> 	* g++.dg/cpp0x/move-return5.C: New test.
> ---
>  gcc/cp/call.cc                                | 41 ++-----------------
>  gcc/cp/cp-tree.h                              |  6 +--
>  gcc/cp/except.cc                              | 23 ++---------
>  gcc/cp/typeck.cc                              | 34 +++------------
>  .../g++.dg/cpp0x/Wredundant-move10.C          |  2 +-
>  gcc/testsuite/g++.dg/cpp0x/Wredundant-move7.C |  6 +--
>  gcc/testsuite/g++.dg/cpp0x/move-return2.C     |  2 +-
>  gcc/testsuite/g++.dg/cpp0x/move-return4.C     |  2 +-
>  gcc/testsuite/g++.dg/cpp0x/move-return5.C     | 20 +++++++++
>  gcc/testsuite/g++.dg/cpp0x/ref-qual20.C       |  2 +-
>  10 files changed, 41 insertions(+), 97 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/cpp0x/move-return5.C
> 
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index 3506b0fcfbb..e100913ea29 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -1272,9 +1272,6 @@ 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 to set LOOKUP_PREFER_RVALUE.  */
> -	conv->rvaluedness_matches_p = true;
>        /* If we're performing copy-initialization, remember to skip
>  	 explicit constructors.  */
>        if (flags & LOOKUP_ONLYCONVERTING)
> @@ -1572,9 +1569,6 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
>  	 type.  A temporary object is created to hold the result of
>  	 the conversion unless we're binding directly to a reference.  */
>        conv->need_temporary_p = !(flags & LOOKUP_NO_TEMP_BIND);
> -      if (flags & LOOKUP_PREFER_RVALUE)
> -	/* Tell convert_like to set LOOKUP_PREFER_RVALUE.  */
> -	conv->rvaluedness_matches_p = true;
>        /* If we're performing copy-initialization, remember to skip
>  	 explicit constructors.  */
>        if (flags & LOOKUP_ONLYCONVERTING)
> @@ -1883,7 +1877,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
>  	  /* Unless it's really a C++20 lvalue being treated as an xvalue.
>  	     But in C++23, such an expression is just an xvalue, not a special
>  	     lvalue, so the binding is once again ill-formed.  */
> -	  && !(cxx_dialect == cxx20
> +	  && !(cxx_dialect <= cxx20
>  	       && (gl_kind & clk_implicit_rval))
>  	  && (!CP_TYPE_CONST_NON_VOLATILE_P (to)
>  	      || (flags & LOOKUP_NO_RVAL_BIND))
> @@ -2044,9 +2038,8 @@ implicit_conversion_1 (tree to, tree from, tree expr, bool c_cast_p,
>    /* Other flags only apply to the primary function in overload
>       resolution, or after we've chosen one.  */
>    flags &= (LOOKUP_ONLYCONVERTING|LOOKUP_NO_CONVERSION|LOOKUP_COPY_PARM
> -	    |LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND|LOOKUP_PREFER_RVALUE
> -	    |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL
> -	    |LOOKUP_SHORTCUT_BAD_CONVS);
> +	    |LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND|LOOKUP_NO_NARROWING
> +	    |LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL|LOOKUP_SHORTCUT_BAD_CONVS);
>  
>    /* FIXME: actually we don't want warnings either, but we can't just
>       have 'complain &= ~(tf_warning|tf_error)' because it would cause
> @@ -4451,14 +4444,6 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags,
>    if (cand->viable == -1)
>      conv->bad_p = true;
>  
> -  /* We're performing the maybe-rvalue overload resolution and
> -     a conversion function is in play.  Reject converting the return
> -     value of the conversion function to a base class.  */
> -  if ((flags & LOOKUP_PREFER_RVALUE) && !DECL_CONSTRUCTOR_P (cand->fn))
> -    for (conversion *t = cand->second_conv; t; t = next_conversion (t))
> -      if (t->kind == ck_base)
> -	return NULL;
> -
>    /* Remember that this was a list-initialization.  */
>    if (flags & LOOKUP_NO_NARROWING)
>      conv->check_narrowing = true;
> @@ -8281,9 +8266,6 @@ convert_like_internal (conversion *convs, tree expr, tree fn, int argnum,
>  	 explicit constructors.  */
>        if (convs->copy_init_p)
>  	flags |= LOOKUP_ONLYCONVERTING;
> -      if (convs->rvaluedness_matches_p)
> -	/* standard_conversion got LOOKUP_PREFER_RVALUE.  */
> -	flags |= LOOKUP_PREFER_RVALUE;
>        expr = build_temp (expr, totype, flags, &diag_kind, complain);
>        if (diag_kind && complain)
>  	{
> @@ -9529,23 +9511,6 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
>  	  ++arg_index;
>  	  parm = TREE_CHAIN (parm);
>  	}
> -
> -      if (cxx_dialect < cxx20
> -	  && (cand->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;
> -	  /* Allow calling a by-value converting constructor even though it
> -	     isn't permitted by the above, because we've allowed it since GCC 5
> -	     (PR58051) and it's allowed in C++20.  But don't call a copy
> -	     constructor.  */
> -	  if ((TYPE_REF_P (ptype) && !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)
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index d0f1b18b015..7acb5373513 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5853,12 +5853,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
>  #define LOOKUP_DESTRUCTOR (1 << 5)
>  /* Do not permit references to bind to temporaries.  */
>  #define LOOKUP_NO_TEMP_BIND (1 << 6)
> -/* We're trying to treat an lvalue as an rvalue.  */
> -/* FIXME remove when we extend the P1825 semantics to all standard modes, the
> -   C++20 approach uses IMPLICIT_RVALUE_P instead.  */
> -#define LOOKUP_PREFER_RVALUE (LOOKUP_NO_TEMP_BIND << 1)
>  /* We're inside an init-list, so narrowing conversions are ill-formed.  */
> -#define LOOKUP_NO_NARROWING (LOOKUP_PREFER_RVALUE << 1)
> +#define LOOKUP_NO_NARROWING (LOOKUP_NO_TEMP_BIND << 1)
>  /* We're looking up a constructor for list-initialization.  */
>  #define LOOKUP_LIST_INIT_CTOR (LOOKUP_NO_NARROWING << 1)
>  /* This is the first parameter of a copy constructor.  */
> diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc
> index 048612de400..1d84b451c9f 100644
> --- a/gcc/cp/except.cc
> +++ b/gcc/cp/except.cc
> @@ -715,25 +715,10 @@ build_throw (location_t loc, tree exp)
>  	     treated as an rvalue for the purposes of overload resolution
>  	     to favor move constructors over copy constructors.  */
>  	  if (tree moved = treat_lvalue_as_rvalue_p (exp, /*return*/false))
> -	    {
> -	      if (cxx_dialect < cxx20)
> -		{
> -		  releasing_vec 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));
> -		  if (moved != error_mark_node)
> -		    {
> -		      exp = moved;
> -		      converted = true;
> -		    }
> -		}
> -	      else
> -		/* In C++20 we just treat the return value as an rvalue that
> -		   can bind to lvalue refs.  */
> -		exp = moved;
> -	    }
> +	    /* In C++20 we just treat the return value as an rvalue that
> +	       can bind to lvalue refs.  In C++23, such an expression is just
> +	       an xvalue.  */
> +	    exp = moved;
>  
>  	  /* Call the copy constructor.  */
>  	  if (!converted)
> diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
> index 5f16c4d2426..c525e880383 100644
> --- a/gcc/cp/typeck.cc
> +++ b/gcc/cp/typeck.cc
> @@ -10692,21 +10692,12 @@ maybe_warn_pessimizing_move (tree expr, tree type, bool return_p)
>  	  tree t = convert_for_initialization (NULL_TREE, type,
>  					       moved,
>  					       (LOOKUP_NORMAL
> -						| LOOKUP_ONLYCONVERTING
> -						| LOOKUP_PREFER_RVALUE),
> +						| LOOKUP_ONLYCONVERTING),
>  					       ICR_RETURN, NULL_TREE, 0,
>  					       tf_none);
>  	  /* If this worked, implicit rvalue would work, so the call to
>  	     std::move is redundant.  */
> -	  if (t != error_mark_node
> -	      /* Trying to move something const will never succeed unless
> -		 there's T(const T&&), which it almost never is, and if
> -		 so, T wouldn't be error_mark_node now: the above convert_
> -		 call with LOOKUP_PREFER_RVALUE returns an error if a const T&
> -		 overload is selected.  */
> -	      || (CP_TYPE_CONST_P (TREE_TYPE (arg))
> -		  && same_type_ignoring_top_level_qualifiers_p
> -		  (TREE_TYPE (arg), type)))
> +	  if (t != error_mark_node)
>  	    {
>  	      auto_diagnostic_group d;
>  	      if (warning_at (loc, OPT_Wredundant_move,
> @@ -11049,23 +11040,10 @@ check_return_expr (tree retval, bool *no_warning)
>  	     ? CLASS_TYPE_P (functype)
>  	     : !SCALAR_TYPE_P (functype) || !SCALAR_TYPE_P (TREE_TYPE (retval)))
>  	    && (moved = treat_lvalue_as_rvalue_p (retval, /*return*/true)))
> -	{
> -	  if (cxx_dialect < cxx20)
> -	    {
> -	      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;
> -		}
> -	    }
> -	  else
> -	    /* In C++20 we just treat the return value as an rvalue that
> -	       can bind to lvalue refs.  */
> -	    retval = moved;
> -	}
> +	  /* In C++20 and earlier we just treat the return value as an rvalue
> +	     that can bind to lvalue refs.  In C++23, such an expression is just
> +	     an xvalue (see reference_binding).  */
> +	  retval = moved;
>  
>        /* The call in a (lambda) thunk needs no conversions.  */
>        if (TREE_CODE (retval) == CALL_EXPR
> diff --git a/gcc/testsuite/g++.dg/cpp0x/Wredundant-move10.C b/gcc/testsuite/g++.dg/cpp0x/Wredundant-move10.C
> index a215a4774d6..17dd807aea8 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/Wredundant-move10.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/Wredundant-move10.C
> @@ -57,5 +57,5 @@ struct S2: S1 {};
>  
>  S1 f3(const S2 s)
>  {
> -  return std::move(s); // { dg-warning "redundant move" "" { target c++20 } }
> +  return std::move(s); // { dg-warning "redundant move" }
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/Wredundant-move7.C b/gcc/testsuite/g++.dg/cpp0x/Wredundant-move7.C
> index 3fec525879d..6547777c007 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/Wredundant-move7.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/Wredundant-move7.C
> @@ -28,7 +28,7 @@ struct S2 : S1 {};
>  S1
>  f (S2 s)
>  {
> -  return std::move(s); // { dg-warning "redundant move in return statement" "" { target c++20 } }
> +  return std::move(s); // { dg-warning "redundant move in return statement" }
>  }
>  
>  struct R1 {
> @@ -40,7 +40,7 @@ struct R2 : R1 {};
>  R1
>  f2 (const R2 s)
>  {
> -  return std::move(s); // { dg-warning "redundant move in return statement" "" { target c++20 } }
> +  return std::move(s); // { dg-warning "redundant move in return statement" }
>  }
>  
>  struct T1 {
> @@ -55,5 +55,5 @@ f3 (const T2 s)
>  {
>    // Without std::move: const T1 &
>    // With std::move: const T1 &&
> -  return std::move(s); // { dg-warning "redundant move in return statement" "" { target c++20 } }
> +  return std::move(s); // { dg-warning "redundant move in return statement" }
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/move-return2.C b/gcc/testsuite/g++.dg/cpp0x/move-return2.C
> index 999f2c95c49..8e750efb870 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/move-return2.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/move-return2.C
> @@ -7,5 +7,5 @@ struct S2 : S1 {};
>  S1
>  f (S2 s)
>  {
> -  return s; // { dg-error "use of deleted function" "" { target c++17_down } }
> +  return s;
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/move-return4.C b/gcc/testsuite/g++.dg/cpp0x/move-return4.C
> index 3fc58089319..0f0ca1fc253 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/move-return4.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/move-return4.C
> @@ -13,5 +13,5 @@ struct A : Base
>  A<int> foo()
>  {
>      A<double> v;
> -    return v; // { dg-error "cannot bind rvalue reference" "" { target c++17_down } }
> +    return v;
>  }
> diff --git a/gcc/testsuite/g++.dg/cpp0x/move-return5.C b/gcc/testsuite/g++.dg/cpp0x/move-return5.C
> new file mode 100644
> index 00000000000..695000b2687
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp0x/move-return5.C
> @@ -0,0 +1,20 @@
> +// { dg-do compile { target c++11 } }
> +// This used to compile in C++11...17 because we performed two
> +// separate overload resolutions: one treating the operand as
> +// an rvalue, and then (if that resolution fails) another one
> +// treating the operand as an lvalue.
> +
> +struct W {
> +  W();
> +};
> +
> +struct F {
> +  F(W&);
> +  F(W&&) = delete;
> +};
> +
> +F fn ()
> +{
> +  W w;
> +  return w; // { dg-error "use of deleted function" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp0x/ref-qual20.C b/gcc/testsuite/g++.dg/cpp0x/ref-qual20.C
> index cfbef300226..314f19bb864 100644
> --- a/gcc/testsuite/g++.dg/cpp0x/ref-qual20.C
> +++ b/gcc/testsuite/g++.dg/cpp0x/ref-qual20.C
> @@ -52,7 +52,7 @@ f5 ()
>  int
>  main ()
>  {
> -  int return_lval = __cplusplus > 201703L ? -1 : 2;
> +  int return_lval = -1;
>    Y y1 = f (A());
>    if (y1.y != return_lval)
>      __builtin_abort ();
> 
> base-commit: 9f65eecdbef6027722e93aadf3fa6b3cc342eb4f
> -- 
> 2.37.3
> 

Marek


  reply	other threads:[~2022-10-10 19:19 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-28 21:26 Marek Polacek
2022-10-10 19:19 ` Marek Polacek [this message]
2022-10-10 20:15 ` Jason Merrill

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Y0RwQSbKc3RuLMpx@redhat.com \
    --to=polacek@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jason@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).