C++ Logo

std-proposals

Advanced search

Re: [std-proposals] fine-control exception/error/UB handling on function boundary

From: Marcin Jaczewski <marcinjaczewski86_at_[hidden]>
Date: Fri, 28 Aug 2026 12:15:51 +0200
pt., 28 sie 2026 o 10:38 Tiago Freire via Std-Proposals
<std-proposals_at_[hidden]> napisał(a):
>
> Well but why?
> Because there are generally 2 camps.
> 1. People who don't use exceptions because they are fundamentally broken.
> 2. People who use exceptions because they don't care.
>
> There is however a minority third camp.
> I don't like exceptions, but am forced to use them to work, because you don't work by yourself and codebases exists before I worked there.
> And I would kill to be able to specify a list of possible exceptions at the interference level to be able to validate the code is exception safe.
>

There will soon be tools that will answer this based on exception
tables and do not need source code at all.

Besides you show bias to exceptions as there can be forth cases where
using exceptions as its right and best tool to solve given problems.
See work of Khalil Estell.

> I care if my code is safe, but we are in a minority that thinks there is a problem that needs to be solved, but even if you do exceptions are still a dumpster fire and you would never use it if you could so why bother?
>
>
>
>
>
> ________________________________
> From: Std-Proposals <std-proposals-bounces_at_[hidden]> on behalf of Simon Schröder via Std-Proposals <std-proposals_at_[hidden]>
> Sent: Thursday, August 27, 2026 8:18:27 PM
> To: std-proposals_at_[hidden] <std-proposals_at_[hidden]>
> Cc: Simon Schröder <dr.simon.schroeder_at_[hidden]>; Zamfir Yonchev <zamfir.yonchev_at_[hidden]>; std-proposals_at_[hidden] <std-proposals_at_[hidden]>
> Subject: Re: [std-proposals] fine-control exception/error/UB handling on function boundary
>
> Concerning exceptions: We used to have dynamic exception specifications. This got deprecated in C++11 and removed in C++17. Instead, you can now use noexcept (just say if there are any exceptions at all). With the old throw() specification you could explicitly list all the exceptions that a function could throw. Nobody used it. So, why would you want to introduce it again (and even extend it)? In theory it sounds nice, but it was never used and thus removed. You need to make a very good point to reintroduce this.
>
> On Aug 27, 2026, at 6:30 PM, Zamfir Yonchev via Std-Proposals <std-proposals_at_[hidden]> wrote:
>
> 
> Hi all,
>
> This is not a proposal with any actual code examples but just an illustration of a general concept.
>
> The problem
> As we know C++ has problems with errors: sometimes errors are just ignored and documented as UB (integer division by 0 for example), sometimes they are detected and thrown as exceptions but no one is prepared to catch them (std::bad_alloc for example) and sometimes the behaviour is defined but is weird and unexpected (unsigned integer underflow for example).
> And sometimes error checking is not even desirable because it impedes performance.
> In summary, error cases in C++ are sometimes important to the programmer but ignored by the compiler and sometimes unimportant to the programmer but eagerly checked at runtime.
>
> The compiler safeguards can usually be turned off at the translation unit level, for example by compiling with -fno-exceptions or with NDEBUG to disable C assertions.
> And also sometimes, the standard library (or any other library) may provide opt-out implementations to avoid unwanted checks (like with using vec[index] instead of vec.at(index) for example).
> But these are case-by-case fixes for a more general problem.
>
> My proposed solution
> My proposal is to provide syntax and compiler support for annotating function signatures with a list of important errors and a list of unwanted errors that can occur in the body of the function.
>
> Why on the function level?
> Functions already form the public API of each program component and error reporting is best done on the API boundaries. This is already true for exceptions (a function can be noexcept or potentially throwing). Same for std::expected - the error is part of the API.
> Error-checking annotation on the statement level or on the expression level is too verbose and on the translation-unit level is too general and not precise enough.
> So, I think functions are the sweet spot.
>
> Errors can be split into 3 categories in terms of how they are handled by the proposed mechanism:
> - implicitly ignored (no checking is done by default);
> - explicitly ignored (checking is done by default but can be opt-out)
> - always checked.
>
> In the "implicitly ignored" category fall all errors that lead to UB (out-of-bounds array access, null-pointer dereferencing, signed integer overflow/underflow, to name a few common examples) and also errors that are well defined but can lead to unexpected behaviour but still, the compiler doesn't check them by default (for example unsigned integer underflow, implicit conversions that cause loss of precision, loss of signedness, etc.)
> In the "explicitly ignored" category fall all errors that are rare enough to ignore them but the compiler still inserts error checking (out-of-memory exceptions, std::bad_optional_access, std::bad_any_cast, std::bad_variant_access, etc.)
> In the "always checked" category fall the rest of the errors that should never be ignored. Instead they should be handled (with a try-catch or by std::expected::value_or, etc.)
> It needs to be decided where failures from contracts fall into these categories (probably in the "always checked" category just to be safe).
>
> Then a function declaration can be annotated with these two lists:
> - a list of errors that the function will look out for and report if it encounters them during execution (called "checked list" for short);
> - a list of errors that the function will silently ignore even if it is theoretically possible to encounter them during execution (called "ignore list" for short).
>
> The checked list can contain any error type (including exception types and specific error types which represent the low-level errors (integer_division_by_0, integer_overflow, loss_of_precision, implicit_conversion_from_signed_to_unsigned, etc.)
> The ignore list can only contain errors that are of the "implicitly ignored" or the "explicitly ignored" categories.
> Both lists are optional, so you can have checked and ignored errors, only checked errors, only ignored errors or no error annotations.
>
> The mechanism is the following:
> - if a function is not annotated with any of these lists it is not affected by the mechanism (can be overridden by a compiler flag that forces all functions in a translation unit to be checked)
> - otherwise:
> - if the compiler encounters an operation that can result in an "implicitly ignored" error and the error type exists in the checked list, then additional checks for this operation are added (for example if std::null_pointer_dereference_access is explicitly checked, then `*ptr` becomes `__checked(ptr)` where __checked(ptr) is { if(ptr != null) return *ptr; else throw std::null_pointer_dereference_access{}; } )
> - if the compiler encounters an operation that can result in an "explicitly ignored" error and the error type exists in the ignore list, the checks for this potential error are removed (for example if std::bad_variant_access is ignored then `std::get<T>(variant)` becomes a simple cast);
> - if the compiler encounters an operation that can result in an "explicitly ignored" error or an "always checked" error and the error type doesn't exist in the checked list, the program is ill-formed and diagnostics is provided;
> - in all other cases the compilation continues normally.
>
> Note, that:
> - the ignore list is the way to opt-out of unwanted checks enforced by the compiler otherwise;
> - the checked list is for opt-in checks for previously unchecked behaviour as well as for clarity (what errors does this function allow to propagate to the callers);
> - the checked list can contain error types that the function cannot really produce. This is to allow forwards-compatibility and more ergonomic generic code.
>
> Main benefits of this approach
> 1. It's opt-in, so backwards compatible with existing code. No behaviour or performance change of the program if the feature is not used.
>
> 2. You can reduce UB by raising the safety checks and explicitly telling the compiler to check cases that were previously unchecked.
> Imagine if you can tell the function to do null-pointer dereference checks.
> Or to do implicit conversion checks.
> Or any kind of sanity checks that were previously only possible by external tools or compiler-specific flags.
>
> 3. You can improve performance by eliminating error checks that the compiler would otherwise do.
> For example, you can tell the compiler to ignore std::bad_alloc exceptions and then a lot of code could be declared noexcept because allocations in these places are now error-free (including copy-constructors, move-constructors, default-constructors, etc. which could have a cascading effect)
> And if you tell the compiler which errors and exceptions you'd like to ignore then it can prove that certain function calls will never throw and the exception-handling scaffolding can be optimized away (both inside the function implementation and outside in the function call).
>
> 4. Better function APIs with cleaner exception interface.
> If a function throws an exception of a type that is not listed in the checked list this will be caught at compile-time.
> This forces the author of the function to be honest about the kinds of errors it can throw.
>
> 5. It's good for documentation.
> Instead of going through the documentation to see what exceptions a library function can throw you can just check the declaration.
>
> 6. Good for code reviews and debugging.
> You can "grep" for all the places where you explicitly ignore certain checks to see if this could be a source of problems.
> Of course, this is not an exhaustive search because functions could still fail or cause UB without any such annotations. But it's a good start.
>
> 7. This allows for highly-customizable safety profiles.
> For example: "checking for out-of-bounds access is critical but I don't care about malloc failures"
> or: "memory is limited, so memory allocations should be checked but implicit conversions are part of the design, so no need to check them",
> etc.
> And these safety profiles can be on a function basis.
> Hot code can have a high-safety profile during testing and low-safety profile during production.
> Cold code can remain with a high-safety profile.
>
> Some more details
> There's no change on the function ABI (unless the compiler can guarantee that ABI won't break).
>
> The compiler could also decide to have two versions of a function: one that can fail and one that can't and choose which one to use depending on what errors are allowed or ignored on the call site.
>
> Additionally, the compiler could provide a flag, which checks if the functions are correctly annotated with all the errors they can produce (except for the implicitly ignored errors) and print warnings for the non-compliant ones.
>
> Ignoring errors can propagate to lower branches of the call tree. If function A calls B and it in turn calls C, if you ignore a certain type of error in A this could result in optimizing the call to C.
>
> The checked and ignore lists could be generated at compile-time using template metaprogramming, reflection or some other compile-time mechanism, so they don't have to be hand-written.
>
> Summary
> This proposal provides the following benefits:
> - low-level optimizations for hot code;
> - mitigation mechanisms for UB and unexpected behaviour;
> - improved function API and exception interface;
> - opt-in in nature for gradual adoption;
> - full interoperability with existing features.
>
> The only downside is there's no real-world implementation to test the feature, yet.
>
> Let me know your thoughts.
>
> Best regards.
> --
> Zamfir Yonchev
> Verification engineer at Broadcom Inc.
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2026-08-28 10:16:09