6

I use noexcept heavily, and unfortunately if some transitive dependency ends up throwing in a rare case (unbeknownst to us), the crashes are extremely hard to debug - because noexcept causes std::terminate to get called.

Is there any way to detect these violations at compile time? In the example below the problem is obvious, but no compiler catches it by default. I realize it's not possible in all cases, but surely simpler cases should be possible

#include <stdexcept>

void baz()
{
    throw std::runtime_error("std::terminate awaits");
}

void bar()
{
    baz();
}

void foo() noexcept
{
    bar();
}

Link to godbolt: https://godbolt.org/z/Ooet58

Are there compiler flags I don't know about? What about a static analysis tool that catches this?

Jesper Juhl
  • 30,449
  • 3
  • 47
  • 70
Alexander Kondratskiy
  • 4,156
  • 2
  • 30
  • 51
  • 1
    Analyzing deep function calls for this seems intractable. Warning about simply using a `noexcept(false)` function seems too annoying. – StoryTeller - Unslander Monica Oct 19 '18 at 15:08
  • 2
    I've learned to omit the `noexcept` tag unless I know for sure that my routine cannot throw. Which usually means I only tag leaf function that do not call anything. That being said, operator overloading slipped in a hard-to-spot function call I hadn't noticed... hello std::terminate. sigh – Eljay Oct 19 '18 at 15:17
  • I agree about "dont tag unless you know for sure", but code changes. Something that used to be a leaf function, gets refactored by someone else, which calls another function that throws. Or, you call a user-provided callback in a noexcept function (e.g. you're writing an event loop), and that throws – Alexander Kondratskiy Oct 19 '18 at 15:27
  • How is the debugging extremely hard? Register your own terminate handler and put a break point there. Or if you're running a daemon that you cannot debug directly, print a stack trace or even better: create a core dump that can be debugged locally. – eerorika Oct 19 '18 at 15:27
  • Within a single compilation unit, this may be tractable, although I don't know of a tool which does it. But more philosophically, how should this be detected when calling functions from shared/dynamic libraries? – André Oct 19 '18 at 15:28
  • @André Agree regarding shared/dynamic libs. I'm not asking for a panacea :) Even single translation unit could be helpful. – Alexander Kondratskiy Oct 19 '18 at 15:30
  • @user2079303 Not saying it's "extremely hard", it's just a bigger pain in the ass than catching this at compile time, or having an actual try/catch handler. I've seen situations where there is a try/catch, but some function in the middle of a callchain was mistakenly marked noexcept, so the exception doesn't get caught. For most C++ keywords there are compile-time guarantees. Imagine if misusing `const` resulted in a runtime `std::terminate`? – Alexander Kondratskiy Oct 19 '18 at 15:32
  • @user2079303 Yes, this does look like a duplicate! – Alexander Kondratskiy Oct 19 '18 at 15:33
  • @AlexanderKondratskiy yeah, run-time checked const would not be nice. But imagine if noexcept had same limitation that you couldn't call non-noexcept functions from noexcept functions. You'd never end up in `std::terminate`, but you also would need a massive update to make all existing codebases "noexcept-correct" in order to make any use of it. (and I imagine you'd need a noexcept_cast analogous to const_cast for some rare cases). – eerorika Oct 19 '18 at 15:36

1 Answers1

0

Check out Microsoft’s static analysis tool, it can check for exactly this violation: Don’t throw in noexcept

Edit: that tool can also tell you which functions can be marked as noexcept which should make it easier to narrow down the scope of throwing functions significantly.

darxsys
  • 1,560
  • 4
  • 20
  • 34