public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Marek Polacek <polacek@redhat.com>
To: Jason Merrill <jason@redhat.com>, GCC Patches <gcc-patches@gcc.gnu.org>
Subject: C++ PATCH/RFC for c++/87109, wrong overload with ref-qualifiers
Date: Thu, 30 Aug 2018 00:03:00 -0000	[thread overview]
Message-ID: <20180830000345.GB12638@redhat.com> (raw)

I've now gotten to the point where I question the validity of this PR, so it's
probably a good time to stop and ask for some advice.

As discussed in <https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01607.html>, we
choose the wrong overload for f1:

struct C { };
struct A {
  operator C() &;
  operator C() &&;
};

C f1(A a)
{
   return a; // should call operator C()&, but calls operator C()&&
}

Since we're returning a local variable, we know it's about to be destroyed,
so even though it's got a name, not for very much longer, so we activate move
semantics.  So we perform overload resolution with 'a' turned into
*NON_LVALUE_EXPR<(A&) &a>, an xvalue.  We need to convert 'a' from A to C,
which is taking place in build_user_type_conversion_1.  It will see two
cadidates:

  A::operator C() &&
  A::operator C() &

when adding these candidates in add_function_cadidate we process the
ref-qualifiers by tweaking the type of the implicit object parameter by turning
it into a reference type.  Then we create an implicit conversion sequence
for converting the type of the argument to the type of the parameter,
so A to A&.  That succeeds in the first case (an xvalue binding to an rvalue
reference) but fails in the second case (an xvalue binding to an lvalue
reference).  And thus we end up using the first overload.

But why is this invalid, again?  [class.copy.elision] says "or if the type of
the first parameter of the selected constructor is not an rvalue reference to
the object's type (possibly cv-qualified), overload resolution is performed
again, considering the object as an lvalue." but I don't see how that applies
here.  (Constructors can't have ref-qualifiers anyway.)

Thoughts?

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2018-08-29  Marek Polacek  <polacek@redhat.com>

	PR c++/87109
	* call.c (add_function_candidate): Skip an rvalue ref-qualified
	candidate function when performing implicit move semantics.

	* g++.dg/cpp0x/ref-qual19.C: New test.

diff --git gcc/cp/call.c gcc/cp/call.c
index a1567026975..2911b4cfdaa 100644
--- gcc/cp/call.c
+++ gcc/cp/call.c
@@ -2194,6 +2194,7 @@ add_function_candidate (struct z_candidate **candidates,
       is_this = (i == 0 && DECL_NONSTATIC_MEMBER_FUNCTION_P (fn)
 		 && ! DECL_CONSTRUCTOR_P (fn));
 
+      bool iop_to_rvalue = false;
       if (parmnode)
 	{
 	  tree parmtype = TREE_VALUE (parmnode);
@@ -2222,6 +2223,7 @@ add_function_candidate (struct z_candidate **candidates,
 		  /* The special handling of 'this' conversions in compare_ics
 		     does not apply if there is a ref-qualifier.  */
 		  is_this = false;
+		  iop_to_rvalue = rv;
 		}
 	      else
 		{
@@ -2247,6 +2249,14 @@ add_function_candidate (struct z_candidate **candidates,
 	  to_type = argtype;
 	}
 
+      /* We are performing implicit move, so the argument is an xvalue.
+	 If this candidate is an rvalue ref-qualified function, it's
+	 turned its parameter to an rvalue reference, in which case this
+	 function would be a viable candidate.  But it seems that's not
+	 what we want.  */
+      if (t && iop_to_rvalue && (flags & LOOKUP_PREFER_RVALUE))
+	t->bad_p = true;
+
       if (t && is_this)
 	t->this_p = true;
 
diff --git gcc/testsuite/g++.dg/cpp0x/ref-qual19.C gcc/testsuite/g++.dg/cpp0x/ref-qual19.C
index e69de29bb2d..b34007cd57d 100644
--- gcc/testsuite/g++.dg/cpp0x/ref-qual19.C
+++ gcc/testsuite/g++.dg/cpp0x/ref-qual19.C
@@ -0,0 +1,42 @@
+// PR c++/87109
+// { dg-do run { target c++11 } }
+
+#include <utility>
+
+struct C { int i; };
+struct A {
+  operator C() & { return { 1 }; }
+  operator C() && { return { 2 }; }
+};
+
+C
+f (A a)
+{
+  return a;
+}
+
+C
+f2 (A a)
+{
+  return std::move (a);
+}
+
+C
+f3 ()
+{
+  return A();
+}
+
+int
+main ()
+{
+  C c1 = f (A());
+  if (c1.i != 1)
+    __builtin_abort ();
+  C c2 = f2 (A());
+  if (c2.i != 2)
+    __builtin_abort ();
+  C c3 = f3 ();
+  if (c3.i != 2)
+    __builtin_abort ();
+}

             reply	other threads:[~2018-08-30  0:03 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-08-30  0:03 Marek Polacek [this message]
2018-08-30 13:22 ` Jason Merrill
2018-09-04 19:02   ` Marek Polacek
2018-09-04 21:29     ` Jason Merrill
2018-09-05 20:09       ` Marek Polacek
2018-09-05 20:56         ` 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=20180830000345.GB12638@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).