https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61372
Bug ID: 61372 Summary: Add warning to detect noexcept functions that might throw Product: gcc Version: 4.9.0 Status: UNCONFIRMED Severity: enhancement Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: scovich at gmail dot com The C++11 standard adds the "noexcept" specification that lets the programmer assert that a function does not throw any exceptions (terminating execution if that assertion ever turns out to be false at runtime). Unfortunately, there is currently no reliable way for a programmer to validate, at compile time, her assertion that a function does or does not throw. The closest thing is -Wnoexcept, which detects the (very narrow) case where the following all apply to some function F: 1. F lacks the noexcept declaration or has declared noexcept(false) 2. The compiler has determined that F cannot throw 3. F causes some noexcept operator to evaluate to false Unfortunately, that narrow formulation makes it really hard to validate much of anything (see example and further discussion below). It would be very helpful to have a warning flag which tells the compiler to report cases where a function's noexcept specification contradicts the compiler's analysis of the function body. Perhaps -Wnoexcept-mismatch={1,2,3}? 1 (high priority): functions declared noexcept(true) but which contain expressions that might throw. This validates stated noexcept assumptions, helping to avoid issues like PR #56166. 2 (medium priority): Also report functions declared noexcept(false) that in fact cannot throw (e.g. cases #1 and #2 for -Wnoexcept). This improves the accuracy of noexcept validation, and also improves performance in general (by eliminating unwind handlers). And makes it easier to avoid/fix things like PR #52562. 3 (low priority): Also report functions which lack any noexcept declaration but which cannot throw (similar to -Wsuggest-attribute for const, pure, etc.). This also improves accuracy of noexcept, but the programmer would have to decide whether to make the API change (marking the function noexcept) or whether it's important to retain the ability to throw in the future. Probably none of the above warnings should be enabled by default, but it might make sense to enable -Wnoexcept-mismatch=1 with -Wall and -Wnoexcept-mismatch=2 with -Wextra. To implement the warning, the compiler would make a pass over each function body (after applying most optimizations, especially inlining and dead code elimination). It would then infer a noexcept value by examining all function calls that remain, and compare that result with the function's actual noexcept specification (or lack thereof). No need for any kind of IPA: if a callee lies about its noexcept status, it's the callee's problem. =========================== Workaround using -Wnoexcept =========================== One might try to combine static_assert with noexcept, e.g: // example.cpp //////////////// void might_throw(int); // lacks noexcept void also_might_throw(); // lacks noexcept void never_throw(int a) noexcept(noexcept(might_throw(a)) && noexcept(also_might_throw())) { if (a) might_throw(a); also_might_throw(); } void foo(int a) noexcept(noexcept(might_throw(a))) { might_throw(a); } static_assert(noexcept(foo(0)), "never_throw might throw"); //////////////////////////////// There are two glaring problems with that approach, however: 1. Every expression in the function body must be part of the noexcept clause, effectively replicating the function body in its signature (but without the ability to declare local variables). - Maintaining the noexcept chain across code changes would be tedious and error-prone for all but the smallest and most stable functions (= ie the ones least in need of verification). - Operator overloading means you can't even assume basic expressions like a+b are nothrow. To get complete coverage would require either a very careful analysis (error prone) or cracking the entire function body into an AST atomic expressions (tedious *and* error prone). - Macro expansions would add even more headaches, because they may expand to more than one statement and/or include control flow. 2. The static_assert must choose one set of inputs for each function call it passes to operator noexcept. - An optimizer (especially after inlining and constant propagation) could conceivably report that the function is noexcept for that particular input, when in fact other inputs exist that could cause an exception to be thrown (this does not seem to be the case currently). - There may not be any easy way to come up with a valid input (objects that lack a default constructor, etc.). Using hacks like (*(T*)0) would violate all sorts of compiler/optimizer assumptions and risks breaking the analysis. Another possibility would be to embed multiple static_assert in the function to verify noexcept status for every line of code. That would at least allow to use existing local variables, and resolve most of issue #2, but issue #1 remains. Code readability would take quite a hit, too.