Bug 116162 - GCC rejects explicitly default move ctor with const X&& parameter
Summary: GCC rejects explicitly default move ctor with const X&& parameter
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 15.0
: P3 normal
Target Milestone: ---
Assignee: Marek Polacek
URL:
Keywords: rejects-valid
Depends on:
Blocks: 119964
  Show dependency treegraph
 
Reported: 2024-07-31 17:08 UTC by Jason Liam
Modified: 2025-04-28 12:38 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2024-08-01 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jason Liam 2024-07-31 17:08:52 UTC
The following valid program is rejected by gcc, clang and msvc. Only EDG accepts it correctly. https://godbolt.org/z/c9Wf8hh16
```
struct C
{
   C(const C&&) = default; // EDG: ok, gcc: No, Clang: No
};
int main()
{
    
}
```

Gcc says:
```
<source>:3:4: error: defaulted declaration 'C::C(const C&&)' does not match the expected signature
    3 |    C(const C&&) = default;
      |    ^
<source>:3:4: note: expected signature: 'constexpr C::C(C&&)'
```
Comment 1 Jakub Jelinek 2024-07-31 17:23:14 UTC
Why do you think this is valid?
https://eel.is/c++draft/class.copy.ctor#9
says implicitly declared move constructor has
X::X(X&&)
form and
https://eel.is/c++draft/dcl.fct.def#default-2
defines rules under which the explicitly defaulted declaration can differ from the implicitly declared one.
For copy constructors there is the
https://eel.is/c++draft/dcl.fct.def#default-2.4
rule that if the implicitly declared would have const C & argument, the explicitly defaulted can have C & argument, but there is no such exception for && and it would need to be in the other direction anyway.
Comment 2 Jakub Jelinek 2024-07-31 17:49:02 UTC
Or do you mean instead of error it should be just defined as deleted perhaps with
warning per
https://eel.is/c++draft/dcl.fct.def#default-2.6

?  In that case, I think clang implements it correctly and while GCC claims to implement https://wg21.link/p0641r2 it probably doesn't implement that fully or correctly.
Comment 3 Jakub Jelinek 2024-07-31 17:52:53 UTC
CCing Marek who implemented CWG1331 aka this paper in r9-1200-g4db9ca8624e902b66
Comment 4 Jason Liam 2024-08-01 01:36:40 UTC
Yes, this is well-formed because the move ctor here is deleted as per 2.6 as explained in this thread: https://stackoverflow.com/a/78817438/12002570

In C++26, constructors have types through CWG2479 which is DRWP. In C+23 as published, ctors don't have types.
Comment 5 Jakub Jelinek 2024-08-01 13:35:43 UTC
Not really sure if this paper was a DR or not.  Seems clang rejects this for -std=c++17 and earlier and accepts with a warning for -std=c++20 and later, what we probably should do is silently make it deleted and when something attempts to use the deleted function, next to the normal diagnostics emit explanation why it was deleted.
Comment 6 Jakub Jelinek 2024-08-01 13:39:24 UTC
Albuquerque 2017 doesn't mention it was a DR when voted in, so yes, C++20 or later I guess.
Comment 7 Marek Polacek 2024-09-10 21:07:26 UTC
I have a patch implementing the clang++ behavior, that is, warning by default in C++20:

116162.C:3:4: warning: explicitly defaulted move constructor is implicitly deleted [-Wdefaulted-function-deleted]
    3 |    C(const C&&) = default; // EDG: ok, gcc: No, Clang: No
      |    ^
116162.C:3:4: note: function is implicitly deleted because its declared type does not match the type of an implicit move constructor
Comment 8 GCC Commits 2024-09-19 21:13:57 UTC
The trunk branch has been updated by Marek Polacek <mpolacek@gcc.gnu.org>:

https://gcc.gnu.org/g:a2746e4347076ea48f4aeb28e13e6337ff7799ad

commit r15-3721-ga2746e4347076ea48f4aeb28e13e6337ff7799ad
Author: Marek Polacek <polacek@redhat.com>
Date:   Mon Sep 9 14:23:33 2024 -0400

    c++: deleting explicitly-defaulted functions [PR116162]
    
    This PR points out the we're not implementing [dcl.fct.def.default]
    properly.  Consider e.g.
    
      struct C {
         C(const C&&) = default;
      };
    
    where we wrongly emit an error, but the move ctor should be just =deleted.
    According to [dcl.fct.def.default], if the type of the special member
    function differs from the type of the corresponding special member function
    that would have been implicitly declared in a way other than as allowed
    by 2.1-4, the function is defined as deleted.  There's an exception for
    assignment operators in which case the program is ill-formed.
    
    clang++ has a warning for when we delete an explicitly-defaulted function
    so this patch adds it too.
    
    When the code is ill-formed, we emit an error in all modes.  Otherwise,
    we emit a pedwarn in C++17 and a warning in C++20.
    
            PR c++/116162
    
    gcc/c-family/ChangeLog:
    
            * c.opt (Wdefaulted-function-deleted): New.
    
    gcc/cp/ChangeLog:
    
            * class.cc (check_bases_and_members): Don't set DECL_DELETED_FN here,
            leave it to defaulted_late_check.
            * cp-tree.h (maybe_delete_defaulted_fn): Declare.
            (defaulted_late_check): Add a tristate parameter.
            * method.cc (maybe_delete_defaulted_fn): New.
            (defaulted_late_check): Add a tristate parameter.  Call
            maybe_delete_defaulted_fn instead of giving an error.
    
    gcc/ChangeLog:
    
            * doc/invoke.texi: Document -Wdefaulted-function-deleted.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp0x/defaulted15.C: Add dg-warning/dg-error.
            * g++.dg/cpp0x/defaulted51.C: Likewise.
            * g++.dg/cpp0x/defaulted52.C: Likewise.
            * g++.dg/cpp0x/defaulted53.C: Likewise.
            * g++.dg/cpp0x/defaulted54.C: Likewise.
            * g++.dg/cpp0x/defaulted56.C: Likewise.
            * g++.dg/cpp0x/defaulted57.C: Likewise.
            * g++.dg/cpp0x/defaulted58.C: Likewise.
            * g++.dg/cpp0x/defaulted59.C: Likewise.
            * g++.dg/cpp0x/defaulted63.C: New test.
            * g++.dg/cpp0x/defaulted64.C: New test.
            * g++.dg/cpp0x/defaulted65.C: New test.
            * g++.dg/cpp0x/defaulted66.C: New test.
            * g++.dg/cpp0x/defaulted67.C: New test.
            * g++.dg/cpp0x/defaulted68.C: New test.
            * g++.dg/cpp0x/defaulted69.C: New test.
            * g++.dg/cpp23/defaulted1.C: New test.
Comment 9 Marek Polacek 2024-09-19 21:14:56 UTC
Should be fixed.