Search code examples
c++undefined-behaviornoreturn

Is returning from a noreturn function undefined behavior, if it returns with a call to another noreturn function?


Let's say that I'm making an application which will be long lasted, and not expected to terminate for any normal reasons (e.g. user terminates application, e.x.: an HTTP server).

I mark main itself with the C++11 standard attribute [[noreturn]], indicating that it must never return control back to the callee without aborting or throwing an exception.

[[noreturn]] int main(
    int const argc,
    char const * const[] argv
) { ... }

I start off by parsing the CLI arguments, and then pass what data was extracted into my "real" main function, where more of the work continues.

The other function, we'll call it realMain, is also marked as [[noreturn]].

In my code, I gave the main function a return statement into realMain, and promptly recieved a generic Clang warning about returning from a noreturn function.

[[noreturn]] int realMain(Data const data) {
    ...

    std::cerr << "Unreachable!" << std::endl;
    std::abort();
}

[[noreturn]] int main(...) {
    ...
    auto const parsed_cli_args = parse(argv, argc);
    ...

    return realMain(parsed_cli_args);
}

Is it safe to return into other noreturn functions, or is this undefined behavior?

(and unrelated, may any compilers take advantage of this? e.g. making main directly jmp instead of call realMain?)


Solution

  • Is it safe to return into other noreturn functions, or is this undefined behavior?

    I wonder if there's some language confusion here.

    I'm not sure if you mean if it's UB for main() to return using the hypothetical return value from realMain() which it never should get; or if it's UB for realMain() to return back to main().

    Anyway, based on the example on cppreference which Casey linked to:

    void q [[ noreturn ]] (int i) {
      // behavior is undefined if called with an argument <= 0
      if (i > 0) {
        throw "positive";
      }
    }
    

    I would understand that merely having that return realMain(); in main() is not UB. It's just UB if realMain() ever actually returns. And by that point, it doesn't matter what main() does, it's already UB since realMain() broke the "noreturn" promise first.

    But since neither is supposed to ever return, I don't understand why you'd write the return in any case, instead of e.g. just leaving the call to realMain() as the last statement of main(), without a return.