Search code examples
c++noreturn

Call to a [[noreturn]] function and order of destruction


Considering following code, I or, rather the developer of original code, would expect that function-local object would be destroyed before the call to [[noreturn]] fubar() function.

#include <iostream>
#include <iostream>

using std::cout;
using std::endl;

class Static
{
public:
    Static() { cout << "Static::Static" << endl; }
    ~Static() { cout << "Static::~" << endl; }
};

class Automatic
{
public:
    Automatic() { cout << "Automatic::Automatic" << endl; }
    ~Automatic() { cout << "Automatic::~" << endl; }
};

[[noreturn]] void fubar() {
    cout << "It's FUBAR" << endl;
    throw 42;
}

void foo() {
    Automatic a;
    static Static b; 
    
    fubar();
    cout << "It's fine." << endl;
}

int main()
{
    try {
        foo();
    }
    catch(int a)
    {
        cout << "main() was FUBARed. Answer is " << a << endl;
    }
    return 0;
}

Apparently that's not true. E.g. output after compiling with GCC 10 shows that Automatic was destroyed after fubar was entered, but it happens before exception was caught. It's exactly same behaviour as if fubar() wasn't noreturn.

Automatic::Automatic
Static::Static
It's FUBAR
Automatic::~
main() was FUBARed. Answer is 42
Static::~

Is that a defined behaviour? It is a result of stack unwinding for exception handling. Further investigation shows that exit(0); (which is declared as [[noreturn]] itself).

[[noreturn]] void fubar() {
    cout << "It's FUBAR" << endl;
    exit(0);
}

result to incomplete destruction of automatics:

Automatic::Automatic
Static::Static
It's FUBAR
Static::~

Does this actually mean that calls to standard functions declared as [[noreturn]] is a big "no" for an OS that doesn't do clean-up after process execution and that the use of local variables to initialize any OS-wide resources is not safe if any such call is possible (no proper deinitialization will happen) or I'm finding a flaw?


Solution

  • It's exactly same behaviour as if fubar() wasn't noreturn.

    As expected; [[noreturn]] is a red herring here, as the fubar() does not return (and thus fulfills the requirement that the attribute is intended to signal).

    From [dcl.attr.noreturn]/2 and [dcl.attr.noreturn]/3 [emphasis mine]:

    /2 If a function f is called where f was previously declared with the noreturn attribute and f eventually returns, the behavior is undefined. [ Note: The function may terminate by throwing an exception. — end note ]

    /3 Recommended practice: Implementations should issue a warning if a function marked [[noreturn]] might return.

    the [[noreturn]] attribute is only essential for functions that do return in some execution path (which is an error that a compiler may help flagging); namely that a function annotated as [[noreturn]] that actually does return results in undefined behaviour, and compilers are recommended to mark programs which contain returning program paths for [[noreturn]] annotated functions with a warning.