public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/114110] New: unhelpful message about non-movable types
@ 2024-02-26 11:37 f.heckenbach@fh-soft.de
  2024-02-26 12:19 ` [Bug c++/114110] " redi at gcc dot gnu.org
  0 siblings, 1 reply; 2+ messages in thread
From: f.heckenbach@fh-soft.de @ 2024-02-26 11:37 UTC (permalink / raw)
  To: gcc-bugs

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

            Bug ID: 114110
           Summary: unhelpful message about non-movable types
           Product: gcc
           Version: 12.2.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: f.heckenbach@fh-soft.de
  Target Milestone: ---

Here's another gcc output that's less than helpful:

% cat test.cpp
#include <utility>
#include <memory>

struct S
{
  std::unique_ptr <int> a, b, c, d, e, f, g, h, i, j;
  ~S () = default;
};

int main ()
{
  S a, b = std::move (a);
}
% g++ test.cpp
test.cpp: In function 'int main()':
test.cpp:12:24: error: use of deleted function 'S::S(const S&)'
   12 |   S a, b = std::move (a);
      |                        ^
test.cpp:4:8: note: 'S::S(const S&)' is implicitly deleted because the default
definition would be ill-formed:
    4 | struct S
      |        ^
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
In file included from /usr/include/c++/12/memory:76,
                 from test.cpp:2:
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~
test.cpp:4:8: error: use of deleted function 'std::unique_ptr<_Tp,
_Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp =
std::default_delete<int>]'
    4 | struct S
      |        ^
/usr/include/c++/12/bits/unique_ptr.h:514:7: note: declared here
  514 |       unique_ptr(const unique_ptr&) = delete;
      |       ^~~~~~~~~~

gcc explains in much detail (and not very readable, cf.
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113854#c4, esp. the bottom part),
repeating the same message for each member, why the type is not copyable. Well,
I know it's not copyable, that's why I'm trying to move it. Sure, a
copy-constructor can be used when there is no useable move-constructor, but if
both copy- and move-constructor are not useable, it's quite misleading to only
explain why the copy-constructor is not useable when moving was requested in
the first place.

The actual problem here is that the move-constructor was deleted because a
destructor was declared, but gcc does not mention this at all (instead of, or
at least in addition to, why the copy-constructor was deleted).

Of course, as usual, in a vastly simplified example like this, it's easy to see
the problem, but in more complex real code with many members and class
hierarchies, it's not so obvious and the output doesn't tell me anything but
"something's wrong here".

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

* [Bug c++/114110] unhelpful message about non-movable types
  2024-02-26 11:37 [Bug c++/114110] New: unhelpful message about non-movable types f.heckenbach@fh-soft.de
@ 2024-02-26 12:19 ` redi at gcc dot gnu.org
  0 siblings, 0 replies; 2+ messages in thread
From: redi at gcc dot gnu.org @ 2024-02-26 12:19 UTC (permalink / raw)
  To: gcc-bugs

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

Jonathan Wakely <redi at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
     Ever confirmed|0                           |1
             Status|UNCONFIRMED                 |NEW
           Keywords|                            |diagnostic
   Last reconfirmed|                            |2024-02-26

--- Comment #1 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Frank Heckenbach from comment #0)
> The actual problem here is that the move-constructor was deleted because a

No, the move constructor is simply *not declared*. If it had been deleted then
it would have been selected by overload resolution as a better match and you'd
get an error telling you the move constructor was deleted.

The distinction is important (and frequently misunderstood).

> destructor was declared, but gcc does not mention this at all (instead of,
> or at least in addition to, why the copy-constructor was deleted).

That would be better.

If move construction fails, it's unlikely that the type is
copyable-but-not-movable, because such types are stupid and should not exist.
So then the question is whether it's supposed to be completely non-copyable and
non-movable, or whether the class has a bug that we can help to diagnose.

The right heuristic is probably:

If initializing T from a T rvalue is ill-formed, check whether an implicit move
constructor was suppressed. If it was, print a fix-it suggesting:

  S(S&&) = default;

Reduced:

struct MO {
  MO(MO&&) { }
};
struct S {
  ~S() = default;
  MO m;
};
S&& f();
S s = f();

This prints:

mo.cc:7:9: error: use of deleted function ‘S::S(const S&)’
    7 | S s = f();
      |         ^
mo.cc:2:8: note: ‘S::S(const S&)’ is implicitly deleted because the default
definition would be ill-formed:
    2 | struct S {
      |        ^
mo.cc:2:8: error: use of deleted function ‘constexpr MO::MO(const MO&)’
mo.cc:1:8: note: ‘constexpr MO::MO(const MO&)’ is implicitly declared as
deleted because ‘MO’ declares a move constructor or move assignment operator
    1 | struct MO {
      |        ^~


I think it would be better to print:

mo.cc:7:9: error: use of deleted function ‘S::S(const S&)’
    7 | S s = f();
      |         ^
mo.cc:5:4: note: ‘S::S(S&&)’ is not implicitly declared 'S' declares a
destructor:
    2 |   ~S() = default;
      |   ^~
mo.cc:5:4: note: add a user-declared move constructor to fix this:
   ~S() = default;
   ^
   S(S&&) = default;

mo.cc:2:8: note: ‘S::S(const S&)’ is implicitly deleted because the default
definition would be ill-formed:
    2 | struct S {
      |        ^
mo.cc:2:8: error: use of deleted function ‘constexpr MO::MO(const MO&)’
mo.cc:1:8: note: ‘constexpr MO::MO(const MO&)’ is implicitly declared as
deleted because ‘MO’ declares a move constructor or move assignment operator
    1 | struct MO {
      |        ^~


N.B. I don't think showing the locations "struct S {" and "struct MO {" for the
implicitly deleted copy constructors is useful, but I think I've said that in
another bug report somewhere.

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

end of thread, other threads:[~2024-02-26 12:19 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-02-26 11:37 [Bug c++/114110] New: unhelpful message about non-movable types f.heckenbach@fh-soft.de
2024-02-26 12:19 ` [Bug c++/114110] " redi at gcc dot gnu.org

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