C++ Logo

sg15

Advanced search

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

From: Herb Sutter <herb.sutter_at_[hidden]>
Date: Fri, 17 Oct 2025 07:23:09 -0700
> Today's

> void fun(Foo* ptr) {

> my_supper_assert_macro (ptr!=nullpter);

> my_supper_assert_macro(ptr->hasData());

> }

> should not have any problems, ever

 

No, that’s not my understanding: Any assertion facility that offers log-and-continue (or similar “observe”) semantics will have the same issue.

 

Many assertion libraries do offer that, don’t they? I did a quick Google search (errors are mine):

* It seems that BOOST_CHECK and BOOST_TEST both continue, and so would have the same issue with this example.
* Various assertion frameworks support a custom violation handler that could return instead of terminate/abort. If they return and do not abort, those would have the same issue again.

 

It’s true that C assert() specifically doesn’t have that problem, but that’s only because it’s not powerful enough – it only offers “ignore” and “quick_enforce.” We do need observe semantics (we’ve already added it in our assertion libraries when C assert wasn’t enough), and as far as I can see any assertion facility with that feature has to have this characteristic for this example. So it seems to me this is a feature, not a bug.

 

 

 

 

From: Harald Achitz <harald_at_[hidden]>
Sent: Friday, October 17, 2025 6:54 AM
To: Herb Sutter <herb.sutter_at_[hidden]>; sg15_at_[hidden]; 'Ville Voutilainen' <ville.voutilainen_at_[hidden]>; sg21_at_[hidden]
Cc: 'Tom Honermann' <tom_at_[hidden]>
Subject: Re: [isocpp-sg15] [isocpp-sg21] P3835 -- Different contract checking for different libraries

 

 

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:

1. the first check fails, and gives a contract violation so you know it was violated
2. 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

 

 


Received on 2025-10-17 14:23:12