Bug 106812 - Throwing a non-copyable exception
Summary: Throwing a non-copyable exception
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 12.2.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: accepts-invalid
Depends on:
Blocks:
 
Reported: 2022-09-02 07:14 UTC by Jens Maurer
Modified: 2022-10-22 00:27 UTC (History)
3 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2022-09-19 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jens Maurer 2022-09-02 07:14:15 UTC
gcc accepts the following program:

struct S
{
  S() = default;
  S(const S&) = delete;
  // int x = 0;  // #3
};

int main()
{
  try {
    throw S();         // #1
  } catch (S s) {      // #2
    return 1;
  }
}

but it is ill-formed at #1 according to [except.throw] p5.

Curiously, if line #3 is added, gcc flags an error at #2 (but not at #1).
Comment 1 Jens Maurer 2022-09-02 07:21:52 UTC
Cross-reference: clang bug https://github.com/llvm/llvm-project/issues/57519
Comment 2 Marek Polacek 2022-09-19 18:53:43 UTC
Confirmed.
Comment 3 Marek Polacek 2022-09-19 19:01:35 UTC
The difference between #3 and not-#3 is that without the NSDMI, S isn't TYPE_NEEDS_CONSTRUCTING, which makes a difference in initialize_handler_parm:

 339   /* If the constructor for the catch parm exits via an exception, we
 340      must call terminate.  See eh23.C.  */
 341   if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (decl)))
 342     {
 343       /* Generate the copy constructor call directly so we can wrap it.
 344          See also expand_default_init.  */
 345       init = ocp_convert (TREE_TYPE (decl), init,
 346                           CONV_IMPLICIT|CONV_FORCE_TEMP, 0,
 347                           tf_warning_or_error);
Comment 4 Charles-Henri Gros 2022-10-14 18:18:35 UTC
It's worse than this, "throw" will even move a non-movable value in the absence of a copy constructor:

#include <iostream>
#include <memory>

using namespace std;

int main(int argc, char const * const *argv)
{
    unique_ptr<int> u(new int);
    cout << "u.get() first: " << (void*)u.get() << endl;
    try {
        throw u;
    } catch(...) {
    }
    cout << "u.get() after throw: " << (void*)u.get() << endl;
}