On Tue, Sep 09, 2025 at 03:30:07PM +0200, Michael Matz wrote: > But it would also be an invalid transformation of the compiler. So I > can't quite see the worry. The second check ("*p > 5") is conditional on > the first one: > > if (!p) > fail() > if (*p > 5) > fail2() > > The *p access would only be unconditional if fail() can fall-through. > > Two cases: > (a) the spec of contracts says that later contracts may depend on earlier > contracts to have succeeded (dependend contracts): the fail() must not > fall through, that would be an invalid implementation of contracts, > and the compiler removing the first !p check would be buggy > (b) the spec of contracts say that all of them are independend: then > fail() can fall through and the compiler may indeed remove the !p > check (if fail() itself doesn't have any interesting side-effects). > But then the author who wrote those two contracts independendly, where > in reality one depends on the other, was wrong. > > In both cases I don't see how an observably_checkpoint facility would > help. > > So, again, a precise spec of the supposed semantics of the builtin are > required. Up to now it seems fairly fuzzy and all cases I've seen in this > thread are trying to work around clear compiler or user bugs. > > So, what is or isn't guaranteed by the builtin?
I think some pedantic readings of the standards suggest that if a program invokes UB, it is invalid as whole and can do anything even before the point of the UB. Which I think is true, but the compiler can't assume code invokes UB if it calls something it doesn't know anything about (because it could loop forever or exit or throw or setjmp out of the UB invoking function etc. and so never actually invoke the UB) before the UB is encountered. So any movement of UB across such points is a compiler bug, while moving it across some statements which certainly won't result in the later UB not being invoked should be fine. Jakub