public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator
@ 2023-12-18 12:04 m.cencora at gmail dot com
  2023-12-18 12:41 ` [Bug c++/113064] " m.cencora at gmail dot com
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: m.cencora at gmail dot com @ 2023-12-18 12:04 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

            Bug ID: 113064
           Summary: assignement from temporary sometimes invokes
                    copy-assign instead of move-assign operator
           Product: gcc
           Version: 13.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: m.cencora at gmail dot com
  Target Milestone: ---

Following code fails to compile on any gcc version with any selected language
version. Works with clang.

When invoking the conversion operator the problem does not occur.
Also the mere existence of second overloaded conversion operator is
problematic.
Even though this conversion operator does not participate in overload
resolution due to being && qualified.
I marked it as deleted to show that this is not the overload being chosen by
compiler but just declaring it normally is enough to trigger the bug.

Compilation fails with error:
<source>: In function 'void test()':
<source>:31:10: error: use of deleted function 'no_copy&
no_copy::operator=(const no_copy&)'
   31 |     nc = f;
      |          ^
<source>:9:14: note: declared here
    9 |     no_copy& operator=(const no_copy&) = delete;
      |              ^~~~~~~~
Compiler returned: 1



// test.cpp
struct no_copy
{
    no_copy() = default;

    no_copy(const no_copy&) = delete;
    no_copy(no_copy&&);

    no_copy& operator=(const no_copy&) = delete;
    no_copy& operator=(no_copy&&);
};

struct foo
{
    operator no_copy() &
    {
        return no_copy();
    }

#ifndef WORKAROUND1
    operator no_copy&&() && = delete;
#endif
};

void test()
{
    foo f;
    no_copy nc;

#ifndef WORKAROUND2
    nc = f;
#else
    nc = f.operator bar();
#endif
}

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
@ 2023-12-18 12:41 ` m.cencora at gmail dot com
  2023-12-18 13:00 ` m.cencora at gmail dot com
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: m.cencora at gmail dot com @ 2023-12-18 12:41 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #1 from m.cencora at gmail dot com ---
Fixed sample (a typo in else branch of WORKAROUND2):

struct no_copy
{
    no_copy() = default;

    no_copy(const no_copy&) = delete;
    no_copy(no_copy&&);

    no_copy& operator=(const no_copy&) = delete;
    no_copy& operator=(no_copy&&);
};

struct foo
{
    operator no_copy() &
    {
        return no_copy();
    }

#ifndef WORKAROUND1
    operator no_copy&&() && = delete;
#endif
};

void test()
{
    foo f;
    no_copy nc;

#ifndef WORKAROUND2
    nc = f;
#else
    nc = f.operator no_copy();
#endif
}

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
  2023-12-18 12:41 ` [Bug c++/113064] " m.cencora at gmail dot com
@ 2023-12-18 13:00 ` m.cencora at gmail dot com
  2023-12-18 19:58 ` mpolacek at gcc dot gnu.org
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: m.cencora at gmail dot com @ 2023-12-18 13:00 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #2 from m.cencora at gmail dot com ---
> When invoking the conversion operator the problem does not occur.
When invoking the conversion operator *explicitly* the problem does not occur.

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
  2023-12-18 12:41 ` [Bug c++/113064] " m.cencora at gmail dot com
  2023-12-18 13:00 ` m.cencora at gmail dot com
@ 2023-12-18 19:58 ` mpolacek at gcc dot gnu.org
  2023-12-19  9:10 ` m.cencora at gmail dot com
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: mpolacek at gcc dot gnu.org @ 2023-12-18 19:58 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

Marek Polacek <mpolacek at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |mpolacek at gcc dot gnu.org

--- Comment #3 from Marek Polacek <mpolacek at gcc dot gnu.org> ---
Doesn't look like a regression.

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (2 preceding siblings ...)
  2023-12-18 19:58 ` mpolacek at gcc dot gnu.org
@ 2023-12-19  9:10 ` m.cencora at gmail dot com
  2023-12-19 11:39 ` m.cencora at gmail dot com
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: m.cencora at gmail dot com @ 2023-12-19  9:10 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #4 from m.cencora at gmail dot com ---
This also might be a just another symptom of the same root cause:

struct bar
{
    bar() = default;

    bar(const bar&);
    bar(bar&&);

    bar& operator=(const bar&);
    bar& operator=(bar&&);
};

struct foo
{
    operator const bar& () const &;

    operator bar& () &;

    operator bar&&() &&;
};

void test()
{
    bar a = foo{}; // ok

    a = foo{}; // not ok - ambiguous call, but why? &&-qualified looks like a
better match

    foo f;
    a = f; // ok

    a = static_cast<const foo&>(foo{}); // ok
}

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (3 preceding siblings ...)
  2023-12-19  9:10 ` m.cencora at gmail dot com
@ 2023-12-19 11:39 ` m.cencora at gmail dot com
  2023-12-19 15:54 ` mpolacek at gcc dot gnu.org
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: m.cencora at gmail dot com @ 2023-12-19 11:39 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #5 from m.cencora at gmail dot com ---
(In reply to m.cencora from comment #4)
> This also might be a just another symptom of the same root cause:

This one is actually a regression (worked on gcc 8.3 and older)

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (4 preceding siblings ...)
  2023-12-19 11:39 ` m.cencora at gmail dot com
@ 2023-12-19 15:54 ` mpolacek at gcc dot gnu.org
  2024-01-03 20:43 ` cvs-commit at gcc dot gnu.org
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: mpolacek at gcc dot gnu.org @ 2023-12-19 15:54 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #6 from Marek Polacek <mpolacek at gcc dot gnu.org> ---
(In reply to m.cencora from comment #4)
> This also might be a just another symptom of the same root cause:
> 
> struct bar
> {
>     bar() = default;
> 
>     bar(const bar&);
>     bar(bar&&);
> 
>     bar& operator=(const bar&);
>     bar& operator=(bar&&);
> };
> 
> struct foo
> {
>     operator const bar& () const &;
> 
>     operator bar& () &;
> 
>     operator bar&&() &&;
> };
> 
> void test()
> {
>     bar a = foo{}; // ok
> 
>     a = foo{}; // not ok - ambiguous call, but why? &&-qualified looks like
> a better match
> 
>     foo f;
>     a = f; // ok
> 
>     a = static_cast<const foo&>(foo{}); // ok
> }

Here the error showed up in r9-6486:

commit c7e936dbc7f59ad09c28ef57e5399a4256061747
Author: Jason Merrill <jason@redhat.com>
Date:   Mon Mar 11 23:19:22 2019 -0400

    PR c++/86521 - wrong overload resolution with ref-qualifiers.

    Here we were wrongly treating binding a const lvalue ref to an xvalue as
    direct binding, which is wrong under [dcl.init.ref] and [over.match.ref].

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (5 preceding siblings ...)
  2023-12-19 15:54 ` mpolacek at gcc dot gnu.org
@ 2024-01-03 20:43 ` cvs-commit at gcc dot gnu.org
  2024-01-03 22:35 ` ppalka at gcc dot gnu.org
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: cvs-commit at gcc dot gnu.org @ 2024-01-03 20:43 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #7 from GCC Commits <cvs-commit at gcc dot gnu.org> ---
The master branch has been updated by Patrick Palka <ppalka@gcc.gnu.org>:

https://gcc.gnu.org/g:1c522c9eafa5b86b78cd7b3044e120762fb4c879

commit r14-6899-g1c522c9eafa5b86b78cd7b3044e120762fb4c879
Author: Patrick Palka <ppalka@redhat.com>
Date:   Wed Jan 3 15:43:28 2024 -0500

    c++: bad direct reference binding via conv fn [PR113064]

    When computing a direct reference binding via a conversion function
    yields a bad conversion, reference_binding incorrectly commits to that
    conversion instead of trying a conversion via a temporary.  This causes
    us to reject the first testcase because the bad direct conversion to B&&
    via the && conversion operator prevents us from considering the good
    conversion via the & conversion operator and a temporary.  (Similar
    story for the second testcase.)

    This patch fixes this by making reference_binding not prematurely commit
    to such a bad direct conversion.  We still fall back to it if using a
    temporary also fails (otherwise the diagnostic for cpp0x/explicit7.C
    regresses).

            PR c++/113064

    gcc/cp/ChangeLog:

            * call.cc (reference_binding): Still try a conversion via a
            temporary if a direct conversion was bad.

    gcc/testsuite/ChangeLog:

            * g++.dg/cpp0x/rv-conv4.C: New test.
            * g++.dg/cpp0x/rv-conv5.C: New test.

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (6 preceding siblings ...)
  2024-01-03 20:43 ` cvs-commit at gcc dot gnu.org
@ 2024-01-03 22:35 ` ppalka at gcc dot gnu.org
  2024-01-03 22:36 ` ppalka at gcc dot gnu.org
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: ppalka at gcc dot gnu.org @ 2024-01-03 22:35 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |ppalka at gcc dot gnu.org

--- Comment #8 from Patrick Palka <ppalka at gcc dot gnu.org> ---
(In reply to m.cencora from comment #4)
> This also might be a just another symptom of the same root cause:
> 
> struct bar
> {
>     bar() = default;
> 
>     bar(const bar&);
>     bar(bar&&);
> 
>     bar& operator=(const bar&);
>     bar& operator=(bar&&);
> };
> 
> struct foo
> {
>     operator const bar& () const &;
> 
>     operator bar& () &;
> 
>     operator bar&&() &&;
> };
> 
> void test()
> {
>     bar a = foo{}; // ok

It seems this copy-init is valid according to
http://eel.is/c++draft/dcl.init#general-16.6.3 -- we first choose the best
conversion function according to the destination type bar (and independent of
bar's constructors) which in this case is operator bar&&() &&, and then perform
overload resolution of bar's constructors using the result of that conversion
function as the argument, which leads to bar(bar&&) unambiguously winning.

> 
>     a = foo{}; // not ok - ambiguous call, but why? &&-qualified looks like
> a better match

Whereas here, we perform the expected overload resolution of bar's constructor
set using the original argument, which gives two viable candidates

  bar(const bar&) through operator const bar&() const&
  bar(bar&&) through operator bar&&() &&

and user-defined conversion sequences that use different conversion functions
are incomparable, so the candidates are ambiguous.

> 
>     foo f;
>     a = f; // ok
> 
>     a = static_cast<const foo&>(foo{}); // ok
> }

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (7 preceding siblings ...)
  2024-01-03 22:35 ` ppalka at gcc dot gnu.org
@ 2024-01-03 22:36 ` ppalka at gcc dot gnu.org
  2024-01-03 22:50 ` ppalka at gcc dot gnu.org
  2024-01-04  8:24 ` m.cencora at gmail dot com
  10 siblings, 0 replies; 12+ messages in thread
From: ppalka at gcc dot gnu.org @ 2024-01-03 22:36 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|UNCONFIRMED                 |RESOLVED
           Assignee|unassigned at gcc dot gnu.org      |ppalka at gcc dot gnu.org
   Target Milestone|---                         |14.0
         Resolution|---                         |FIXED

--- Comment #9 from Patrick Palka <ppalka at gcc dot gnu.org> ---
The original testcase should be fixed for GCC 14 after r14-6899.

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (8 preceding siblings ...)
  2024-01-03 22:36 ` ppalka at gcc dot gnu.org
@ 2024-01-03 22:50 ` ppalka at gcc dot gnu.org
  2024-01-04  8:24 ` m.cencora at gmail dot com
  10 siblings, 0 replies; 12+ messages in thread
From: ppalka at gcc dot gnu.org @ 2024-01-03 22:50 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           See Also|                            |https://gcc.gnu.org/bugzill
                   |                            |a/show_bug.cgi?id=86521

--- Comment #10 from Patrick Palka <ppalka at gcc dot gnu.org> ---
N.B. the comment #4 testcase is similar to the one from PR86521, and I based
the above analysis off of that PR..

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

* [Bug c++/113064] assignement from temporary sometimes invokes copy-assign instead of move-assign operator
  2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
                   ` (9 preceding siblings ...)
  2024-01-03 22:50 ` ppalka at gcc dot gnu.org
@ 2024-01-04  8:24 ` m.cencora at gmail dot com
  10 siblings, 0 replies; 12+ messages in thread
From: m.cencora at gmail dot com @ 2024-01-04  8:24 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113064

--- Comment #11 from m.cencora at gmail dot com ---
This is surprising to say the least because apparently for following code
(where both conversion operators return same type) the compiler somehow
correctly chooses && qualified overload:

struct bar
{
    bar() = default;

    bar(const bar&);
    bar(bar&&);

    bar& operator=(const bar&);
    bar& operator=(bar&&);
};

struct foo
{
    operator bar() const &;

    operator bar() &&;
};

void test()
{
    bar a = foo{}; // ok

    a = foo{};

    foo f;
    a = f; // ok
}

So why cannot it apply the same overload resolution, when conversion operators
return different type as in comment #4?

Do you think this qualifies for a defect report?

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

end of thread, other threads:[~2024-01-04  8:24 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-18 12:04 [Bug c++/113064] New: assignement from temporary sometimes invokes copy-assign instead of move-assign operator m.cencora at gmail dot com
2023-12-18 12:41 ` [Bug c++/113064] " m.cencora at gmail dot com
2023-12-18 13:00 ` m.cencora at gmail dot com
2023-12-18 19:58 ` mpolacek at gcc dot gnu.org
2023-12-19  9:10 ` m.cencora at gmail dot com
2023-12-19 11:39 ` m.cencora at gmail dot com
2023-12-19 15:54 ` mpolacek at gcc dot gnu.org
2024-01-03 20:43 ` cvs-commit at gcc dot gnu.org
2024-01-03 22:35 ` ppalka at gcc dot gnu.org
2024-01-03 22:36 ` ppalka at gcc dot gnu.org
2024-01-03 22:50 ` ppalka at gcc dot gnu.org
2024-01-04  8:24 ` m.cencora at gmail dot com

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).