C++ Logo

sg15

Advanced search

Re: [isocpp-sg15] [isocpp-sg21] P3835 -- Different contract checking for different libraries

From: Joshua Berne <berne_at_[hidden]>
Date: Fri, 17 Oct 2025 10:00:38 -0400
If "my_supper_assert_macro" supports the idea of checking and continuing
--- i.e., observing whether there has been a violation but not otherwise
altering program behavior --- then your program using your macro in that
configuration will have exactly the same behavior as contracts assertions
when they are observed.

If your macros support any level of configurability at fine granularity --
whether in source or through other configuration, which many assertion
facilities in the wild do do --- you have exactly the same issue if you
ever choose to observe or ignore the first assertion and check the second
one.

Of course, that choice to observe or ignore these assertions should be
based on believing that the program is correct but seeking to identify
whether it is just seeming to be correct when not working. The ability to
do that, and to make that decision when BUILDING and DEPLOYING a program
and not when writing it, is what Contracts are offering. If you were
calling `fun(nullptr)` before but it happened to not be observably bad you
weren't in a good place --- observing the contracts will log a violation,
let you know clearly that you have a problem, and then put you back in an
otherwise equivalent place to where you were before you enabled contracts.

Call that expert-level, call that risky, but we've also repeatedly
demonstrated that multiple different groups that have actually deployed
contracts at scale in real scenarios have had the need to be able to
observe contract assertions in exactly this way.

On Fri, Oct 17, 2025 at 9:53 AM Harald Achitz via SG21 <
sg21_at_[hidden]> wrote:

>
> On 2025-10-17 15:28, Herb Sutter wrote:
>
> Harald asked:
>
> > On the internet I saw someone saying
>
> >
>
> > void fun(Foo* ptr) pre (ptr!=nullpter), pre(ptr->hasData()) { ... }
>
> >
>
> > might be a problem (for the second pre) and should be written like this
>
> >
>
> > void fun(Foo* ptr) pre (ptr!=nullpter && ptr->hasData()){ ... }
>
> >
>
> > is that true?
>
>
>
> With ignore, enforce, or quick_enforce there’s no issue. The second check
> will not be evaluated, no UB.
>
>
>
> With observe semantics (only), if the pointer is null then:
>
> - the first check fails, and gives a contract violation so you know it
> was violated
> - the second check then gives UB (no time travel, does not affect the
> first check that already happened)
>
>
>
> IMO the issue isn’t about contracts, it’s about short-circuit evaluation.
> This kind of issue can come up generally in the language if you split the
> (p) and (p->foo()) conditions, so we already teach that to get
> short-circuit evaluation you need to write the && using (p && p->foo()).
>
>
>
> This also comes up (again, generally throughout the language) with other
> examples like
>
>
>
> (is_sorted(v) && binary_search(v, x))
>
>
>
> because implementations of binary_search might try to perform unsafe
> out-of-bounds access if the range is not already sorted.
>
>
>
> In case it's helpful, here's the current slide I teach about that example
> (updated and extended since CppCon):
>
>
>
>
>
> Herb
>
>
>
>
> Thank you.
>
> Today's
>
> void fun(Foo* ptr) {
> my_supper_assert_macro (ptr!=nullpter);
> my_supper_assert_macro(ptr->hasData());
> }
>
> should not have any problems, ever
>
> so replacing this blindly against
>
> void fun(Foo* ptr) {
> contract_assert (ptr!=nullpter);
> contract_assert(ptr->hasData());
> }
>
> does not work, as I understand, since observe could be a problem?
>
> (I know now that when using pre I have to re-write with &&)
>
> /Harald
>
>
>
> _______________________________________________
> SG21 mailing list
> SG21_at_[hidden]
> Subscription: https://lists.isocpp.org/mailman/listinfo.cgi/sg21
> Link to this post: http://lists.isocpp.org/sg21/2025/10/11341.php
>

Received on 2025-10-17 14:00:56